Merge "Remove use of INSTALL_DISABLE_VERIFICATION for staged install"
diff --git a/Android.bp b/Android.bp
index 4121b8a..f3225e2 100644
--- a/Android.bp
+++ b/Android.bp
@@ -330,6 +330,7 @@
         ":gatekeeper_aidl",
         ":gsiservice_aidl",
         ":incidentcompanion_aidl",
+        ":inputconstants_aidl",
         ":installd_aidl",
         ":keystore_aidl",
         ":libaudioclient_aidl",
@@ -373,7 +374,7 @@
 java_library {
     name: "framework-updatable-stubs-module_libs_api",
     static_libs: [
-        "framework-appsearch-stubs", // TODO: Update to module_libs_api when there is one.
+        "framework-appsearch.stubs.module_lib",
         "framework-graphics.stubs.module_lib",
         "framework-media.stubs.module_lib",
         "framework-mediaprovider.stubs.module_lib",
@@ -392,7 +393,7 @@
     installable: false,
     static_libs: [
         "framework-minus-apex",
-        "framework-appsearch",
+        "framework-appsearch.impl",
         "framework-graphics.impl",
         "framework-mediaprovider.impl",
         "framework-permission.impl",
@@ -465,6 +466,7 @@
         "android.hardware.cas-V1.2-java",
         "android.hardware.contexthub-V1.0-java",
         "android.hardware.contexthub-V1.1-java",
+        "android.hardware.contexthub-V1.2-java",
         "android.hardware.gnss-V1.0-java",
         "android.hardware.gnss-V2.1-java",
         "android.hardware.health-V1.0-java-constants",
@@ -481,6 +483,7 @@
         "android.hardware.thermal-V2.0-java",
         "android.hardware.tv.input-V1.0-java-constants",
         "android.hardware.tv.tuner-V1.0-java-constants",
+        "android.hardware.tv.tuner-V1.1-java-constants",
         "android.hardware.usb-V1.0-java-constants",
         "android.hardware.usb-V1.1-java-constants",
         "android.hardware.usb-V1.2-java-constants",
@@ -537,8 +540,8 @@
     path: "core/java",
 }
 
-java_library {
-    name: "framework-minus-apex",
+java_defaults {
+    name: "framework-minus-apex-defaults",
     defaults: ["framework-aidl-export-defaults"],
     srcs: [
         ":framework-non-updatable-sources",
@@ -554,7 +557,6 @@
         "--core-library",
         "--multi-dex",
     ],
-    installable: true,
     jarjar_rules: ":framework-jarjar-rules",
     javac_shard_size: 150,
     plugins: [
@@ -587,6 +589,12 @@
         "mediatranscoding_aidl_interface-java",
         "soundtrigger_middleware-aidl-java",
     ],
+}
+
+java_library {
+    name: "framework-minus-apex",
+    defaults: ["framework-minus-apex-defaults"],
+    installable: true,
     // For backwards compatibility.
     stem: "framework",
     apex_available: ["//apex_available:platform"],
@@ -600,12 +608,19 @@
     ],
     errorprone: {
         javacflags: [
+            "-Xep:AndroidFrameworkBinderIdentity:ERROR",
             "-Xep:AndroidFrameworkCompatChange:ERROR",
             "-Xep:AndroidFrameworkUid:ERROR",
         ],
     },
 }
 
+java_library {
+    name: "framework-minus-apex-intdefs",
+    defaults: ["framework-minus-apex-defaults"],
+    plugins: ["intdef-annotation-processor"],
+}
+
 // This "framework" module is NOT installed to the device. It's
 // "framework-minus-apex" that gets installed to the device. Note that
 // the filename is still framework.jar (via the stem property) for
@@ -622,8 +637,7 @@
     static_libs: [
         "app-compat-annotations",
         "framework-minus-apex",
-        // TODO(b/146218515): should be removed
-        "framework-appsearch",
+        "framework-appsearch.impl", // TODO(b/146218515): should be removed
         "framework-updatable-stubs-module_libs_api",
     ],
     sdk_version: "core_platform",
@@ -795,7 +809,6 @@
     name: "framework-services-net-module-wifi-shared-srcs",
     srcs: [
         "core/java/android/net/DhcpResults.java",
-        "core/java/android/net/util/IpUtils.java",
         "core/java/android/util/LocalLog.java",
     ],
 }
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index f66d12a..cdf5df6c 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -9,6 +9,7 @@
                cmds/uinput/
                core/jni/
                libs/input/
+               native/
                services/core/jni/
                services/incremental/
                tests/
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 53053ce..9604466 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -70,6 +70,7 @@
         "android.hardware.thermal-V2.0-java",
         "android.hardware.tv.input-V1.0-java-constants",
         "android.hardware.tv.tuner-V1.0-java-constants",
+        "android.hardware.tv.tuner-V1.1-java-constants",
         "android.hardware.usb-V1.0-java-constants",
         "android.hardware.usb-V1.1-java-constants",
         "android.hardware.usb.gadget-V1.0-java",
@@ -121,7 +122,6 @@
 droidstubs {
     name: "api-stubs-docs",
     defaults: ["metalava-full-api-stubs-default"],
-    removed_dex_api_filename: "removed-dex.txt",
     arg_files: [
         "core/res/AndroidManifest.xml",
     ],
@@ -142,12 +142,6 @@
             baseline_file: "api/lint-baseline.txt",
         },
     },
-    dist: {
-        targets: ["sdk", "win_sdk"],
-        dir: "apistubs/android/public/api",
-        dest: "android.txt",
-    },
-    jdiff_enabled: true,
 }
 
 droidstubs {
@@ -188,7 +182,6 @@
 droidstubs {
     name: "system-api-stubs-docs",
     defaults: ["metalava-full-api-stubs-default"],
-    removed_dex_api_filename: "system-removed-dex.txt",
     arg_files: [
         "core/res/AndroidManifest.xml",
     ],
@@ -209,12 +202,6 @@
             baseline_file: "api/system-lint-baseline.txt",
         },
     },
-    dist: {
-        targets: ["sdk", "win_sdk"],
-        dir: "apistubs/android/system/api",
-        dest: "android.txt",
-    },
-    jdiff_enabled: true,
 }
 
 droidstubs {
@@ -242,11 +229,15 @@
 
 droidstubs {
     name: "test-api-stubs-docs",
-    defaults: ["metalava-full-api-stubs-default"],
+    defaults: ["metalava-non-updatable-api-stubs-default"],
     arg_files: [
         "core/res/AndroidManifest.xml",
     ],
-    args: metalava_framework_docs_args + " --show-annotation android.annotation.TestApi",
+    args: metalava_framework_docs_args
+        + " --show-annotation android.annotation.TestApi"
+        + " --show-for-stub-purposes-annotation android.annotation.SystemApi\\("
+        +     "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS"
+        + "\\)",
     check_api: {
         current: {
             api_file: "api/test-current.txt",
@@ -294,11 +285,6 @@
             baseline_file: "api/module-lib-lint-baseline.txt",
         },
     },
-    dist: {
-        targets: ["sdk", "win_sdk"],
-        dir: "apistubs/android/module-lib/api",
-        dest: "android.txt",
-    },
 }
 
 droidstubs {
@@ -311,6 +297,10 @@
             api_file: "non-updatable-api/module-lib-current.txt",
             removed_api_file: "non-updatable-api/module-lib-removed.txt",
         },
+        last_released: {
+            api_file: ":android-non-updatable.api.module-lib.latest",
+            removed_api_file: ":android-non-updatable-removed.api.module-lib.latest",
+        },
         api_lint: {
             enabled: true,
             new_since: ":android-non-updatable.api.module-lib.latest",
@@ -363,6 +353,7 @@
     srcs: [ ":api-stubs-docs-non-updatable" ],
     static_libs: [
         "conscrypt.module.public.api.stubs",
+        "framework-appsearch.stubs",
         "framework-graphics.stubs",
         "framework-media.stubs",
         "framework-mediaprovider.stubs",
@@ -408,7 +399,7 @@
     srcs: [ ":system-api-stubs-docs-non-updatable" ],
     static_libs: [
         "conscrypt.module.public.api.stubs",
-        "framework-appsearch-stubs", // TODO: standardize appsearch stubs
+        "framework-appsearch.stubs.system",
         "framework-graphics.stubs.system",
         "framework-media.stubs.system",
         "framework-mediaprovider.stubs.system",
@@ -431,7 +422,21 @@
 java_library_static {
     name: "android_test_stubs_current",
     srcs: [ ":test-api-stubs-docs" ],
-    static_libs: [ "private-stub-annotations-jar" ],
+    static_libs: [
+        // Modules do not have test APIs, but we want to include their SystemApis, like we include
+        // the SystemApi of framework-non-updatable-sources.
+        "conscrypt.module.public.api.stubs",
+        "framework-appsearch.stubs.system",
+        "framework-graphics.stubs.system",
+        "framework-media.stubs.system",
+        "framework-mediaprovider.stubs.system",
+        "framework-permission.stubs.system",
+        "framework-sdkextensions.stubs.system",
+        "framework-statsd.stubs.system",
+        "framework-tethering.stubs.system",
+        "framework-wifi.stubs.system",
+        "private-stub-annotations-jar",
+    ],
     defaults: [
         "android_defaults_stubs_current",
         "android_stubs_dists_default",
diff --git a/apct-tests/perftests/autofill/src/android/view/autofill/AutofillTestHelper.java b/apct-tests/perftests/autofill/src/android/view/autofill/AutofillTestHelper.java
new file mode 100644
index 0000000..0763729
--- /dev/null
+++ b/apct-tests/perftests/autofill/src/android/view/autofill/AutofillTestHelper.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.autofill;
+
+import android.app.assist.AssistStructure;
+import android.app.assist.AssistStructure.ViewNode;
+import android.app.assist.AssistStructure.WindowNode;
+import android.service.autofill.FillContext;
+import android.util.Log;
+
+import java.util.List;
+
+/**
+ * Helper for common funcionalities.
+ */
+public class AutofillTestHelper {
+    private static final String TAG = "AutofillTestHelper";
+
+    /**
+     * Gets a node given its Android resource id, or {@code null} if not found.
+     */
+    public static ViewNode findNodeByResourceId(List<FillContext> contexts, String resourceId) {
+        for (FillContext context : contexts) {
+            ViewNode node = findNodeByResourceId(context.getStructure(), resourceId);
+            if (node != null) {
+                return node;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Gets a node if it matches the filter criteria for the given id.
+     */
+    private static ViewNode findNodeByResourceId(AssistStructure structure, String id) {
+        Log.v(TAG, "Parsing request for activity " + structure.getActivityComponent());
+        final int nodes = structure.getWindowNodeCount();
+        for (int i = 0; i < nodes; i++) {
+            final WindowNode windowNode = structure.getWindowNodeAt(i);
+            final ViewNode rootNode = windowNode.getRootViewNode();
+            final ViewNode node = findNodeByResourceId(rootNode, id);
+            if (node != null) {
+                return node;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Gets a node if it matches the filter criteria for the given id.
+     */
+    private static ViewNode findNodeByResourceId(ViewNode node, String id) {
+        if (id.equals(node.getIdEntry())) {
+            return node;
+        }
+        final int childrenSize = node.getChildCount();
+        if (childrenSize > 0) {
+            for (int i = 0; i < childrenSize; i++) {
+                final ViewNode found = findNodeByResourceId(node.getChildAt(i), id);
+                if (found != null) {
+                    return found;
+                }
+            }
+        }
+        return null;
+    }
+}
diff --git a/apct-tests/perftests/autofill/src/android/view/autofill/AutofillTestWatcher.java b/apct-tests/perftests/autofill/src/android/view/autofill/AutofillTestWatcher.java
index 2475d98..f65067f 100644
--- a/apct-tests/perftests/autofill/src/android/view/autofill/AutofillTestWatcher.java
+++ b/apct-tests/perftests/autofill/src/android/view/autofill/AutofillTestWatcher.java
@@ -26,6 +26,8 @@
 import androidx.annotation.Nullable;
 import androidx.test.InstrumentationRegistry;
 
+import com.android.compatibility.common.util.Timeout;
+
 import org.junit.rules.TestWatcher;
 import org.junit.runner.Description;
 
@@ -50,6 +52,7 @@
         final String testName = description.getDisplayName();
         Log.i(TAG, "Starting " + testName);
 
+        prepareDevice();
         enableVerboseLog();
         // Prepare the service before each test.
         // Disable the current AutofillService.
@@ -81,10 +84,21 @@
      * Uses the {@code settings} binary to set the autofill service.
      */
     void setAutofillService() {
+        String serviceName = MyAutofillService.COMPONENT_NAME;
         SettingsHelper.syncSet(InstrumentationRegistry.getTargetContext(),
                 SettingsHelper.NAMESPACE_SECURE,
                 Settings.Secure.AUTOFILL_SERVICE,
-                MyAutofillService.COMPONENT_NAME);
+                serviceName);
+        // Waits until the service is actually enabled.
+        Timeout timeout = new Timeout("CONNECTION_TIMEOUT", GENERIC_TIMEOUT_MS, 2F,
+                GENERIC_TIMEOUT_MS);
+        try {
+            timeout.run("Enabling Autofill service", () -> {
+                return isAutofillServiceEnabled(serviceName) ? serviceName : null;
+            });
+        } catch (Exception e) {
+            throw new AssertionError("Enabling Autofill service failed.");
+        }
     }
 
     /**
@@ -96,6 +110,26 @@
                 Settings.Secure.AUTOFILL_SERVICE);
     }
 
+    /**
+     * Checks whether the given service is set as the autofill service for the default user.
+     */
+    private boolean isAutofillServiceEnabled(String serviceName) {
+        String actualName = SettingsHelper.get(SettingsHelper.NAMESPACE_SECURE,
+                Settings.Secure.AUTOFILL_SERVICE);
+        return serviceName.equals(actualName);
+    }
+
+    private void prepareDevice() {
+        // Unlock screen.
+        runShellCommand("input keyevent KEYCODE_WAKEUP");
+
+        // Dismiss keyguard, in case it's set as "Swipe to unlock".
+        runShellCommand("wm dismiss-keyguard");
+
+        // Collapse notifications.
+        runShellCommand("cmd statusbar collapse");
+    }
+
     private void enableMyAutofillService() {
         MyAutofillService.resetStaticState();
         MyAutofillService.setEnabled(true);
diff --git a/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java b/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java
index 37b4bde..a5d1e00 100644
--- a/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java
+++ b/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java
@@ -28,12 +28,14 @@
 
 import com.android.perftests.autofill.R;
 
-import org.junit.Ignore;
 import org.junit.Test;
 
 @LargeTest
 public class LoginTest extends AbstractAutofillPerfTestCase {
 
+    public static final String ID_USERNAME = "username";
+    public static final String ID_PASSWORD = "password";
+
     private EditText mUsername;
     private EditText mPassword;
     private AutofillManager mAfm;
@@ -78,6 +80,7 @@
         // Must first focus in a field to trigger autofill and wait for service response
         // outside the loop
         mActivityRule.runOnUiThread(() -> mUsername.requestFocus());
+        mTestWatcher.waitServiceConnect();
         MyAutofillService.getLastFillRequest();
         // Then focus on password so loop start with focus away from username
         mActivityRule.runOnUiThread(() -> mPassword.requestFocus());
@@ -94,13 +97,11 @@
     /**
      * Now the service returns autofill data, for both username and password.
      */
-    // TODO(b/162216576): fix fail test and re-enable it
-    @Ignore
     @Test
     public void testFocus_autofillBothFields() throws Throwable {
         MyAutofillService.newCannedResponse()
-                .setUsername(mUsername.getAutofillId(), "user")
-                .setPassword(mPassword.getAutofillId(), "pass")
+                .setUsername(ID_USERNAME, "user")
+                .setPassword(ID_PASSWORD, "pass")
                 .reply();
         mTestWatcher.setAutofillService();
 
@@ -112,6 +113,7 @@
 
         // Must first trigger autofill and wait for service response outside the loop
         mActivityRule.runOnUiThread(() -> mUsername.requestFocus());
+        mTestWatcher.waitServiceConnect();
         MyAutofillService.getLastFillRequest();
         callback.expectEvent(mUsername, EVENT_INPUT_SHOWN);
 
@@ -148,14 +150,12 @@
     /**
      * Now the service returns autofill data, but just for username.
      */
-    // TODO(b/162216576): fix fail test and re-enable it
-    @Ignore
     @Test
     public void testFocus_autofillUsernameOnly() throws Throwable {
         // Must set ignored ids so focus on password does not trigger new requests
         MyAutofillService.newCannedResponse()
-                .setUsername(mUsername.getAutofillId(), "user")
-                .setIgnored(mPassword.getAutofillId())
+                .setUsername(ID_USERNAME, "user")
+                .setIgnored(ID_PASSWORD)
                 .reply();
         mTestWatcher.setAutofillService();
 
@@ -167,6 +167,7 @@
 
         // Must first trigger autofill and wait for service response outside the loop
         mActivityRule.runOnUiThread(() -> mUsername.requestFocus());
+        mTestWatcher.waitServiceConnect();
         MyAutofillService.getLastFillRequest();
         callback.expectEvent(mUsername, EVENT_INPUT_SHOWN);
 
@@ -224,8 +225,8 @@
     @Test
     public void testChange_autofillBothFields() throws Throwable {
         MyAutofillService.newCannedResponse()
-                .setUsername(mUsername.getAutofillId(), "user")
-                .setPassword(mPassword.getAutofillId(), "pass")
+                .setUsername(ID_USERNAME, "user")
+                .setPassword(ID_PASSWORD, "pass")
                 .reply();
         mTestWatcher.setAutofillService();
 
@@ -239,8 +240,8 @@
     public void testChange_autofillUsernameOnly() throws Throwable {
         // Must set ignored ids so focus on password does not trigger new requests
         MyAutofillService.newCannedResponse()
-                .setUsername(mUsername.getAutofillId(), "user")
-                .setIgnored(mPassword.getAutofillId())
+                .setUsername(ID_USERNAME, "user")
+                .setIgnored(ID_PASSWORD)
                 .reply();
         mTestWatcher.setAutofillService();
 
@@ -266,13 +267,11 @@
         }
     }
 
-    // TODO(b/162216576): fix fail test and re-enable it
-    @Ignore
     @Test
     public void testCallbacks() throws Throwable {
         MyAutofillService.newCannedResponse()
-                .setUsername(mUsername.getAutofillId(), "user")
-                .setPassword(mPassword.getAutofillId(), "pass")
+                .setUsername(ID_USERNAME, "user")
+                .setPassword(ID_PASSWORD, "pass")
                 .reply();
         mTestWatcher.setAutofillService();
 
@@ -282,6 +281,7 @@
         // Must first focus in a field to trigger autofill and wait for service response
         // outside the loop
         mActivityRule.runOnUiThread(() -> mUsername.requestFocus());
+        mTestWatcher.waitServiceConnect();
         MyAutofillService.getLastFillRequest();
         callback.expectEvent(mUsername, EVENT_INPUT_SHOWN);
 
diff --git a/apct-tests/perftests/autofill/src/android/view/autofill/MyAutofillService.java b/apct-tests/perftests/autofill/src/android/view/autofill/MyAutofillService.java
index ddac68b..5db6597 100644
--- a/apct-tests/perftests/autofill/src/android/view/autofill/MyAutofillService.java
+++ b/apct-tests/perftests/autofill/src/android/view/autofill/MyAutofillService.java
@@ -15,6 +15,7 @@
  */
 package android.view.autofill;
 
+import android.app.assist.AssistStructure.ViewNode;
 import android.os.CancellationSignal;
 import android.service.autofill.AutofillService;
 import android.service.autofill.Dataset;
@@ -126,25 +127,31 @@
             onError("ignoring onFillRequest(): response not set", callback);
             return;
         }
-        // TODO(b/162216576): fix error FillResponse
         Dataset.Builder dataset = new Dataset.Builder(newDatasetPresentation("dataset"));
         boolean hasData = false;
         if (response.mUsername != null) {
             hasData = true;
-            dataset.setValue(response.mUsername.first,
-                    AutofillValue.forText(response.mUsername.second));
+            AutofillId autofillId = getAutofillIdByResourceId(request, response.mUsername.first);
+            AutofillValue value = AutofillValue.forText(response.mUsername.second);
+            dataset.setValue(autofillId, value, newDatasetPresentation("dataset"));
         }
         if (response.mPassword != null) {
             hasData = true;
-            dataset.setValue(response.mPassword.first,
-                    AutofillValue.forText(response.mPassword.second));
+            AutofillId autofillId = getAutofillIdByResourceId(request, response.mPassword.first);
+            AutofillValue value = AutofillValue.forText(response.mPassword.second);
+            dataset.setValue(autofillId, value, newDatasetPresentation("dataset"));
         }
         if (hasData) {
             FillResponse.Builder fillResponse = new FillResponse.Builder();
             if (response.mIgnoredIds != null) {
-                fillResponse.setIgnoredIds(response.mIgnoredIds);
+                int length = response.mIgnoredIds.length;
+                AutofillId[] requiredIds = new AutofillId[length];
+                for (int i = 0; i < length; i++) {
+                    String resourceId = response.mIgnoredIds[i];
+                    requiredIds[i] = getAutofillIdByResourceId(request, resourceId);
+                }
+                fillResponse.setIgnoredIds(requiredIds);
             }
-
             callback.onSuccess(fillResponse.addDataset(dataset.build()).build());
         } else {
             callback.onSuccess(null);
@@ -154,6 +161,16 @@
         }
     }
 
+    private AutofillId getAutofillIdByResourceId(FillRequest request, String resourceId)
+            throws Exception {
+        ViewNode node = AutofillTestHelper.findNodeByResourceId(request.getFillContexts(),
+                resourceId);
+        if (node == null) {
+            throw new AssertionError("No node with resource id " + resourceId);
+        }
+        return node.getAutofillId();
+    }
+
     @Override
     public void onSaveRequest(SaveRequest request, SaveCallback callback) {
         // No current test should have triggered it...
@@ -162,9 +179,9 @@
     }
 
     static final class CannedResponse {
-        private final Pair<AutofillId, String> mUsername;
-        private final Pair<AutofillId, String> mPassword;
-        private final AutofillId[] mIgnoredIds;
+        private final Pair<String, String> mUsername;
+        private final Pair<String, String> mPassword;
+        private final String[] mIgnoredIds;
 
         private CannedResponse(@NonNull Builder builder) {
             mUsername = builder.mUsername;
@@ -173,24 +190,24 @@
         }
 
         static class Builder {
-            private Pair<AutofillId, String> mUsername;
-            private Pair<AutofillId, String> mPassword;
-            private AutofillId[] mIgnoredIds;
+            private Pair<String, String> mUsername;
+            private Pair<String, String> mPassword;
+            private String[] mIgnoredIds;
 
             @NonNull
-            Builder setUsername(@NonNull AutofillId id, @NonNull String value) {
+            Builder setUsername(@NonNull String id, @NonNull String value) {
                 mUsername = new Pair<>(id, value);
                 return this;
             }
 
             @NonNull
-            Builder setPassword(@NonNull AutofillId id, @NonNull String value) {
+            Builder setPassword(@NonNull String id, @NonNull String value) {
                 mPassword = new Pair<>(id, value);
                 return this;
             }
 
             @NonNull
-            Builder setIgnored(AutofillId... ids) {
+            Builder setIgnored(String... ids) {
                 mIgnoredIds = ids;
                 return this;
             }
diff --git a/apct-tests/perftests/core/src/android/content/pm/PackageManagerBenchmark.java b/apct-tests/perftests/core/src/android/content/pm/PackageManagerBenchmark.java
new file mode 100644
index 0000000..a82fab4
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/content/pm/PackageManagerBenchmark.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.content.Context;
+import android.os.UserHandle;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class PackageManagerBenchmark {
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Test
+    public void createUserContextBenchmark() {
+        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+        while (state.keepRunning()) {
+            context.createContextAsUser(UserHandle.SYSTEM, /* flags */ 0);
+        }
+    }
+
+    @Test
+    public void getResourcesForApplication_byStarAsUser()
+            throws PackageManager.NameNotFoundException {
+        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+        while (state.keepRunning()) {
+            context.getPackageManager().getResourcesForApplicationAsUser(context.getPackageName(),
+                    UserHandle.USER_SYSTEM);
+        }
+    }
+
+    @Test
+    public void getResourcesApplication_byCreateContextAsUser()
+            throws PackageManager.NameNotFoundException {
+        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+        while (state.keepRunning()) {
+            context.createContextAsUser(UserHandle.SYSTEM, /* flags */ 0).getPackageManager()
+                    .getResourcesForApplication(context.getPackageName());
+        }
+    }
+}
diff --git a/apct-tests/perftests/core/src/android/text/TextUtilsPerfTest.java b/apct-tests/perftests/core/src/android/text/TextUtilsPerfTest.java
new file mode 100644
index 0000000..c62269e
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/text/TextUtilsPerfTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.text;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.function.Supplier;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class TextUtilsPerfTest {
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    public static final String TEMPLATE = "Template that combines %s and %d together";
+
+    public String mVar1 = "example";
+    public int mVar2 = 42;
+
+    /**
+     * Measure overhead of formatting a string via {@link String#format}.
+     */
+    @Test
+    public void timeFormatUpstream() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            String res = String.format(TEMPLATE, mVar1, mVar2);
+        }
+    }
+
+    /**
+     * Measure overhead of formatting a string via
+     * {@link TextUtils#formatSimple}.
+     */
+    @Test
+    public void timeFormatLocal() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            String res = TextUtils.formatSimple(TEMPLATE, mVar1, mVar2);
+        }
+    }
+
+    /**
+     * Measure overhead of formatting a string inline.
+     */
+    @Test
+    public void timeFormatInline() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            String res = "Template that combines " + mVar1 + " and " + mVar2 + " together";
+        }
+    }
+
+    /**
+     * Measure overhead of a passing null-check that uses a lambda to
+     * communicate a custom error message.
+     */
+    @Test
+    public void timeFormat_Skip_Lambda() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            requireNonNull(this, () -> {
+                return String.format(TEMPLATE, mVar1, mVar2);
+            });
+        }
+    }
+
+    /**
+     * Measure overhead of a passing null-check that uses varargs to communicate
+     * a custom error message.
+     */
+    @Test
+    public void timeFormat_Skip_Varargs() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            requireNonNull(this, TEMPLATE, mVar1, mVar2);
+        }
+    }
+
+    private static <T> T requireNonNull(T obj, Supplier<String> messageSupplier) {
+        return obj;
+    }
+
+    private static <T> T requireNonNull(T obj, String format, Object... args) {
+        return obj;
+    }
+}
diff --git a/apct-tests/perftests/textclassifier/run.sh b/apct-tests/perftests/textclassifier/run.sh
index d36d190..9a0f4f9 100755
--- a/apct-tests/perftests/textclassifier/run.sh
+++ b/apct-tests/perftests/textclassifier/run.sh
@@ -1,8 +1,8 @@
 set -e
-build/soong/soong_ui.bash --make-mode TextClassifierPerfTests perf-setup.sh
+build/soong/soong_ui.bash --make-mode TextClassifierPerfTests perf-setup
 adb install ${OUT}/testcases/TextClassifierPerfTests/arm64/TextClassifierPerfTests.apk
 adb shell cmd package compile -m speed -f com.android.perftests.textclassifier
-adb push ${OUT}/obj/EXECUTABLES/perf-setup.sh_intermediates/perf-setup.sh /data/local/tmp/
+adb push ${OUT}/obj/EXECUTABLES/perf-setup_intermediates/perf-setup.sh /data/local/tmp/
 adb shell chmod +x /data/local/tmp/perf-setup.sh
 adb shell /data/local/tmp/perf-setup.sh
-adb shell am instrument -w -e package android.view.textclassifier com.android.perftests.textclassifier/androidx.test.runner.AndroidJUnitRunner
\ No newline at end of file
+adb shell am instrument -w -e package android.view.textclassifier com.android.perftests.textclassifier/androidx.test.runner.AndroidJUnitRunner
diff --git a/apct-tests/perftests/windowmanager/README.md b/apct-tests/perftests/windowmanager/README.md
index 8b5292f..7a0019a 100644
--- a/apct-tests/perftests/windowmanager/README.md
+++ b/apct-tests/perftests/windowmanager/README.md
@@ -4,7 +4,7 @@
 To reduce the variance of the test, if `perf-setup.sh` (platform_testing/scripts/perf-setup)
 is available, it is better to use the following instructions to lock CPU and GPU frequencies.
 ```
-m perf-setup.sh
+m perf-setup
 PERF_SETUP_PATH=/data/local/tmp/perf-setup.sh
 adb push $OUT/$PERF_SETUP_PATH $PERF_SETUP_PATH
 adb shell chmod +x $PERF_SETUP_PATH
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
index ecd1499..1be68f5 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
@@ -135,7 +135,6 @@
         final int mHeight;
         final Point mOutSurfaceSize = new Point();
         final SurfaceControl mOutSurfaceControl;
-        final SurfaceControl mOutBlastSurfaceControl = new SurfaceControl();
 
         final IntSupplier mViewVisibility;
 
@@ -158,7 +157,7 @@
                 session.relayout(mWindow, mParams, mWidth, mHeight,
                         mViewVisibility.getAsInt(), mFlags, mFrameNumber, mOutFrames,
                         mOutMergedConfiguration, mOutSurfaceControl, mOutInsetsState, mOutControls,
-                        mOutSurfaceSize, mOutBlastSurfaceControl);
+                        mOutSurfaceSize);
             }
         }
     }
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
index b11d7464..2960603 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
@@ -84,9 +84,8 @@
 
     private static class TestWindow extends BaseIWindow {
         final WindowManager.LayoutParams mLayoutParams = new WindowManager.LayoutParams();
+        final InsetsState mRequestedVisibility = new InsetsState();
         final Rect mOutFrame = new Rect();
-        final Rect mOutContentInsets = new Rect();
-        final Rect mOutStableInsets = new Rect();
         final DisplayCutout.ParcelableWrapper mOutDisplayCutout =
                 new DisplayCutout.ParcelableWrapper();
         final InsetsState mOutInsetsState = new InsetsState();
@@ -108,7 +107,7 @@
 
                 long startTime = SystemClock.elapsedRealtimeNanos();
                 session.addToDisplay(this, mLayoutParams, View.VISIBLE,
-                        Display.DEFAULT_DISPLAY, mOutFrame, mOutContentInsets, mOutStableInsets,
+                        Display.DEFAULT_DISPLAY, mRequestedVisibility, mOutFrame,
                         mOutDisplayCutout, inputChannel, mOutInsetsState, mOutControls);
                 final long elapsedTimeNsOfAdd = SystemClock.elapsedRealtimeNanos() - startTime;
                 state.addExtraResult("add", elapsedTimeNsOfAdd);
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/WmPerfRunListener.java b/apct-tests/perftests/windowmanager/src/android/wm/WmPerfRunListener.java
index d955289..a9d5716 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/WmPerfRunListener.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/WmPerfRunListener.java
@@ -115,7 +115,7 @@
         runWithShellPermissionIdentity(() -> {
             final ActivityTaskManager atm = mContext.getSystemService(ActivityTaskManager.class);
             atm.removeAllVisibleRecentTasks();
-            atm.removeStacksWithActivityTypes(new int[] { ACTIVITY_TYPE_STANDARD,
+            atm.removeRootTasksWithActivityTypes(new int[] { ACTIVITY_TYPE_STANDARD,
                     ACTIVITY_TYPE_ASSISTANT, ACTIVITY_TYPE_RECENTS, ACTIVITY_TYPE_UNDEFINED });
         });
         PhoneWindow.sendCloseSystemWindows(mContext, "WmPerfTests");
diff --git a/apex/Android.bp b/apex/Android.bp
index 266e672..c5b4901 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -107,7 +107,7 @@
 
     // Hide impl library and stub sources
     impl_library_visibility: [
-        ":__package__",
+        ":__pkg__",
         "//frameworks/base", // For framework-all
     ],
     stubs_source_visibility: ["//visibility:private"],
diff --git a/apex/appsearch/framework/Android.bp b/apex/appsearch/framework/Android.bp
index 321f471..12afde4 100644
--- a/apex/appsearch/framework/Android.bp
+++ b/apex/appsearch/framework/Android.bp
@@ -21,60 +21,15 @@
     path: "java",
 }
 
-java_library {
+java_sdk_library {
     name: "framework-appsearch",
-    installable: true,
+    srcs: [ ":framework-appsearch-sources" ],
     sdk_version: "core_platform", // TODO(b/146218515) should be module_current
-    srcs: [":framework-appsearch-sources"],
-    hostdex: true, // for hiddenapi check
-    libs: [
-        "framework-minus-apex",  // TODO(b/146218515) should be removed
-    ],
-    static_libs: ["icing-java-proto-lite"],
-    visibility: [
-        // TODO(b/146218515) remove this when framework is built with the stub of appsearch
-        "//frameworks/base",
-        "//frameworks/base/apex/appsearch:__subpackages__",
-    ],
-    jarjar_rules: "jarjar-rules.txt",
+    impl_only_libs: ["framework-minus-apex"], // TODO(b/146218515) should be removed
+    defaults: ["framework-module-defaults"],
     permitted_packages: ["android.app.appsearch"],
+    aidl: {
+        include_dirs: ["frameworks/base/core/java"], // TODO(b/146218515) should be removed
+    },
     apex_available: ["com.android.appsearch"],
 }
-
-metalava_appsearch_docs_args =
-    "--hide-package com.android.server " +
-    "--error UnhiddenSystemApi " +
-    "--hide RequiresPermission " +
-    "--hide MissingPermission " +
-    "--hide BroadcastBehavior " +
-    "--hide HiddenSuperclass " +
-    "--hide DeprecationMismatch " +
-    "--hide UnavailableSymbol " +
-    "--hide SdkConstant " +
-    "--hide HiddenTypeParameter " +
-    "--hide Todo --hide Typo " +
-    "--hide HiddenTypedefConstant " +
-    "--show-annotation android.annotation.SystemApi "
-
-droidstubs {
-    name: "framework-appsearch-stubs-srcs",
-    srcs: [":framework-appsearch-sources"],
-    libs: ["framework-annotations-lib"],
-    aidl: {
-        include_dirs: ["frameworks/base/core/java"],
-    },
-    args: metalava_appsearch_docs_args,
-    sdk_version: "module_current",
-}
-
-java_library {
-    name: "framework-appsearch-stubs",
-    srcs: [":framework-appsearch-stubs-srcs"],
-    aidl: {
-        export_include_dirs: [
-            "java",
-        ],
-    },
-    sdk_version: "module_current",
-    installable: false,
-}
diff --git a/apex/appsearch/framework/api/current.txt b/apex/appsearch/framework/api/current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/appsearch/framework/api/current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/appsearch/framework/api/module-lib-current.txt b/apex/appsearch/framework/api/module-lib-current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/appsearch/framework/api/module-lib-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/appsearch/framework/api/module-lib-removed.txt b/apex/appsearch/framework/api/module-lib-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/appsearch/framework/api/module-lib-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/appsearch/framework/api/removed.txt b/apex/appsearch/framework/api/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/appsearch/framework/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/appsearch/framework/api/system-current.txt b/apex/appsearch/framework/api/system-current.txt
new file mode 100644
index 0000000..4a6194e
--- /dev/null
+++ b/apex/appsearch/framework/api/system-current.txt
@@ -0,0 +1,9 @@
+// Signature format: 2.0
+package android.app.appsearch {
+
+  public class AppSearchManagerFrameworkInitializer {
+    method public static void initialize();
+  }
+
+}
+
diff --git a/apex/appsearch/framework/api/system-removed.txt b/apex/appsearch/framework/api/system-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/appsearch/framework/api/system-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/appsearch/framework/jarjar-rules.txt b/apex/appsearch/framework/jarjar-rules.txt
deleted file mode 100644
index acf759a..0000000
--- a/apex/appsearch/framework/jarjar-rules.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-rule com.google.protobuf.** android.app.appsearch.protobuf.@1
-rule com.google.android.icing.proto.** android.app.appsearch.proto.@1
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java
index dc75825..98daa66 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -22,6 +22,8 @@
 import android.os.Parcelable;
 import android.util.ArrayMap;
 
+import com.android.internal.util.Preconditions;
+
 import java.util.Collections;
 import java.util.Map;
 
@@ -33,11 +35,11 @@
  * @param <ValueType> The type of result objects associated with the keys.
  * @hide
  */
-public class AppSearchBatchResult<KeyType, ValueType> implements Parcelable {
+public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelable {
     @NonNull private final Map<KeyType, ValueType> mSuccesses;
     @NonNull private final Map<KeyType, AppSearchResult<ValueType>> mFailures;
 
-    private AppSearchBatchResult(
+    AppSearchBatchResult(
             @NonNull Map<KeyType, ValueType> successes,
             @NonNull Map<KeyType, AppSearchResult<ValueType>> failures) {
         mSuccesses = successes;
@@ -61,8 +63,8 @@
     }
 
     /**
-     * Returns a {@link Map} of all successful keys mapped to the successful {@link ValueType}
-     * values they produced.
+     * Returns a {@link Map} of all successful keys mapped to the successful
+     * {@link AppSearchResult}s they produced.
      *
      * <p>The values of the {@link Map} will not be {@code null}.
      */
@@ -82,6 +84,22 @@
         return mFailures;
     }
 
+    /**
+     * Asserts that this {@link AppSearchBatchResult} has no failures.
+     * @hide
+     */
+    public void checkSuccess() {
+        if (!isSuccess()) {
+            throw new IllegalStateException("AppSearchBatchResult has failures: " + this);
+        }
+    }
+
+    @Override
+    @NonNull
+    public String toString() {
+        return "{\n  successes: " + mSuccesses + "\n  failures: " + mFailures + "\n}";
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -112,16 +130,18 @@
     public static final class Builder<KeyType, ValueType> {
         private final Map<KeyType, ValueType> mSuccesses = new ArrayMap<>();
         private final Map<KeyType, AppSearchResult<ValueType>> mFailures = new ArrayMap<>();
-
-        /** Creates a new {@link Builder} for this {@link AppSearchBatchResult}. */
-        public Builder() {}
+        private boolean mBuilt = false;
 
         /**
          * Associates the {@code key} with the given successful return value.
          *
          * <p>Any previous mapping for a key, whether success or failure, is deleted.
          */
-        public Builder setSuccess(@NonNull KeyType key, @Nullable ValueType result) {
+        @NonNull
+        public Builder<KeyType, ValueType> setSuccess(
+                @NonNull KeyType key, @Nullable ValueType result) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkNotNull(key);
             return setResult(key, AppSearchResult.newSuccessfulResult(result));
         }
 
@@ -130,10 +150,13 @@
          *
          * <p>Any previous mapping for a key, whether success or failure, is deleted.
          */
-        public Builder setFailure(
+        @NonNull
+        public Builder<KeyType, ValueType> setFailure(
                 @NonNull KeyType key,
                 @AppSearchResult.ResultCode int resultCode,
                 @Nullable String errorMessage) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkNotNull(key);
             return setResult(key, AppSearchResult.newFailedResult(resultCode, errorMessage));
         }
 
@@ -143,7 +166,11 @@
          * <p>Any previous mapping for a key, whether success or failure, is deleted.
          */
         @NonNull
-        public Builder setResult(@NonNull KeyType key, @NonNull AppSearchResult<ValueType> result) {
+        public Builder<KeyType, ValueType> setResult(
+                @NonNull KeyType key, @NonNull AppSearchResult<ValueType> result) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkNotNull(key);
+            Preconditions.checkNotNull(result);
             if (result.isSuccess()) {
                 mSuccesses.put(key, result.getResultValue());
                 mFailures.remove(key);
@@ -157,6 +184,8 @@
         /** Builds an {@link AppSearchBatchResult} from the contents of this {@link Builder}. */
         @NonNull
         public AppSearchBatchResult<KeyType, ValueType> build() {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            mBuilt = true;
             return new AppSearchBatchResult<>(mSuccesses, mFailures);
         }
     }
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchDocument.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchDocument.java
deleted file mode 100644
index 7d2b64e..0000000
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchDocument.java
+++ /dev/null
@@ -1,698 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.appsearch;
-
-import android.annotation.CurrentTimeMillisLong;
-import android.annotation.DurationMillisLong;
-import android.annotation.IntRange;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.util.ArrayMap;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.Preconditions;
-
-import com.google.android.icing.proto.DocumentProto;
-import com.google.android.icing.proto.PropertyProto;
-import com.google.protobuf.ByteString;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-/**
- * Represents a document unit.
- *
- * <p>Documents are constructed via {@link AppSearchDocument.Builder}.
- * @hide
- */
-public class AppSearchDocument {
-    private static final String TAG = "AppSearchDocument";
-
-    /** The default empty namespace.*/
-    // TODO(adorokhine): Allow namespace to be specified in the document.
-    public static final String DEFAULT_NAMESPACE = "";
-
-    /**
-     * The maximum number of elements in a repeatable field. Will reject the request if exceed
-     * this limit.
-     */
-    private static final int MAX_REPEATED_PROPERTY_LENGTH = 100;
-
-    /**
-     * The maximum {@link String#length} of a {@link String} field. Will reject the request if
-     * {@link String}s longer than this.
-     */
-    private static final int MAX_STRING_LENGTH = 20_000;
-
-    /**
-     * Contains {@link AppSearchDocument} basic information (uri, schemaType etc) and properties
-     * ordered by keys.
-     */
-    @NonNull
-    private final DocumentProto mProto;
-
-    /** Contains all properties in {@link #mProto} to support getting properties via keys. */
-    @NonNull
-    private final Map<String, Object> mProperties;
-
-    /**
-     * Creates a new {@link AppSearchDocument}.
-     * @param proto Contains {@link AppSearchDocument} basic information (uri, schemaType etc) and
-     *               properties ordered by keys.
-     * @param propertiesMap Contains all properties in {@link #mProto} to support get properties
-     *                      via keys.
-     */
-    private AppSearchDocument(@NonNull DocumentProto proto,
-            @NonNull Map<String, Object> propertiesMap) {
-        mProto = proto;
-        mProperties = propertiesMap;
-    }
-
-    /**
-     * Creates a new {@link AppSearchDocument} from an existing instance.
-     *
-     * <p>This method should be only used by constructor of a subclass.
-     */
-    protected AppSearchDocument(@NonNull AppSearchDocument document) {
-        this(document.mProto, document.mProperties);
-    }
-
-    /** @hide */
-    AppSearchDocument(@NonNull DocumentProto documentProto) {
-        this(documentProto, new ArrayMap<>());
-        for (int i = 0; i < documentProto.getPropertiesCount(); i++) {
-            PropertyProto property = documentProto.getProperties(i);
-            String name = property.getName();
-            if (property.getStringValuesCount() > 0) {
-                String[] values = new String[property.getStringValuesCount()];
-                for (int j = 0; j < values.length; j++) {
-                    values[j] = property.getStringValues(j);
-                }
-                mProperties.put(name, values);
-            } else if (property.getInt64ValuesCount() > 0) {
-                long[] values = new long[property.getInt64ValuesCount()];
-                for (int j = 0; j < values.length; j++) {
-                    values[j] = property.getInt64Values(j);
-                }
-                mProperties.put(property.getName(), values);
-            } else if (property.getDoubleValuesCount() > 0) {
-                double[] values = new double[property.getDoubleValuesCount()];
-                for (int j = 0; j < values.length; j++) {
-                    values[j] = property.getDoubleValues(j);
-                }
-                mProperties.put(property.getName(), values);
-            } else if (property.getBooleanValuesCount() > 0) {
-                boolean[] values = new boolean[property.getBooleanValuesCount()];
-                for (int j = 0; j < values.length; j++) {
-                    values[j] = property.getBooleanValues(j);
-                }
-                mProperties.put(property.getName(), values);
-            } else if (property.getBytesValuesCount() > 0) {
-                byte[][] values = new byte[property.getBytesValuesCount()][];
-                for (int j = 0; j < values.length; j++) {
-                    values[j] = property.getBytesValues(j).toByteArray();
-                }
-                mProperties.put(name, values);
-            } else if (property.getDocumentValuesCount() > 0) {
-                AppSearchDocument[] values =
-                        new AppSearchDocument[property.getDocumentValuesCount()];
-                for (int j = 0; j < values.length; j++) {
-                    values[j] = new AppSearchDocument(property.getDocumentValues(j));
-                }
-                mProperties.put(name, values);
-            } else {
-                throw new IllegalStateException("Unknown type of value: " + name);
-            }
-        }
-    }
-
-    /**
-     * Returns the {@link DocumentProto} of the {@link AppSearchDocument}.
-     *
-     * <p>The {@link DocumentProto} contains {@link AppSearchDocument}'s basic information and all
-     *    properties ordered by keys.
-     * @hide
-     */
-    @NonNull
-    @VisibleForTesting
-    public DocumentProto getProto() {
-        return mProto;
-    }
-
-    /** Returns the URI of the {@link AppSearchDocument}. */
-    @NonNull
-    public String getUri() {
-        return mProto.getUri();
-    }
-
-    /** Returns the schema type of the {@link AppSearchDocument}. */
-    @NonNull
-    public String getSchemaType() {
-        return mProto.getSchema();
-    }
-
-    /**
-     * Returns the creation timestamp in milliseconds of the {@link AppSearchDocument}. Value will
-     * be in the {@link System#currentTimeMillis()} time base.
-     */
-    @CurrentTimeMillisLong
-    public long getCreationTimestampMillis() {
-        return mProto.getCreationTimestampMs();
-    }
-
-    /**
-     * Returns the TTL (Time To Live) of the {@link AppSearchDocument}, in milliseconds.
-     *
-     * <p>The default value is 0, which means the document is permanent and won't be auto-deleted
-     *    until the app is uninstalled.
-     */
-    @DurationMillisLong
-    public long getTtlMillis() {
-        return mProto.getTtlMs();
-    }
-
-    /**
-     * Returns the score of the {@link AppSearchDocument}.
-     *
-     * <p>The score is a query-independent measure of the document's quality, relative to other
-     * {@link AppSearchDocument}s of the same type.
-     *
-     * <p>The default value is 0.
-     */
-    public int getScore() {
-        return mProto.getScore();
-    }
-
-    /**
-     * Retrieve a {@link String} value by key.
-     *
-     * @param key The key to look for.
-     * @return The first {@link String} associated with the given key or {@code null} if there
-     *         is no such key or the value is of a different type.
-     */
-    @Nullable
-    public String getPropertyString(@NonNull String key) {
-        String[] propertyArray = getPropertyStringArray(key);
-        if (ArrayUtils.isEmpty(propertyArray)) {
-            return null;
-        }
-        warnIfSinglePropertyTooLong("String", key, propertyArray.length);
-        return propertyArray[0];
-    }
-
-    /**
-     * Retrieve a {@code long} value by key.
-     *
-     * @param key The key to look for.
-     * @return The first {@code long} associated with the given key or default value {@code 0} if
-     *         there is no such key or the value is of a different type.
-     */
-    public long getPropertyLong(@NonNull String key) {
-        long[] propertyArray = getPropertyLongArray(key);
-        if (ArrayUtils.isEmpty(propertyArray)) {
-            return 0;
-        }
-        warnIfSinglePropertyTooLong("Long", key, propertyArray.length);
-        return propertyArray[0];
-    }
-
-    /**
-     * Retrieve a {@code double} value by key.
-     *
-     * @param key The key to look for.
-     * @return The first {@code double} associated with the given key or default value {@code 0.0}
-     *         if there is no such key or the value is of a different type.
-     */
-    public double getPropertyDouble(@NonNull String key) {
-        double[] propertyArray = getPropertyDoubleArray(key);
-        // TODO(tytytyww): Add support double array to ArraysUtils.isEmpty().
-        if (propertyArray == null || propertyArray.length == 0) {
-            return 0.0;
-        }
-        warnIfSinglePropertyTooLong("Double", key, propertyArray.length);
-        return propertyArray[0];
-    }
-
-    /**
-     * Retrieve a {@code boolean} value by key.
-     *
-     * @param key The key to look for.
-     * @return The first {@code boolean} associated with the given key or default value
-     *         {@code false} if there is no such key or the value is of a different type.
-     */
-    public boolean getPropertyBoolean(@NonNull String key) {
-        boolean[] propertyArray = getPropertyBooleanArray(key);
-        if (ArrayUtils.isEmpty(propertyArray)) {
-            return false;
-        }
-        warnIfSinglePropertyTooLong("Boolean", key, propertyArray.length);
-        return propertyArray[0];
-    }
-
-    /**
-     * Retrieve a {@code byte[]} value by key.
-     *
-     * @param key The key to look for.
-     * @return The first {@code byte[]} associated with the given key or {@code null} if there
-     *         is no such key or the value is of a different type.
-     */
-    @Nullable
-    public byte[] getPropertyBytes(@NonNull String key) {
-        byte[][] propertyArray = getPropertyBytesArray(key);
-        if (ArrayUtils.isEmpty(propertyArray)) {
-            return null;
-        }
-        warnIfSinglePropertyTooLong("ByteArray", key, propertyArray.length);
-        return propertyArray[0];
-    }
-
-    /**
-     * Retrieve a {@link AppSearchDocument} value by key.
-     *
-     * @param key The key to look for.
-     * @return The first {@link AppSearchDocument} associated with the given key or {@code null} if
-     *         there is no such key or the value is of a different type.
-     */
-    @Nullable
-    public AppSearchDocument getPropertyDocument(@NonNull String key) {
-        AppSearchDocument[] propertyArray = getPropertyDocumentArray(key);
-        if (ArrayUtils.isEmpty(propertyArray)) {
-            return null;
-        }
-        warnIfSinglePropertyTooLong("Document", key, propertyArray.length);
-        return propertyArray[0];
-    }
-
-    /** Prints a warning to logcat if the given propertyLength is greater than 1. */
-    private static void warnIfSinglePropertyTooLong(
-            @NonNull String propertyType, @NonNull String key, int propertyLength) {
-        if (propertyLength > 1) {
-            Log.w(TAG, "The value for \"" + key + "\" contains " + propertyLength
-                    + " elements. Only the first one will be returned from "
-                    + "getProperty" + propertyType + "(). Try getProperty" + propertyType
-                    + "Array().");
-        }
-    }
-
-    /**
-     * Retrieve a repeated {@link String} property by key.
-     *
-     * @param key The key to look for.
-     * @return The {@code String[]} associated with the given key, or {@code null} if no value
-     *         is set or the value is of a different type.
-     */
-    @Nullable
-    public String[] getPropertyStringArray(@NonNull String key) {
-        return getAndCastPropertyArray(key, String[].class);
-    }
-
-    /**
-     * Retrieve a repeated {@code long} property by key.
-     *
-     * @param key The key to look for.
-     * @return The {@code long[]} associated with the given key, or {@code null} if no value is
-     *         set or the value is of a different type.
-     */
-    @Nullable
-    public long[] getPropertyLongArray(@NonNull String key) {
-        return getAndCastPropertyArray(key, long[].class);
-    }
-
-    /**
-     * Retrieve a repeated {@code double} property by key.
-     *
-     * @param key The key to look for.
-     * @return The {@code double[]} associated with the given key, or {@code null} if no value
-     *         is set or the value is of a different type.
-     */
-    @Nullable
-    public double[] getPropertyDoubleArray(@NonNull String key) {
-        return getAndCastPropertyArray(key, double[].class);
-    }
-
-    /**
-     * Retrieve a repeated {@code boolean} property by key.
-     *
-     * @param key The key to look for.
-     * @return The {@code boolean[]} associated with the given key, or {@code null} if no value
-     *         is set or the value is of a different type.
-     */
-    @Nullable
-    public boolean[] getPropertyBooleanArray(@NonNull String key) {
-        return getAndCastPropertyArray(key, boolean[].class);
-    }
-
-    /**
-     * Retrieve a {@code byte[][]} property by key.
-     *
-     * @param key The key to look for.
-     * @return The {@code byte[][]} associated with the given key, or {@code null} if no value
-     *         is set or the value is of a different type.
-     */
-    @Nullable
-    public byte[][] getPropertyBytesArray(@NonNull String key) {
-        return getAndCastPropertyArray(key, byte[][].class);
-    }
-
-    /**
-     * Retrieve a repeated {@link AppSearchDocument} property by key.
-     *
-     * @param key The key to look for.
-     * @return The {@link AppSearchDocument[]} associated with the given key, or {@code null} if no
-     *         value is set or the value is of a different type.
-     */
-    @Nullable
-    public AppSearchDocument[] getPropertyDocumentArray(@NonNull String key) {
-        return getAndCastPropertyArray(key, AppSearchDocument[].class);
-    }
-
-    /**
-     * Gets a repeated property of the given key, and casts it to the given class type, which
-     * must be an array class type.
-     */
-    @Nullable
-    private <T> T getAndCastPropertyArray(@NonNull String key, @NonNull Class<T> tClass) {
-        Object value = mProperties.get(key);
-        if (value == null) {
-            return null;
-        }
-        try {
-            return tClass.cast(value);
-        } catch (ClassCastException e) {
-            Log.w(TAG, "Error casting to requested type for key \"" + key + "\"", e);
-            return null;
-        }
-    }
-
-    @Override
-    public boolean equals(@Nullable Object other) {
-        // Check only proto's equality is sufficient here since all properties in
-        // mProperties are ordered by keys and stored in proto.
-        if (this == other) {
-            return true;
-        }
-        if (!(other instanceof AppSearchDocument)) {
-            return false;
-        }
-        AppSearchDocument otherDocument = (AppSearchDocument) other;
-        return this.mProto.equals(otherDocument.mProto);
-    }
-
-    @Override
-    public int hashCode() {
-        // Hash only proto is sufficient here since all properties in mProperties are ordered by
-        // keys and stored in proto.
-        return mProto.hashCode();
-    }
-
-    @Override
-    public String toString() {
-        return mProto.toString();
-    }
-
-    /**
-     * The builder class for {@link AppSearchDocument}.
-     *
-     * @param <BuilderType> Type of subclass who extend this.
-     */
-    public static class Builder<BuilderType extends Builder> {
-
-        private final Map<String, Object> mProperties = new ArrayMap<>();
-        private final DocumentProto.Builder mProtoBuilder = DocumentProto.newBuilder();
-        private final BuilderType mBuilderTypeInstance;
-
-        /**
-         * Creates a new {@link AppSearchDocument.Builder}.
-         *
-         * <p>The URI is a unique string opaque to AppSearch.
-         *
-         * @param uri The uri of {@link AppSearchDocument}.
-         * @param schemaType The schema type of the {@link AppSearchDocument}. The passed-in
-         *       {@code schemaType} must be defined using {@link AppSearchManager#setSchema} prior
-         *       to inserting a document of this {@code schemaType} into the AppSearch index using
-         *       {@link AppSearchManager#putDocuments(List)}. Otherwise, the document will be
-         *       rejected by {@link AppSearchManager#putDocuments(List)}.
-         */
-        public Builder(@NonNull String uri, @NonNull String schemaType) {
-            mBuilderTypeInstance = (BuilderType) this;
-            mProtoBuilder.setUri(uri).setSchema(schemaType).setNamespace(DEFAULT_NAMESPACE);
-            // Set current timestamp for creation timestamp by default.
-            setCreationTimestampMillis(System.currentTimeMillis());
-        }
-
-        /**
-         * Sets the score of the {@link AppSearchDocument}.
-         *
-         * <p>The score is a query-independent measure of the document's quality, relative to
-         * other {@link AppSearchDocument}s of the same type.
-         *
-         * @throws IllegalArgumentException If the provided value is negative.
-         */
-        @NonNull
-        public BuilderType setScore(@IntRange(from = 0, to = Integer.MAX_VALUE) int score) {
-            if (score < 0) {
-                throw new IllegalArgumentException("Document score cannot be negative.");
-            }
-            mProtoBuilder.setScore(score);
-            return mBuilderTypeInstance;
-        }
-
-        /**
-         * Set the creation timestamp in milliseconds of the {@link AppSearchDocument}. Should be
-         * set using a value obtained from the {@link System#currentTimeMillis()} time base.
-         */
-        @NonNull
-        public BuilderType setCreationTimestampMillis(
-                @CurrentTimeMillisLong long creationTimestampMillis) {
-            mProtoBuilder.setCreationTimestampMs(creationTimestampMillis);
-            return mBuilderTypeInstance;
-        }
-
-        /**
-         * Set the TTL (Time To Live) of the {@link AppSearchDocument}, in milliseconds.
-         *
-         * <p>After this many milliseconds since the {@link #setCreationTimestampMillis(long)}
-         * creation timestamp}, the document is deleted.
-         *
-         * @param ttlMillis A non-negative duration in milliseconds.
-         * @throws IllegalArgumentException If the provided value is negative.
-         */
-        @NonNull
-        public BuilderType setTtlMillis(@DurationMillisLong long ttlMillis) {
-            Preconditions.checkArgumentNonNegative(
-                    ttlMillis, "Document ttlMillis cannot be negative.");
-            mProtoBuilder.setTtlMs(ttlMillis);
-            return mBuilderTypeInstance;
-        }
-
-        /**
-         * Sets one or multiple {@code String} values for a property, replacing its previous
-         * values.
-         *
-         * @param key The key associated with the {@code values}.
-         * @param values The {@code String} values of the property.
-         */
-        @NonNull
-        public BuilderType setProperty(@NonNull String key, @NonNull String... values) {
-            putInPropertyMap(key, values);
-            return mBuilderTypeInstance;
-        }
-
-        /**
-         * Sets one or multiple {@code boolean} values for a property, replacing its previous
-         * values.
-         *
-         * @param key The key associated with the {@code values}.
-         * @param values The {@code boolean} values of the property.
-         */
-        @NonNull
-        public BuilderType setProperty(@NonNull String key, @NonNull boolean... values) {
-            putInPropertyMap(key, values);
-            return mBuilderTypeInstance;
-        }
-
-        /**
-         * Sets one or multiple {@code long} values for a property, replacing its previous
-         * values.
-         *
-         * @param key The key associated with the {@code values}.
-         * @param values The {@code long} values of the property.
-         */
-        @NonNull
-        public BuilderType setProperty(@NonNull String key, @NonNull long... values) {
-            putInPropertyMap(key, values);
-            return mBuilderTypeInstance;
-        }
-
-        /**
-         * Sets one or multiple {@code double} values for a property, replacing its previous
-         * values.
-         *
-         * @param key The key associated with the {@code values}.
-         * @param values The {@code double} values of the property.
-         */
-        @NonNull
-        public BuilderType setProperty(@NonNull String key, @NonNull double... values) {
-            putInPropertyMap(key, values);
-            return mBuilderTypeInstance;
-        }
-
-        /**
-         * Sets one or multiple {@code byte[]} for a property, replacing its previous values.
-         *
-         * @param key The key associated with the {@code values}.
-         * @param values The {@code byte[]} of the property.
-         */
-        @NonNull
-        public BuilderType setProperty(@NonNull String key, @NonNull byte[]... values) {
-            putInPropertyMap(key, values);
-            return mBuilderTypeInstance;
-        }
-
-        /**
-         * Sets one or multiple {@link AppSearchDocument} values for a property, replacing its
-         * previous values.
-         *
-         * @param key The key associated with the {@code values}.
-         * @param values The {@link AppSearchDocument} values of the property.
-         */
-        @NonNull
-        public BuilderType setProperty(@NonNull String key, @NonNull AppSearchDocument... values) {
-            putInPropertyMap(key, values);
-            return mBuilderTypeInstance;
-        }
-
-        private void putInPropertyMap(@NonNull String key, @NonNull String[] values)
-                throws IllegalArgumentException {
-            Objects.requireNonNull(key);
-            Objects.requireNonNull(values);
-            validateRepeatedPropertyLength(key, values.length);
-            for (int i = 0; i < values.length; i++) {
-                if (values[i] == null) {
-                    throw new IllegalArgumentException("The String at " + i + " is null.");
-                } else if (values[i].length() > MAX_STRING_LENGTH) {
-                    throw new IllegalArgumentException("The String at " + i + " length is: "
-                            + values[i].length()  + ", which exceeds length limit: "
-                            + MAX_STRING_LENGTH + ".");
-                }
-            }
-            mProperties.put(key, values);
-        }
-
-        private void putInPropertyMap(@NonNull String key, @NonNull boolean[] values) {
-            Objects.requireNonNull(key);
-            Objects.requireNonNull(values);
-            validateRepeatedPropertyLength(key, values.length);
-            mProperties.put(key, values);
-        }
-
-        private void putInPropertyMap(@NonNull String key, @NonNull double[] values) {
-            Objects.requireNonNull(key);
-            Objects.requireNonNull(values);
-            validateRepeatedPropertyLength(key, values.length);
-            mProperties.put(key, values);
-        }
-
-        private void putInPropertyMap(@NonNull String key, @NonNull long[] values) {
-            Objects.requireNonNull(key);
-            Objects.requireNonNull(values);
-            validateRepeatedPropertyLength(key, values.length);
-            mProperties.put(key, values);
-        }
-
-        private void putInPropertyMap(@NonNull String key, @NonNull byte[][] values) {
-            Objects.requireNonNull(key);
-            Objects.requireNonNull(values);
-            validateRepeatedPropertyLength(key, values.length);
-            mProperties.put(key, values);
-        }
-
-        private void putInPropertyMap(@NonNull String key, @NonNull AppSearchDocument[] values) {
-            Objects.requireNonNull(key);
-            Objects.requireNonNull(values);
-            for (int i = 0; i < values.length; i++) {
-                if (values[i] == null) {
-                    throw new IllegalArgumentException("The document at " + i + " is null.");
-                }
-            }
-            validateRepeatedPropertyLength(key, values.length);
-            mProperties.put(key, values);
-        }
-
-        private static void validateRepeatedPropertyLength(@NonNull String key, int length) {
-            if (length == 0) {
-                throw new IllegalArgumentException("The input array is empty.");
-            } else if (length > MAX_REPEATED_PROPERTY_LENGTH) {
-                throw new IllegalArgumentException(
-                        "Repeated property \"" + key + "\" has length " + length
-                                + ", which exceeds the limit of "
-                                + MAX_REPEATED_PROPERTY_LENGTH);
-            }
-        }
-
-        /** Builds the {@link AppSearchDocument} object. */
-        @NonNull
-        public AppSearchDocument build() {
-            // Build proto by sorting the keys in mProperties to exclude the influence of
-            // order. Therefore documents will generate same proto as long as the contents are
-            // same. Note that the order of repeated fields is still preserved.
-            ArrayList<String> keys = new ArrayList<>(mProperties.keySet());
-            Collections.sort(keys);
-            for (int i = 0; i < keys.size(); i++) {
-                String name = keys.get(i);
-                Object values = mProperties.get(name);
-                PropertyProto.Builder propertyProto = PropertyProto.newBuilder().setName(name);
-                if (values instanceof boolean[]) {
-                    for (boolean value : (boolean[]) values) {
-                        propertyProto.addBooleanValues(value);
-                    }
-                } else if (values instanceof long[]) {
-                    for (long value : (long[]) values) {
-                        propertyProto.addInt64Values(value);
-                    }
-                } else if (values instanceof double[]) {
-                    for (double value : (double[]) values) {
-                        propertyProto.addDoubleValues(value);
-                    }
-                } else if (values instanceof String[]) {
-                    for (String value : (String[]) values) {
-                        propertyProto.addStringValues(value);
-                    }
-                } else if (values instanceof AppSearchDocument[]) {
-                    for (AppSearchDocument value : (AppSearchDocument[]) values) {
-                        propertyProto.addDocumentValues(value.getProto());
-                    }
-                } else if (values instanceof byte[][]) {
-                    for (byte[] value : (byte[][]) values) {
-                        propertyProto.addBytesValues(ByteString.copyFrom(value));
-                    }
-                } else {
-                    throw new IllegalStateException(
-                            "Property \"" + name + "\" has unsupported value type \""
-                                    + values.getClass().getSimpleName() + "\"");
-                }
-                mProtoBuilder.addProperties(propertyProto);
-            }
-            return new AppSearchDocument(mProtoBuilder.build(), mProperties);
-        }
-    }
-}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchEmail.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchEmail.java
index b13dd9f..5f2fabe 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchEmail.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchEmail.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,20 +16,26 @@
 
 package android.app.appsearch;
 
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+
 import android.app.appsearch.AppSearchSchema.PropertyConfig;
 
 /**
- * Encapsulates a {@link AppSearchDocument} that represent an email.
+ * Encapsulates a {@link GenericDocument} that represent an email.
  *
- * <p>This class is a higher level implement of {@link AppSearchDocument}.
+ * <p>This class is a higher level implement of {@link GenericDocument}.
  *
  * <p>This class will eventually migrate to Jetpack, where it will become public API.
  *
  * @hide
  */
-public class AppSearchEmail extends AppSearchDocument {
+
+public class AppSearchEmail extends GenericDocument {
+    /** The name of the schema type for {@link AppSearchEmail} documents.*/
+    public static final String SCHEMA_TYPE = "builtin:Email";
+
     private static final String KEY_FROM = "from";
     private static final String KEY_TO = "to";
     private static final String KEY_CC = "cc";
@@ -37,46 +43,43 @@
     private static final String KEY_SUBJECT = "subject";
     private static final String KEY_BODY = "body";
 
-    /** The name of the schema type for {@link AppSearchEmail} documents.*/
-    public static final String SCHEMA_TYPE = "builtin:Email";
-
     public static final AppSearchSchema SCHEMA = new AppSearchSchema.Builder(SCHEMA_TYPE)
-            .addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_FROM)
+            .addProperty(new PropertyConfig.Builder(KEY_FROM)
                     .setDataType(PropertyConfig.DATA_TYPE_STRING)
                     .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
                     .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
                     .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
                     .build()
 
-            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_TO)
+            ).addProperty(new PropertyConfig.Builder(KEY_TO)
                     .setDataType(PropertyConfig.DATA_TYPE_STRING)
                     .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
                     .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
                     .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
                     .build()
 
-            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_CC)
+            ).addProperty(new PropertyConfig.Builder(KEY_CC)
                     .setDataType(PropertyConfig.DATA_TYPE_STRING)
                     .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
                     .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
                     .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
                     .build()
 
-            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_BCC)
+            ).addProperty(new PropertyConfig.Builder(KEY_BCC)
                     .setDataType(PropertyConfig.DATA_TYPE_STRING)
                     .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
                     .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
                     .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
                     .build()
 
-            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_SUBJECT)
+            ).addProperty(new PropertyConfig.Builder(KEY_SUBJECT)
                     .setDataType(PropertyConfig.DATA_TYPE_STRING)
                     .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
                     .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
                     .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
                     .build()
 
-            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_BODY)
+            ).addProperty(new PropertyConfig.Builder(KEY_BODY)
                     .setDataType(PropertyConfig.DATA_TYPE_STRING)
                     .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
                     .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
@@ -87,12 +90,11 @@
 
     /**
      * Creates a new {@link AppSearchEmail} from the contents of an existing
-     * {@link AppSearchDocument}.
+     * {@link GenericDocument}.
      *
-     * @param document The {@link AppSearchDocument} containing the email content.
-     * @hide
+     * @param document The {@link GenericDocument} containing the email content.
      */
-    public AppSearchEmail(@NonNull AppSearchDocument document) {
+    public AppSearchEmail(@NonNull GenericDocument document) {
         super(document);
     }
 
@@ -101,7 +103,6 @@
      *
      * @return Returns the subject of {@link AppSearchEmail} or {@code null} if it's not been set
      *         yet.
-     * @hide
      */
     @Nullable
     public String getFrom() {
@@ -113,7 +114,6 @@
      *
      * @return Returns the destination addresses of {@link AppSearchEmail} or {@code null} if it's
      *         not been set yet.
-     * @hide
      */
     @Nullable
     public String[] getTo() {
@@ -125,7 +125,6 @@
      *
      * @return Returns the CC list of {@link AppSearchEmail} or {@code null} if it's not been set
      *         yet.
-     * @hide
      */
     @Nullable
     public String[] getCc() {
@@ -137,7 +136,6 @@
      *
      * @return Returns the BCC list of {@link AppSearchEmail} or {@code null} if it's not been set
      *         yet.
-     * @hide
      */
     @Nullable
     public String[] getBcc() {
@@ -149,7 +147,6 @@
      *
      * @return Returns the value subject of {@link AppSearchEmail} or {@code null} if it's not been
      *         set yet.
-     * @hide
      */
     @Nullable
     public String getSubject() {
@@ -160,7 +157,6 @@
      * Get the body of {@link AppSearchEmail}.
      *
      * @return Returns the body of {@link AppSearchEmail} or {@code null} if it's not been set yet.
-     * @hide
      */
     @Nullable
     public String getBody() {
@@ -169,14 +165,12 @@
 
     /**
      * The builder class for {@link AppSearchEmail}.
-     * @hide
      */
-    public static class Builder extends AppSearchDocument.Builder<AppSearchEmail.Builder> {
+    public static class Builder extends GenericDocument.Builder<AppSearchEmail.Builder> {
 
         /**
          * Create a new {@link AppSearchEmail.Builder}
          * @param uri The Uri of the Email.
-         * @hide
          */
         public Builder(@NonNull String uri) {
             super(uri, SCHEMA_TYPE);
@@ -184,7 +178,6 @@
 
         /**
          * Set the from address of {@link AppSearchEmail}
-         * @hide
          */
         @NonNull
         public AppSearchEmail.Builder setFrom(@NonNull String from) {
@@ -194,7 +187,6 @@
 
         /**
          * Set the destination address of {@link AppSearchEmail}
-         * @hide
          */
         @NonNull
         public AppSearchEmail.Builder setTo(@NonNull String... to) {
@@ -204,7 +196,6 @@
 
         /**
          * Set the CC list of {@link AppSearchEmail}
-         * @hide
          */
         @NonNull
         public AppSearchEmail.Builder setCc(@NonNull String... cc) {
@@ -214,7 +205,6 @@
 
         /**
          * Set the BCC list of {@link AppSearchEmail}
-         * @hide
          */
         @NonNull
         public AppSearchEmail.Builder setBcc(@NonNull String... bcc) {
@@ -224,7 +214,6 @@
 
         /**
          * Set the subject of {@link AppSearchEmail}
-         * @hide
          */
         @NonNull
         public AppSearchEmail.Builder setSubject(@NonNull String subject) {
@@ -234,7 +223,6 @@
 
         /**
          * Set the body of {@link AppSearchEmail}
-         * @hide
          */
         @NonNull
         public AppSearchEmail.Builder setBody(@NonNull String body) {
@@ -242,11 +230,7 @@
             return this;
         }
 
-        /**
-         * Builds the {@link AppSearchEmail} object.
-         *
-         * @hide
-         */
+        /** Builds the {@link AppSearchEmail} object. */
         @NonNull
         @Override
         public AppSearchEmail build() {
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
index 64264c0..18bc59b 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
@@ -18,19 +18,13 @@
 import android.annotation.NonNull;
 import android.annotation.SystemService;
 import android.content.Context;
+import android.os.Bundle;
 import android.os.RemoteException;
 
 import com.android.internal.infra.AndroidFuture;
-
-import com.google.android.icing.proto.DocumentProto;
-import com.google.android.icing.proto.SchemaProto;
-import com.google.android.icing.proto.SearchResultProto;
-import com.google.android.icing.proto.SearchSpecProto;
-import com.google.android.icing.proto.StatusProto;
-import com.google.protobuf.InvalidProtocolBufferException;
+import com.android.internal.util.Preconditions;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ExecutionException;
@@ -46,6 +40,7 @@
 // TODO(b/148046169): This class header needs a detailed example/tutorial.
 @SystemService(Context.APP_SEARCH_SERVICE)
 public class AppSearchManager {
+    private static final String DEFAULT_DATABASE = "";
     private final IAppSearchManager mService;
 
     /** @hide */
@@ -79,8 +74,8 @@
      *     <li>Removal of an existing type
      *     <li>Removal of a property from a type
      *     <li>Changing the data type ({@code boolean}, {@code long}, etc.) of an existing property
-     *     <li>For properties of {@code AppSearchDocument} type, changing the schema type of
-     *         {@code AppSearchDocument}s of that property
+     *     <li>For properties of {@code GenericDocument} type, changing the schema type of
+     *         {@code GenericDocument}s of that property
      *     <li>Changing the cardinality of a data type to be more restrictive (e.g. changing an
      *         {@link android.app.appsearch.AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL
      *             OPTIONAL} property into a
@@ -102,50 +97,23 @@
      * <p>It is a no-op to set the same schema as has been previously set; this is handled
      * efficiently.
      *
-     * @param schemas The schema configs for the types used by the calling app.
+     * @param request The schema update request.
      * @return the result of performing this operation.
      *
      * @hide
      */
     @NonNull
-    public AppSearchResult<Void> setSchema(@NonNull AppSearchSchema... schemas) {
-        return setSchema(Arrays.asList(schemas), /*forceOverride=*/false);
-    }
-
-    /**
-     * Sets the schema being used by documents provided to the {@link #putDocuments} method.
-     *
-     * <p>This method is similar to {@link #setSchema(AppSearchSchema...)}, except for the
-     * {@code forceOverride} parameter. If a backwards-incompatible schema is specified but the
-     * {@code forceOverride} parameter is set to {@code true}, instead of returning an
-     * {@link AppSearchResult} with the {@link AppSearchResult#RESULT_INVALID_SCHEMA} code, all
-     * documents which are not compatible with the new schema will be deleted and the incompatible
-     * schema will be applied.
-     *
-     * @param schemas The schema configs for the types used by the calling app.
-     * @param forceOverride Whether to force the new schema to be applied even if there are
-     *     incompatible changes versus the previously set schema. Documents which are incompatible
-     *     with the new schema will be deleted.
-     * @return the result of performing this operation.
-     *
-     * @hide
-     */
-    @NonNull
-    public AppSearchResult<Void> setSchema(
-            @NonNull List<AppSearchSchema> schemas, boolean forceOverride) {
-        // Prepare the merged schema for transmission.
-        SchemaProto.Builder schemaProtoBuilder = SchemaProto.newBuilder();
-        for (AppSearchSchema schema : schemas) {
-            schemaProtoBuilder.addTypes(schema.getProto());
-        }
-
-        // Serialize and send the schema.
+    public AppSearchResult<Void> setSchema(@NonNull SetSchemaRequest request) {
+        Preconditions.checkNotNull(request);
         // TODO: This should use com.android.internal.infra.RemoteStream or another mechanism to
         //  avoid binder limits.
-        byte[] schemaBytes = schemaProtoBuilder.build().toByteArray();
+        List<Bundle> schemaBundles = new ArrayList<>(request.getSchemas().size());
+        for (AppSearchSchema schema : request.getSchemas()) {
+            schemaBundles.add(schema.getBundle());
+        }
         AndroidFuture<AppSearchResult> future = new AndroidFuture<>();
         try {
-            mService.setSchema(schemaBytes, forceOverride, future);
+            mService.setSchema(DEFAULT_DATABASE, schemaBundles, request.isForceOverride(), future);
         } catch (RemoteException e) {
             future.completeExceptionally(e);
         }
@@ -153,31 +121,32 @@
     }
 
     /**
-     * Index {@link AppSearchDocument}s into AppSearch.
+     * Index {@link GenericDocument}s into AppSearch.
      *
      * <p>You should not call this method directly; instead, use the
      * {@code AppSearch#putDocuments()} API provided by JetPack.
      *
-     * <p>Each {@link AppSearchDocument}'s {@code schemaType} field must be set to the name of a
+     * <p>Each {@link GenericDocument}'s {@code schemaType} field must be set to the name of a
      * schema type previously registered via the {@link #setSchema} method.
      *
-     * @param documents {@link AppSearchDocument}s that need to be indexed.
-     * @return An {@link AppSearchBatchResult} mapping the document URIs to {@link Void} if they
-     *     were successfully indexed, or a {@link Throwable} describing the failure if they could
-     *     not be indexed.
+     * @param request {@link PutDocumentsRequest} containing documents to be indexed
+     * @return The pending result of performing this operation. The keys of the returned
+     * {@link AppSearchBatchResult} are the URIs of the input documents. The values are
+     * {@code null} if they were successfully indexed, or a failed {@link AppSearchResult}
+     * otherwise.
      * @hide
      */
-    public AppSearchBatchResult<String, Void> putDocuments(
-            @NonNull List<AppSearchDocument> documents) {
+    public AppSearchBatchResult<String, Void> putDocuments(@NonNull PutDocumentsRequest request) {
         // TODO(b/146386470): Transmit these documents as a RemoteStream instead of sending them in
         // one big list.
-        List<byte[]> documentsBytes = new ArrayList<>(documents.size());
-        for (AppSearchDocument document : documents) {
-            documentsBytes.add(document.getProto().toByteArray());
+        List<GenericDocument> documents = request.getDocuments();
+        List<Bundle> documentBundles = new ArrayList<>(documents.size());
+        for (int i = 0; i < documents.size(); i++) {
+            documentBundles.add(documents.get(i).getBundle());
         }
         AndroidFuture<AppSearchBatchResult> future = new AndroidFuture<>();
         try {
-            mService.putDocuments(documentsBytes, future);
+            mService.putDocuments(DEFAULT_DATABASE, documentBundles, future);
         } catch (RemoteException e) {
             future.completeExceptionally(e);
         }
@@ -185,65 +154,59 @@
     }
 
     /**
-     * Retrieves {@link AppSearchDocument}s by URI.
+     * Retrieves {@link GenericDocument}s by URI.
      *
      * <p>You should not call this method directly; instead, use the
      * {@code AppSearch#getDocuments()} API provided by JetPack.
      *
-     * @param uris URIs of the documents to look up.
-     * @return An {@link AppSearchBatchResult} mapping the document URIs to
-     *     {@link AppSearchDocument} values if they were successfully retrieved, a {@code null}
-     *     failure if they were not found, or a {@link Throwable} failure describing the problem if
-     *     an error occurred.
+     * @param request {@link GetByUriRequest} containing URIs to be retrieved.
+     * @return The pending result of performing this operation. The keys of the returned
+     * {@link AppSearchBatchResult} are the input URIs. The values are the returned
+     * {@link GenericDocument}s on success, or a failed {@link AppSearchResult} otherwise.
+     * URIs that are not found will return a failed {@link AppSearchResult} with a result code
+     * of {@link AppSearchResult#RESULT_NOT_FOUND}.
      */
-    public AppSearchBatchResult<String, AppSearchDocument> getDocuments(
-            @NonNull List<String> uris) {
+    public AppSearchBatchResult<String, GenericDocument> getByUri(
+            @NonNull GetByUriRequest request) {
         // TODO(b/146386470): Transmit the result documents as a RemoteStream instead of sending
         //     them in one big list.
+        List<String> uris = new ArrayList<>(request.getUris());
         AndroidFuture<AppSearchBatchResult> future = new AndroidFuture<>();
         try {
-            mService.getDocuments(uris, future);
+            mService.getDocuments(DEFAULT_DATABASE, request.getNamespace(), uris, future);
         } catch (RemoteException e) {
             future.completeExceptionally(e);
         }
 
-        // Deserialize the protos into Document objects
-        AppSearchBatchResult<String, byte[]> protoResults = getFutureOrThrow(future);
-        AppSearchBatchResult.Builder<String, AppSearchDocument> documentResultBuilder =
+        // Translate from document bundles to GenericDocument instances
+        AppSearchBatchResult<String, Bundle> bundleResult = getFutureOrThrow(future);
+        AppSearchBatchResult.Builder<String, GenericDocument> documentResultBuilder =
                 new AppSearchBatchResult.Builder<>();
 
         // Translate successful results
-        for (Map.Entry<String, byte[]> protoResult : protoResults.getSuccesses().entrySet()) {
-            DocumentProto documentProto;
+        for (Map.Entry<String, Bundle> bundleEntry : bundleResult.getSuccesses().entrySet()) {
+            GenericDocument document;
             try {
-                documentProto = DocumentProto.parseFrom(protoResult.getValue());
-            } catch (InvalidProtocolBufferException e) {
-                documentResultBuilder.setFailure(
-                        protoResult.getKey(), AppSearchResult.RESULT_IO_ERROR, e.getMessage());
-                continue;
-            }
-            AppSearchDocument document;
-            try {
-                document = new AppSearchDocument(documentProto);
+                document = new GenericDocument(bundleEntry.getValue());
             } catch (Throwable t) {
                 // These documents went through validation, so how could this fail? We must have
                 // done something wrong.
                 documentResultBuilder.setFailure(
-                        protoResult.getKey(),
+                        bundleEntry.getKey(),
                         AppSearchResult.RESULT_INTERNAL_ERROR,
                         t.getMessage());
                 continue;
             }
-            documentResultBuilder.setSuccess(protoResult.getKey(), document);
+            documentResultBuilder.setSuccess(bundleEntry.getKey(), document);
         }
 
         // Translate failed results
-        for (Map.Entry<String, AppSearchResult<byte[]>> protoResult :
-                protoResults.getFailures().entrySet()) {
+        for (Map.Entry<String, AppSearchResult<Bundle>> bundleEntry :
+                bundleResult.getFailures().entrySet()) {
             documentResultBuilder.setFailure(
-                    protoResult.getKey(),
-                    protoResult.getValue().getResultCode(),
-                    protoResult.getValue().getErrorMessage());
+                    bundleEntry.getKey(),
+                    bundleEntry.getValue().getResultCode(),
+                    bundleEntry.getValue().getErrorMessage());
         }
 
         return documentResultBuilder.build();
@@ -292,62 +255,46 @@
      * @hide
      */
     @NonNull
-    public AppSearchResult<SearchResults> query(
+    public AppSearchResult<List<SearchResult>> query(
             @NonNull String queryExpression, @NonNull SearchSpec searchSpec) {
         // TODO(b/146386470): Transmit the result documents as a RemoteStream instead of sending
         //     them in one big list.
-        AndroidFuture<AppSearchResult> searchResultFuture = new AndroidFuture<>();
+        AndroidFuture<AppSearchResult> searchResultsFuture = new AndroidFuture<>();
         try {
-            SearchSpecProto searchSpecProto = searchSpec.getSearchSpecProto();
-            searchSpecProto = searchSpecProto.toBuilder().setQuery(queryExpression).build();
-            mService.query(
-                    searchSpecProto.toByteArray(),
-                    searchSpec.getResultSpecProto().toByteArray(),
-                    searchSpec.getScoringSpecProto().toByteArray(),
-                    searchResultFuture);
+            mService.query(DEFAULT_DATABASE, queryExpression,
+                    searchSpec.getBundle(), searchResultsFuture);
         } catch (RemoteException e) {
-            searchResultFuture.completeExceptionally(e);
+            searchResultsFuture.completeExceptionally(e);
         }
 
-        // Deserialize the protos into Document objects
-        AppSearchResult<byte[]> searchResultBytes = getFutureOrThrow(searchResultFuture);
-        if (!searchResultBytes.isSuccess()) {
+        // Translate the list of Bundle into a list of SearchResult
+        AppSearchResult<SearchResults> searchResultsResult = getFutureOrThrow(searchResultsFuture);
+        if (!searchResultsResult.isSuccess()) {
             return AppSearchResult.newFailedResult(
-                    searchResultBytes.getResultCode(), searchResultBytes.getErrorMessage());
+                    searchResultsResult.getResultCode(), searchResultsResult.getErrorMessage());
         }
-        SearchResultProto searchResultProto;
-        try {
-            searchResultProto = SearchResultProto.parseFrom(searchResultBytes.getResultValue());
-        } catch (InvalidProtocolBufferException e) {
-            return AppSearchResult.newFailedResult(
-                    AppSearchResult.RESULT_INTERNAL_ERROR, e.getMessage());
-        }
-        if (searchResultProto.getStatus().getCode() != StatusProto.Code.OK) {
-            // This should never happen; AppSearchManagerService should catch failed searchResults
-            // entries and transmit them as a failed AppSearchResult.
-            return AppSearchResult.newFailedResult(
-                    AppSearchResult.RESULT_INTERNAL_ERROR,
-                    searchResultProto.getStatus().getMessage());
-        }
-
-        return AppSearchResult.newSuccessfulResult(new SearchResults(searchResultProto));
+        SearchResults searchResults = searchResultsResult.getResultValue();
+        return AppSearchResult.newSuccessfulResult(searchResults.mResults);
     }
 
     /**
-     * Deletes {@link AppSearchDocument}s by URI.
+     * Deletes {@link GenericDocument}s by URI.
      *
      * <p>You should not call this method directly; instead, use the {@code AppSearch#delete()} API
      * provided by JetPack.
      *
-     * @param uris URIs of the documents to delete
-     * @return An {@link AppSearchBatchResult} mapping each URI to a {@code null} success if
-     *     deletion was successful, to a {@code null} failure if the document did not exist, or to a
-     *     {@code throwable} failure if deletion failed for another reason.
+     * @param request Request containing URIs to be removed.
+     * @return The pending result of performing this operation. The keys of the returned
+     * {@link AppSearchBatchResult} are the input URIs. The values are {@code null} on success,
+     * or a failed {@link AppSearchResult} otherwise. URIs that are not found will return a
+     * failed {@link AppSearchResult} with a result code of
+     * {@link AppSearchResult#RESULT_NOT_FOUND}.
      */
-    public AppSearchBatchResult<String, Void> delete(@NonNull List<String> uris) {
+    public AppSearchBatchResult<String, Void> removeByUri(@NonNull RemoveByUriRequest request) {
+        List<String> uris = new ArrayList<>(request.getUris());
         AndroidFuture<AppSearchBatchResult> future = new AndroidFuture<>();
         try {
-            mService.delete(uris, future);
+            mService.delete(DEFAULT_DATABASE, request.getNamespace(), uris, future);
         } catch (RemoteException e) {
             future.completeExceptionally(e);
         }
@@ -355,7 +302,7 @@
     }
 
     /**
-     * Deletes {@link android.app.appsearch.AppSearch.Document}s by schema type.
+     * Deletes {@link android.app.appsearch.GenericDocument}s by schema type.
      *
      * <p>You should not call this method directly; instead, use the
      * {@code AppSearch#deleteByType()} API provided by JetPack.
@@ -368,7 +315,7 @@
     public AppSearchBatchResult<String, Void> deleteByTypes(@NonNull List<String> schemaTypes) {
         AndroidFuture<AppSearchBatchResult> future = new AndroidFuture<>();
         try {
-            mService.deleteByTypes(schemaTypes, future);
+            mService.deleteByTypes(DEFAULT_DATABASE, schemaTypes, future);
         } catch (RemoteException e) {
             future.completeExceptionally(e);
         }
@@ -379,7 +326,7 @@
     public AppSearchResult<Void> deleteAll() {
         AndroidFuture<AppSearchResult> future = new AndroidFuture<>();
         try {
-            mService.deleteAll(future);
+            mService.deleteAll(DEFAULT_DATABASE, future);
         } catch (RemoteException e) {
             future.completeExceptionally(e);
         }
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java
index 7f38348..979eab9 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -32,9 +32,12 @@
  * @param <ValueType> The type of result object for successful calls.
  * @hide
  */
-public class AppSearchResult<ValueType> implements Parcelable {
-    /** Result codes from {@link AppSearchManager} methods. */
-    @IntDef(prefix = {"RESULT_"}, value = {
+public final class AppSearchResult<ValueType> implements Parcelable {
+    /**
+     * Result codes from {@link AppSearchManager} methods.
+     * @hide
+     */
+    @IntDef(value = {
             RESULT_OK,
             RESULT_UNKNOWN_ERROR,
             RESULT_INTERNAL_ERROR,
@@ -120,15 +123,18 @@
     }
 
     /**
-     * Returns the returned value associated with this result.
+     * Returns the result value associated with this result, if it was successful.
      *
-     * <p>If {@link #isSuccess} is {@code false}, the result value is always {@code null}. The value
-     * may be {@code null} even if {@link #isSuccess} is {@code true}. See the documentation of the
-     * particular {@link AppSearchManager} call producing this {@link AppSearchResult} for what is
-     * returned by {@link #getResultValue}.
+     * <p>See the documentation of the particular {@link AppSearchManager} call producing this
+     * {@link AppSearchResult} for what is placed in the result value by that call.
+     *
+     * @throws IllegalStateException if this {@link AppSearchResult} is not successful.
      */
     @Nullable
     public ValueType getResultValue() {
+        if (!isSuccess()) {
+            throw new IllegalStateException("AppSearchResult is a failure: " + this);
+        }
         return mResultValue;
     }
 
@@ -146,14 +152,14 @@
     }
 
     @Override
-    public boolean equals(Object other) {
+    public boolean equals(@Nullable Object other) {
         if (this == other) {
             return true;
         }
         if (!(other instanceof AppSearchResult)) {
             return false;
         }
-        AppSearchResult<?> otherResult = (AppSearchResult) other;
+        AppSearchResult<?> otherResult = (AppSearchResult<?>) other;
         return mResultCode == otherResult.mResultCode
                 && Objects.equals(mResultValue, otherResult.mResultValue)
                 && Objects.equals(mErrorMessage, otherResult.mErrorMessage);
@@ -168,9 +174,9 @@
     @NonNull
     public String toString() {
         if (isSuccess()) {
-            return "AppSearchResult [SUCCESS]: " + mResultValue;
+            return "[SUCCESS]: " + mResultValue;
         }
-        return "AppSearchResult [FAILURE(" + mResultCode + ")]: " + mErrorMessage;
+        return "[FAILURE(" + mResultCode + ")]: " + mErrorMessage;
     }
 
     @Override
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java
index 4b0b41b..90e4df6 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,18 +16,18 @@
 
 package android.app.appsearch;
 
+import android.os.Bundle;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+
+import android.app.appsearch.exceptions.IllegalSchemaException;
 import android.util.ArraySet;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import com.google.android.icing.proto.PropertyConfigProto;
-import com.google.android.icing.proto.SchemaTypeConfigProto;
-import com.google.android.icing.proto.TermMatchType;
+import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
 import java.util.Set;
 
 /**
@@ -36,45 +36,64 @@
  * <p>For example, an e-mail message or a music recording could be a schema type.
  *
  * <p>The schema consists of type information, properties, and config (like tokenization type).
- *
  * @hide
  */
 public final class AppSearchSchema {
-    private final SchemaTypeConfigProto mProto;
+    /** @hide */
+    
+    public static final String SCHEMA_TYPE_FIELD = "schemaType";
 
-    private AppSearchSchema(SchemaTypeConfigProto proto) {
-        mProto = proto;
+    /** @hide */
+    
+    public static final String PROPERTIES_FIELD = "properties";
+
+    private final Bundle mBundle;
+
+    /** @hide */
+    
+    public AppSearchSchema(@NonNull Bundle bundle) {
+        Preconditions.checkNotNull(bundle);
+        mBundle = bundle;
     }
 
     /**
-     * Returns the {@link SchemaTypeConfigProto} populated by this builder.
+     * Returns the {@link Bundle} populated by this builder.
      * @hide
      */
+    
     @NonNull
-    @VisibleForTesting
-    public SchemaTypeConfigProto getProto() {
-        return mProto;
+    public Bundle getBundle() {
+        return mBundle;
     }
 
     @Override
     public String toString() {
-        return mProto.toString();
+        return mBundle.toString();
     }
 
     /** Builder for {@link AppSearchSchema objects}. */
     public static final class Builder {
-        private final SchemaTypeConfigProto.Builder mProtoBuilder =
-                SchemaTypeConfigProto.newBuilder();
+        private final String mTypeName;
+        private final ArrayList<Bundle> mProperties = new ArrayList<>();
+        private final Set<String> mPropertyNames = new ArraySet<>();
+        private boolean mBuilt = false;
 
         /** Creates a new {@link AppSearchSchema.Builder}. */
         public Builder(@NonNull String typeName) {
-            mProtoBuilder.setSchemaType(typeName);
+            Preconditions.checkNotNull(typeName);
+            mTypeName = typeName;
         }
 
         /** Adds a property to the given type. */
         @NonNull
         public AppSearchSchema.Builder addProperty(@NonNull PropertyConfig propertyConfig) {
-            mProtoBuilder.addProperties(propertyConfig.mProto);
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkNotNull(propertyConfig);
+            if (!mPropertyNames.add(propertyConfig.mName)) {
+                throw new IllegalSchemaException(
+                        "Property defined more than once: " + propertyConfig.mName);
+            }
+            mProperties.add(propertyConfig.mBundle);
             return this;
         }
 
@@ -85,15 +104,12 @@
          */
         @NonNull
         public AppSearchSchema build() {
-            Set<String> propertyNames = new ArraySet<>();
-            for (PropertyConfigProto propertyConfigProto : mProtoBuilder.getPropertiesList()) {
-                if (!propertyNames.add(propertyConfigProto.getPropertyName())) {
-                    throw new IllegalSchemaException(
-                            "Property defined more than once: "
-                                    + propertyConfigProto.getPropertyName());
-                }
-            }
-            return new AppSearchSchema(mProtoBuilder.build());
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Bundle bundle = new Bundle();
+            bundle.putString(AppSearchSchema.SCHEMA_TYPE_FIELD, mTypeName);
+            bundle.putParcelableArrayList(AppSearchSchema.PROPERTIES_FIELD, mProperties);
+            mBuilt = true;
+            return new AppSearchSchema(bundle);
         }
     }
 
@@ -104,10 +120,37 @@
      * a property.
      */
     public static final class PropertyConfig {
-        /** Physical data-types of the contents of the property. */
+        /** @hide */
+        
+        public static final String NAME_FIELD = "name";
+
+        /** @hide */
+        
+        public static final String DATA_TYPE_FIELD = "dataType";
+
+        /** @hide */
+        
+        public static final String SCHEMA_TYPE_FIELD = "schemaType";
+
+        /** @hide */
+        
+        public static final String CARDINALITY_FIELD = "cardinality";
+
+        /** @hide */
+        
+        public static final String INDEXING_TYPE_FIELD = "indexingType";
+
+        /** @hide */
+        
+        public static final String TOKENIZER_TYPE_FIELD = "tokenizerType";
+
+        /**
+         * Physical data-types of the contents of the property.
+         * @hide
+         */
         // NOTE: The integer values of these constants must match the proto enum constants in
         // com.google.android.icing.proto.PropertyConfigProto.DataType.Code.
-        @IntDef(prefix = {"DATA_TYPE_"}, value = {
+        @IntDef(value = {
                 DATA_TYPE_STRING,
                 DATA_TYPE_INT64,
                 DATA_TYPE_DOUBLE,
@@ -133,10 +176,13 @@
          */
         public static final int DATA_TYPE_DOCUMENT = 6;
 
-        /** The cardinality of the property (whether it is required, optional or repeated). */
+        /**
+         * The cardinality of the property (whether it is required, optional or repeated).
+         * @hide
+         */
         // NOTE: The integer values of these constants must match the proto enum constants in
         // com.google.android.icing.proto.PropertyConfigProto.Cardinality.Code.
-        @IntDef(prefix = {"CARDINALITY_"}, value = {
+        @IntDef(value = {
                 CARDINALITY_REPEATED,
                 CARDINALITY_OPTIONAL,
                 CARDINALITY_REQUIRED,
@@ -153,8 +199,11 @@
         /** Exactly one value [1]. */
         public static final int CARDINALITY_REQUIRED = 3;
 
-        /** Encapsulates the configurations on how AppSearch should query/index these terms. */
-        @IntDef(prefix = {"INDEXING_TYPE_"}, value = {
+        /**
+         * Encapsulates the configurations on how AppSearch should query/index these terms.
+         * @hide
+         */
+        @IntDef(value = {
                 INDEXING_TYPE_NONE,
                 INDEXING_TYPE_EXACT_TERMS,
                 INDEXING_TYPE_PREFIXES,
@@ -188,10 +237,13 @@
          */
         public static final int INDEXING_TYPE_PREFIXES = 2;
 
-        /** Configures how tokens should be extracted from this property. */
+        /**
+         * Configures how tokens should be extracted from this property.
+         * @hide
+         */
         // NOTE: The integer values of these constants must match the proto enum constants in
         // com.google.android.icing.proto.IndexingConfig.TokenizerType.Code.
-        @IntDef(prefix = {"TOKENIZER_TYPE_"}, value = {
+        @IntDef(value = {
                 TOKENIZER_TYPE_NONE,
                 TOKENIZER_TYPE_PLAIN,
         })
@@ -207,15 +259,17 @@
         /** Tokenization for plain text. */
         public static final int TOKENIZER_TYPE_PLAIN = 1;
 
-        private final PropertyConfigProto mProto;
+        final String mName;
+        final Bundle mBundle;
 
-        private PropertyConfig(PropertyConfigProto proto) {
-            mProto = proto;
+        PropertyConfig(@NonNull String name, @NonNull Bundle bundle) {
+            mName = Preconditions.checkNotNull(name);
+            mBundle = Preconditions.checkNotNull(bundle);
         }
 
         @Override
         public String toString() {
-            return mProto.toString();
+            return mBundle.toString();
         }
 
         /**
@@ -232,15 +286,14 @@
          * is also required.
          */
         public static final class Builder {
-            private final PropertyConfigProto.Builder mPropertyConfigProto =
-                    PropertyConfigProto.newBuilder();
-            private final com.google.android.icing.proto.IndexingConfig.Builder
-                    mIndexingConfigProto =
-                        com.google.android.icing.proto.IndexingConfig.newBuilder();
+            private final String mName;
+            private final Bundle mBundle = new Bundle();
+            private boolean mBuilt = false;
 
             /** Creates a new {@link PropertyConfig.Builder}. */
             public Builder(@NonNull String propertyName) {
-                mPropertyConfigProto.setPropertyName(propertyName);
+                mName = Preconditions.checkNotNull(propertyName);
+                mBundle.putString(NAME_FIELD, propertyName);
             }
 
             /**
@@ -250,24 +303,24 @@
              */
             @NonNull
             public PropertyConfig.Builder setDataType(@DataType int dataType) {
-                PropertyConfigProto.DataType.Code dataTypeProto =
-                        PropertyConfigProto.DataType.Code.forNumber(dataType);
-                if (dataTypeProto == null) {
-                    throw new IllegalArgumentException("Invalid dataType: " + dataType);
-                }
-                mPropertyConfigProto.setDataType(dataTypeProto);
+                Preconditions.checkState(!mBuilt, "Builder has already been used");
+                Preconditions.checkArgumentInRange(
+                        dataType, DATA_TYPE_STRING, DATA_TYPE_DOCUMENT, "dataType");
+                mBundle.putInt(DATA_TYPE_FIELD, dataType);
                 return this;
             }
 
             /**
              * The logical schema-type of the contents of this property.
              *
-             * <p>Only required when {@link #setDataType(int)} is set to
+             * <p>Only required when {@link #setDataType} is set to
              * {@link #DATA_TYPE_DOCUMENT}. Otherwise, it is ignored.
              */
             @NonNull
             public PropertyConfig.Builder setSchemaType(@NonNull String schemaType) {
-                mPropertyConfigProto.setSchemaType(schemaType);
+                Preconditions.checkState(!mBuilt, "Builder has already been used");
+                Preconditions.checkNotNull(schemaType);
+                mBundle.putString(SCHEMA_TYPE_FIELD, schemaType);
                 return this;
             }
 
@@ -278,12 +331,10 @@
              */
             @NonNull
             public PropertyConfig.Builder setCardinality(@Cardinality int cardinality) {
-                PropertyConfigProto.Cardinality.Code cardinalityProto =
-                        PropertyConfigProto.Cardinality.Code.forNumber(cardinality);
-                if (cardinalityProto == null) {
-                    throw new IllegalArgumentException("Invalid cardinality: " + cardinality);
-                }
-                mPropertyConfigProto.setCardinality(cardinalityProto);
+                Preconditions.checkState(!mBuilt, "Builder has already been used");
+                Preconditions.checkArgumentInRange(
+                        cardinality, CARDINALITY_REPEATED, CARDINALITY_REQUIRED, "cardinality");
+                mBundle.putInt(CARDINALITY_FIELD, cardinality);
                 return this;
             }
 
@@ -292,35 +343,20 @@
              */
             @NonNull
             public PropertyConfig.Builder setIndexingType(@IndexingType int indexingType) {
-                TermMatchType.Code termMatchTypeProto;
-                switch (indexingType) {
-                    case INDEXING_TYPE_NONE:
-                        termMatchTypeProto = TermMatchType.Code.UNKNOWN;
-                        break;
-                    case INDEXING_TYPE_EXACT_TERMS:
-                        termMatchTypeProto = TermMatchType.Code.EXACT_ONLY;
-                        break;
-                    case INDEXING_TYPE_PREFIXES:
-                        termMatchTypeProto = TermMatchType.Code.PREFIX;
-                        break;
-                    default:
-                        throw new IllegalArgumentException("Invalid indexingType: " + indexingType);
-                }
-                mIndexingConfigProto.setTermMatchType(termMatchTypeProto);
+                Preconditions.checkState(!mBuilt, "Builder has already been used");
+                Preconditions.checkArgumentInRange(
+                        indexingType, INDEXING_TYPE_NONE, INDEXING_TYPE_PREFIXES, "indexingType");
+                mBundle.putInt(INDEXING_TYPE_FIELD, indexingType);
                 return this;
             }
 
             /** Configures how this property should be tokenized (split into words). */
             @NonNull
             public PropertyConfig.Builder setTokenizerType(@TokenizerType int tokenizerType) {
-                com.google.android.icing.proto.IndexingConfig.TokenizerType.Code
-                        tokenizerTypeProto =
-                            com.google.android.icing.proto.IndexingConfig
-                                .TokenizerType.Code.forNumber(tokenizerType);
-                if (tokenizerTypeProto == null) {
-                    throw new IllegalArgumentException("Invalid tokenizerType: " + tokenizerType);
-                }
-                mIndexingConfigProto.setTokenizerType(tokenizerTypeProto);
+                Preconditions.checkState(!mBuilt, "Builder has already been used");
+                Preconditions.checkArgumentInRange(
+                        tokenizerType, TOKENIZER_TYPE_NONE, TOKENIZER_TYPE_PLAIN, "tokenizerType");
+                mBundle.putInt(TOKENIZER_TYPE_FIELD, tokenizerType);
                 return this;
             }
 
@@ -334,25 +370,23 @@
              */
             @NonNull
             public PropertyConfig build() {
-                mPropertyConfigProto.setIndexingConfig(mIndexingConfigProto);
+                Preconditions.checkState(!mBuilt, "Builder has already been used");
                 // TODO(b/147692920): Send the schema to Icing Lib for official validation, instead
                 //     of partially reimplementing some of the validation Icing does here.
-                if (mPropertyConfigProto.getDataType()
-                        == PropertyConfigProto.DataType.Code.UNKNOWN) {
+                if (!mBundle.containsKey(DATA_TYPE_FIELD)) {
                     throw new IllegalSchemaException("Missing field: dataType");
                 }
-                if (mPropertyConfigProto.getSchemaType().isEmpty()
-                        && mPropertyConfigProto.getDataType()
-                            == PropertyConfigProto.DataType.Code.DOCUMENT) {
+                if (mBundle.getString(SCHEMA_TYPE_FIELD, "").isEmpty()
+                        && mBundle.getInt(DATA_TYPE_FIELD) == DATA_TYPE_DOCUMENT) {
                     throw new IllegalSchemaException(
                             "Missing field: schemaType (required for configs with "
                                     + "dataType = DOCUMENT)");
                 }
-                if (mPropertyConfigProto.getCardinality()
-                        == PropertyConfigProto.Cardinality.Code.UNKNOWN) {
+                if (!mBundle.containsKey(CARDINALITY_FIELD)) {
                     throw new IllegalSchemaException("Missing field: cardinality");
                 }
-                return new PropertyConfig(mPropertyConfigProto.build());
+                mBuilt = true;
+                return new PropertyConfig(mName, mBundle);
             }
         }
     }
diff --git a/apex/appsearch/framework/java/android/app/appsearch/GenericDocument.java b/apex/appsearch/framework/java/android/app/appsearch/GenericDocument.java
new file mode 100644
index 0000000..9fe2c67
--- /dev/null
+++ b/apex/appsearch/framework/java/android/app/appsearch/GenericDocument.java
@@ -0,0 +1,923 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch;
+
+import android.annotation.SuppressLint;
+import android.os.Bundle;
+import android.util.Log;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import android.app.appsearch.exceptions.AppSearchException;
+import com.android.internal.util.Preconditions;
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Set;
+
+/**
+ * Represents a document unit.
+ *
+ * <p>Documents are constructed via {@link GenericDocument.Builder}.
+ * @hide
+ */
+public class GenericDocument {
+    private static final String TAG = "GenericDocument";
+
+    /** The default empty namespace.*/
+    public static final String DEFAULT_NAMESPACE = "";
+
+    /**
+     * The maximum number of elements in a repeatable field. Will reject the request if exceed
+     * this limit.
+     */
+    private static final int MAX_REPEATED_PROPERTY_LENGTH = 100;
+
+    /**
+     * The maximum {@link String#length} of a {@link String} field. Will reject the request if
+     * {@link String}s longer than this.
+     */
+    private static final int MAX_STRING_LENGTH = 20_000;
+
+    /** The maximum number of indexed properties a document can have. */
+    private static final int MAX_INDEXED_PROPERTIES = 16;
+
+    /** The default score of document. */
+    private static final int DEFAULT_SCORE = 0;
+
+    /** The default time-to-live in millisecond of a document, which is infinity. */
+    private static final long DEFAULT_TTL_MILLIS = 0L;
+
+    /** @hide */
+    
+    public static final String PROPERTIES_FIELD = "properties";
+
+    /** @hide */
+    
+    public static final String BYTE_ARRAY_FIELD = "byteArray";
+
+    static final String SCHEMA_TYPE_FIELD = "schemaType";
+    static final String URI_FIELD = "uri";
+    static final String SCORE_FIELD = "score";
+    static final String TTL_MILLIS_FIELD = "ttlMillis";
+    static final String CREATION_TIMESTAMP_MILLIS_FIELD = "creationTimestampMillis";
+    static final String NAMESPACE_FIELD = "namespace";
+
+    /**
+     * The maximum number of indexed properties a document can have.
+     *
+     * <p>Indexed properties are properties where the
+     * {@link android.app.appsearch.annotation.AppSearchDocument.Property#indexingType} constant is
+     * anything other than {@link
+     * android.app.appsearch.AppSearchSchema.PropertyConfig.IndexingType#INDEXING_TYPE_NONE}.
+     */
+    public static int getMaxIndexedProperties() {
+        return MAX_INDEXED_PROPERTIES;
+    }
+
+    /** Contains {@link GenericDocument} basic information (uri, schemaType etc).*/
+    @NonNull
+    final Bundle mBundle;
+
+    /** Contains all properties in {@link GenericDocument} to support getting properties via keys.*/
+    @NonNull
+    private final Bundle mProperties;
+
+    @NonNull
+    private final String mUri;
+    @NonNull
+    private final String mSchemaType;
+    private final long mCreationTimestampMillis;
+    @Nullable
+    private Integer mHashCode;
+
+    /**
+     * Rebuilds a {@link GenericDocument} by the a bundle.
+     * @param bundle Contains {@link GenericDocument} basic information (uri, schemaType etc) and
+     *               a properties bundle contains all properties in {@link GenericDocument} to
+     *               support getting properties via keys.
+     * @hide
+     */
+    
+    public GenericDocument(@NonNull Bundle bundle) {
+        Preconditions.checkNotNull(bundle);
+        mBundle = bundle;
+        mProperties = Preconditions.checkNotNull(bundle.getParcelable(PROPERTIES_FIELD));
+        mUri = Preconditions.checkNotNull(mBundle.getString(URI_FIELD));
+        mSchemaType = Preconditions.checkNotNull(mBundle.getString(SCHEMA_TYPE_FIELD));
+        mCreationTimestampMillis = mBundle.getLong(CREATION_TIMESTAMP_MILLIS_FIELD,
+                System.currentTimeMillis());
+    }
+
+    /**
+     * Creates a new {@link GenericDocument} from an existing instance.
+     *
+     * <p>This method should be only used by constructor of a subclass.
+     */
+    protected GenericDocument(@NonNull GenericDocument document) {
+        this(document.mBundle);
+    }
+
+    /**
+     * Returns the {@link Bundle} populated by this builder.
+     * @hide
+     */
+    
+    @NonNull
+    public Bundle getBundle() {
+        return mBundle;
+    }
+
+    /** Returns the URI of the {@link GenericDocument}. */
+    @NonNull
+    public String getUri() {
+        return mUri;
+    }
+
+    /** Returns the namespace of the {@link GenericDocument}. */
+    @NonNull
+    public String getNamespace() {
+        return mBundle.getString(NAMESPACE_FIELD, DEFAULT_NAMESPACE);
+    }
+
+    /** Returns the schema type of the {@link GenericDocument}. */
+    @NonNull
+    public String getSchemaType() {
+        return mSchemaType;
+    }
+
+    /** Returns the creation timestamp of the {@link GenericDocument}, in milliseconds. */
+    public long getCreationTimestampMillis() {
+        return mCreationTimestampMillis;
+    }
+
+    /**
+     * Returns the TTL (Time To Live) of the {@link GenericDocument}, in milliseconds.
+     *
+     * <p>The default value is 0, which means the document is permanent and won't be auto-deleted
+     *    until the app is uninstalled.
+     */
+    public long getTtlMillis() {
+        return mBundle.getLong(TTL_MILLIS_FIELD, DEFAULT_TTL_MILLIS);
+    }
+
+    /**
+     * Returns the score of the {@link GenericDocument}.
+     *
+     * <p>The score is a query-independent measure of the document's quality, relative to other
+     * {@link GenericDocument}s of the same type.
+     *
+     * <p>The default value is 0.
+     */
+    public int getScore() {
+        return mBundle.getInt(SCORE_FIELD, DEFAULT_SCORE);
+    }
+
+    /**
+     * Retrieves a {@link String} value by key.
+     *
+     * @param key The key to look for.
+     * @return The first {@link String} associated with the given key or {@code null} if there
+     *         is no such key or the value is of a different type.
+     */
+    @Nullable
+    public String getPropertyString(@NonNull String key) {
+        Preconditions.checkNotNull(key);
+        String[] propertyArray = getPropertyStringArray(key);
+        if (propertyArray == null || propertyArray.length == 0) {
+            return null;
+        }
+        warnIfSinglePropertyTooLong("String", key, propertyArray.length);
+        return propertyArray[0];
+    }
+
+    /**
+     * Retrieves a {@code long} value by key.
+     *
+     * @param key The key to look for.
+     * @return The first {@code long} associated with the given key or default value {@code 0} if
+     *         there is no such key or the value is of a different type.
+     */
+    public long getPropertyLong(@NonNull String key) {
+        Preconditions.checkNotNull(key);
+        long[] propertyArray = getPropertyLongArray(key);
+        if (propertyArray == null || propertyArray.length == 0) {
+            return 0;
+        }
+        warnIfSinglePropertyTooLong("Long", key, propertyArray.length);
+        return propertyArray[0];
+    }
+
+    /**
+     * Retrieves a {@code double} value by key.
+     *
+     * @param key The key to look for.
+     * @return The first {@code double} associated with the given key or default value {@code 0.0}
+     *         if there is no such key or the value is of a different type.
+     */
+    public double getPropertyDouble(@NonNull String key) {
+        Preconditions.checkNotNull(key);
+        double[] propertyArray = getPropertyDoubleArray(key);
+        if (propertyArray == null || propertyArray.length == 0) {
+            return 0.0;
+        }
+        warnIfSinglePropertyTooLong("Double", key, propertyArray.length);
+        return propertyArray[0];
+    }
+
+    /**
+     * Retrieves a {@code boolean} value by key.
+     *
+     * @param key The key to look for.
+     * @return The first {@code boolean} associated with the given key or default value
+     *         {@code false} if there is no such key or the value is of a different type.
+     */
+    public boolean getPropertyBoolean(@NonNull String key) {
+        Preconditions.checkNotNull(key);
+        boolean[] propertyArray = getPropertyBooleanArray(key);
+        if (propertyArray == null || propertyArray.length == 0) {
+            return false;
+        }
+        warnIfSinglePropertyTooLong("Boolean", key, propertyArray.length);
+        return propertyArray[0];
+    }
+
+    /**
+     * Retrieves a {@code byte[]} value by key.
+     *
+     * @param key The key to look for.
+     * @return The first {@code byte[]} associated with the given key or {@code null} if there
+     *         is no such key or the value is of a different type.
+     */
+    @Nullable
+    public byte[] getPropertyBytes(@NonNull String key) {
+        Preconditions.checkNotNull(key);
+        byte[][] propertyArray = getPropertyBytesArray(key);
+        if (propertyArray == null || propertyArray.length == 0) {
+            return null;
+        }
+        warnIfSinglePropertyTooLong("ByteArray", key, propertyArray.length);
+        return propertyArray[0];
+    }
+
+    /**
+     * Retrieves a {@link GenericDocument} value by key.
+     *
+     * @param key The key to look for.
+     * @return The first {@link GenericDocument} associated with the given key or {@code null} if
+     *         there is no such key or the value is of a different type.
+     */
+    @Nullable
+    public GenericDocument getPropertyDocument(@NonNull String key) {
+        Preconditions.checkNotNull(key);
+        GenericDocument[] propertyArray = getPropertyDocumentArray(key);
+        if (propertyArray == null || propertyArray.length == 0) {
+            return null;
+        }
+        warnIfSinglePropertyTooLong("Document", key, propertyArray.length);
+        return propertyArray[0];
+    }
+
+    /** Prints a warning to logcat if the given propertyLength is greater than 1. */
+    private static void warnIfSinglePropertyTooLong(
+            @NonNull String propertyType, @NonNull String key, int propertyLength) {
+        if (propertyLength > 1) {
+            Log.w(TAG, "The value for \"" + key + "\" contains " + propertyLength
+                    + " elements. Only the first one will be returned from "
+                    + "getProperty" + propertyType + "(). Try getProperty" + propertyType
+                    + "Array().");
+        }
+    }
+
+    /**
+     * Retrieves a repeated {@code String} property by key.
+     *
+     * @param key The key to look for.
+     * @return The {@code String[]} associated with the given key, or {@code null} if no value
+     *         is set or the value is of a different type.
+     */
+    @Nullable
+    public String[] getPropertyStringArray(@NonNull String key) {
+        Preconditions.checkNotNull(key);
+        return getAndCastPropertyArray(key, String[].class);
+    }
+
+    /**
+     * Retrieves a repeated {@link String} property by key.
+     *
+     * @param key The key to look for.
+     * @return The {@code long[]} associated with the given key, or {@code null} if no value is
+     *         set or the value is of a different type.
+     */
+    @Nullable
+    public long[] getPropertyLongArray(@NonNull String key) {
+        Preconditions.checkNotNull(key);
+        return getAndCastPropertyArray(key, long[].class);
+    }
+
+    /**
+     * Retrieves a repeated {@code double} property by key.
+     *
+     * @param key The key to look for.
+     * @return The {@code double[]} associated with the given key, or {@code null} if no value
+     *         is set or the value is of a different type.
+     */
+    @Nullable
+    public double[] getPropertyDoubleArray(@NonNull String key) {
+        Preconditions.checkNotNull(key);
+        return getAndCastPropertyArray(key, double[].class);
+    }
+
+    /**
+     * Retrieves a repeated {@code boolean} property by key.
+     *
+     * @param key The key to look for.
+     * @return The {@code boolean[]} associated with the given key, or {@code null} if no value
+     *         is set or the value is of a different type.
+     */
+    @Nullable
+    public boolean[] getPropertyBooleanArray(@NonNull String key) {
+        Preconditions.checkNotNull(key);
+        return getAndCastPropertyArray(key, boolean[].class);
+    }
+
+    /**
+     * Retrieves a {@code byte[][]} property by key.
+     *
+     * @param key The key to look for.
+     * @return The {@code byte[][]} associated with the given key, or {@code null} if no value
+     *         is set or the value is of a different type.
+     */
+    @SuppressLint("ArrayReturn")
+    @Nullable
+    @SuppressWarnings("unchecked")
+    public byte[][] getPropertyBytesArray(@NonNull String key) {
+        Preconditions.checkNotNull(key);
+        ArrayList<Bundle> bundles = getAndCastPropertyArray(key, ArrayList.class);
+        if (bundles == null || bundles.size() == 0) {
+            return null;
+        }
+        byte[][] bytes = new byte[bundles.size()][];
+        for (int i = 0; i < bundles.size(); i++) {
+            Bundle bundle = bundles.get(i);
+            if (bundle == null) {
+                Log.e(TAG, "The inner bundle is null at " + i + ", for key: " + key);
+                continue;
+            }
+            byte[] innerBytes = bundle.getByteArray(BYTE_ARRAY_FIELD);
+            if (innerBytes == null) {
+                Log.e(TAG, "The bundle at " + i + " contains a null byte[].");
+                continue;
+            }
+            bytes[i] = innerBytes;
+        }
+        return bytes;
+    }
+
+    /**
+     * Retrieves a repeated {@link GenericDocument} property by key.
+     *
+     * @param key The key to look for.
+     * @return The {@link GenericDocument}[] associated with the given key, or {@code null} if no
+     *         value is set or the value is of a different type.
+     */
+    @SuppressLint("ArrayReturn")
+    @Nullable
+    public GenericDocument[] getPropertyDocumentArray(@NonNull String key) {
+        Preconditions.checkNotNull(key);
+        Bundle[] bundles = getAndCastPropertyArray(key, Bundle[].class);
+        if (bundles == null || bundles.length == 0) {
+            return null;
+        }
+        GenericDocument[] documents = new GenericDocument[bundles.length];
+        for (int i = 0; i < bundles.length; i++) {
+            if (bundles[i] == null) {
+                Log.e(TAG, "The inner bundle is null at " + i + ", for key: " + key);
+                continue;
+            }
+            documents[i] = new GenericDocument(bundles[i]);
+        }
+        return documents;
+    }
+
+    /**
+     * Gets a repeated property of the given key, and casts it to the given class type, which
+     * must be an array class type.
+     */
+    @Nullable
+    private <T> T getAndCastPropertyArray(@NonNull String key, @NonNull Class<T> tClass) {
+        Object value = mProperties.get(key);
+        if (value == null) {
+            return null;
+        }
+        try {
+            return tClass.cast(value);
+        } catch (ClassCastException e) {
+            Log.w(TAG, "Error casting to requested type for key \"" + key + "\"", e);
+            return null;
+        }
+    }
+
+    @Override
+    public boolean equals(@Nullable Object other) {
+        // Check only proto's equality is sufficient here since all properties in
+        // mProperties are ordered by keys and stored in proto.
+        if (this == other) {
+            return true;
+        }
+        if (!(other instanceof GenericDocument)) {
+            return false;
+        }
+        GenericDocument otherDocument = (GenericDocument) other;
+        return bundleEquals(this.mBundle, otherDocument.mBundle);
+    }
+
+    /**
+     * Deeply checks two bundle is equally or not.
+     * <p> Two bundle will be considered equally if they contains same content.
+     */
+    @SuppressWarnings("unchecked")
+    private static boolean bundleEquals(Bundle one, Bundle two) {
+        if (one.size() != two.size()) {
+            return false;
+        }
+        Set<String> keySetOne = one.keySet();
+        Object valueOne;
+        Object valueTwo;
+        // Bundle inherit its equals() from Object.java, which only compare their memory address.
+        // We should iterate all keys and check their presents and values in both bundle.
+        for (String key : keySetOne) {
+            valueOne = one.get(key);
+            valueTwo = two.get(key);
+            if (valueOne instanceof Bundle
+                    && valueTwo instanceof Bundle
+                    && !bundleEquals((Bundle) valueOne, (Bundle) valueTwo)) {
+                return false;
+            } else if (valueOne == null && (valueTwo != null || !two.containsKey(key))) {
+                // If we call bundle.get(key) when the 'key' doesn't actually exist in the
+                // bundle, we'll get  back a null. So make sure that both values are null and
+                // both keys exist in the bundle.
+                return false;
+            } else if (valueOne instanceof boolean[]) {
+                if (!(valueTwo instanceof boolean[])
+                        || !Arrays.equals((boolean[]) valueOne, (boolean[]) valueTwo)) {
+                    return false;
+                }
+            } else if (valueOne instanceof long[]) {
+                if (!(valueTwo instanceof long[])
+                        || !Arrays.equals((long[]) valueOne, (long[]) valueTwo)) {
+                    return false;
+                }
+            } else if (valueOne instanceof double[]) {
+                if (!(valueTwo instanceof double[])
+                        || !Arrays.equals((double[]) valueOne, (double[]) valueTwo)) {
+                    return false;
+                }
+            } else if (valueOne instanceof Bundle[]) {
+                if (!(valueTwo instanceof Bundle[])) {
+                    return false;
+                }
+                Bundle[] bundlesOne = (Bundle[]) valueOne;
+                Bundle[] bundlesTwo = (Bundle[]) valueTwo;
+                if (bundlesOne.length != bundlesTwo.length) {
+                    return false;
+                }
+                for (int i = 0; i < bundlesOne.length; i++) {
+                    if (!bundleEquals(bundlesOne[i], bundlesTwo[i])) {
+                        return false;
+                    }
+                }
+            } else if (valueOne instanceof ArrayList) {
+                if (!(valueTwo instanceof ArrayList)) {
+                    return false;
+                }
+                ArrayList<Bundle> bundlesOne = (ArrayList<Bundle>) valueOne;
+                ArrayList<Bundle> bundlesTwo = (ArrayList<Bundle>) valueTwo;
+                if (bundlesOne.size() != bundlesTwo.size()) {
+                    return false;
+                }
+                for (int i = 0; i < bundlesOne.size(); i++) {
+                    if (!bundleEquals(bundlesOne.get(i), bundlesTwo.get(i))) {
+                        return false;
+                    }
+                }
+            } else if (valueOne instanceof Object[]) {
+                if (!(valueTwo instanceof Object[])
+                        || !Arrays.equals((Object[]) valueOne, (Object[]) valueTwo)) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        if (mHashCode == null) {
+            mHashCode = bundleHashCode(mBundle);
+        }
+        return mHashCode;
+    }
+
+    /**
+     * Calculates the hash code for a bundle.
+     * <p> The hash code is only effected by the content in the bundle. Bundles will get
+     * consistent hash code if they have same content.
+     */
+    @SuppressWarnings("unchecked")
+    private static int bundleHashCode(Bundle bundle) {
+        int[] hashCodes = new int[bundle.size()];
+        int i = 0;
+        // Bundle inherit its hashCode() from Object.java, which only relative to their memory
+        // address. Bundle doesn't have an order, so we should iterate all keys and combine
+        // their value's hashcode into an array. And use the hashcode of the array to be
+        // the hashcode of the bundle.
+        for (String key : bundle.keySet()) {
+            Object value = bundle.get(key);
+            if (value instanceof boolean[]) {
+                hashCodes[i++] = Arrays.hashCode((boolean[]) value);
+            } else if (value instanceof long[]) {
+                hashCodes[i++] = Arrays.hashCode((long[]) value);
+            } else if (value instanceof double[]) {
+                hashCodes[i++] = Arrays.hashCode((double[]) value);
+            } else if (value instanceof String[]) {
+                hashCodes[i++] = Arrays.hashCode((Object[]) value);
+            } else if (value instanceof Bundle) {
+                hashCodes[i++] = bundleHashCode((Bundle) value);
+            } else if (value instanceof Bundle[]) {
+                Bundle[] bundles = (Bundle[]) value;
+                int[] innerHashCodes = new int[bundles.length];
+                for (int j = 0; j < innerHashCodes.length; j++) {
+                    innerHashCodes[j] = bundleHashCode(bundles[j]);
+                }
+                hashCodes[i++] = Arrays.hashCode(innerHashCodes);
+            } else if (value instanceof ArrayList) {
+                ArrayList<Bundle> bundles = (ArrayList<Bundle>) value;
+                int[] innerHashCodes = new int[bundles.size()];
+                for (int j = 0; j < innerHashCodes.length; j++) {
+                    innerHashCodes[j] = bundleHashCode(bundles.get(j));
+                }
+                hashCodes[i++] = Arrays.hashCode(innerHashCodes);
+            } else {
+                hashCodes[i++] = value.hashCode();
+            }
+        }
+        return Arrays.hashCode(hashCodes);
+    }
+
+    @Override
+    @NonNull
+    public String toString() {
+        return bundleToString(mBundle).toString();
+    }
+
+    @SuppressWarnings("unchecked")
+    private static StringBuilder bundleToString(Bundle bundle) {
+        StringBuilder stringBuilder = new StringBuilder();
+        try {
+            final Set<String> keySet = bundle.keySet();
+            String[] keys = keySet.toArray(new String[0]);
+            // Sort keys to make output deterministic. We need a custom comparator to handle
+            // nulls (arbitrarily putting them first, similar to Comparator.nullsFirst, which is
+            // only available since N).
+            Arrays.sort(
+                    keys,
+                    (@Nullable String s1, @Nullable String s2) -> {
+                        if (s1 == null) {
+                            return s2 == null ? 0 : -1;
+                        } else if (s2 == null) {
+                            return 1;
+                        } else {
+                            return s1.compareTo(s2);
+                        }
+                    });
+            for (String key : keys) {
+                stringBuilder.append("{ key: '").append(key).append("' value: ");
+                Object valueObject = bundle.get(key);
+                if (valueObject == null) {
+                    stringBuilder.append("<null>");
+                } else if (valueObject instanceof Bundle) {
+                    stringBuilder.append(bundleToString((Bundle) valueObject));
+                } else if (valueObject.getClass().isArray()) {
+                    stringBuilder.append("[ ");
+                    for (int i = 0; i < Array.getLength(valueObject); i++) {
+                        Object element = Array.get(valueObject, i);
+                        stringBuilder.append("'");
+                        if (element instanceof Bundle) {
+                            stringBuilder.append(bundleToString((Bundle) element));
+                        } else {
+                            stringBuilder.append(Array.get(valueObject, i));
+                        }
+                        stringBuilder.append("' ");
+                    }
+                    stringBuilder.append("]");
+                } else if (valueObject instanceof ArrayList) {
+                    for (Bundle innerBundle : (ArrayList<Bundle>) valueObject) {
+                        stringBuilder.append(bundleToString(innerBundle));
+                    }
+                } else {
+                    stringBuilder.append(valueObject.toString());
+                }
+                stringBuilder.append(" } ");
+            }
+        } catch (RuntimeException e) {
+            // Catch any exceptions here since corrupt Bundles can throw different types of
+            // exceptions (e.g. b/38445840 & b/68937025).
+            stringBuilder.append("<error>");
+        }
+        return stringBuilder;
+    }
+
+    /**
+     * The builder class for {@link GenericDocument}.
+     *
+     * @param <BuilderType> Type of subclass who extend this.
+     */
+    public static class Builder<BuilderType extends Builder> {
+
+        private final Bundle mProperties = new Bundle();
+        private final Bundle mBundle = new Bundle();
+        private final BuilderType mBuilderTypeInstance;
+        private boolean mBuilt = false;
+
+        /**
+         * Create a new {@link GenericDocument.Builder}.
+         *
+         * @param uri The uri of {@link GenericDocument}.
+         * @param schemaType The schema type of the {@link GenericDocument}. The passed-in
+         *        {@code schemaType} must be defined using {@code AppSearchManager#setSchema} prior
+         *        to inserting a document of this {@code schemaType} into the AppSearch index using
+         *        {@code AppSearchManager#putDocuments}. Otherwise, the document will be
+         *        rejected by {@code AppSearchManager#putDocuments}.
+         */
+        //TODO(b/157082794) Linkify AppSearchManager once that API is public.
+        @SuppressWarnings("unchecked")
+        public Builder(@NonNull String uri, @NonNull String schemaType) {
+            Preconditions.checkNotNull(uri);
+            Preconditions.checkNotNull(schemaType);
+            mBuilderTypeInstance = (BuilderType) this;
+            mBundle.putString(GenericDocument.URI_FIELD, uri);
+            mBundle.putString(GenericDocument.SCHEMA_TYPE_FIELD, schemaType);
+            mBundle.putString(GenericDocument.NAMESPACE_FIELD, DEFAULT_NAMESPACE);
+            // Set current timestamp for creation timestamp by default.
+            mBundle.putLong(GenericDocument.CREATION_TIMESTAMP_MILLIS_FIELD,
+                    System.currentTimeMillis());
+            mBundle.putLong(GenericDocument.TTL_MILLIS_FIELD, DEFAULT_TTL_MILLIS);
+            mBundle.putInt(GenericDocument.SCORE_FIELD, DEFAULT_SCORE);
+            mBundle.putBundle(PROPERTIES_FIELD, mProperties);
+        }
+
+        /**
+         * Set the app-defined namespace this Document resides in. No special values  are
+         * reserved or understood by the infrastructure. URIs are unique within a namespace. The
+         * number of namespaces per app should be kept small for efficiency reasons.
+         */
+        @NonNull
+        public BuilderType setNamespace(@NonNull String namespace) {
+            mBundle.putString(GenericDocument.NAMESPACE_FIELD, namespace);
+            return mBuilderTypeInstance;
+        }
+
+        /**
+         * Sets the score of the {@link GenericDocument}.
+         *
+         * <p>The score is a query-independent measure of the document's quality, relative to
+         * other {@link GenericDocument}s of the same type.
+         *
+         * @throws IllegalArgumentException If the provided value is negative.
+         */
+        @NonNull
+        public BuilderType setScore(@IntRange(from = 0, to = Integer.MAX_VALUE) int score) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            if (score < 0) {
+                throw new IllegalArgumentException("Document score cannot be negative.");
+            }
+            mBundle.putInt(GenericDocument.SCORE_FIELD, score);
+            return mBuilderTypeInstance;
+        }
+
+        /**
+         * Set the creation timestamp in milliseconds of the {@link GenericDocument}. Should be
+         * set using a value obtained from the {@link System#currentTimeMillis()} time base.
+         */
+        @NonNull
+        public BuilderType setCreationTimestampMillis(long creationTimestampMillis) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            mBundle.putLong(GenericDocument.CREATION_TIMESTAMP_MILLIS_FIELD,
+                    creationTimestampMillis);
+            return mBuilderTypeInstance;
+        }
+
+        /**
+         * Set the TTL (Time To Live) of the {@link GenericDocument}, in milliseconds.
+         *
+         * <p>After this many milliseconds since the {@link #setCreationTimestampMillis creation
+         * timestamp}, the document is deleted.
+         *
+         * @param ttlMillis A non-negative duration in milliseconds.
+         * @throws IllegalArgumentException If the provided value is negative.
+         */
+        @NonNull
+        public BuilderType setTtlMillis(long ttlMillis) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            if (ttlMillis < 0) {
+                throw new IllegalArgumentException("Document ttlMillis cannot be negative.");
+            }
+            mBundle.putLong(GenericDocument.TTL_MILLIS_FIELD, ttlMillis);
+            return mBuilderTypeInstance;
+        }
+
+        /**
+         * Sets one or multiple {@code String} values for a property, replacing its previous
+         * values.
+         *
+         * @param key The key associated with the {@code values}.
+         * @param values The {@code String} values of the property.
+         */
+        @NonNull
+        public BuilderType setProperty(@NonNull String key, @NonNull String... values) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkNotNull(key);
+            Preconditions.checkNotNull(values);
+            putInPropertyBundle(key, values);
+            return mBuilderTypeInstance;
+        }
+
+        /**
+         * Sets one or multiple {@code boolean} values for a property, replacing its previous
+         * values.
+         *
+         * @param key The key associated with the {@code values}.
+         * @param values The {@code boolean} values of the property.
+         */
+        @NonNull
+        public BuilderType setProperty(@NonNull String key, @NonNull boolean... values) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkNotNull(key);
+            Preconditions.checkNotNull(values);
+            putInPropertyBundle(key, values);
+            return mBuilderTypeInstance;
+        }
+
+        /**
+         * Sets one or multiple {@code long} values for a property, replacing its previous
+         * values.
+         *
+         * @param key The key associated with the {@code values}.
+         * @param values The {@code long} values of the property.
+         */
+        @NonNull
+        public BuilderType setProperty(@NonNull String key, @NonNull long... values) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkNotNull(key);
+            Preconditions.checkNotNull(values);
+            putInPropertyBundle(key, values);
+            return mBuilderTypeInstance;
+        }
+
+        /**
+         * Sets one or multiple {@code double} values for a property, replacing its previous
+         * values.
+         *
+         * @param key The key associated with the {@code values}.
+         * @param values The {@code double} values of the property.
+         */
+        @NonNull
+        public BuilderType setProperty(@NonNull String key, @NonNull double... values) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkNotNull(key);
+            Preconditions.checkNotNull(values);
+            putInPropertyBundle(key, values);
+            return mBuilderTypeInstance;
+        }
+
+        /**
+         * Sets one or multiple {@code byte[]} for a property, replacing its previous values.
+         *
+         * @param key The key associated with the {@code values}.
+         * @param values The {@code byte[]} of the property.
+         */
+        @NonNull
+        public BuilderType setProperty(@NonNull String key, @NonNull byte[]... values) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkNotNull(key);
+            Preconditions.checkNotNull(values);
+            putInPropertyBundle(key, values);
+            return mBuilderTypeInstance;
+        }
+
+        /**
+         * Sets one or multiple {@link GenericDocument} values for a property, replacing its
+         * previous values.
+         *
+         * @param key The key associated with the {@code values}.
+         * @param values The {@link GenericDocument} values of the property.
+         */
+        @NonNull
+        public BuilderType setProperty(@NonNull String key, @NonNull GenericDocument... values) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkNotNull(key);
+            Preconditions.checkNotNull(values);
+            putInPropertyBundle(key, values);
+            return mBuilderTypeInstance;
+        }
+
+        private void putInPropertyBundle(@NonNull String key, @NonNull String[] values)
+                throws IllegalArgumentException {
+            validateRepeatedPropertyLength(key, values.length);
+            for (int i = 0; i < values.length; i++) {
+                if (values[i] == null) {
+                    throw new IllegalArgumentException("The String at " + i + " is null.");
+                } else if (values[i].length() > MAX_STRING_LENGTH) {
+                    throw new IllegalArgumentException("The String at " + i + " length is: "
+                            + values[i].length()  + ", which exceeds length limit: "
+                            + MAX_STRING_LENGTH + ".");
+                }
+            }
+            mProperties.putStringArray(key, values);
+        }
+
+        private void putInPropertyBundle(@NonNull String key, @NonNull boolean[] values) {
+            validateRepeatedPropertyLength(key, values.length);
+            mProperties.putBooleanArray(key, values);
+        }
+
+        private void putInPropertyBundle(@NonNull String key, @NonNull double[] values) {
+            validateRepeatedPropertyLength(key, values.length);
+            mProperties.putDoubleArray(key, values);
+        }
+
+        private void putInPropertyBundle(@NonNull String key, @NonNull long[] values) {
+            validateRepeatedPropertyLength(key, values.length);
+            mProperties.putLongArray(key, values);
+        }
+
+        /**
+         * Converts and saves a byte[][] into {@link #mProperties}.
+         *
+         * <p>Bundle doesn't support for two dimension array byte[][], we are converting byte[][]
+         * into ArrayList<Bundle>, and each elements will contain a one dimension byte[].
+         */
+        private void putInPropertyBundle(@NonNull String key, @NonNull byte[][] values) {
+            validateRepeatedPropertyLength(key, values.length);
+            ArrayList<Bundle> bundles = new ArrayList<>(values.length);
+            for (int i = 0; i < values.length; i++) {
+                if (values[i] == null) {
+                    throw new IllegalArgumentException("The byte[] at " + i + " is null.");
+                }
+                Bundle bundle = new Bundle();
+                bundle.putByteArray(BYTE_ARRAY_FIELD, values[i]);
+                bundles.add(bundle);
+            }
+            mProperties.putParcelableArrayList(key, bundles);
+        }
+
+        private void putInPropertyBundle(@NonNull String key, @NonNull GenericDocument[] values) {
+            validateRepeatedPropertyLength(key, values.length);
+            Bundle[] documentBundles = new Bundle[values.length];
+            for (int i = 0; i < values.length; i++) {
+                if (values[i] == null) {
+                    throw new IllegalArgumentException("The document at " + i + " is null.");
+                }
+                documentBundles[i] = values[i].mBundle;
+            }
+            mProperties.putParcelableArray(key, documentBundles);
+        }
+
+        private static void validateRepeatedPropertyLength(@NonNull String key, int length) {
+            if (length == 0) {
+                throw new IllegalArgumentException("The input array is empty.");
+            } else if (length > MAX_REPEATED_PROPERTY_LENGTH) {
+                throw new IllegalArgumentException(
+                        "Repeated property \"" + key + "\" has length " + length
+                                + ", which exceeds the limit of "
+                                + MAX_REPEATED_PROPERTY_LENGTH);
+            }
+        }
+
+        /** Builds the {@link GenericDocument} object. */
+        @NonNull
+        public GenericDocument build() {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            mBuilt = true;
+            return new GenericDocument(mBundle);
+        }
+    }
+}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/GetByUriRequest.java b/apex/appsearch/framework/java/android/app/appsearch/GetByUriRequest.java
new file mode 100644
index 0000000..3c0e746
--- /dev/null
+++ b/apex/appsearch/framework/java/android/app/appsearch/GetByUriRequest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch;
+
+import android.annotation.NonNull;
+
+import android.util.ArraySet;
+import com.android.internal.util.Preconditions;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Set;
+
+/**
+ * Encapsulates a request to retrieve documents by namespace and URI.
+ *
+ * @see AppSearchManager#getByUri
+ * @hide
+ */
+public final class GetByUriRequest {
+    private final String mNamespace;
+    private final Set<String> mUris;
+
+    GetByUriRequest(@NonNull String namespace, @NonNull Set<String> uris) {
+        mNamespace = namespace;
+        mUris = uris;
+    }
+
+    /** @hide */
+    
+    @NonNull
+    public String getNamespace() {
+        return mNamespace;
+    }
+
+    /** @hide */
+    
+    @NonNull
+    public Set<String> getUris() {
+        return mUris;
+    }
+
+    /** Builder for {@link GetByUriRequest} objects. */
+    public static final class Builder {
+        private String mNamespace = GenericDocument.DEFAULT_NAMESPACE;
+        private final Set<String> mUris = new ArraySet<>();
+        private boolean mBuilt = false;
+
+        /**
+         * Sets which namespace these documents will be retrieved from.
+         *
+         * <p>If this is not set, it defaults to {@link GenericDocument#DEFAULT_NAMESPACE}.
+         */
+        @NonNull
+        public Builder setNamespace(@NonNull String namespace) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkNotNull(namespace);
+            mNamespace = namespace;
+            return this;
+        }
+
+        /** Adds one or more URIs to the request. */
+        @NonNull
+        public Builder addUris(@NonNull String... uris) {
+            Preconditions.checkNotNull(uris);
+            return addUris(Arrays.asList(uris));
+        }
+
+        /** Adds one or more URIs to the request. */
+        @NonNull
+        public Builder addUris(@NonNull Collection<String> uris) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkNotNull(uris);
+            mUris.addAll(uris);
+            return this;
+        }
+
+        /** Builds a new {@link GetByUriRequest}. */
+        @NonNull
+        public GetByUriRequest build() {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            mBuilt = true;
+            return new GetByUriRequest(mNamespace, mUris);
+        }
+    }
+}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
index 68de4f0..352a980 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
+++ b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
@@ -15,29 +15,37 @@
  */
 package android.app.appsearch;
 
+import android.os.Bundle;
+
 import com.android.internal.infra.AndroidFuture;
 
 parcelable AppSearchResult;
 parcelable AppSearchBatchResult;
+parcelable SearchResults;
 
 /** {@hide} */
 interface IAppSearchManager {
     /**
      * Sets the schema.
      *
-     * @param schemaBytes Serialized SchemaProto.
+     * @param databaseName  The databaseName this document resides in.
+     * @param schemaBundles List of AppSearchSchema bundles.
      * @param forceOverride Whether to apply the new schema even if it is incompatible. All
      *     incompatible documents will be deleted.
      * @param callback {@link AndroidFuture}&lt;{@link AppSearchResult}&lt;{@link Void}&gt&gt;.
      *     The results of the call.
      */
     void setSchema(
-        in byte[] schemaBytes, boolean forceOverride, in AndroidFuture<AppSearchResult> callback);
+        in String databaseName,
+        in List<Bundle> schemaBundles,
+        boolean forceOverride,
+        in AndroidFuture<AppSearchResult> callback);
 
     /**
      * Inserts documents into the index.
      *
-     * @param documentsBytes {@link List}&lt;byte[]&gt; of serialized DocumentProtos.
+     * @param databaseName  The name of the database where this document lives.
+     * @param documentBundes List of GenericDocument bundles.
      * @param callback
      *     {@link AndroidFuture}&lt;{@link AppSearchBatchResult}&lt;{@link String}, {@link Void}&gt;&gt;.
      *     If the call fails to start, {@code callback} will be completed exceptionally. Otherwise,
@@ -45,37 +53,49 @@
      *     {@link AppSearchBatchResult}&lt;{@link String}, {@link Void}&gt;
      *     where the keys are document URIs, and the values are {@code null}.
      */
-    void putDocuments(in List documentsBytes, in AndroidFuture<AppSearchBatchResult> callback);
+    void putDocuments(
+        in String databaseName,
+        in List<Bundle> documentBundles,
+        in AndroidFuture<AppSearchBatchResult> callback);
 
     /**
      * Retrieves documents from the index.
      *
+     * @param databaseName  The databaseName this document resides in.
+     * @param namespace    The namespace this document resides in.
      * @param uris The URIs of the documents to retrieve
      * @param callback
-     *     {@link AndroidFuture}&lt;{@link AppSearchBatchResult}&lt;{@link String}, {@link byte[]}&gt;&gt;.
+     *     {@link AndroidFuture}&lt;{@link AppSearchBatchResult}&lt;{@link String}, {@link Bundle}&gt;&gt;.
      *     If the call fails to start, {@code callback} will be completed exceptionally. Otherwise,
      *     {@code callback} will be completed with an
-     *     {@link AppSearchBatchResult}&lt;{@link String}, {@link byte[]}&gt;
-     *     where the keys are document URIs, and the values are serialized Document protos.
+     *     {@link AppSearchBatchResult}&lt;{@link String}, {@link Bundle}&gt;
+     *     where the keys are document URIs, and the values are Document bundles.
      */
-    void getDocuments(in List<String> uris, in AndroidFuture<AppSearchBatchResult> callback);
+    void getDocuments(
+        in String databaseName,
+        in String namespace,
+        in List<String> uris,
+        in AndroidFuture<AppSearchBatchResult> callback);
 
     /**
      * Searches a document based on a given specifications.
      *
-     * @param searchSpecBytes Serialized SearchSpecProto.
-     * @param resultSpecBytes Serialized SearchResultsProto.
-     * @param scoringSpecBytes Serialized ScoringSpecProto.
-     * @param callback {@link AndroidFuture}&lt;{@link AppSearchResult}&lt;{@link byte[]}&gt;&gt;
-     *     Will be completed with a serialized {@link SearchResultsProto}.
+     * @param databaseName The databaseName this query for.
+     * @param queryExpression String to search for
+     * @param searchSpecBundle SearchSpec bundle
+     * @param callback {@link AndroidFuture}&lt;{@link AppSearchResult}&lt;{@link SearchResults}&gt;&gt;
      */
     void query(
-        in byte[] searchSpecBytes, in byte[] resultSpecBytes, in byte[] scoringSpecBytes,
+        in String databaseName,
+        in String queryExpression,
+        in Bundle searchSpecBundle,
         in AndroidFuture<AppSearchResult> callback);
 
     /**
      * Deletes documents by URI.
      *
+     * @param databaseName The databaseName the document is in.
+     * @param namespace    Namespace of the document to remove.
      * @param uris The URIs of the documents to delete
      * @param callback
      *     {@link AndroidFuture}&lt;{@link AppSearchBatchResult}&lt;{@link String}, {@link Void}&gt;&gt;.
@@ -85,11 +105,16 @@
      *     where the keys are document URIs. If a document doesn't exist, it will be reported as a
      *     failure where the {@code throwable} is {@code null}.
      */
-    void delete(in List<String> uris, in AndroidFuture<AppSearchBatchResult> callback);
+    void delete(
+        in String databaseName,
+        in String namespace,
+        in List<String> uris,
+        in AndroidFuture<AppSearchBatchResult> callback);
 
     /**
      * Deletes documents by schema type.
      *
+     * @param databaseName The databaseName the document is in.
      * @param schemaTypes The schema types of the documents to delete
      * @param callback
      *     {@link AndroidFuture}&lt;{@link AppSearchBatchResult}&lt;{@link String}, {@link Void}&gt;&gt;.
@@ -100,13 +125,16 @@
      *     failure where the {@code throwable} is {@code null}.
      */
     void deleteByTypes(
-        in List<String> schemaTypes, in AndroidFuture<AppSearchBatchResult> callback);
+        in String databaseName,
+        in List<String> schemaTypes,
+        in AndroidFuture<AppSearchBatchResult> callback);
 
     /**
      * Deletes all documents belonging to the calling app.
      *
+     * @param databaseName The databaseName to remove all documents from.
      * @param callback {@link AndroidFuture}&lt;{@link AppSearchResult}&lt;{@link Void}&gt;&gt;.
      *     Will be completed with the result of the call.
      */
-    void deleteAll(in AndroidFuture<AppSearchResult> callback);
+    void deleteAll(in String databaseName, in AndroidFuture<AppSearchResult> callback);
 }
diff --git a/apex/appsearch/framework/java/android/app/appsearch/MatchInfo.java b/apex/appsearch/framework/java/android/app/appsearch/MatchInfo.java
deleted file mode 100644
index 5ce2960..0000000
--- a/apex/appsearch/framework/java/android/app/appsearch/MatchInfo.java
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.appsearch;
-
-import android.annotation.NonNull;
-import android.util.Range;
-
-import com.google.android.icing.proto.SnippetMatchProto;
-
-/**
- * Snippet: It refers to a substring of text from the content of document that is returned as a
- * part of search result.
- * This class represents a match objects for any Snippets that might be present in
- * {@link SearchResults} from query. Using this class user can get the full text, exact matches and
- * Snippets of document content for a given match.
- *
- * <p>Class Example 1:
- * A document contains following text in property subject:
- * <p>A commonly used fake word is foo. Another nonsense word that’s used a lot is bar.
- *
- * <p>If the queryExpression is "foo".
- *
- * <p>{@link MatchInfo#getPropertyPath()} returns "subject"
- * <p>{@link MatchInfo#getFullText()} returns "A commonly used fake word is foo. Another nonsense
- * word that’s used a lot is bar."
- * <p>{@link MatchInfo#getExactMatchPosition()} returns [29, 32]
- * <p>{@link MatchInfo#getExactMatch()} returns "foo"
- * <p>{@link MatchInfo#getSnippetPosition()} returns [29, 41]
- * <p>{@link MatchInfo#getSnippet()} returns "is foo. Another"
- * <p>
- * <p>Class Example 2:
- * A document contains a property name sender which contains 2 property names name and email, so
- * we will have 2 property paths: {@code sender.name} and {@code sender.email}.
- * <p> Let {@code sender.name = "Test Name Jr."} and {@code sender.email = "TestNameJr@gmail.com"}
- *
- * <p>If the queryExpression is "Test". We will have 2 matches.
- *
- * <p> Match-1
- * <p>{@link MatchInfo#getPropertyPath()} returns "sender.name"
- * <p>{@link MatchInfo#getFullText()} returns "Test Name Jr."
- * <p>{@link MatchInfo#getExactMatchPosition()} returns [0, 4]
- * <p>{@link MatchInfo#getExactMatch()} returns "Test"
- * <p>{@link MatchInfo#getSnippetPosition()} returns [0, 9]
- * <p>{@link MatchInfo#getSnippet()} returns "Test Name Jr."
- * <p> Match-2
- * <p>{@link MatchInfo#getPropertyPath()} returns "sender.email"
- * <p>{@link MatchInfo#getFullText()} returns "TestNameJr@gmail.com"
- * <p>{@link MatchInfo#getExactMatchPosition()} returns [0, 20]
- * <p>{@link MatchInfo#getExactMatch()} returns "TestNameJr@gmail.com"
- * <p>{@link MatchInfo#getSnippetPosition()} returns [0, 20]
- * <p>{@link MatchInfo#getSnippet()} returns "TestNameJr@gmail.com"
- * @hide
- */
-// TODO(sidchhabra): Capture real snippet after integration with icingLib.
-public final class MatchInfo {
-
-    private final String mPropertyPath;
-    private final SnippetMatchProto mSnippetMatch;
-    private final AppSearchDocument mDocument;
-    /**
-     * List of content with same property path in a document when there are multiple matches in
-     * repeated sections.
-     */
-    private final String[] mValues;
-
-    /** @hide */
-    public MatchInfo(@NonNull String propertyPath, @NonNull SnippetMatchProto snippetMatch,
-            @NonNull AppSearchDocument document) {
-        mPropertyPath = propertyPath;
-        mSnippetMatch = snippetMatch;
-        mDocument = document;
-        // In IcingLib snippeting is available for only 3 data types i.e String, double and long,
-        // so we need to check which of these three are requested.
-        // TODO (sidchhabra): getPropertyStringArray takes property name, handle for property path.
-        String[] values = mDocument.getPropertyStringArray(propertyPath);
-        if (values == null) {
-            values = doubleToString(mDocument.getPropertyDoubleArray(propertyPath));
-        }
-        if (values == null) {
-            values = longToString(mDocument.getPropertyLongArray(propertyPath));
-        }
-        if (values == null) {
-            throw new IllegalStateException("No content found for requested property path!");
-        }
-        mValues = values;
-    }
-
-    /**
-     * Gets the property path corresponding to the given entry.
-     * <p>Property Path: '.' - delimited sequence of property names indicating which property in
-     * the Document these snippets correspond to.
-     * <p>Example properties: 'body', 'sender.name', 'sender.emailaddress', etc.
-     * For class example 1 this returns "subject"
-     */
-    @NonNull
-    public String getPropertyPath() {
-        return mPropertyPath;
-    }
-
-    /**
-     * Gets the full text corresponding to the given entry.
-     * <p>For class example this returns "A commonly used fake word is foo. Another nonsense word
-     * that’s used a lot is bar."
-     */
-    @NonNull
-    public String getFullText() {
-        return mValues[mSnippetMatch.getValuesIndex()];
-    }
-
-    /**
-     * Gets the exact match range corresponding to the given entry.
-     * <p>For class example 1 this returns [29, 32]
-     */
-    @NonNull
-    public Range getExactMatchPosition() {
-        return new Range(mSnippetMatch.getExactMatchPosition(),
-                mSnippetMatch.getExactMatchPosition() + mSnippetMatch.getExactMatchBytes());
-    }
-
-    /**
-     * Gets the exact match corresponding to the given entry.
-     * <p>For class example 1 this returns "foo"
-     */
-    @NonNull
-    public CharSequence getExactMatch() {
-        return getSubstring(getExactMatchPosition());
-    }
-
-    /**
-     * Gets the snippet range corresponding to the given entry.
-     * <p>For class example 1 this returns [29, 41]
-     */
-    @NonNull
-    public Range getSnippetPosition() {
-        return new Range(mSnippetMatch.getWindowPosition(),
-                mSnippetMatch.getWindowPosition() + mSnippetMatch.getWindowBytes());
-    }
-
-    /**
-     * Gets the snippet corresponding to the given entry.
-     * <p>Snippet - Provides a subset of the content to display. The
-     * length of this content can be changed {@link SearchSpec.Builder#setMaxSnippetSize(int)}.
-     * Windowing is centered around the middle of the matched token with content on either side
-     * clipped to token boundaries.
-     * <p>For class example 1 this returns "foo. Another"
-     */
-    @NonNull
-    public CharSequence getSnippet() {
-        return getSubstring(getSnippetPosition());
-    }
-
-    private CharSequence getSubstring(Range range) {
-        return getFullText()
-                .substring((int) range.getLower(), (int) range.getUpper());
-    }
-
-    /** Utility method to convert double[] to String[] */
-    private String[] doubleToString(double[] values) {
-        //TODO(sidchhabra): Implement the method.
-        return null;
-    }
-
-    /** Utility method to convert long[] to String[] */
-    private String[] longToString(long[] values) {
-        //TODO(sidchhabra): Implement the method.
-        return null;
-    }
-}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/PutDocumentsRequest.java b/apex/appsearch/framework/java/android/app/appsearch/PutDocumentsRequest.java
new file mode 100644
index 0000000..7e97542
--- /dev/null
+++ b/apex/appsearch/framework/java/android/app/appsearch/PutDocumentsRequest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch;
+
+import android.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Encapsulates a request to index a document into an {@link AppSearchManager} database.
+ *
+ * @see AppSearchManager#putDocuments
+ * @hide
+ */
+public final class PutDocumentsRequest {
+    private final List<GenericDocument> mDocuments;
+
+    PutDocumentsRequest(List<GenericDocument> documents) {
+        mDocuments = documents;
+    }
+
+    /** @hide */
+    
+    @NonNull
+    public List<GenericDocument> getDocuments() {
+        return mDocuments;
+    }
+
+    /** Builder for {@link PutDocumentsRequest} objects. */
+    public static final class Builder {
+        private final List<GenericDocument> mDocuments = new ArrayList<>();
+        private boolean mBuilt = false;
+
+        /** Adds one or more documents to the request. */
+        @NonNull
+        public Builder addGenericDocument(@NonNull GenericDocument... documents) {
+            Preconditions.checkNotNull(documents);
+            return addGenericDocument(Arrays.asList(documents));
+        }
+
+        /** Adds one or more documents to the request. */
+        @NonNull
+        public Builder addGenericDocument(@NonNull Collection<GenericDocument> documents) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkNotNull(documents);
+            mDocuments.addAll(documents);
+            return this;
+        }
+
+        /** Builds a new {@link PutDocumentsRequest}. */
+        @NonNull
+        public PutDocumentsRequest build() {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            mBuilt = true;
+            return new PutDocumentsRequest(mDocuments);
+        }
+    }
+}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/RemoveByUriRequest.java b/apex/appsearch/framework/java/android/app/appsearch/RemoveByUriRequest.java
new file mode 100644
index 0000000..a047041
--- /dev/null
+++ b/apex/appsearch/framework/java/android/app/appsearch/RemoveByUriRequest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch;
+
+import android.annotation.NonNull;
+
+import android.util.ArraySet;
+import com.android.internal.util.Preconditions;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Set;
+
+/**
+ * Encapsulates a request to remove documents by namespace and URI.
+ *
+ * @see AppSearchManager#removeByUri
+ * @hide
+ */
+public final class RemoveByUriRequest {
+    private final String mNamespace;
+    private final Set<String> mUris;
+
+    RemoveByUriRequest(String namespace, Set<String> uris) {
+        mNamespace = namespace;
+        mUris = uris;
+    }
+
+    /** @hide */
+    
+    @NonNull
+    public String getNamespace() {
+        return mNamespace;
+    }
+
+    /** @hide */
+    
+    @NonNull
+    public Set<String> getUris() {
+        return mUris;
+    }
+
+    /** Builder for {@link RemoveByUriRequest} objects. */
+    public static final class Builder {
+        private String mNamespace = GenericDocument.DEFAULT_NAMESPACE;
+        private final Set<String> mUris = new ArraySet<>();
+        private boolean mBuilt = false;
+
+        /**
+         * Sets which namespace these documents will be removed from.
+         *
+         * <p>If this is not set, it defaults to {@link GenericDocument#DEFAULT_NAMESPACE}.
+         */
+        @NonNull
+        public Builder setNamespace(@NonNull String namespace) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkNotNull(namespace);
+            mNamespace = namespace;
+            return this;
+        }
+
+        /** Adds one or more URIs to the request. */
+        @NonNull
+        public Builder addUris(@NonNull String... uris) {
+            Preconditions.checkNotNull(uris);
+            return addUris(Arrays.asList(uris));
+        }
+
+        /** Adds one or more URIs to the request. */
+        @NonNull
+        public Builder addUris(@NonNull Collection<String> uris) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkNotNull(uris);
+            mUris.addAll(uris);
+            return this;
+        }
+
+        /** Builds a new {@link RemoveByUriRequest}. */
+        @NonNull
+        public RemoveByUriRequest build() {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            mBuilt = true;
+            return new RemoveByUriRequest(mNamespace, mUris);
+        }
+    }
+}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchResult.java b/apex/appsearch/framework/java/android/app/appsearch/SearchResult.java
new file mode 100644
index 0000000..758280b
--- /dev/null
+++ b/apex/appsearch/framework/java/android/app/appsearch/SearchResult.java
@@ -0,0 +1,368 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch;
+
+import android.os.Bundle;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.util.Objects;
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class represents one of the results obtained from the query.
+ *
+ * <p>It contains the document which matched, information about which section(s) in the document
+ * matched, and snippet information containing textual summaries of the document's match(es).
+ * @hide
+ */
+public final class SearchResult {
+    /** @hide */
+    
+    public static final String DOCUMENT_FIELD = "document";
+
+    /** @hide */
+    
+    public static final String MATCHES_FIELD = "matches";
+
+    @NonNull
+    private final Bundle mBundle;
+
+    @NonNull
+    private final Bundle mDocumentBundle;
+
+    @Nullable
+    private GenericDocument mDocument;
+
+    @Nullable
+    private final List<Bundle> mMatchBundles;
+
+    /**
+     * Contains a list of Snippets that matched the request. Only populated when requested in
+     * both {@link SearchSpec.Builder#setSnippetCount(int)}
+     * and {@link SearchSpec.Builder#setSnippetCountPerProperty(int)}.
+     *
+     * @see #getMatches()
+     */
+    @Nullable
+    private List<MatchInfo> mMatches;
+
+    /** @hide */
+    
+    public SearchResult(@NonNull Bundle bundle) {
+        mBundle = Preconditions.checkNotNull(bundle);
+        mDocumentBundle = Preconditions.checkNotNull(bundle.getBundle(DOCUMENT_FIELD));
+        mMatchBundles = bundle.getParcelableArrayList(MATCHES_FIELD);
+    }
+
+    /** @hide */
+    
+    @NonNull
+    public Bundle getBundle() {
+        return mBundle;
+    }
+
+    /**
+     * Contains the matching {@link GenericDocument}.
+     * @return Document object which matched the query.
+     */
+    @NonNull
+    public GenericDocument getDocument() {
+        if (mDocument == null) {
+            mDocument = new GenericDocument(mDocumentBundle);
+        }
+        return mDocument;
+    }
+
+    /**
+     * Contains a list of Snippets that matched the request. Only populated when requested in
+     * both {@link SearchSpec.Builder#setSnippetCount(int)}
+     * and {@link SearchSpec.Builder#setSnippetCountPerProperty(int)}.
+     *
+     * @return  List of matches based on {@link SearchSpec}, if snippeting is disabled and this
+     * method is called it will return {@code null}. Users can also restrict snippet population
+     * using {@link SearchSpec.Builder#setSnippetCount} and
+     * {@link SearchSpec.Builder#setSnippetCountPerProperty(int)}, for all results after that
+     * value this method will return {@code null}.
+     */
+    @Nullable
+    public List<MatchInfo> getMatches() {
+        if (mMatchBundles != null && mMatches == null) {
+            mMatches = new ArrayList<>(mMatchBundles.size());
+            for (int i = 0; i < mMatchBundles.size(); i++) {
+                MatchInfo matchInfo = new MatchInfo(getDocument(), mMatchBundles.get(i));
+                mMatches.add(matchInfo);
+            }
+        }
+        return mMatches;
+    }
+
+    /**
+     * Snippet: It refers to a substring of text from the content of document that is returned as a
+     * part of search result.
+     * This class represents a match objects for any Snippets that might be present in
+     * {@link SearchResults} from query. Using this class user can get the full text, exact matches
+     * and Snippets of document content for a given match.
+     *
+     * <p>Class Example 1:
+     * A document contains following text in property subject:
+     * <p>A commonly used fake word is foo. Another nonsense word that’s used a lot is bar.
+     *
+     * <p>If the queryExpression is "foo".
+     *
+     * <p>{@link MatchInfo#getPropertyPath()} returns "subject"
+     * <p>{@link MatchInfo#getFullText()} returns "A commonly used fake word is foo. Another
+     * nonsense word that’s used a lot is bar."
+     * <p>{@link MatchInfo#getExactMatchPosition()} returns [29, 32]
+     * <p>{@link MatchInfo#getExactMatch()} returns "foo"
+     * <p>{@link MatchInfo#getSnippetPosition()} returns [26, 33]
+     * <p>{@link MatchInfo#getSnippet()} returns "is foo."
+     * <p>
+     * <p>Class Example 2:
+     * A document contains a property name sender which contains 2 property names name and email, so
+     * we will have 2 property paths: {@code sender.name} and {@code sender.email}.
+     * <p>Let {@code sender.name = "Test Name Jr."} and
+     * {@code sender.email = "TestNameJr@gmail.com"}
+     *
+     * <p>If the queryExpression is "Test". We will have 2 matches.
+     *
+     * <p> Match-1
+     * <p>{@link MatchInfo#getPropertyPath()} returns "sender.name"
+     * <p>{@link MatchInfo#getFullText()} returns "Test Name Jr."
+     * <p>{@link MatchInfo#getExactMatchPosition()} returns [0, 4]
+     * <p>{@link MatchInfo#getExactMatch()} returns "Test"
+     * <p>{@link MatchInfo#getSnippetPosition()} returns [0, 9]
+     * <p>{@link MatchInfo#getSnippet()} returns "Test Name"
+     * <p> Match-2
+     * <p>{@link MatchInfo#getPropertyPath()} returns "sender.email"
+     * <p>{@link MatchInfo#getFullText()} returns "TestNameJr@gmail.com"
+     * <p>{@link MatchInfo#getExactMatchPosition()} returns [0, 20]
+     * <p>{@link MatchInfo#getExactMatch()} returns "TestNameJr@gmail.com"
+     * <p>{@link MatchInfo#getSnippetPosition()} returns [0, 20]
+     * <p>{@link MatchInfo#getSnippet()} returns "TestNameJr@gmail.com"
+     */
+    public static final class MatchInfo {
+        /**
+         * The path of the matching snippet property.
+         * @hide
+         */
+        
+        public static final String PROPERTY_PATH_FIELD = "propertyPath";
+
+        /**
+         * The index of matching value in its property. A property may have multiple values. This
+         * index indicates which value is the match.
+         * @hide
+         */
+        
+        public static final String VALUES_INDEX_FIELD = "valuesIndex";
+
+        /** @hide */
+        
+        public static final String EXACT_MATCH_POSITION_LOWER_FIELD = "exactMatchPositionLower";
+
+        /** @hide */
+        
+        public static final String EXACT_MATCH_POSITION_UPPER_FIELD = "exactMatchPositionUpper";
+
+        /** @hide */
+        
+        public static final String WINDOW_POSITION_LOWER_FIELD = "windowPositionLower";
+
+        /** @hide */
+        
+        public static final String WINDOW_POSITION_UPPER_FIELD = "windowPositionUpper";
+
+        private final String mFullText;
+        private final String mPropertyPath;
+        private final Bundle mBundle;
+        private MatchRange mExactMatchRange;
+        private MatchRange mWindowRange;
+
+        MatchInfo(@NonNull GenericDocument document, @NonNull Bundle bundle) {
+            mBundle = Preconditions.checkNotNull(bundle);
+            Preconditions.checkNotNull(document);
+            mPropertyPath = Preconditions.checkNotNull(bundle.getString(PROPERTY_PATH_FIELD));
+            mFullText = getPropertyValues(
+                    document, mPropertyPath, mBundle.getInt(VALUES_INDEX_FIELD));
+        }
+
+        /**
+         * Gets the property path corresponding to the given entry.
+         * <p>Property Path: '.' - delimited sequence of property names indicating which property in
+         * the Document these snippets correspond to.
+         * <p>Example properties: 'body', 'sender.name', 'sender.emailaddress', etc.
+         * For class example 1 this returns "subject"
+         */
+        @NonNull
+        public String getPropertyPath() {
+            return mPropertyPath;
+        }
+
+        /**
+         * Gets the full text corresponding to the given entry.
+         * <p>For class example this returns "A commonly used fake word is foo. Another nonsense
+         * word that's used a lot is bar."
+         */
+        @NonNull
+        public String getFullText() {
+            return mFullText;
+        }
+
+        /**
+         * Gets the exact {@link MatchRange} corresponding to the given entry.
+         * <p>For class example 1 this returns [29, 32]
+         */
+        @NonNull
+        public MatchRange getExactMatchPosition() {
+            if (mExactMatchRange == null) {
+                mExactMatchRange = new MatchRange(
+                        mBundle.getInt(EXACT_MATCH_POSITION_LOWER_FIELD),
+                        mBundle.getInt(EXACT_MATCH_POSITION_UPPER_FIELD));
+            }
+            return mExactMatchRange;
+        }
+
+        /**
+         * Gets the  {@link MatchRange} corresponding to the given entry.
+         * <p>For class example 1 this returns "foo"
+         */
+        @NonNull
+        public CharSequence getExactMatch() {
+            return getSubstring(getExactMatchPosition());
+        }
+
+        /**
+         * Gets the snippet {@link MatchRange} corresponding to the given entry.
+         * <p>Only populated when set maxSnippetSize > 0 in
+         * {@link SearchSpec.Builder#setMaxSnippetSize}.
+         * <p>For class example 1 this returns [29, 41].
+         */
+        @NonNull
+        public MatchRange getSnippetPosition() {
+            if (mWindowRange == null) {
+                mWindowRange = new MatchRange(
+                        mBundle.getInt(WINDOW_POSITION_LOWER_FIELD),
+                        mBundle.getInt(WINDOW_POSITION_UPPER_FIELD));
+            }
+            return mWindowRange;
+        }
+
+        /**
+         * Gets the snippet corresponding to the given entry.
+         * <p>Snippet - Provides a subset of the content to display. Only populated when requested
+         * maxSnippetSize > 0. The size of this content can be changed by
+         * {@link SearchSpec.Builder#setMaxSnippetSize}. Windowing is centered around the middle of
+         * the matched token with content on either side clipped to token boundaries.
+         * <p>For class example 1 this returns "foo. Another"
+         */
+        @NonNull
+        public CharSequence getSnippet() {
+            return getSubstring(getSnippetPosition());
+        }
+
+        private CharSequence getSubstring(MatchRange range) {
+            return getFullText().substring(range.getStart(), range.getEnd());
+        }
+
+        /** Extracts the matching string from the document. */
+        private static String getPropertyValues(
+                GenericDocument document, String propertyName, int valueIndex) {
+            // In IcingLib snippeting is available for only 3 data types i.e String, double and
+            // long, so we need to check which of these three are requested.
+            // TODO (tytytyww): getPropertyStringArray takes property name, handle for property
+            //  path.
+            // TODO (tytytyww): support double[] and long[].
+            String[] values = document.getPropertyStringArray(propertyName);
+            if (values == null) {
+                throw new IllegalStateException("No content found for requested property path!");
+            }
+            return values[valueIndex];
+        }
+    }
+
+    /**
+     * Class providing the position range of matching information.
+     *
+     * <p> All ranges are finite, and the left side of the range is always {@code <=} the right
+     * side of the range.
+     *
+     * <p> Example: MatchRange(0, 100) represent a hundred ints from 0 to 99."
+     *
+     */
+    public static final class MatchRange {
+        private final int mEnd;
+        private final int mStart;
+
+        /**
+         * Creates a new immutable range.
+         * <p> The endpoints are {@code [start, end)}; that is the range is bounded. {@code start}
+         * must be lesser or equal to {@code end}.
+         *
+         * @param start The start point (inclusive)
+         * @param end The end point (exclusive)
+         * @hide
+         */
+        
+        public MatchRange(int start, int end) {
+            if (start > end) {
+                throw new IllegalArgumentException("Start point must be less than or equal to "
+                        + "end point");
+            }
+            mStart = start;
+            mEnd = end;
+        }
+
+        /** Gets the start point (inclusive). */
+        public int getStart() {
+            return mStart;
+        }
+
+        /** Gets the end point (exclusive). */
+        public int getEnd() {
+            return mEnd;
+        }
+
+        @Override
+        public boolean equals(@Nullable Object other) {
+            if (this == other) {
+                return true;
+            }
+            if (!(other instanceof MatchRange)) {
+                return false;
+            }
+            MatchRange otherMatchRange = (MatchRange) other;
+            return this.getStart() == otherMatchRange.getStart()
+                    && this.getEnd() == otherMatchRange.getEnd();
+        }
+
+        @Override
+        @NonNull
+        public String toString() {
+            return "MatchRange { start: " + mStart + " , end: " + mEnd + "}";
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mStart, mEnd);
+        }
+    }
+}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
index 7287fe6..9f37625 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
@@ -17,112 +17,62 @@
 package android.app.appsearch;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-import com.google.android.icing.proto.SearchResultProto;
-import com.google.android.icing.proto.SnippetMatchProto;
-import com.google.android.icing.proto.SnippetProto;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
 
 import java.util.ArrayList;
-import java.util.Iterator;
 import java.util.List;
-import java.util.NoSuchElementException;
 
 /**
- * SearchResults are a list of results that are returned from a query. Each result from this
- * list contains a document and may contain other fields like snippets based on request.
- * This iterator class is not thread safe.
+ * Structure for transmitting a page of search results across binder.
  * @hide
  */
-public final class SearchResults implements Iterator<SearchResults.Result> {
+public final class SearchResults implements Parcelable {
+    final List<SearchResult> mResults;
+    final long mNextPageToken;
 
-    private final SearchResultProto mSearchResultProto;
-    private int mNextIdx;
+    public SearchResults(@NonNull List<SearchResult> results, long nextPageToken) {
+        mResults = results;
+        mNextPageToken = nextPageToken;
+    }
 
-    /** @hide */
-    public SearchResults(SearchResultProto searchResultProto) {
-        mSearchResultProto = searchResultProto;
+    private SearchResults(@NonNull Parcel in) {
+        List<Bundle> resultBundles = in.readArrayList(/*loader=*/ null);
+        mResults = new ArrayList<>(resultBundles.size());
+        for (int i = 0; i < resultBundles.size(); i++) {
+            SearchResult searchResult = new SearchResult(resultBundles.get(i));
+            mResults.add(searchResult);
+        }
+        mNextPageToken = in.readLong();
     }
 
     @Override
-    public boolean hasNext() {
-        return mNextIdx < mSearchResultProto.getResultsCount();
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        List<Bundle> resultBundles = new ArrayList<>(mResults.size());
+        for (int i = 0; i < mResults.size(); i++) {
+            resultBundles.add(mResults.get(i).getBundle());
+        }
+        dest.writeList(resultBundles);
+        dest.writeLong(mNextPageToken);
     }
 
-    @NonNull
     @Override
-    public Result next() {
-        if (!hasNext()) {
-            throw new NoSuchElementException();
-        }
-        Result result = new Result(mSearchResultProto.getResults(mNextIdx));
-        mNextIdx++;
-        return result;
+    public int describeContents() {
+        return 0;
     }
 
-
-
-    /**
-     * This class represents the result obtained from the query. It will contain the document which
-     * which matched the specified query string and specifications.
-     * @hide
-     */
-    public static final class Result {
-        private final SearchResultProto.ResultProto mResultProto;
-
-        @Nullable
-        private AppSearchDocument mDocument;
-
-        private Result(SearchResultProto.ResultProto resultProto) {
-            mResultProto = resultProto;
-        }
-
-        /**
-         * Contains the matching {@link AppSearchDocument}.
-         * @return Document object which matched the query.
-         * @hide
-         */
+    public static final Creator<SearchResults> CREATOR = new Creator<SearchResults>() {
         @NonNull
-        public AppSearchDocument getDocument() {
-            if (mDocument == null) {
-                mDocument = new AppSearchDocument(mResultProto.getDocument());
-            }
-            return mDocument;
+        @Override
+        public SearchResults createFromParcel(@NonNull Parcel in) {
+            return new SearchResults(in);
         }
 
-        /**
-         * Contains a list of Snippets that matched the request. Only populated when requested in
-         * {@link SearchSpec.Builder#setMaxSnippetSize(int)}.
-         * @return  List of matches based on {@link SearchSpec}, if snippeting is disabled and this
-         * method is called it will return {@code null}. Users can also restrict snippet population
-         * using {@link SearchSpec.Builder#setNumToSnippet} and
-         * {@link SearchSpec.Builder#setNumMatchesPerProperty}, for all results after that value
-         * this method will return {@code null}.
-         * @hide
-         */
-        // TODO(sidchhabra): Replace Document with proper constructor.
-        @Nullable
-        public List<MatchInfo> getMatchInfo() {
-            if (!mResultProto.hasSnippet()) {
-                return null;
-            }
-            AppSearchDocument document = getDocument();
-            List<MatchInfo> matchList = new ArrayList<>();
-            for (Iterator entryProtoIterator = mResultProto.getSnippet()
-                    .getEntriesList().iterator(); entryProtoIterator.hasNext(); ) {
-                SnippetProto.EntryProto entry = (SnippetProto.EntryProto) entryProtoIterator.next();
-                for (Iterator snippetMatchProtoIterator = entry.getSnippetMatchesList().iterator();
-                        snippetMatchProtoIterator.hasNext(); ) {
-                    matchList.add(new MatchInfo(entry.getPropertyName(),
-                            (SnippetMatchProto) snippetMatchProtoIterator.next(), document));
-                }
-            }
-            return matchList;
+        @NonNull
+        @Override
+        public SearchResults[] newArray(int size) {
+            return new SearchResults[size];
         }
-    }
-
-    @Override
-    public String toString() {
-        return mSearchResultProto.toString();
-    }
+    };
 }
diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java b/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java
index 6e644ff..c871905 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,18 +16,17 @@
 
 package android.app.appsearch;
 
+import android.os.Bundle;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 
-import com.google.android.icing.proto.ResultSpecProto;
-import com.google.android.icing.proto.ScoringSpecProto;
-import com.google.android.icing.proto.SearchSpecProto;
-import com.google.android.icing.proto.TermMatchType;
+import android.app.appsearch.exceptions.IllegalSearchSpecException;
+import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
-
 /**
  * This class represents the specification logic for AppSearch. It can be used to set the type of
  * search, like prefix or exact only or apply filters to search for a specific schema type only etc.
@@ -35,67 +34,100 @@
  */
 // TODO(sidchhabra) : AddResultSpec fields for Snippets etc.
 public final class SearchSpec {
-
-    private final SearchSpecProto mSearchSpecProto;
-    private final ResultSpecProto mResultSpecProto;
-    private final ScoringSpecProto mScoringSpecProto;
-
-    private SearchSpec(@NonNull SearchSpecProto searchSpecProto,
-            @NonNull ResultSpecProto resultSpecProto, @NonNull ScoringSpecProto scoringSpecProto) {
-        mSearchSpecProto = searchSpecProto;
-        mResultSpecProto = resultSpecProto;
-        mScoringSpecProto = scoringSpecProto;
-    }
-
-    /** Creates a new {@link SearchSpec.Builder}. */
-    @NonNull
-    public static SearchSpec.Builder newBuilder() {
-        return new SearchSpec.Builder();
-    }
+    /** @hide */
+    
+    public static final String TERM_MATCH_TYPE_FIELD = "termMatchType";
 
     /** @hide */
-    @NonNull
-    SearchSpecProto getSearchSpecProto() {
-        return mSearchSpecProto;
-    }
+    
+    public static final String SCHEMA_TYPES_FIELD = "schemaType";
 
     /** @hide */
-    @NonNull
-    ResultSpecProto getResultSpecProto() {
-        return mResultSpecProto;
-    }
+    
+    public static final String NAMESPACE_FIELD = "namespace";
 
     /** @hide */
-    @NonNull
-    ScoringSpecProto getScoringSpecProto() {
-        return mScoringSpecProto;
+    
+    public static final String NUM_PER_PAGE_FIELD = "numPerPage";
+
+    /** @hide */
+    
+    public static final String RANKING_STRATEGY_FIELD = "rankingStrategy";
+
+    /** @hide */
+    
+    public static final String ORDER_FIELD = "order";
+
+    /** @hide */
+    
+    public static final String SNIPPET_COUNT_FIELD = "snippetCount";
+
+    /** @hide */
+    
+    public static final String SNIPPET_COUNT_PER_PROPERTY_FIELD = "snippetCountPerProperty";
+
+    /** @hide */
+    
+    public static final String MAX_SNIPPET_FIELD = "maxSnippet";
+
+    /** @hide */
+    
+    public static final int DEFAULT_NUM_PER_PAGE = 10;
+
+    private static final int MAX_NUM_PER_PAGE = 10_000;
+    private static final int MAX_SNIPPET_COUNT = 10_000;
+    private static final int MAX_SNIPPET_PER_PROPERTY_COUNT = 10_000;
+    private static final int MAX_SNIPPET_SIZE_LIMIT = 10_000;
+
+    private final Bundle mBundle;
+
+    /** @hide */
+    
+    public SearchSpec(@NonNull Bundle bundle) {
+        Preconditions.checkNotNull(bundle);
+        mBundle = bundle;
     }
 
-    /** Term Match Type for the query. */
+    /**
+     * Returns the {@link Bundle} populated by this builder.
+     * @hide
+     */
+    @NonNull
+    public Bundle getBundle() {
+        return mBundle;
+    }
+
+    /**
+     * Term Match Type for the query.
+     * @hide
+     */
     // NOTE: The integer values of these constants must match the proto enum constants in
     // {@link com.google.android.icing.proto.SearchSpecProto.termMatchType}
-    @IntDef(prefix = {"TERM_MATCH_TYPE_"}, value = {
-            TERM_MATCH_TYPE_EXACT_ONLY,
-            TERM_MATCH_TYPE_PREFIX
+    @IntDef(value = {
+            TERM_MATCH_EXACT_ONLY,
+            TERM_MATCH_PREFIX
     })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface TermMatchTypeCode {}
+    public @interface TermMatchCode {}
 
     /**
      * Query terms will only match exact tokens in the index.
      * <p>Ex. A query term "foo" will only match indexed token "foo", and not "foot" or "football".
      */
-    public static final int TERM_MATCH_TYPE_EXACT_ONLY = 1;
+    public static final int TERM_MATCH_EXACT_ONLY = 1;
     /**
      * Query terms will match indexed tokens when the query term is a prefix of the token.
      * <p>Ex. A query term "foo" will match indexed tokens like "foo", "foot", and "football".
      */
-    public static final int TERM_MATCH_TYPE_PREFIX = 2;
+    public static final int TERM_MATCH_PREFIX = 2;
 
-    /** Ranking Strategy for query result.*/
+    /**
+     * Ranking Strategy for query result.
+     * @hide
+     */
     // NOTE: The integer values of these constants must match the proto enum constants in
     // {@link ScoringSpecProto.RankingStrategy.Code }
-    @IntDef(prefix = {"RANKING_STRATEGY_"}, value = {
+    @IntDef(value = {
             RANKING_STRATEGY_NONE,
             RANKING_STRATEGY_DOCUMENT_SCORE,
             RANKING_STRATEGY_CREATION_TIMESTAMP
@@ -110,10 +142,13 @@
     /** Ranked by document creation timestamps. */
     public static final int RANKING_STRATEGY_CREATION_TIMESTAMP = 2;
 
-    /** Order for query result.*/
+    /**
+     * Order for query result.
+     * @hide
+     */
     // NOTE: The integer values of these constants must match the proto enum constants in
     // {@link ScoringSpecProto.Order.Code }
-    @IntDef(prefix = {"ORDER_"}, value = {
+    @IntDef(value = {
             ORDER_DESCENDING,
             ORDER_ASCENDING
     })
@@ -128,27 +163,24 @@
     /** Builder for {@link SearchSpec objects}. */
     public static final class Builder {
 
-        private final SearchSpecProto.Builder mSearchSpecBuilder = SearchSpecProto.newBuilder();
-        private final ResultSpecProto.Builder mResultSpecBuilder = ResultSpecProto.newBuilder();
-        private final ScoringSpecProto.Builder mScoringSpecBuilder = ScoringSpecProto.newBuilder();
-        private final ResultSpecProto.SnippetSpecProto.Builder mSnippetSpecBuilder =
-                ResultSpecProto.SnippetSpecProto.newBuilder();
+        private final Bundle mBundle;
+        private boolean mBuilt = false;
 
-        private Builder() {
+        /** Creates a new {@link SearchSpec.Builder}. */
+        public Builder() {
+            mBundle = new Bundle();
+            mBundle.putInt(NUM_PER_PAGE_FIELD, DEFAULT_NUM_PER_PAGE);
         }
 
         /**
-         * Indicates how the query terms should match {@link TermMatchTypeCode} in the index.
+         * Indicates how the query terms should match {@code TermMatchCode} in the index.
          */
         @NonNull
-        public Builder setTermMatchType(@TermMatchTypeCode int termMatchTypeCode) {
-            TermMatchType.Code termMatchTypeCodeProto =
-                    TermMatchType.Code.forNumber(termMatchTypeCode);
-            if (termMatchTypeCodeProto == null) {
-                throw new IllegalArgumentException("Invalid term match type: "
-                        + termMatchTypeCode);
-            }
-            mSearchSpecBuilder.setTermMatchType(termMatchTypeCodeProto);
+        public Builder setTermMatch(@TermMatchCode int termMatchTypeCode) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkArgumentInRange(termMatchTypeCode, TERM_MATCH_EXACT_ONLY,
+                    TERM_MATCH_PREFIX, "Term match type");
+            mBundle.putInt(TERM_MATCH_TYPE_FIELD, termMatchTypeCode);
             return this;
         }
 
@@ -159,31 +191,44 @@
          */
         @NonNull
         public Builder setSchemaTypes(@NonNull String... schemaTypes) {
-            for (String schemaType : schemaTypes) {
-                mSearchSpecBuilder.addSchemaTypeFilters(schemaType);
-            }
+            Preconditions.checkNotNull(schemaTypes);
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            mBundle.putStringArray(SCHEMA_TYPES_FIELD, schemaTypes);
             return this;
         }
 
-        /** Sets the maximum number of results to retrieve from the query */
+        /**
+         * Adds a namespace filter to {@link SearchSpec} Entry. Only search for documents that
+         * have the specified namespaces.
+         * <p>If unset, the query will search over all namespaces.
+         */
         @NonNull
-        public SearchSpec.Builder setNumToRetrieve(int numToRetrieve) {
-            // Just retrieve everything in one page.
-            // TODO(b/152359656): Realign these two apis properly.
-            mResultSpecBuilder.setNumPerPage(numToRetrieve);
+        public Builder setNamespaces(@NonNull String... namespaces) {
+            Preconditions.checkNotNull(namespaces);
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            mBundle.putStringArray(NAMESPACE_FIELD, namespaces);
+            return this;
+        }
+
+        /**
+         * Sets the number of results per page in the returned object.
+         * <p> The default number of results per page is 10. And should be set in range [0, 10k].
+         */
+        @NonNull
+        public SearchSpec.Builder setNumPerPage(int numPerPage) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkArgumentInRange(numPerPage, 0, MAX_NUM_PER_PAGE, "NumPerPage");
+            mBundle.putInt(NUM_PER_PAGE_FIELD, numPerPage);
             return this;
         }
 
         /** Sets ranking strategy for AppSearch results.*/
         @NonNull
         public Builder setRankingStrategy(@RankingStrategyCode int rankingStrategy) {
-            ScoringSpecProto.RankingStrategy.Code rankingStrategyCodeProto =
-                    ScoringSpecProto.RankingStrategy.Code.forNumber(rankingStrategy);
-            if (rankingStrategyCodeProto == null) {
-                throw new IllegalArgumentException("Invalid result ranking strategy: "
-                        + rankingStrategyCodeProto);
-            }
-            mScoringSpecBuilder.setRankBy(rankingStrategyCodeProto);
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkArgumentInRange(rankingStrategy, RANKING_STRATEGY_NONE,
+                    RANKING_STRATEGY_CREATION_TIMESTAMP, "Result ranking strategy");
+            mBundle.putInt(RANKING_STRATEGY_FIELD, rankingStrategy);
             return this;
         }
 
@@ -194,37 +239,45 @@
          */
         @NonNull
         public Builder setOrder(@OrderCode int order) {
-            ScoringSpecProto.Order.Code orderCodeProto =
-                    ScoringSpecProto.Order.Code.forNumber(order);
-            if (orderCodeProto == null) {
-                throw new IllegalArgumentException("Invalid result ranking order: "
-                        + orderCodeProto);
-            }
-            mScoringSpecBuilder.setOrderBy(orderCodeProto);
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkArgumentInRange(order, ORDER_DESCENDING, ORDER_ASCENDING,
+                    "Result ranking order");
+            mBundle.putInt(ORDER_FIELD, order);
             return this;
         }
 
         /**
-         * Only the first {@code numToSnippet} documents based on the ranking strategy
+         * Only the first {@code snippetCount} documents based on the ranking strategy
          * will have snippet information provided.
-         * <p>If set to 0 (default), snippeting is disabled and
-         * {@link SearchResults.Result#getMatchInfo} will return {@code null} for that result.
+         *
+         * <p>If set to 0 (default), snippeting is disabled and {@link SearchResult#getMatches} will
+         * return {@code null} for that result.
+         *
+         * <p>The value should be set in range[0, 10k].
          */
         @NonNull
-        public SearchSpec.Builder setNumToSnippet(int numToSnippet) {
-            mSnippetSpecBuilder.setNumToSnippet(numToSnippet);
+        public SearchSpec.Builder setSnippetCount(int snippetCount) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkArgumentInRange(snippetCount, 0, MAX_SNIPPET_COUNT, "snippetCount");
+            mBundle.putInt(SNIPPET_COUNT_FIELD, snippetCount);
             return this;
         }
 
         /**
-         * Only the first {@code numMatchesPerProperty} matches for a every property of
-         * {@link AppSearchDocument} will contain snippet information.
-         * <p>If set to 0, snippeting is disabled and {@link SearchResults.Result#getMatchInfo}
-         * will return {@code null} for that result.
+         * Only the first {@code matchesCountPerProperty} matches for a every property of
+         * {@link GenericDocument} will contain snippet information.
+         *
+         * <p>If set to 0, snippeting is disabled and {@link SearchResult#getMatches} will return
+         * {@code null} for that result.
+         *
+         * <p>The value should be set in range[0, 10k].
          */
         @NonNull
-        public SearchSpec.Builder setNumMatchesPerProperty(int numMatchesPerProperty) {
-            mSnippetSpecBuilder.setNumMatchesPerProperty(numMatchesPerProperty);
+        public SearchSpec.Builder setSnippetCountPerProperty(int snippetCountPerProperty) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkArgumentInRange(snippetCountPerProperty,
+                    0, MAX_SNIPPET_PER_PROPERTY_COUNT, "snippetCountPerProperty");
+            mBundle.putInt(SNIPPET_COUNT_PER_PROPERTY_FIELD, snippetCountPerProperty);
             return this;
         }
 
@@ -237,10 +290,14 @@
          * be returned. If matches enabled is also set to false, then snippeting is disabled.
          * <p>Ex. {@code maxSnippetSize} = 16. "foo bar baz bat rat" with a query of "baz" will
          * return a window of "bar baz bat" which is only 11 bytes long.
+         * <p>The value should be in range[0, 10k].
          */
         @NonNull
         public SearchSpec.Builder setMaxSnippetSize(int maxSnippetSize) {
-            mSnippetSpecBuilder.setMaxWindowBytes(maxSnippetSize);
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkArgumentInRange(
+                    maxSnippetSize, 0, MAX_SNIPPET_SIZE_LIMIT, "maxSnippetSize");
+            mBundle.putInt(MAX_SNIPPET_FIELD, maxSnippetSize);
             return this;
         }
 
@@ -251,12 +308,12 @@
          */
         @NonNull
         public SearchSpec build() {
-            if (mSearchSpecBuilder.getTermMatchType() == TermMatchType.Code.UNKNOWN) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            if (!mBundle.containsKey(TERM_MATCH_TYPE_FIELD)) {
                 throw new IllegalSearchSpecException("Missing termMatchType field.");
             }
-            mResultSpecBuilder.setSnippetSpec(mSnippetSpecBuilder);
-            return new SearchSpec(mSearchSpecBuilder.build(), mResultSpecBuilder.build(),
-                    mScoringSpecBuilder.build());
+            mBuilt = true;
+            return new SearchSpec(mBundle);
         }
     }
 }
diff --git a/apex/appsearch/framework/java/android/app/appsearch/SetSchemaRequest.java b/apex/appsearch/framework/java/android/app/appsearch/SetSchemaRequest.java
new file mode 100644
index 0000000..b2e9d46
--- /dev/null
+++ b/apex/appsearch/framework/java/android/app/appsearch/SetSchemaRequest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch;
+
+import android.annotation.NonNull;
+import android.util.ArraySet;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Set;
+
+/**
+ * Encapsulates a request to update the schema of an {@link AppSearchManager} database.
+ *
+ * @see AppSearchManager#setSchema
+ * @hide
+ */
+public final class SetSchemaRequest {
+    private final Set<AppSearchSchema> mSchemas;
+    private final boolean mForceOverride;
+
+    SetSchemaRequest(Set<AppSearchSchema> schemas, boolean forceOverride) {
+        mSchemas = schemas;
+        mForceOverride = forceOverride;
+    }
+
+    /** @hide */
+    
+    @NonNull
+    public Set<AppSearchSchema> getSchemas() {
+        return mSchemas;
+    }
+
+    /** @hide */
+    
+    public boolean isForceOverride() {
+        return mForceOverride;
+    }
+
+    /** Builder for {@link SetSchemaRequest} objects. */
+    public static final class Builder {
+        private final Set<AppSearchSchema> mSchemas = new ArraySet<>();
+        private boolean mForceOverride = false;
+        private boolean mBuilt = false;
+
+        /** Adds one or more types to the schema. */
+        @NonNull
+        public Builder addSchema(@NonNull AppSearchSchema... schemas) {
+            Preconditions.checkNotNull(schemas);
+            return addSchema(Arrays.asList(schemas));
+        }
+
+        /** Adds one or more types to the schema. */
+        @NonNull
+        public Builder addSchema(@NonNull Collection<AppSearchSchema> schemas) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkNotNull(schemas);
+            mSchemas.addAll(schemas);
+            return this;
+        }
+
+        /**
+         * Configures the {@link SetSchemaRequest} to delete any existing documents that don't
+         * follow the new schema.
+         *
+         * <p>By default, this is {@code false} and schema incompatibility causes the
+         * {@link AppSearchManager#setSchema} call to fail.
+         *
+         * @see AppSearchManager#setSchema
+         */
+        @NonNull
+        public Builder setForceOverride(boolean forceOverride) {
+            mForceOverride = forceOverride;
+            return this;
+        }
+
+        /** Builds a new {@link SetSchemaRequest}. */
+        @NonNull
+        public SetSchemaRequest build() {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            mBuilt = true;
+            return new SetSchemaRequest(mSchemas, mForceOverride);
+        }
+    }
+}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/exceptions/AppSearchException.java b/apex/appsearch/framework/java/android/app/appsearch/exceptions/AppSearchException.java
index 00f6e75..d490469 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/exceptions/AppSearchException.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/exceptions/AppSearchException.java
@@ -54,6 +54,10 @@
         mResultCode = resultCode;
     }
 
+    public @AppSearchResult.ResultCode int getResultCode() {
+        return mResultCode;
+    }
+
     /**
      * Converts this {@link java.lang.Exception} into a failed {@link AppSearchResult}
      */
diff --git a/apex/appsearch/framework/java/android/app/appsearch/IllegalSchemaException.java b/apex/appsearch/framework/java/android/app/appsearch/exceptions/IllegalSchemaException.java
similarity index 92%
rename from apex/appsearch/framework/java/android/app/appsearch/IllegalSchemaException.java
rename to apex/appsearch/framework/java/android/app/appsearch/exceptions/IllegalSchemaException.java
index f9e528c..6dd86f5 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/IllegalSchemaException.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/exceptions/IllegalSchemaException.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright 2020 The Android Open 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,16 +14,18 @@
  * limitations under the License.
  */
 
-package android.app.appsearch;
+package android.app.appsearch.exceptions;
 
 import android.annotation.NonNull;
 
+
 /**
  * Indicates that a {@link android.app.appsearch.AppSearchSchema} has logical inconsistencies such
  * as unpopulated mandatory fields or illegal combinations of parameters.
  *
  * @hide
  */
+
 public class IllegalSchemaException extends IllegalArgumentException {
     /**
      * Constructs a new {@link IllegalSchemaException}.
diff --git a/apex/appsearch/framework/java/android/app/appsearch/IllegalSearchSpecException.java b/apex/appsearch/framework/java/android/app/appsearch/exceptions/IllegalSearchSpecException.java
similarity index 84%
rename from apex/appsearch/framework/java/android/app/appsearch/IllegalSearchSpecException.java
rename to apex/appsearch/framework/java/android/app/appsearch/exceptions/IllegalSearchSpecException.java
index 0d029f0..3ef887f0 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/IllegalSearchSpecException.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/exceptions/IllegalSearchSpecException.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright 2020 The Android Open 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,16 +14,18 @@
  * limitations under the License.
  */
 
-package android.app.appsearch;
+package android.app.appsearch.exceptions;
 
 import android.annotation.NonNull;
 
+
 /**
- * Indicates that a {@link android.app.appsearch.SearchResults} has logical inconsistencies such
+ * Indicates that a {@link android.app.appsearch.SearchResult} has logical inconsistencies such
  * as unpopulated mandatory fields or illegal combinations of parameters.
  *
  * @hide
  */
+
 public class IllegalSearchSpecException extends IllegalArgumentException {
     /**
      * Constructs a new {@link IllegalSearchSpecException}.
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
index 75fad82..7cd6ee2 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -17,26 +17,33 @@
 
 import android.annotation.NonNull;
 import android.app.appsearch.AppSearchBatchResult;
-import android.app.appsearch.AppSearchDocument;
 import android.app.appsearch.AppSearchResult;
+import android.app.appsearch.AppSearchSchema;
+import android.app.appsearch.GenericDocument;
 import android.app.appsearch.IAppSearchManager;
+import android.app.appsearch.SearchResult;
+import android.app.appsearch.SearchResults;
+import android.app.appsearch.SearchSpec;
 import android.app.appsearch.exceptions.AppSearchException;
 import android.content.Context;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.UserHandle;
 
 import com.android.internal.infra.AndroidFuture;
 import com.android.internal.util.Preconditions;
 import com.android.server.SystemService;
 import com.android.server.appsearch.external.localbackend.AppSearchImpl;
+import com.android.server.appsearch.external.localbackend.converter.GenericDocumentToProtoConverter;
+import com.android.server.appsearch.external.localbackend.converter.SchemaToProtoConverter;
+import com.android.server.appsearch.external.localbackend.converter.SearchResultToProtoConverter;
+import com.android.server.appsearch.external.localbackend.converter.SearchSpecToProtoConverter;
 
 import com.google.android.icing.proto.DocumentProto;
-import com.google.android.icing.proto.ResultSpecProto;
 import com.google.android.icing.proto.SchemaProto;
-import com.google.android.icing.proto.ScoringSpecProto;
+import com.google.android.icing.proto.SchemaTypeConfigProto;
 import com.google.android.icing.proto.SearchResultProto;
 import com.google.android.icing.proto.SearchSpecProto;
-import com.google.android.icing.proto.StatusProto;
 
 import java.io.IOException;
 import java.util.List;
@@ -46,6 +53,7 @@
  */
 public class AppSearchManagerService extends SystemService {
     private static final String TAG = "AppSearchManagerService";
+    private static final char CALLING_NAME_DATABASE_DELIMITER = '$';
 
     public AppSearchManagerService(Context context) {
         super(context);
@@ -59,20 +67,26 @@
     private class Stub extends IAppSearchManager.Stub {
         @Override
         public void setSchema(
-                @NonNull byte[] schemaBytes,
+                @NonNull String databaseName,
+                @NonNull List<Bundle> schemaBundles,
                 boolean forceOverride,
                 @NonNull AndroidFuture<AppSearchResult> callback) {
-            Preconditions.checkNotNull(schemaBytes);
+            Preconditions.checkNotNull(schemaBundles);
             Preconditions.checkNotNull(callback);
             int callingUid = Binder.getCallingUidOrThrow();
             int callingUserId = UserHandle.getUserId(callingUid);
-            long callingIdentity = Binder.clearCallingIdentity();
+            final long callingIdentity = Binder.clearCallingIdentity();
             try {
-                SchemaProto schema = SchemaProto.parseFrom(schemaBytes);
+                SchemaProto.Builder schemaProtoBuilder = SchemaProto.newBuilder();
+                for (int i = 0; i < schemaBundles.size(); i++) {
+                    AppSearchSchema schema = new AppSearchSchema(schemaBundles.get(i));
+                    SchemaTypeConfigProto schemaTypeProto = SchemaToProtoConverter.convert(schema);
+                    schemaProtoBuilder.addTypes(schemaTypeProto);
+                }
                 AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
-                String databaseName = makeDatabaseName(callingUid);
-                impl.setSchema(databaseName, schema, forceOverride);
-                callback.complete(AppSearchResult.newSuccessfulResult(/*value=*/ null));
+                databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid);
+                impl.setSchema(databaseName, schemaProtoBuilder.build(), forceOverride);
+                callback.complete(AppSearchResult.newSuccessfulResult(/*result=*/ null));
             } catch (Throwable t) {
                 callback.complete(throwableToFailedResult(t));
             } finally {
@@ -82,24 +96,25 @@
 
         @Override
         public void putDocuments(
-                @NonNull List documentsBytes,
+                @NonNull String databaseName,
+                @NonNull List<Bundle> documentBundles,
                 @NonNull AndroidFuture<AppSearchBatchResult> callback) {
-            Preconditions.checkNotNull(documentsBytes);
+            Preconditions.checkNotNull(documentBundles);
             Preconditions.checkNotNull(callback);
             int callingUid = Binder.getCallingUidOrThrow();
             int callingUserId = UserHandle.getUserId(callingUid);
-            long callingIdentity = Binder.clearCallingIdentity();
+            final long callingIdentity = Binder.clearCallingIdentity();
             try {
                 AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
-                String databaseName = makeDatabaseName(callingUid);
+                databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid);
                 AppSearchBatchResult.Builder<String, Void> resultBuilder =
                         new AppSearchBatchResult.Builder<>();
-                for (int i = 0; i < documentsBytes.size(); i++) {
-                    byte[] documentBytes = (byte[]) documentsBytes.get(i);
-                    DocumentProto document = DocumentProto.parseFrom(documentBytes);
+                for (int i = 0; i < documentBundles.size(); i++) {
+                    GenericDocument document = new GenericDocument(documentBundles.get(i));
+                    DocumentProto documentProto = GenericDocumentToProtoConverter.convert(document);
                     try {
-                        impl.putDocument(databaseName, document);
-                        resultBuilder.setSuccess(document.getUri(), /*value=*/ null);
+                        impl.putDocument(databaseName, documentProto);
+                        resultBuilder.setSuccess(document.getUri(), /*result=*/ null);
                     } catch (Throwable t) {
                         resultBuilder.setResult(document.getUri(), throwableToFailedResult(t));
                     }
@@ -113,28 +128,30 @@
         }
 
         @Override
-        public void getDocuments(
+        public void getDocuments(@NonNull String databaseName, @NonNull String namespace,
                 @NonNull List<String> uris, @NonNull AndroidFuture<AppSearchBatchResult> callback) {
             Preconditions.checkNotNull(uris);
             Preconditions.checkNotNull(callback);
             int callingUid = Binder.getCallingUidOrThrow();
             int callingUserId = UserHandle.getUserId(callingUid);
-            long callingIdentity = Binder.clearCallingIdentity();
+            final long callingIdentity = Binder.clearCallingIdentity();
             try {
                 AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
-                String databaseName = makeDatabaseName(callingUid);
-                AppSearchBatchResult.Builder<String, byte[]> resultBuilder =
+                databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid);
+                AppSearchBatchResult.Builder<String, Bundle> resultBuilder =
                         new AppSearchBatchResult.Builder<>();
                 for (int i = 0; i < uris.size(); i++) {
                     String uri = uris.get(i);
                     try {
-                        DocumentProto document = impl.getDocument(
-                                databaseName, AppSearchDocument.DEFAULT_NAMESPACE, uri);
-                        if (document == null) {
+                        DocumentProto documentProto = impl.getDocument(
+                                databaseName, namespace, uri);
+                        if (documentProto == null) {
                             resultBuilder.setFailure(
                                     uri, AppSearchResult.RESULT_NOT_FOUND, /*errorMessage=*/ null);
                         } else {
-                            resultBuilder.setSuccess(uri, document.toByteArray());
+                            GenericDocument genericDocument =
+                                    GenericDocumentToProtoConverter.convert(documentProto);
+                            resultBuilder.setSuccess(uri, genericDocument.getBundle());
                         }
                     } catch (Throwable t) {
                         resultBuilder.setResult(uri, throwableToFailedResult(t));
@@ -151,36 +168,35 @@
         // TODO(sidchhabra): Do this in a threadpool.
         @Override
         public void query(
-                @NonNull byte[] searchSpecBytes,
-                @NonNull byte[] resultSpecBytes,
-                @NonNull byte[] scoringSpecBytes,
+                @NonNull String databaseName,
+                @NonNull String queryExpression,
+                @NonNull Bundle searchSpecBundle,
                 @NonNull AndroidFuture<AppSearchResult> callback) {
-            Preconditions.checkNotNull(searchSpecBytes);
-            Preconditions.checkNotNull(resultSpecBytes);
-            Preconditions.checkNotNull(scoringSpecBytes);
+            Preconditions.checkNotNull(queryExpression);
+            Preconditions.checkNotNull(searchSpecBundle);
             Preconditions.checkNotNull(callback);
             int callingUid = Binder.getCallingUidOrThrow();
             int callingUserId = UserHandle.getUserId(callingUid);
-            long callingIdentity = Binder.clearCallingIdentity();
+            final long callingIdentity = Binder.clearCallingIdentity();
             try {
-                SearchSpecProto searchSpecProto = SearchSpecProto.parseFrom(searchSpecBytes);
-                ResultSpecProto resultSpecProto = ResultSpecProto.parseFrom(resultSpecBytes);
-                ScoringSpecProto scoringSpecProto = ScoringSpecProto.parseFrom(scoringSpecBytes);
+                SearchSpec searchSpec = new SearchSpec(searchSpecBundle);
+                SearchSpecProto searchSpecProto =
+                        SearchSpecToProtoConverter.toSearchSpecProto(searchSpec);
+                searchSpecProto = searchSpecProto.toBuilder()
+                        .setQuery(queryExpression).build();
                 AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
-                String databaseName = makeDatabaseName(callingUid);
+                databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid);
+                // TODO(adorokhine): handle pagination
                 SearchResultProto searchResultProto = impl.query(
-                        databaseName, searchSpecProto, resultSpecProto, scoringSpecProto);
-                // TODO(sidchhabra): Translate SearchResultProto errors into error codes. This might
-                //     better be done in AppSearchImpl by throwing an AppSearchException.
-                if (searchResultProto.getStatus().getCode() != StatusProto.Code.OK) {
-                    callback.complete(
-                            AppSearchResult.newFailedResult(
-                                    AppSearchResult.RESULT_INTERNAL_ERROR,
-                                    searchResultProto.getStatus().getMessage()));
-                } else {
-                    callback.complete(
-                            AppSearchResult.newSuccessfulResult(searchResultProto.toByteArray()));
-                }
+                        databaseName,
+                        searchSpecProto,
+                        SearchSpecToProtoConverter.toResultSpecProto(searchSpec),
+                        SearchSpecToProtoConverter.toScoringSpecProto(searchSpec));
+                List<SearchResult> searchResultList =
+                        SearchResultToProtoConverter.convert(searchResultProto);
+                SearchResults searchResults =
+                        new SearchResults(searchResultList, searchResultProto.getNextPageToken());
+                callback.complete(AppSearchResult.newSuccessfulResult(searchResults));
             } catch (Throwable t) {
                 callback.complete(throwableToFailedResult(t));
             } finally {
@@ -189,22 +205,23 @@
         }
 
         @Override
-        public void delete(List<String> uris, AndroidFuture<AppSearchBatchResult> callback) {
+        public void delete(@NonNull String databaseName, @NonNull String namespace,
+                List<String> uris, AndroidFuture<AppSearchBatchResult> callback) {
             Preconditions.checkNotNull(uris);
             Preconditions.checkNotNull(callback);
             int callingUid = Binder.getCallingUidOrThrow();
             int callingUserId = UserHandle.getUserId(callingUid);
-            long callingIdentity = Binder.clearCallingIdentity();
+            final long callingIdentity = Binder.clearCallingIdentity();
             try {
                 AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
-                String databaseName = makeDatabaseName(callingUid);
+                databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid);
                 AppSearchBatchResult.Builder<String, Void> resultBuilder =
                         new AppSearchBatchResult.Builder<>();
                 for (int i = 0; i < uris.size(); i++) {
                     String uri = uris.get(i);
                     try {
-                        impl.remove(databaseName, AppSearchDocument.DEFAULT_NAMESPACE, uri);
-                        resultBuilder.setSuccess(uri, /*value= */null);
+                        impl.remove(databaseName, namespace, uri);
+                        resultBuilder.setSuccess(uri, /*result= */null);
                     } catch (Throwable t) {
                         resultBuilder.setResult(uri, throwableToFailedResult(t));
                     }
@@ -218,23 +235,23 @@
         }
 
         @Override
-        public void deleteByTypes(
+        public void deleteByTypes(@NonNull String databaseName,
                 List<String> schemaTypes, AndroidFuture<AppSearchBatchResult> callback) {
             Preconditions.checkNotNull(schemaTypes);
             Preconditions.checkNotNull(callback);
             int callingUid = Binder.getCallingUidOrThrow();
             int callingUserId = UserHandle.getUserId(callingUid);
-            long callingIdentity = Binder.clearCallingIdentity();
+            final long callingIdentity = Binder.clearCallingIdentity();
             try {
                 AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
-                String databaseName = makeDatabaseName(callingUid);
+                databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid);
                 AppSearchBatchResult.Builder<String, Void> resultBuilder =
                         new AppSearchBatchResult.Builder<>();
                 for (int i = 0; i < schemaTypes.size(); i++) {
                     String schemaType = schemaTypes.get(i);
                     try {
                         impl.removeByType(databaseName, schemaType);
-                        resultBuilder.setSuccess(schemaType, /*value=*/ null);
+                        resultBuilder.setSuccess(schemaType, /*result=*/ null);
                     } catch (Throwable t) {
                         resultBuilder.setResult(schemaType, throwableToFailedResult(t));
                     }
@@ -248,14 +265,15 @@
         }
 
         @Override
-        public void deleteAll(@NonNull AndroidFuture<AppSearchResult> callback) {
+        public void deleteAll(@NonNull String databaseName,
+                @NonNull AndroidFuture<AppSearchResult> callback) {
             Preconditions.checkNotNull(callback);
             int callingUid = Binder.getCallingUidOrThrow();
             int callingUserId = UserHandle.getUserId(callingUid);
-            long callingIdentity = Binder.clearCallingIdentity();
+            final long callingIdentity = Binder.clearCallingIdentity();
             try {
                 AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
-                String databaseName = makeDatabaseName(callingUid);
+                databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid);
                 impl.removeAll(databaseName);
                 callback.complete(AppSearchResult.newSuccessfulResult(null));
             } catch (Throwable t) {
@@ -266,13 +284,13 @@
         }
 
         /**
-         * Returns a unique database name for the given uid.
+         * Rewrites the database name by adding a prefix of unique name for the given uid.
          *
          * <p>The current implementation returns the package name of the app with this uid in a
          * format like {@code com.example.package} or {@code com.example.sharedname:5678}.
          */
         @NonNull
-        private String makeDatabaseName(int callingUid) {
+        private String rewriteDatabaseNameWithUid(String databaseName, int callingUid) {
             // For regular apps, this call will return the package name. If callingUid is an
             // android:sharedUserId, this value may be another type of name and have a :uid suffix.
             String callingUidName = getContext().getPackageManager().getNameForUid(callingUid);
@@ -281,7 +299,7 @@
                 throw new IllegalStateException(
                         "Failed to look up package name for uid " + callingUid);
             }
-            return callingUidName;
+            return callingUidName + CALLING_NAME_DATABASE_DELIMITER + databaseName;
         }
 
         private <ValueType> AppSearchResult<ValueType> throwableToFailedResult(
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
index c1e6b0f..60f7005 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
@@ -69,9 +69,7 @@
     private static AppSearchImpl createImpl(@NonNull Context context, @UserIdInt int userId)
             throws AppSearchException {
         File appSearchDir = getAppSearchDir(context, userId);
-        AppSearchImpl appSearchImpl = new AppSearchImpl(appSearchDir);
-        appSearchImpl.initialize();
-        return appSearchImpl;
+        return AppSearchImpl.create(appSearchDir);
     }
 
     private static File getAppSearchDir(@NonNull Context context, @UserIdInt int userId) {
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/AppSearchImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/AppSearchImpl.java
index 462f458..642378d 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/AppSearchImpl.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/AppSearchImpl.java
@@ -18,7 +18,6 @@
 
 import android.util.Log;
 
-import android.annotation.AnyThread;
 import com.android.internal.annotations.GuardedBy;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -27,6 +26,7 @@
 import android.annotation.WorkerThread;
 import android.app.appsearch.AppSearchResult;
 import android.app.appsearch.exceptions.AppSearchException;
+import com.android.internal.util.Preconditions;
 
 import com.google.android.icing.IcingSearchEngine;
 import com.google.android.icing.proto.DeleteByNamespaceResultProto;
@@ -58,7 +58,6 @@
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
@@ -66,8 +65,7 @@
  * Manages interaction with the native IcingSearchEngine and other components to implement AppSearch
  * functionality.
  *
- * <p>Callers should call {@link #initialize} before using the AppSearchImpl instance. Never create
- * two instances using the same folder.
+ * <p>Never create two instances using the same folder.
  *
  * <p>A single instance of {@link AppSearchImpl} can support all databases. Schemas and documents
  * are physically saved together in {@link IcingSearchEngine}, but logically isolated:
@@ -106,9 +104,7 @@
     static final int CHECK_OPTIMIZE_INTERVAL = 100;
 
     private final ReadWriteLock mReadWriteLock = new ReentrantReadWriteLock();
-    private final CountDownLatch mInitCompleteLatch = new CountDownLatch(1);
-    private final File mIcingDir;
-    private IcingSearchEngine mIcingSearchEngine;
+    private final IcingSearchEngine mIcingSearchEngine;
 
     // The map contains schemaTypes and namespaces for all database. All values in the map have
     // been already added database name prefix.
@@ -121,33 +117,24 @@
      */
     private int mOptimizeIntervalCount = 0;
 
-    /** Creates an instance of {@link AppSearchImpl} which writes data to the given folder. */
-    @AnyThread
-    public AppSearchImpl(@NonNull File icingDir) {
-        mIcingDir = icingDir;
+    /**
+     * Creates and initializes an instance of {@link AppSearchImpl} which writes data to the given
+     * folder.
+     */
+    @NonNull
+    public static AppSearchImpl create(@NonNull File icingDir) throws AppSearchException {
+        Preconditions.checkNotNull(icingDir);
+        return new AppSearchImpl(icingDir);
     }
 
-    /**
-     * Initializes the underlying IcingSearchEngine.
-     *
-     * <p>This method belongs to mutate group.
-     *
-     * @throws AppSearchException on IcingSearchEngine error.
-     */
-    public void initialize() throws AppSearchException {
-        if (isInitialized()) {
-            return;
-        }
+    private AppSearchImpl(@NonNull File icingDir) throws AppSearchException {
         boolean isReset = false;
         mReadWriteLock.writeLock().lock();
         try {
-        // We synchronize here because we don't want to call IcingSearchEngine.initialize() more
-        // than once. It's unnecessary and can be a costly operation.
-            if (isInitialized()) {
-                return;
-            }
+            // We synchronize here because we don't want to call IcingSearchEngine.initialize() more
+            // than once. It's unnecessary and can be a costly operation.
             IcingSearchEngineOptions options = IcingSearchEngineOptions.newBuilder()
-                    .setBaseDir(mIcingDir.getAbsolutePath()).build();
+                    .setBaseDir(icingDir.getAbsolutePath()).build();
             mIcingSearchEngine = new IcingSearchEngine(options);
 
             InitializeResultProto initializeResultProto = mIcingSearchEngine.initialize();
@@ -170,7 +157,8 @@
             for (String qualifiedNamespace : getAllNamespacesResultProto.getNamespacesList()) {
                 addToMap(mNamespaceMap, getDatabaseName(qualifiedNamespace), qualifiedNamespace);
             }
-            mInitCompleteLatch.countDown();
+            // TODO(b/155939114): It's possible to optimize after init, which would reduce the time
+            //   to when we're able to serve queries. Consider moving this optimize call out.
             if (!isReset) {
                 checkForOptimize(/* force= */ true);
             }
@@ -179,12 +167,6 @@
         }
     }
 
-    /** Checks if the internal state of {@link AppSearchImpl} has been initialized. */
-    @AnyThread
-    public boolean isInitialized() {
-        return mInitCompleteLatch.getCount() == 0;
-    }
-
     /**
      * Updates the AppSearch schema for this app.
      *
@@ -195,12 +177,9 @@
      * @param forceOverride Whether to force-apply the schema even if it is incompatible. Documents
      *                      which do not comply with the new schema will be deleted.
      * @throws AppSearchException on IcingSearchEngine error.
-     * @throws InterruptedException if the current thread was interrupted during execution.
      */
     public void setSchema(@NonNull String databaseName, @NonNull SchemaProto origSchema,
-            boolean forceOverride) throws AppSearchException, InterruptedException {
-        awaitInitialized();
-
+            boolean forceOverride) throws AppSearchException {
         SchemaProto schemaProto = getSchemaProto();
 
         SchemaProto.Builder existingSchemaBuilder = schemaProto.toBuilder();
@@ -212,10 +191,32 @@
         SetSchemaResultProto setSchemaResultProto;
         mReadWriteLock.writeLock().lock();
         try {
-            setSchemaResultProto = mIcingSearchEngine.setSchema(existingSchemaBuilder.build(),
-                    forceOverride);
-            checkSuccess(setSchemaResultProto.getStatus());
+            // Apply schema
+            setSchemaResultProto =
+                    mIcingSearchEngine.setSchema(existingSchemaBuilder.build(), forceOverride);
+
+            // Determine whether it succeeded.
+            try {
+                checkSuccess(setSchemaResultProto.getStatus());
+            } catch (AppSearchException e) {
+                // Improve the error message by merging in information about incompatible types.
+                if (setSchemaResultProto.getDeletedSchemaTypesCount() > 0
+                        || setSchemaResultProto.getIncompatibleSchemaTypesCount() > 0) {
+                    String newMessage = e.getMessage()
+                            + "\n  Deleted types: "
+                            + setSchemaResultProto.getDeletedSchemaTypesList()
+                            + "\n  Incompatible types: "
+                            + setSchemaResultProto.getIncompatibleSchemaTypesList();
+                    throw new AppSearchException(e.getResultCode(), newMessage, e.getCause());
+                } else {
+                    throw e;
+                }
+            }
+
+            // Update derived data structures.
             mSchemaMap.put(databaseName, newTypeNames);
+
+            // Determine whether to schedule an immediate optimize.
             if (setSchemaResultProto.getDeletedSchemaTypesCount() > 0
                     || (setSchemaResultProto.getIncompatibleSchemaTypesCount() > 0
                     && forceOverride)) {
@@ -237,12 +238,9 @@
      * @param databaseName The databaseName this document resides in.
      * @param document     The document to index.
      * @throws AppSearchException on IcingSearchEngine error.
-     * @throws InterruptedException if the current thread was interrupted during execution.
      */
     public void putDocument(@NonNull String databaseName, @NonNull DocumentProto document)
-            throws AppSearchException, InterruptedException {
-        awaitInitialized();
-
+            throws AppSearchException {
         DocumentProto.Builder documentBuilder = document.toBuilder();
         rewriteDocumentTypes(getDatabasePrefix(databaseName), documentBuilder, /*add=*/ true);
 
@@ -270,12 +268,10 @@
      * @param uri          The URI of the document to get.
      * @return The Document contents, or {@code null} if no such URI exists in the system.
      * @throws AppSearchException on IcingSearchEngine error.
-     * @throws InterruptedException if the current thread was interrupted during execution.
      */
     @Nullable
     public DocumentProto getDocument(@NonNull String databaseName, @NonNull String namespace,
-            @NonNull String uri) throws AppSearchException, InterruptedException {
-        awaitInitialized();
+            @NonNull String uri) throws AppSearchException {
         GetResultProto getResultProto;
         mReadWriteLock.readLock().lock();
         try {
@@ -303,16 +299,13 @@
      * @return The results of performing this search  The proto might have no {@code results} if no
      * documents matched the query.
      * @throws AppSearchException on IcingSearchEngine error.
-     * @throws InterruptedException if the current thread was interrupted during execution.
      */
     @NonNull
     public SearchResultProto query(
             @NonNull String databaseName,
             @NonNull SearchSpecProto searchSpec,
             @NonNull ResultSpecProto resultSpec,
-            @NonNull ScoringSpecProto scoringSpec) throws AppSearchException, InterruptedException {
-        awaitInitialized();
-
+            @NonNull ScoringSpecProto scoringSpec) throws AppSearchException {
         SearchSpecProto.Builder searchSpecBuilder = searchSpec.toBuilder();
         SearchResultProto searchResultProto;
         mReadWriteLock.readLock().lock();
@@ -347,13 +340,10 @@
      * @param nextPageToken The token of pre-loaded results of previously executed query.
      * @return The next page of results of previously executed query.
      * @throws AppSearchException on IcingSearchEngine error.
-     * @throws InterruptedException if the current thread was interrupted during execution.
      */
     @NonNull
     public SearchResultProto getNextPage(@NonNull String databaseName, long nextPageToken)
-            throws AppSearchException, InterruptedException {
-        awaitInitialized();
-
+            throws AppSearchException {
         SearchResultProto searchResultProto = mIcingSearchEngine.getNextPage(nextPageToken);
         checkSuccess(searchResultProto.getStatus());
         if (searchResultProto.getResultsCount() == 0) {
@@ -367,8 +357,7 @@
      * @param nextPageToken The token of pre-loaded results of previously executed query to be
      *                      Invalidated.
      */
-    public void invalidateNextPageToken(long nextPageToken) throws InterruptedException {
-        awaitInitialized();
+    public void invalidateNextPageToken(long nextPageToken) {
         mIcingSearchEngine.invalidateNextPageToken(nextPageToken);
     }
 
@@ -381,12 +370,9 @@
      * @param namespace    Namespace of the document to remove.
      * @param uri          URI of the document to remove.
      * @throws AppSearchException on IcingSearchEngine error.
-     * @throws InterruptedException if the current thread was interrupted during execution.
      */
     public void remove(@NonNull String databaseName, @NonNull String namespace,
-            @NonNull String uri) throws AppSearchException, InterruptedException {
-        awaitInitialized();
-
+            @NonNull String uri) throws AppSearchException {
         String qualifiedNamespace = getDatabasePrefix(databaseName) + namespace;
         DeleteResultProto deleteResultProto;
         mReadWriteLock.writeLock().lock();
@@ -407,12 +393,9 @@
      * @param databaseName The databaseName that contains documents of schemaType.
      * @param schemaType   The schemaType of documents to remove.
      * @throws AppSearchException on IcingSearchEngine error.
-     * @throws InterruptedException if the current thread was interrupted during execution.
      */
     public void removeByType(@NonNull String databaseName, @NonNull String schemaType)
-            throws AppSearchException, InterruptedException {
-        awaitInitialized();
-
+            throws AppSearchException {
         String qualifiedType = getDatabasePrefix(databaseName) + schemaType;
         DeleteBySchemaTypeResultProto deleteBySchemaTypeResultProto;
         mReadWriteLock.writeLock().lock();
@@ -437,12 +420,9 @@
      * @param databaseName The databaseName that contains documents of namespace.
      * @param namespace    The namespace of documents to remove.
      * @throws AppSearchException on IcingSearchEngine error.
-     * @throws InterruptedException if the current thread was interrupted during execution.
      */
     public void removeByNamespace(@NonNull String databaseName, @NonNull String namespace)
-            throws AppSearchException, InterruptedException {
-        awaitInitialized();
-
+            throws AppSearchException {
         String qualifiedNamespace = getDatabasePrefix(databaseName) + namespace;
         DeleteByNamespaceResultProto deleteByNamespaceResultProto;
         mReadWriteLock.writeLock().lock();
@@ -469,11 +449,9 @@
      *
      * @param databaseName The databaseName to remove all documents from.
      * @throws AppSearchException on IcingSearchEngine error.
-     * @throws InterruptedException if the current thread was interrupted during execution.
      */
     public void removeAll(@NonNull String databaseName)
-            throws AppSearchException, InterruptedException {
-        awaitInitialized();
+            throws AppSearchException {
         mReadWriteLock.writeLock().lock();
         try {
             Set<String> existingNamespaces = mNamespaceMap.get(databaseName);
@@ -733,15 +711,6 @@
     }
 
     /**
-     * Waits for the instance to become initialized.
-     *
-     * @throws InterruptedException if the current thread was interrupted during waiting.
-     */
-    private void awaitInitialized() throws InterruptedException {
-        mInitCompleteLatch.await();
-    }
-
-    /**
      * Checks the given status code and throws an {@link AppSearchException} if code is an error.
      *
      * @throws AppSearchException on error codes.
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/converter/GenericDocumentToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/converter/GenericDocumentToProtoConverter.java
new file mode 100644
index 0000000..fdeb90d
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/converter/GenericDocumentToProtoConverter.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.external.localbackend.converter;
+
+import android.os.Bundle;
+
+import android.annotation.NonNull;
+
+import android.app.appsearch.GenericDocument;
+import com.android.internal.util.Preconditions;
+
+import com.google.android.icing.proto.DocumentProto;
+import com.google.android.icing.proto.PropertyProto;
+import com.google.protobuf.ByteString;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ * Translates a {@link GenericDocument} into a {@link DocumentProto}.
+ * @hide
+ */
+
+public final class GenericDocumentToProtoConverter {
+    private GenericDocumentToProtoConverter() {}
+
+    /** Converts a {@link GenericDocument} into a {@link DocumentProto}. */
+    @NonNull
+    @SuppressWarnings("unchecked")
+    public static DocumentProto convert(@NonNull GenericDocument document) {
+        Preconditions.checkNotNull(document);
+        Bundle properties = document.getBundle().getBundle(GenericDocument.PROPERTIES_FIELD);
+        DocumentProto.Builder mProtoBuilder = DocumentProto.newBuilder();
+        mProtoBuilder.setUri(document.getUri())
+                .setSchema(document.getSchemaType())
+                .setNamespace(document.getNamespace())
+                .setScore(document.getScore())
+                .setTtlMs(document.getTtlMillis())
+                .setCreationTimestampMs(document.getCreationTimestampMillis());
+        ArrayList<String> keys = new ArrayList<>(properties.keySet());
+        Collections.sort(keys);
+        for (int i = 0; i < keys.size(); i++) {
+            String name = keys.get(i);
+            Object values = properties.get(name);
+            PropertyProto.Builder propertyProto = PropertyProto.newBuilder().setName(name);
+            if (values instanceof boolean[]) {
+                for (boolean value : (boolean[]) values) {
+                    propertyProto.addBooleanValues(value);
+                }
+            } else if (values instanceof long[]) {
+                for (long value : (long[]) values) {
+                    propertyProto.addInt64Values(value);
+                }
+            } else if (values instanceof double[]) {
+                for (double value : (double[]) values) {
+                    propertyProto.addDoubleValues(value);
+                }
+            } else if (values instanceof String[]) {
+                for (String value : (String[]) values) {
+                    propertyProto.addStringValues(value);
+                }
+            } else if (values instanceof ArrayList) {
+                for (Bundle bundle : (ArrayList<Bundle>) values) {
+                    byte[] value = bundle.getByteArray(GenericDocument.BYTE_ARRAY_FIELD);
+                    propertyProto.addBytesValues(ByteString.copyFrom(value));
+                }
+            } else if (values instanceof Bundle[]) {
+                for (Bundle bundle : (Bundle[]) values) {
+                    GenericDocument value = new GenericDocument(bundle);
+                    propertyProto.addDocumentValues(convert(value));
+                }
+            } else {
+                throw new IllegalStateException(
+                        "Property \"" + name + "\" has unsupported value type \""
+                                + values.getClass().getSimpleName() + "\"");
+            }
+            mProtoBuilder.addProperties(propertyProto);
+        }
+        return mProtoBuilder.build();
+    }
+
+    /** Converts a {@link DocumentProto} into a {@link GenericDocument}. */
+    @NonNull
+    public static GenericDocument convert(@NonNull DocumentProto proto) {
+        Preconditions.checkNotNull(proto);
+        GenericDocument.Builder<?> documentBuilder =
+                new GenericDocument.Builder<>(proto.getUri(), proto.getSchema())
+                        .setNamespace(proto.getNamespace())
+                        .setScore(proto.getScore())
+                        .setTtlMillis(proto.getTtlMs())
+                        .setCreationTimestampMillis(proto.getCreationTimestampMs());
+
+        for (int i = 0; i < proto.getPropertiesCount(); i++) {
+            PropertyProto property = proto.getProperties(i);
+            String name = property.getName();
+            if (property.getBooleanValuesCount() > 0) {
+                boolean[] values = new boolean[property.getBooleanValuesCount()];
+                for (int j = 0; j < values.length; j++) {
+                    values[j] = property.getBooleanValues(j);
+                }
+                documentBuilder.setProperty(name, values);
+            } else if (property.getInt64ValuesCount() > 0) {
+                long[] values = new long[property.getInt64ValuesCount()];
+                for (int j = 0; j < values.length; j++) {
+                    values[j] = property.getInt64Values(j);
+                }
+                documentBuilder.setProperty(name, values);
+            } else if (property.getDoubleValuesCount() > 0) {
+                double[] values = new double[property.getDoubleValuesCount()];
+                for (int j = 0; j < values.length; j++) {
+                    values[j] = property.getDoubleValues(j);
+                }
+                documentBuilder.setProperty(name, values);
+            } else if (property.getStringValuesCount() > 0) {
+                String[] values = new String[property.getStringValuesCount()];
+                for (int j = 0; j < values.length; j++) {
+                    values[j] = property.getStringValues(j);
+                }
+                documentBuilder.setProperty(name, values);
+            } else if (property.getBytesValuesCount() > 0) {
+                byte[][] values = new byte[property.getBytesValuesCount()][];
+                for (int j = 0; j < values.length; j++) {
+                    values[j] = property.getBytesValues(j).toByteArray();
+                }
+                documentBuilder.setProperty(name, values);
+            } else if (property.getDocumentValuesCount() > 0) {
+                GenericDocument[] values = new GenericDocument[property.getDocumentValuesCount()];
+                for (int j = 0; j < values.length; j++) {
+                    values[j] = convert(property.getDocumentValues(j));
+                }
+                documentBuilder.setProperty(name, values);
+            } else {
+                throw new IllegalStateException("Unknown type of value: " + name);
+            }
+        }
+        return documentBuilder.build();
+    }
+}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/converter/SchemaToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/converter/SchemaToProtoConverter.java
new file mode 100644
index 0000000..ca0d2ee
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/converter/SchemaToProtoConverter.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.external.localbackend.converter;
+
+import android.os.Bundle;
+
+import android.annotation.NonNull;
+
+import android.app.appsearch.AppSearchSchema;
+import com.android.internal.util.Preconditions;
+
+import com.google.android.icing.proto.IndexingConfig;
+import com.google.android.icing.proto.PropertyConfigProto;
+import com.google.android.icing.proto.SchemaTypeConfigProto;
+import com.google.android.icing.proto.TermMatchType;
+
+import java.util.ArrayList;
+
+/**
+ * Translates an {@link AppSearchSchema} into a {@link SchemaTypeConfigProto}.
+ * @hide
+ */
+
+public final class SchemaToProtoConverter {
+    private SchemaToProtoConverter() {}
+
+    /**
+     * Converts an {@link android.app.appsearch.AppSearchSchema} into a
+     * {@link SchemaTypeConfigProto}.
+     */
+    @NonNull
+    public static SchemaTypeConfigProto convert(@NonNull AppSearchSchema schema) {
+        Preconditions.checkNotNull(schema);
+        Bundle bundle = schema.getBundle();
+        SchemaTypeConfigProto.Builder protoBuilder =
+                SchemaTypeConfigProto.newBuilder()
+                        .setSchemaType(bundle.getString(AppSearchSchema.SCHEMA_TYPE_FIELD, ""));
+        ArrayList<Bundle> properties =
+                bundle.getParcelableArrayList(AppSearchSchema.PROPERTIES_FIELD);
+        if (properties != null) {
+            for (int i = 0; i < properties.size(); i++) {
+                PropertyConfigProto propertyProto = convertProperty(properties.get(i));
+                protoBuilder.addProperties(propertyProto);
+            }
+        }
+        return protoBuilder.build();
+    }
+
+    @NonNull
+    private static PropertyConfigProto convertProperty(@NonNull Bundle bundle) {
+        Preconditions.checkNotNull(bundle);
+        PropertyConfigProto.Builder propertyConfigProto = PropertyConfigProto.newBuilder()
+                .setPropertyName(bundle.getString(AppSearchSchema.PropertyConfig.NAME_FIELD, ""));
+        IndexingConfig.Builder indexingConfig = IndexingConfig.newBuilder();
+
+        // Set dataType
+        @AppSearchSchema.PropertyConfig.DataType int dataType =
+                bundle.getInt(AppSearchSchema.PropertyConfig.DATA_TYPE_FIELD);
+        PropertyConfigProto.DataType.Code dataTypeProto =
+                PropertyConfigProto.DataType.Code.forNumber(dataType);
+        if (dataTypeProto == null) {
+            throw new IllegalArgumentException("Invalid dataType: " + dataType);
+        }
+        propertyConfigProto.setDataType(dataTypeProto);
+
+        // Set schemaType
+        propertyConfigProto.setSchemaType(
+                bundle.getString(AppSearchSchema.PropertyConfig.SCHEMA_TYPE_FIELD, ""));
+
+        // Set cardinality
+        @AppSearchSchema.PropertyConfig.Cardinality int cardinality =
+                bundle.getInt(AppSearchSchema.PropertyConfig.CARDINALITY_FIELD);
+        PropertyConfigProto.Cardinality.Code cardinalityProto =
+                PropertyConfigProto.Cardinality.Code.forNumber(cardinality);
+        if (cardinalityProto == null) {
+            throw new IllegalArgumentException("Invalid cardinality: " + dataType);
+        }
+        propertyConfigProto.setCardinality(cardinalityProto);
+
+        // Set indexingType
+        @AppSearchSchema.PropertyConfig.IndexingType int indexingType =
+                bundle.getInt(AppSearchSchema.PropertyConfig.INDEXING_TYPE_FIELD);
+        TermMatchType.Code termMatchTypeProto;
+        switch (indexingType) {
+            case AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE:
+                termMatchTypeProto = TermMatchType.Code.UNKNOWN;
+                break;
+            case AppSearchSchema.PropertyConfig.INDEXING_TYPE_EXACT_TERMS:
+                termMatchTypeProto = TermMatchType.Code.EXACT_ONLY;
+                break;
+            case AppSearchSchema.PropertyConfig.INDEXING_TYPE_PREFIXES:
+                termMatchTypeProto = TermMatchType.Code.PREFIX;
+                break;
+            default:
+                throw new IllegalArgumentException("Invalid indexingType: " + indexingType);
+        }
+        indexingConfig.setTermMatchType(termMatchTypeProto);
+
+        // Set tokenizerType
+        @AppSearchSchema.PropertyConfig.TokenizerType int tokenizerType =
+                bundle.getInt(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_FIELD);
+        IndexingConfig.TokenizerType.Code tokenizerTypeProto =
+                IndexingConfig.TokenizerType.Code.forNumber(tokenizerType);
+        if (tokenizerTypeProto == null) {
+            throw new IllegalArgumentException("Invalid tokenizerType: " + tokenizerType);
+        }
+        indexingConfig.setTokenizerType(tokenizerTypeProto);
+
+        // Build!
+        propertyConfigProto.setIndexingConfig(indexingConfig);
+        return propertyConfigProto.build();
+    }
+}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/converter/SearchResultToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/converter/SearchResultToProtoConverter.java
new file mode 100644
index 0000000..524c80d
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/converter/SearchResultToProtoConverter.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.external.localbackend.converter;
+
+import android.os.Bundle;
+
+import android.annotation.NonNull;
+
+import android.app.appsearch.GenericDocument;
+import android.app.appsearch.SearchResult;
+import android.app.appsearch.SearchResults;
+
+import com.google.android.icing.proto.SearchResultProto;
+import com.google.android.icing.proto.SnippetMatchProto;
+import com.google.android.icing.proto.SnippetProto;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Translates a {@link SearchResultProto} into {@link SearchResults}.
+ * @hide
+ */
+
+public class SearchResultToProtoConverter {
+    private SearchResultToProtoConverter() {}
+
+    /** Translates a {@link SearchResultProto} into a list of {@link SearchResult}. */
+    @NonNull
+    public static List<SearchResult> convert(@NonNull SearchResultProto searchResultProto) {
+        List<SearchResult> results = new ArrayList<>(searchResultProto.getResultsCount());
+        for (int i = 0; i < searchResultProto.getResultsCount(); i++) {
+            results.add(convertSearchResult(searchResultProto.getResults(i)));
+        }
+        return results;
+    }
+
+    /** Translate a {@link SearchResultProto.ResultProto} into {@link SearchResult}. */
+    @NonNull
+    static SearchResult convertSearchResult(@NonNull SearchResultProto.ResultProto proto) {
+        Bundle bundle = new Bundle();
+        GenericDocument document = GenericDocumentToProtoConverter.convert(proto.getDocument());
+        bundle.putBundle(SearchResult.DOCUMENT_FIELD, document.getBundle());
+
+        ArrayList<Bundle> matchList = null;
+        if (proto.hasSnippet()) {
+            matchList = new ArrayList<>();
+            for (int i = 0; i < proto.getSnippet().getEntriesCount(); i++) {
+                SnippetProto.EntryProto entry = proto.getSnippet().getEntries(i);
+                for (int j = 0; j < entry.getSnippetMatchesCount(); j++) {
+                    Bundle matchInfoBundle = convertToMatchInfoBundle(
+                            entry.getSnippetMatches(j), entry.getPropertyName());
+                    matchList.add(matchInfoBundle);
+                }
+            }
+        }
+        bundle.putParcelableArrayList(SearchResult.MATCHES_FIELD, matchList);
+
+        return new SearchResult(bundle);
+    }
+
+    private static Bundle convertToMatchInfoBundle(
+            SnippetMatchProto snippetMatchProto, String propertyPath) {
+        Bundle bundle = new Bundle();
+        bundle.putString(SearchResult.MatchInfo.PROPERTY_PATH_FIELD, propertyPath);
+        bundle.putInt(
+                SearchResult.MatchInfo.VALUES_INDEX_FIELD, snippetMatchProto.getValuesIndex());
+        bundle.putInt(
+                SearchResult.MatchInfo.EXACT_MATCH_POSITION_LOWER_FIELD,
+                snippetMatchProto.getExactMatchPosition());
+        bundle.putInt(
+                SearchResult.MatchInfo.EXACT_MATCH_POSITION_UPPER_FIELD,
+                snippetMatchProto.getExactMatchPosition() + snippetMatchProto.getExactMatchBytes());
+        bundle.putInt(
+                SearchResult.MatchInfo.WINDOW_POSITION_LOWER_FIELD,
+                snippetMatchProto.getWindowPosition());
+        bundle.putInt(
+                SearchResult.MatchInfo.WINDOW_POSITION_UPPER_FIELD,
+                snippetMatchProto.getWindowPosition() + snippetMatchProto.getWindowBytes());
+        return bundle;
+    }
+}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/converter/SearchSpecToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/converter/SearchSpecToProtoConverter.java
new file mode 100644
index 0000000..a5d913a
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/converter/SearchSpecToProtoConverter.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.external.localbackend.converter;
+
+import android.os.Bundle;
+
+import android.annotation.NonNull;
+
+import android.app.appsearch.SearchSpec;
+import com.android.internal.util.Preconditions;
+
+import com.google.android.icing.proto.ResultSpecProto;
+import com.google.android.icing.proto.ScoringSpecProto;
+import com.google.android.icing.proto.SearchSpecProto;
+import com.google.android.icing.proto.TermMatchType;
+
+import java.util.Arrays;
+
+/**
+ * Translates a {@link SearchSpec} into icing search protos.
+ * @hide
+ */
+
+public final class SearchSpecToProtoConverter {
+    private SearchSpecToProtoConverter() {}
+
+    /** Extracts {@link SearchSpecProto} information from a {@link SearchSpec}. */
+    @NonNull
+    public static SearchSpecProto toSearchSpecProto(@NonNull SearchSpec spec) {
+        Preconditions.checkNotNull(spec);
+        Bundle bundle = spec.getBundle();
+        SearchSpecProto.Builder protoBuilder = SearchSpecProto.newBuilder();
+
+        @SearchSpec.TermMatchCode int termMatchCode =
+                bundle.getInt(SearchSpec.TERM_MATCH_TYPE_FIELD);
+        TermMatchType.Code termMatchCodeProto = TermMatchType.Code.forNumber(termMatchCode);
+        if (termMatchCodeProto == null || termMatchCodeProto.equals(TermMatchType.Code.UNKNOWN)) {
+            throw new IllegalArgumentException("Invalid term match type: " + termMatchCode);
+        }
+        protoBuilder.setTermMatchType(termMatchCodeProto);
+
+        String[] schemaTypes = bundle.getStringArray(SearchSpec.SCHEMA_TYPES_FIELD);
+        if (schemaTypes != null) {
+            protoBuilder.addAllSchemaTypeFilters(Arrays.asList(schemaTypes));
+        }
+        String[] namespaces = bundle.getStringArray(SearchSpec.NAMESPACE_FIELD);
+        if (namespaces != null) {
+            protoBuilder.addAllNamespaceFilters(Arrays.asList(namespaces));
+        }
+        return protoBuilder.build();
+    }
+
+    /** Extracts {@link ResultSpecProto} information from a {@link SearchSpec}. */
+    @NonNull
+    public static ResultSpecProto toResultSpecProto(@NonNull SearchSpec spec) {
+        Preconditions.checkNotNull(spec);
+        Bundle bundle = spec.getBundle();
+        return ResultSpecProto.newBuilder()
+                .setNumPerPage(bundle.getInt(
+                        SearchSpec.NUM_PER_PAGE_FIELD, SearchSpec.DEFAULT_NUM_PER_PAGE))
+                .setSnippetSpec(ResultSpecProto.SnippetSpecProto.newBuilder()
+                        .setNumToSnippet(bundle.getInt(SearchSpec.SNIPPET_COUNT_FIELD))
+                        .setNumMatchesPerProperty(
+                                bundle.getInt(SearchSpec.SNIPPET_COUNT_PER_PROPERTY_FIELD))
+                        .setMaxWindowBytes(bundle.getInt(SearchSpec.MAX_SNIPPET_FIELD)))
+                .build();
+
+    }
+
+    /** Extracts {@link ScoringSpecProto} information from a {@link SearchSpec}. */
+    @NonNull
+    public static ScoringSpecProto toScoringSpecProto(@NonNull SearchSpec spec) {
+        Preconditions.checkNotNull(spec);
+        Bundle bundle = spec.getBundle();
+        ScoringSpecProto.Builder protoBuilder = ScoringSpecProto.newBuilder();
+
+        @SearchSpec.OrderCode int orderCode = bundle.getInt(SearchSpec.ORDER_FIELD);
+        ScoringSpecProto.Order.Code orderCodeProto =
+                ScoringSpecProto.Order.Code.forNumber(orderCode);
+        if (orderCodeProto == null) {
+            throw new IllegalArgumentException("Invalid result ranking order: " + orderCode);
+        }
+        protoBuilder.setOrderBy(orderCodeProto);
+
+        @SearchSpec.RankingStrategyCode int rankingStrategyCode =
+                bundle.getInt(SearchSpec.RANKING_STRATEGY_FIELD);
+        ScoringSpecProto.RankingStrategy.Code rankingStrategyCodeProto =
+                ScoringSpecProto.RankingStrategy.Code.forNumber(rankingStrategyCode);
+        if (rankingStrategyCodeProto == null) {
+            throw new IllegalArgumentException("Invalid result ranking strategy: "
+                    + rankingStrategyCode);
+        }
+        protoBuilder.setRankBy(rankingStrategyCodeProto);
+
+        return protoBuilder.build();
+    }
+}
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java
index 1d07e88..8f94273 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java
@@ -24,6 +24,7 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
+import android.os.UserHandle;
 import android.text.format.TimeMigrationUtils;
 import android.util.Slog;
 
@@ -34,8 +35,8 @@
     static Resources getPackageResources(@NonNull Context context,
             @NonNull String packageName, int userId) {
         try {
-            return context.getPackageManager()
-                    .getResourcesForApplicationAsUser(packageName, userId);
+            return context.createContextAsUser(UserHandle.of(userId), /* flags */ 0)
+                    .getPackageManager().getResourcesForApplication(packageName);
         } catch (PackageManager.NameNotFoundException e) {
             Slog.d(TAG, "Unknown package in user " + userId + ": "
                     + packageName, e);
diff --git a/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java b/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java
index ada562e..752c36e 100644
--- a/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java
+++ b/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java
@@ -29,7 +29,6 @@
  *
  * @hide
  */
-@TestApi
 @SystemApi
 @SystemService(Context.DEVICE_IDLE_CONTROLLER)
 public class DeviceIdleManager {
diff --git a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
index 16dcd06..ab87222 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
@@ -21,7 +21,6 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
-import android.annotation.TestApi;
 import android.content.Context;
 
 import java.lang.annotation.Retention;
@@ -39,7 +38,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 @SystemService(Context.POWER_WHITELIST_MANAGER)
 public class PowerWhitelistManager {
     private final Context mContext;
diff --git a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
index 8e26052..4dc9cf8 100644
--- a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
+++ b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
@@ -1269,19 +1269,14 @@
     }
 
     /**
-     * @deprecated use {@link #dump(IndentingPrintWriter)} instead.
-     */
-    @Deprecated
-    public void dump(PrintWriter pw, String prefix) {
-        dump(new IndentingPrintWriter(pw, "  ").setIndent(prefix));
-    }
-
-    /**
      * Dump the internal state to the given PrintWriter. Can be included in the dump
      * of a binder service to be output on the shell command "dumpsys".
      */
     public void dump(IndentingPrintWriter pw) {
         synchronized (mLock) {
+            pw.println("Current AppStateTracker State:");
+
+            pw.increaseIndent();
             pw.println("Forced App Standby Feature enabled: " + mForcedAppStandbyEnabled);
 
             pw.print("Force all apps standby: ");
@@ -1339,6 +1334,7 @@
             pw.decreaseIndent();
 
             mStatLogger.dump(pw);
+            pw.decreaseIndent();
         }
     }
 
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 59915e1..81f22fe 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -1682,7 +1682,7 @@
             }
             getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
                     null);
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
                 return addPowerSaveWhitelistAppsInternal(packageNames);
             } finally {
@@ -1696,7 +1696,7 @@
             }
             getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
                     null);
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
                 if (!removePowerSaveWhitelistAppInternal(name)
                         && mPowerSaveWhitelistAppsExceptIdle.containsKey(name)) {
@@ -1713,7 +1713,7 @@
             }
             getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
                     null);
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
                 removeSystemPowerWhitelistAppInternal(name);
             } finally {
@@ -1727,7 +1727,7 @@
             }
             getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
                     null);
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
                 restoreSystemPowerWhitelistAppInternal(name);
             } finally {
@@ -1815,7 +1815,7 @@
         @Override public void exitIdle(String reason) {
             getContext().enforceCallingOrSelfPermission(Manifest.permission.DEVICE_POWER,
                     null);
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
                 exitIdleInternal(reason);
             } finally {
@@ -1826,7 +1826,7 @@
         @Override public int setPreIdleTimeoutMode(int mode) {
             getContext().enforceCallingOrSelfPermission(Manifest.permission.DEVICE_POWER,
                     null);
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
                 return DeviceIdleController.this.setPreIdleTimeoutMode(mode);
             } finally {
@@ -1837,7 +1837,7 @@
         @Override public void resetPreIdleTimeoutMode() {
             getContext().enforceCallingOrSelfPermission(Manifest.permission.DEVICE_POWER,
                     null);
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
                 DeviceIdleController.this.resetPreIdleTimeoutMode();
             } finally {
@@ -2179,7 +2179,7 @@
                 if (getContext().getResources().getBoolean(
                         com.android.internal.R.bool.config_autoPowerModePrefetchLocation)) {
                     mLocationRequest = new LocationRequest.Builder(/*intervalMillis=*/ 0)
-                        .setQuality(LocationRequest.ACCURACY_FINE)
+                        .setQuality(LocationRequest.QUALITY_HIGH_ACCURACY)
                         .setMaxUpdates(1)
                         .build();
                 }
@@ -4031,7 +4031,7 @@
             getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
                     null);
             synchronized (this) {
-                long token = Binder.clearCallingIdentity();
+                final long token = Binder.clearCallingIdentity();
                 String arg = shell.getNextArg();
                 try {
                     if (arg == null || "deep".equals(arg)) {
@@ -4052,7 +4052,7 @@
             getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
                     null);
             synchronized (this) {
-                long token = Binder.clearCallingIdentity();
+                final long token = Binder.clearCallingIdentity();
                 String arg = shell.getNextArg();
                 try {
                     if (arg == null || "deep".equals(arg)) {
@@ -4100,7 +4100,7 @@
             getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
                     null);
             synchronized (this) {
-                long token = Binder.clearCallingIdentity();
+                final long token = Binder.clearCallingIdentity();
                 try {
                     mForceIdle = true;
                     becomeInactiveIfAppropriateLocked();
@@ -4116,7 +4116,7 @@
             getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
                     null);
             synchronized (this) {
-                long token = Binder.clearCallingIdentity();
+                final long token = Binder.clearCallingIdentity();
                 try {
                     exitForceIdleLocked();
                     pw.print("Light state: ");
@@ -4133,7 +4133,7 @@
             synchronized (this) {
                 String arg = shell.getNextArg();
                 if (arg != null) {
-                    long token = Binder.clearCallingIdentity();
+                    final long token = Binder.clearCallingIdentity();
                     try {
                         switch (arg) {
                             case "light": pw.println(lightStateToString(mLightState)); break;
@@ -4156,7 +4156,7 @@
             getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
                     null);
             synchronized (this) {
-                long token = Binder.clearCallingIdentity();
+                final long token = Binder.clearCallingIdentity();
                 String arg = shell.getNextArg();
                 try {
                     boolean becomeActive = false;
@@ -4193,7 +4193,7 @@
             getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
                     null);
             synchronized (this) {
-                long token = Binder.clearCallingIdentity();
+                final long token = Binder.clearCallingIdentity();
                 String arg = shell.getNextArg();
                 try {
                     boolean becomeInactive = false;
@@ -4242,7 +4242,7 @@
             if (arg != null) {
                 getContext().enforceCallingOrSelfPermission(
                         android.Manifest.permission.DEVICE_POWER, null);
-                long token = Binder.clearCallingIdentity();
+                final long token = Binder.clearCallingIdentity();
                 try {
                     do {
                         if (arg.length() < 1 || (arg.charAt(0) != '-'
@@ -4418,7 +4418,7 @@
             getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
                     null);
             synchronized (this) {
-                long token = Binder.clearCallingIdentity();
+                final long token = Binder.clearCallingIdentity();
                 try {
                     motionLocked();
                     pw.print("Light state: ");
@@ -4433,7 +4433,7 @@
             getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
                     null);
             synchronized (this) {
-                long token = Binder.clearCallingIdentity();
+                final long token = Binder.clearCallingIdentity();
                 int ret  = SET_IDLE_FACTOR_RESULT_UNINIT;
                 try {
                     String arg = shell.getNextArg();
@@ -4468,7 +4468,7 @@
             getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
                     null);
             synchronized (this) {
-                long token = Binder.clearCallingIdentity();
+                final long token = Binder.clearCallingIdentity();
                 try {
                     resetPreIdleTimeoutMode();
                 } finally {
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java b/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java
index f672e4b..04feef4 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java
@@ -16,10 +16,13 @@
 
 package com.android.server.alarm;
 
+import static android.app.AlarmManager.ELAPSED_REALTIME;
 import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
 import static android.app.AlarmManager.RTC;
 import static android.app.AlarmManager.RTC_WAKEUP;
 
+import static com.android.server.alarm.AlarmManagerService.clampPositive;
+
 import android.app.AlarmManager;
 import android.app.IAlarmListener;
 import android.app.PendingIntent;
@@ -28,12 +31,31 @@
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 
-import java.io.PrintWriter;
 import java.text.SimpleDateFormat;
 import java.util.Date;
 
+/**
+ * Class to describe an alarm that is used to the set the kernel timer that returns when the timer
+ * expires. The timer will wake up the device if the alarm is a "wakeup" alarm.
+ */
 class Alarm {
+    private static final int NUM_POLICIES = 2;
+    /**
+     * Index used to store the time the alarm was requested to expire. To be used with
+     * {@link #setPolicyElapsed(int, long)}
+     */
+    public static final int REQUESTER_POLICY_INDEX = 0;
+    /**
+     * Index used to store the earliest time the alarm can expire based on app-standby policy.
+     * To be used with {@link #setPolicyElapsed(int, long)}
+     */
+    public static final int APP_STANDBY_POLICY_INDEX = 1;
+
     public final int type;
+    /**
+     * The original trigger time supplied by the caller. This can be in the elapsed or rtc time base
+     * depending on the type of this alarm
+     */
     public final long origWhen;
     public final boolean wakeup;
     public final PendingIntent operation;
@@ -47,42 +69,40 @@
     public final int creatorUid;
     public final String packageName;
     public final String sourcePackage;
+    public final long windowLength;
+    public final long repeatInterval;
     public int count;
-    public long when;
-    public long windowLength;
-    public long whenElapsed;    // 'when' in the elapsed time base
-    public long maxWhenElapsed; // also in the elapsed time base
-    // Expected alarm expiry time before app standby deferring is applied.
-    public long expectedWhenElapsed;
-    public long expectedMaxWhenElapsed;
-    public long repeatInterval;
+    /** The earliest time this alarm is eligible to fire according to each policy */
+    private long[] mPolicyWhenElapsed;
+    /** The ultimate delivery time to be used for this alarm */
+    private long mWhenElapsed;
+    private long mMaxWhenElapsed;
     public AlarmManagerService.PriorityClass priorityClass;
 
-    Alarm(int _type, long _when, long _whenElapsed, long _windowLength, long _maxWhen,
-            long _interval, PendingIntent _op, IAlarmListener _rec, String _listenerTag,
-            WorkSource _ws, int _flags, AlarmManager.AlarmClockInfo _info,
-            int _uid, String _pkgName) {
-        type = _type;
-        origWhen = _when;
-        wakeup = _type == AlarmManager.ELAPSED_REALTIME_WAKEUP
-                || _type == AlarmManager.RTC_WAKEUP;
-        when = _when;
-        whenElapsed = _whenElapsed;
-        expectedWhenElapsed = _whenElapsed;
-        windowLength = _windowLength;
-        maxWhenElapsed = expectedMaxWhenElapsed = AlarmManagerService.clampPositive(_maxWhen);
-        repeatInterval = _interval;
-        operation = _op;
-        listener = _rec;
-        listenerTag = _listenerTag;
-        statsTag = makeTag(_op, _listenerTag, _type);
-        workSource = _ws;
-        flags = _flags;
-        alarmClock = _info;
-        uid = _uid;
-        packageName = _pkgName;
+    Alarm(int type, long when, long requestedWhenElapsed, long windowLength, long interval,
+            PendingIntent op, IAlarmListener rec, String listenerTag, WorkSource ws, int flags,
+            AlarmManager.AlarmClockInfo info, int uid, String pkgName) {
+        this.type = type;
+        origWhen = when;
+        wakeup = type == AlarmManager.ELAPSED_REALTIME_WAKEUP
+                || type == AlarmManager.RTC_WAKEUP;
+        mPolicyWhenElapsed = new long[NUM_POLICIES];
+        mPolicyWhenElapsed[REQUESTER_POLICY_INDEX] = requestedWhenElapsed;
+        mWhenElapsed = requestedWhenElapsed;
+        this.windowLength = windowLength;
+        mMaxWhenElapsed = clampPositive(requestedWhenElapsed + windowLength);
+        repeatInterval = interval;
+        operation = op;
+        listener = rec;
+        this.listenerTag = listenerTag;
+        statsTag = makeTag(op, listenerTag, type);
+        workSource = ws;
+        this.flags = flags;
+        alarmClock = info;
+        this.uid = uid;
+        packageName = pkgName;
         sourcePackage = (operation != null) ? operation.getCreatorPackage() : packageName;
-        creatorUid = (operation != null) ? operation.getCreatorUid() : uid;
+        creatorUid = (operation != null) ? operation.getCreatorUid() : this.uid;
     }
 
     public static String makeTag(PendingIntent pi, String tag, int type) {
@@ -91,13 +111,6 @@
         return (pi != null) ? pi.getTag(alarmString) : (alarmString + tag);
     }
 
-    public AlarmManagerService.WakeupEvent makeWakeupEvent(long nowRTC) {
-        return new AlarmManagerService.WakeupEvent(nowRTC, creatorUid,
-                (operation != null)
-                    ? operation.getIntent().getAction()
-                    : ("<listener>:" + listenerTag));
-    }
-
     // Returns true if either matches
     public boolean matches(PendingIntent pi, IAlarmListener rec) {
         return (operation != null)
@@ -109,6 +122,65 @@
         return packageName.equals(sourcePackage);
     }
 
+    /**
+     * Get the earliest time this alarm is allowed to expire based on the given policy.
+     *
+     * @param policyIndex The index of the policy. One of [{@link #REQUESTER_POLICY_INDEX},
+     *                    {@link #APP_STANDBY_POLICY_INDEX}].
+     */
+    public long getPolicyElapsed(int policyIndex) {
+        return mPolicyWhenElapsed[policyIndex];
+    }
+
+    /**
+     * Get the earliest time that this alarm should be delivered to the requesting app.
+     */
+    public long getWhenElapsed() {
+        return mWhenElapsed;
+    }
+
+    /**
+     * Get the latest time that this alarm should be delivered to the requesting app. Will be equal
+     * to {@link #getWhenElapsed()} in case this is an exact alarm.
+     */
+    public long getMaxWhenElapsed() {
+        return mMaxWhenElapsed;
+    }
+
+    /**
+     * Set the earliest time this alarm can expire based on the passed policy index.
+     *
+     * @return {@code true} if this change resulted in a change in the ultimate delivery time (or
+     * time window in the case of inexact alarms) of this alarm.
+     * @see #getWhenElapsed()
+     * @see #getMaxWhenElapsed()
+     * @see #getPolicyElapsed(int)
+     */
+    public boolean setPolicyElapsed(int policyIndex, long policyElapsed) {
+        mPolicyWhenElapsed[policyIndex] = policyElapsed;
+        return updateWhenElapsed();
+    }
+
+    /**
+     * @return {@code true} if either {@link #mWhenElapsed} or {@link #mMaxWhenElapsed} changes
+     * due to this call.
+     */
+    private boolean updateWhenElapsed() {
+        final long oldWhenElapsed = mWhenElapsed;
+        mWhenElapsed = 0;
+        for (int i = 0; i < NUM_POLICIES; i++) {
+            mWhenElapsed = Math.max(mWhenElapsed, mPolicyWhenElapsed[i]);
+        }
+
+        final long oldMaxWhenElapsed = mMaxWhenElapsed;
+        // windowLength should always be >= 0 here.
+        final long maxRequestedElapsed = clampPositive(
+                mPolicyWhenElapsed[REQUESTER_POLICY_INDEX] + windowLength);
+        mMaxWhenElapsed = Math.max(maxRequestedElapsed, mWhenElapsed);
+
+        return (oldWhenElapsed != mWhenElapsed) || (oldMaxWhenElapsed != mMaxWhenElapsed);
+    }
+
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder(128);
@@ -116,24 +188,41 @@
         sb.append(Integer.toHexString(System.identityHashCode(this)));
         sb.append(" type ");
         sb.append(type);
-        sb.append(" when ");
-        sb.append(when);
+        sb.append(" origWhen ");
+        sb.append(origWhen);
         sb.append(" ");
         sb.append(" whenElapsed ");
-        sb.append(whenElapsed);
+        sb.append(getWhenElapsed());
         sb.append(" ");
         sb.append(sourcePackage);
         sb.append('}');
         return sb.toString();
     }
 
-    /**
-     * @deprecated Use {{@link #dump(IndentingPrintWriter, long, SimpleDateFormat)}} instead.
-     */
-    @Deprecated
-    public void dump(PrintWriter pw, String prefix, long nowELAPSED, SimpleDateFormat sdf) {
-        final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, prefix, prefix);
-        dump(ipw, nowELAPSED, sdf);
+    private static String policyIndexToString(int index) {
+        switch (index) {
+            case REQUESTER_POLICY_INDEX:
+                return "requester";
+            case APP_STANDBY_POLICY_INDEX:
+                return "app_standby";
+            default:
+                return "unknown";
+        }
+    }
+
+    public static String typeToString(int type) {
+        switch (type) {
+            case RTC:
+                return "RTC";
+            case RTC_WAKEUP:
+                return "RTC_WAKEUP";
+            case ELAPSED_REALTIME:
+                return "ELAPSED";
+            case ELAPSED_REALTIME_WAKEUP:
+                return "ELAPSED_WAKEUP";
+            default:
+                return "--unknown--";
+        }
     }
 
     public void dump(IndentingPrintWriter ipw, long nowELAPSED, SimpleDateFormat sdf) {
@@ -142,24 +231,14 @@
         ipw.println(statsTag);
 
         ipw.print("type=");
-        ipw.print(type);
-        ipw.print(" expectedWhenElapsed=");
-        TimeUtils.formatDuration(expectedWhenElapsed, nowELAPSED, ipw);
-        ipw.print(" expectedMaxWhenElapsed=");
-        TimeUtils.formatDuration(expectedMaxWhenElapsed, nowELAPSED, ipw);
-        ipw.print(" whenElapsed=");
-        TimeUtils.formatDuration(whenElapsed, nowELAPSED, ipw);
-        ipw.print(" maxWhenElapsed=");
-        TimeUtils.formatDuration(maxWhenElapsed, nowELAPSED, ipw);
-        ipw.print(" when=");
+        ipw.print(typeToString(type));
+        ipw.print(" origWhen=");
         if (isRtc) {
-            ipw.print(sdf.format(new Date(when)));
+            ipw.print(sdf.format(new Date(origWhen)));
         } else {
-            TimeUtils.formatDuration(when, nowELAPSED, ipw);
+            TimeUtils.formatDuration(origWhen, nowELAPSED, ipw);
         }
-        ipw.println();
-
-        ipw.print("window=");
+        ipw.print(" window=");
         TimeUtils.formatDuration(windowLength, ipw);
         ipw.print(" repeatInterval=");
         ipw.print(repeatInterval);
@@ -168,6 +247,19 @@
         ipw.print(" flags=0x");
         ipw.println(Integer.toHexString(flags));
 
+        ipw.print("policyWhenElapsed:");
+        for (int i = 0; i < NUM_POLICIES; i++) {
+            ipw.print(" " + policyIndexToString(i) + "=");
+            TimeUtils.formatDuration(mPolicyWhenElapsed[i], nowELAPSED, ipw);
+        }
+        ipw.println();
+
+        ipw.print("whenElapsed=");
+        TimeUtils.formatDuration(getWhenElapsed(), nowELAPSED, ipw);
+        ipw.print(" maxWhenElapsed=");
+        TimeUtils.formatDuration(mMaxWhenElapsed, nowELAPSED, ipw);
+        ipw.println();
+
         if (alarmClock != null) {
             ipw.println("Alarm clock:");
 
@@ -177,9 +269,10 @@
             ipw.print("  showIntent=");
             ipw.println(alarmClock.getShowIntent());
         }
-        ipw.print("operation=");
-        ipw.println(operation);
-
+        if (operation != null) {
+            ipw.print("operation=");
+            ipw.println(operation);
+        }
         if (listener != null) {
             ipw.print("listener=");
             ipw.println(listener.asBinder());
@@ -191,7 +284,7 @@
 
         proto.write(AlarmProto.TAG, statsTag);
         proto.write(AlarmProto.TYPE, type);
-        proto.write(AlarmProto.TIME_UNTIL_WHEN_ELAPSED_MS, whenElapsed - nowElapsed);
+        proto.write(AlarmProto.TIME_UNTIL_WHEN_ELAPSED_MS, getWhenElapsed() - nowElapsed);
         proto.write(AlarmProto.WINDOW_LENGTH_MS, windowLength);
         proto.write(AlarmProto.REPEAT_INTERVAL_MS, repeatInterval);
         proto.write(AlarmProto.COUNT, count);
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 05910a5..f196567 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -26,6 +26,9 @@
 import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
 import static android.os.UserHandle.USER_SYSTEM;
 
+import static com.android.server.alarm.Alarm.APP_STANDBY_POLICY_INDEX;
+import static com.android.server.alarm.Alarm.REQUESTER_POLICY_INDEX;
+
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
 import android.app.Activity;
@@ -342,10 +345,6 @@
             return (i < 0) ? 0 : history.get(i);
         }
 
-        void dump(PrintWriter pw, String prefix, long nowElapsed) {
-            dump(new IndentingPrintWriter(pw, "  ").setIndent(prefix), nowElapsed);
-        }
-
         void dump(IndentingPrintWriter pw, long nowElapsed) {
             pw.println("App Alarm history:");
             pw.increaseIndent();
@@ -595,10 +594,6 @@
                             DEFAULT_APP_STANDBY_RESTRICTED_WINDOW));
         }
 
-        void dump(PrintWriter pw, String prefix) {
-            dump(new IndentingPrintWriter(pw, "  ").setIndent(prefix));
-        }
-
         void dump(IndentingPrintWriter pw) {
             pw.println("Settings:");
 
@@ -727,9 +722,9 @@
             }
 
             // within each class, sort by nominal delivery time
-            if (lhs.whenElapsed < rhs.whenElapsed) {
+            if (lhs.getWhenElapsed() < rhs.getWhenElapsed()) {
                 return -1;
-            } else if (lhs.whenElapsed > rhs.whenElapsed) {
+            } else if (lhs.getWhenElapsed() > rhs.getWhenElapsed()) {
                 return 1;
             }
 
@@ -798,9 +793,12 @@
         this(context, new Injector(context));
     }
 
+    private static boolean isRtc(int type) {
+        return (type == RTC || type == RTC_WAKEUP);
+    }
+
     private long convertToElapsed(long when, int type) {
-        final boolean isRtc = (type == RTC || type == RTC_WAKEUP);
-        if (isRtc) {
+        if (isRtc(type)) {
             when -= mInjector.getCurrentTimeMillis() - mInjector.getElapsedRealtime();
         }
         return when;
@@ -823,13 +821,29 @@
     }
 
     // The RTC clock has moved arbitrarily, so we need to recalculate all the RTC alarm deliveries.
-    void reevaluateRtcAlarms(final long nowElapsed) {
+    void reevaluateRtcAlarms() {
         synchronized (mLock) {
-            final ArrayList<Alarm> rtcAlarms = mAlarmStore.remove(a -> (a.type == RTC
-                    || a.type == RTC_WAKEUP));
-            for (final Alarm a : rtcAlarms) {
-                restoreAlarmLocked(a, nowElapsed);
-                setImplLocked(a);
+            boolean changed = mAlarmStore.updateAlarmDeliveries(a -> {
+                if (!isRtc(a.type)) {
+                    return false;
+                }
+                return restoreRequestedTime(a);
+            });
+
+            if (mNextWakeFromIdle != null && isRtc(mNextWakeFromIdle.type)) {
+                // The next wake from idle got updated due to the rtc time change, implying we need
+                // to update the time we have to come out of idle too.
+                changed |= mAlarmStore.updateAlarmDeliveries(a -> {
+                    if (a != mPendingIdleUntil) {
+                        return false;
+                    }
+                    return adjustIdleUntilTime(a);
+                });
+            }
+
+            if (changed) {
+                rescheduleKernelAlarmsLocked();
+                // Only time shifted, so the next alarm clock will not change
             }
         }
     }
@@ -844,7 +858,7 @@
     boolean reorderAlarmsBasedOnStandbyBuckets(ArraySet<Pair<String, Integer>> targetPackages) {
         final long start = mStatLogger.getTime();
 
-        final boolean changed = mAlarmStore.recalculateAlarmDeliveries(a -> {
+        final boolean changed = mAlarmStore.updateAlarmDeliveries(a -> {
             final Pair<String, Integer> packageUser =
                     Pair.create(a.sourcePackage, UserHandle.getUserId(a.creatorUid));
             if (targetPackages != null && !targetPackages.contains(packageUser)) {
@@ -857,23 +871,8 @@
         return changed;
     }
 
-    private void restoreAlarmLocked(Alarm a, long nowElapsed) {
-        a.when = a.origWhen;
-        long whenElapsed = convertToElapsed(a.when, a.type);
-        final long maxElapsed;
-        if (a.windowLength == AlarmManager.WINDOW_EXACT) {
-            // Exact
-            maxElapsed = whenElapsed;
-        } else {
-            // Not exact.  Preserve any explicit window, otherwise recalculate
-            // the window based on the alarm's new futurity.  Note that this
-            // reflects a policy of preferring timely to deferred delivery.
-            maxElapsed = (a.windowLength > 0)
-                    ? clampPositive(whenElapsed + a.windowLength)
-                    : maxTriggerTime(nowElapsed, whenElapsed, a.repeatInterval);
-        }
-        a.expectedWhenElapsed = a.whenElapsed = whenElapsed;
-        a.expectedMaxWhenElapsed = a.maxWhenElapsed = maxElapsed;
+    private boolean restoreRequestedTime(Alarm a) {
+        return a.setPolicyElapsed(REQUESTER_POLICY_INDEX, convertToElapsed(a.origWhen, a.type));
     }
 
     static long clampPositive(long val) {
@@ -973,14 +972,17 @@
             // Recurring alarms may have passed several alarm intervals while the
             // alarm was kept pending. Send the appropriate trigger count.
             if (alarm.repeatInterval > 0) {
-                alarm.count += (nowELAPSED - alarm.expectedWhenElapsed) / alarm.repeatInterval;
+                alarm.count += (nowELAPSED - alarm.getPolicyElapsed(REQUESTER_POLICY_INDEX))
+                        / alarm.repeatInterval;
                 // Also schedule its next recurrence
                 final long delta = alarm.count * alarm.repeatInterval;
-                final long nextElapsed = alarm.expectedWhenElapsed + delta;
-                setImplLocked(alarm.type, alarm.when + delta, nextElapsed, alarm.windowLength,
-                        maxTriggerTime(nowELAPSED, nextElapsed, alarm.repeatInterval),
-                        alarm.repeatInterval, alarm.operation, null, null, alarm.flags,
-                        alarm.workSource, alarm.alarmClock, alarm.uid, alarm.packageName);
+                final long nextElapsed = alarm.getPolicyElapsed(REQUESTER_POLICY_INDEX) + delta;
+                final long nextMaxElapsed = maxTriggerTime(nowELAPSED, nextElapsed,
+                        alarm.repeatInterval);
+                setImplLocked(alarm.type, alarm.origWhen + delta, nextElapsed,
+                        nextMaxElapsed - nextElapsed, alarm.repeatInterval, alarm.operation, null,
+                        null, alarm.flags, alarm.workSource, alarm.alarmClock, alarm.uid,
+                        alarm.packageName);
                 // Kernel alarms will be rescheduled as needed in setImplLocked
             }
         }
@@ -1026,18 +1028,10 @@
         if (mPendingWhileIdleAlarms.size() > 0) {
             ArrayList<Alarm> alarms = mPendingWhileIdleAlarms;
             mPendingWhileIdleAlarms = new ArrayList<>();
-            final long nowElapsed = mInjector.getElapsedRealtime();
             for (int i = alarms.size() - 1; i >= 0; i--) {
-                Alarm a = alarms.get(i);
-                restoreAlarmLocked(a, nowElapsed);
-                setImplLocked(a);
+                setImplLocked(alarms.get(i));
             }
         }
-
-        // Reschedule everything.
-        rescheduleKernelAlarmsLocked();
-        updateNextAlarmClockLocked();
-
     }
 
     static final class InFlight {
@@ -1449,6 +1443,11 @@
             }
         }
 
+        if ((flags & AlarmManager.FLAG_IDLE_UNTIL) != 0) {
+            // Do not support windows for idle-until alarms.
+            windowLength = AlarmManager.WINDOW_EXACT;
+        }
+
         // Sanity check the window length.  This will catch people mistakenly
         // trying to pass an end-of-window timestamp rather than a duration.
         if (windowLength > AlarmManager.INTERVAL_HALF_DAY) {
@@ -1515,17 +1514,17 @@
                 Slog.w(TAG, errorMsg);
                 throw new IllegalStateException(errorMsg);
             }
-            setImplLocked(type, triggerAtTime, triggerElapsed, windowLength, maxElapsed,
-                    interval, operation, directReceiver, listenerTag, flags, workSource,
-                    alarmClock, callingUid, callingPackage);
+            setImplLocked(type, triggerAtTime, triggerElapsed, windowLength, interval, operation,
+                    directReceiver, listenerTag, flags, workSource, alarmClock, callingUid,
+                    callingPackage);
         }
     }
 
     private void setImplLocked(int type, long when, long whenElapsed, long windowLength,
-            long maxWhen, long interval, PendingIntent operation, IAlarmListener directReceiver,
+            long interval, PendingIntent operation, IAlarmListener directReceiver,
             String listenerTag, int flags, WorkSource workSource,
             AlarmManager.AlarmClockInfo alarmClock, int callingUid, String callingPackage) {
-        Alarm a = new Alarm(type, when, whenElapsed, windowLength, maxWhen, interval,
+        final Alarm a = new Alarm(type, when, whenElapsed, windowLength, interval,
                 operation, directReceiver, listenerTag, workSource, flags, alarmClock,
                 callingUid, callingPackage);
         if (mActivityManagerInternal.isAppStartModeDisabled(callingUid, callingPackage)) {
@@ -1560,72 +1559,55 @@
     }
 
     /**
-     * Adjusts the idle-until alarm delivery time based on the upcoming wake-from-idle alarm.
+     * An alarm with {@link AlarmManager#FLAG_IDLE_UNTIL} is a special alarm that will put the
+     * system into idle until it goes off. We need to pull it earlier if there are existing alarms
+     * that have requested to bring us out of idle at an earlier time.
      *
      * @param alarm The alarm to adjust
      * @return true if the alarm delivery time was updated.
      */
     private boolean adjustIdleUntilTime(Alarm alarm) {
-        if ((alarm.flags & AlarmManager.FLAG_IDLE_UNTIL) != 0) {
+        if ((alarm.flags & AlarmManager.FLAG_IDLE_UNTIL) == 0) {
             return false;
         }
-        // This is a special alarm that will put the system into idle until it goes off.
-        // The caller has given the time they want this to happen at, however we need
-        // to pull that earlier if there are existing alarms that have requested to
-        // bring us out of idle at an earlier time.
-        if (mNextWakeFromIdle != null && alarm.whenElapsed > mNextWakeFromIdle.whenElapsed) {
-            alarm.when = alarm.whenElapsed = alarm.maxWhenElapsed = mNextWakeFromIdle.whenElapsed;
+        restoreRequestedTime(alarm);
+        long triggerBeforeFuzz = alarm.getPolicyElapsed(REQUESTER_POLICY_INDEX);
+        if (mNextWakeFromIdle != null && triggerBeforeFuzz > mNextWakeFromIdle.getWhenElapsed()) {
+            triggerBeforeFuzz = mNextWakeFromIdle.getWhenElapsed();
         }
         // Add fuzz to make the alarm go off some time before the actual desired time.
-        final long nowElapsed = mInjector.getElapsedRealtime();
-        final int fuzz = fuzzForDuration(alarm.whenElapsed - nowElapsed);
+        final int fuzz = fuzzForDuration(alarm.getWhenElapsed() - mInjector.getElapsedRealtime());
+        final int delta;
         if (fuzz > 0) {
             if (mRandom == null) {
                 mRandom = new Random();
             }
-            final int delta = mRandom.nextInt(fuzz);
-            alarm.whenElapsed -= delta;
-            if (false) {
-                Slog.d(TAG, "Alarm when: " + alarm.whenElapsed);
-                Slog.d(TAG, "Delta until alarm: " + (alarm.whenElapsed - nowElapsed));
-                Slog.d(TAG, "Applied fuzz: " + fuzz);
-                Slog.d(TAG, "Final delta: " + delta);
-                Slog.d(TAG, "Final when: " + alarm.whenElapsed);
-            }
-            alarm.when = alarm.maxWhenElapsed = alarm.whenElapsed;
+            delta = mRandom.nextInt(fuzz);
+        } else {
+            delta = 0;
         }
+        alarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, triggerBeforeFuzz - delta);
         return true;
     }
 
     /**
-     * Adjusts the alarm delivery time based on the current app standby bucket.
+     * Adjusts the alarm's policy time for app_standby.
      *
-     * @param alarm The alarm to adjust
-     * @return true if the alarm delivery time was updated.
+     * @param alarm The alarm to update.
+     * @return {@code true} if the actual delivery time of the given alarm was updated due to
+     *         adjustments made in this call.
      */
     private boolean adjustDeliveryTimeBasedOnBucketLocked(Alarm alarm) {
-        if (isExemptFromAppStandby(alarm)) {
-            return false;
+        final long nowElapsed = mInjector.getElapsedRealtime();
+        if (isExemptFromAppStandby(alarm) || mAppStandbyParole) {
+            return alarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, nowElapsed);
         }
-        if (mAppStandbyParole) {
-            if (alarm.whenElapsed > alarm.expectedWhenElapsed) {
-                // We did defer this alarm earlier, restore original requirements
-                alarm.whenElapsed = alarm.expectedWhenElapsed;
-                alarm.maxWhenElapsed = alarm.expectedMaxWhenElapsed;
-                return true;
-            }
-            return false;
-        }
-        final long oldWhenElapsed = alarm.whenElapsed;
-        final long oldMaxWhenElapsed = alarm.maxWhenElapsed;
 
         final String sourcePackage = alarm.sourcePackage;
         final int sourceUserId = UserHandle.getUserId(alarm.creatorUid);
         final int standbyBucket = mUsageStatsManagerInternal.getAppStandbyBucket(
-                sourcePackage, sourceUserId, mInjector.getElapsedRealtime());
+                sourcePackage, sourceUserId, nowElapsed);
 
-        // Quota deferring implementation:
-        boolean deferred = false;
         final int wakeupsInWindow = mAppWakeupHistory.getTotalWakeupsInWindow(sourcePackage,
                 sourceUserId);
         if (standbyBucket == UsageStatsManager.STANDBY_BUCKET_RESTRICTED) {
@@ -1635,14 +1617,9 @@
             if (wakeupsInWindow > 0) {
                 final long lastWakeupTime = mAppWakeupHistory.getNthLastWakeupForPackage(
                         sourcePackage, sourceUserId, mConstants.APP_STANDBY_RESTRICTED_QUOTA);
-                if (mInjector.getElapsedRealtime() - lastWakeupTime
-                        < mConstants.APP_STANDBY_RESTRICTED_WINDOW) {
-                    final long minElapsed =
-                            lastWakeupTime + mConstants.APP_STANDBY_RESTRICTED_WINDOW;
-                    if (alarm.expectedWhenElapsed < minElapsed) {
-                        alarm.whenElapsed = alarm.maxWhenElapsed = minElapsed;
-                        deferred = true;
-                    }
+                if ((nowElapsed - lastWakeupTime) < mConstants.APP_STANDBY_RESTRICTED_WINDOW) {
+                    return alarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX,
+                            lastWakeupTime + mConstants.APP_STANDBY_RESTRICTED_WINDOW);
                 }
             }
         } else {
@@ -1651,7 +1628,7 @@
                 final long minElapsed;
                 if (quotaForBucket <= 0) {
                     // Just keep deferring for a day till the quota changes
-                    minElapsed = mInjector.getElapsedRealtime() + MILLIS_IN_DAY;
+                    minElapsed = nowElapsed + MILLIS_IN_DAY;
                 } else {
                     // Suppose the quota for window was q, and the qth last delivery time for this
                     // package was t(q) then the next delivery must be after t(q) + <window_size>
@@ -1659,19 +1636,11 @@
                             sourcePackage, sourceUserId, quotaForBucket);
                     minElapsed = t + mConstants.APP_STANDBY_WINDOW;
                 }
-                if (alarm.expectedWhenElapsed < minElapsed) {
-                    alarm.whenElapsed = alarm.maxWhenElapsed = minElapsed;
-                    deferred = true;
-                }
+                return alarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, minElapsed);
             }
         }
-        if (!deferred) {
-            // Restore original requirements in case they were changed earlier.
-            alarm.whenElapsed = alarm.expectedWhenElapsed;
-            alarm.maxWhenElapsed = alarm.expectedMaxWhenElapsed;
-        }
-
-        return (oldWhenElapsed != alarm.whenElapsed || oldMaxWhenElapsed != alarm.maxWhenElapsed);
+        // wakeupsInWindow are less than the permitted quota, hence no deferring is needed.
+        return alarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, nowElapsed);
     }
 
     private static boolean isAllowedWhileIdle(Alarm a) {
@@ -1691,7 +1660,7 @@
                 ent.tag = a.operation.getTag("");
                 ent.op = "SET";
                 ent.elapsedRealtime = mInjector.getElapsedRealtime();
-                ent.argRealtime = a.whenElapsed;
+                ent.argRealtime = a.getWhenElapsed();
                 mAllowWhileIdleDispatches.add(ent);
                 if (mPendingIdleUntil == null) {
                     IdleDispatchEntry ent2 = new IdleDispatchEntry();
@@ -1704,6 +1673,7 @@
             if ((mPendingIdleUntil != a) && (mPendingIdleUntil != null)) {
                 Slog.wtfStack(TAG, "setImplLocked: idle until changed from " + mPendingIdleUntil
                         + " to " + a);
+                mAlarmStore.remove(mPendingIdleUntil::equals);
             }
             mPendingIdleUntil = a;
             final ArrayList<Alarm> notAllowedWhileIdleAlarms = mAlarmStore.remove(
@@ -1718,18 +1688,16 @@
             }
         }
         if ((a.flags & AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) {
-            if (mNextWakeFromIdle == null || mNextWakeFromIdle.whenElapsed > a.whenElapsed) {
+            if (mNextWakeFromIdle == null || mNextWakeFromIdle.getWhenElapsed()
+                    > a.getWhenElapsed()) {
                 mNextWakeFromIdle = a;
                 // If this wake from idle is earlier than whatever was previously scheduled,
-                // and we are currently idling, then we need to rebatch alarms in case the idle
-                // until time needs to be updated.
+                // and we are currently idling, then the idle-until time needs to be updated.
                 if (mPendingIdleUntil != null) {
-                    final long nowElapsed = mInjector.getElapsedRealtime();
-                    mAlarmStore.recalculateAlarmDeliveries(alarm -> {
+                    mAlarmStore.updateAlarmDeliveries(alarm -> {
                         if (alarm != mPendingIdleUntil) {
                             return false;
                         }
-                        restoreAlarmLocked(alarm, nowElapsed);
                         return adjustIdleUntilTime(alarm);
                     });
                 }
@@ -1904,7 +1872,7 @@
             if (args.length > 0 && "--proto".equals(args[0])) {
                 dumpProto(fd);
             } else {
-                dumpImpl(pw);
+                dumpImpl(new IndentingPrintWriter(pw, "  "));
             }
         }
 
@@ -1916,18 +1884,20 @@
         }
     };
 
-    void dumpImpl(PrintWriter pw) {
+    void dumpImpl(IndentingPrintWriter pw) {
         synchronized (mLock) {
             pw.println("Current Alarm Manager state:");
-            mConstants.dump(pw, "  ");
+            pw.increaseIndent();
+
+            mConstants.dump(pw);
             pw.println();
 
             if (mAppStateTracker != null) {
-                mAppStateTracker.dump(pw, "  ");
+                mAppStateTracker.dump(pw);
                 pw.println();
             }
 
-            pw.println("  App Standby Parole: " + mAppStandbyParole);
+            pw.println("App Standby Parole: " + mAppStandbyParole);
             pw.println();
 
             final long nowELAPSED = mInjector.getElapsedRealtime();
@@ -1935,7 +1905,7 @@
             final long nowRTC = mInjector.getCurrentTimeMillis();
             SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
 
-            pw.print("  nowRTC=");
+            pw.print("nowRTC=");
             pw.print(nowRTC);
             pw.print("=");
             pw.print(sdf.format(new Date(nowRTC)));
@@ -1943,110 +1913,125 @@
             pw.print(nowELAPSED);
             pw.println();
 
-            pw.print("  mLastTimeChangeClockTime=");
+            pw.print("mLastTimeChangeClockTime=");
             pw.print(mLastTimeChangeClockTime);
             pw.print("=");
             pw.println(sdf.format(new Date(mLastTimeChangeClockTime)));
 
-            pw.print("  mLastTimeChangeRealtime=");
+            pw.print("mLastTimeChangeRealtime=");
             pw.println(mLastTimeChangeRealtime);
 
-            pw.print("  mLastTickReceived=");
+            pw.print("mLastTickReceived=");
             pw.println(sdf.format(new Date(mLastTickReceived)));
 
-            pw.print("  mLastTickSet=");
+            pw.print("mLastTickSet=");
             pw.println(sdf.format(new Date(mLastTickSet)));
 
             if (RECORD_ALARMS_IN_HISTORY) {
                 pw.println();
-                pw.println("  Recent TIME_TICK history:");
+                pw.println("Recent TIME_TICK history:");
+                pw.increaseIndent();
                 int i = mNextTickHistory;
                 do {
                     i--;
                     if (i < 0) i = TICK_HISTORY_DEPTH - 1;
                     final long time = mTickHistory[i];
-                    pw.print("    ");
                     pw.println((time > 0)
                             ? sdf.format(new Date(nowRTC - (nowELAPSED - time)))
                             : "-");
                 } while (i != mNextTickHistory);
+                pw.decreaseIndent();
             }
 
             SystemServiceManager ssm = LocalServices.getService(SystemServiceManager.class);
             if (ssm != null) {
                 pw.println();
-                pw.print("  RuntimeStarted=");
+                pw.print("RuntimeStarted=");
                 pw.print(sdf.format(
                         new Date(nowRTC - nowELAPSED + ssm.getRuntimeStartElapsedTime())));
                 if (ssm.isRuntimeRestarted()) {
                     pw.print("  (Runtime restarted)");
                 }
                 pw.println();
-                pw.print("  Runtime uptime (elapsed): ");
+
+                pw.print("Runtime uptime (elapsed): ");
                 TimeUtils.formatDuration(nowELAPSED, ssm.getRuntimeStartElapsedTime(), pw);
                 pw.println();
-                pw.print("  Runtime uptime (uptime): ");
+
+                pw.print("Runtime uptime (uptime): ");
                 TimeUtils.formatDuration(nowUPTIME, ssm.getRuntimeStartUptime(), pw);
                 pw.println();
             }
 
             pw.println();
             if (!mInteractive) {
-                pw.print("  Time since non-interactive: ");
+                pw.print("Time since non-interactive: ");
                 TimeUtils.formatDuration(nowELAPSED - mNonInteractiveStartTime, pw);
                 pw.println();
             }
-            pw.print("  Max wakeup delay: ");
+            pw.print("Max wakeup delay: ");
             TimeUtils.formatDuration(currentNonWakeupFuzzLocked(nowELAPSED), pw);
             pw.println();
-            pw.print("  Time since last dispatch: ");
+
+            pw.print("Time since last dispatch: ");
             TimeUtils.formatDuration(nowELAPSED - mLastAlarmDeliveryTime, pw);
             pw.println();
-            pw.print("  Next non-wakeup delivery time: ");
+
+            pw.print("Next non-wakeup delivery time: ");
             TimeUtils.formatDuration(mNextNonWakeupDeliveryTime, nowELAPSED, pw);
             pw.println();
 
             long nextWakeupRTC = mNextWakeup + (nowRTC - nowELAPSED);
             long nextNonWakeupRTC = mNextNonWakeup + (nowRTC - nowELAPSED);
-            pw.print("  Next non-wakeup alarm: ");
+            pw.print("Next non-wakeup alarm: ");
             TimeUtils.formatDuration(mNextNonWakeup, nowELAPSED, pw);
             pw.print(" = ");
             pw.print(mNextNonWakeup);
             pw.print(" = ");
             pw.println(sdf.format(new Date(nextNonWakeupRTC)));
-            pw.print("    set at ");
+
+            pw.increaseIndent();
+            pw.print("set at ");
             TimeUtils.formatDuration(mNextNonWakeUpSetAt, nowELAPSED, pw);
+            pw.decreaseIndent();
             pw.println();
-            pw.print("  Next wakeup alarm: ");
+
+            pw.print("Next wakeup alarm: ");
             TimeUtils.formatDuration(mNextWakeup, nowELAPSED, pw);
             pw.print(" = ");
             pw.print(mNextWakeup);
             pw.print(" = ");
             pw.println(sdf.format(new Date(nextWakeupRTC)));
-            pw.print("    set at ");
+
+            pw.increaseIndent();
+            pw.print("set at ");
             TimeUtils.formatDuration(mNextWakeUpSetAt, nowELAPSED, pw);
+            pw.decreaseIndent();
             pw.println();
 
-            pw.print("  Next kernel non-wakeup alarm: ");
+            pw.print("Next kernel non-wakeup alarm: ");
             TimeUtils.formatDuration(mInjector.getNextAlarm(ELAPSED_REALTIME), pw);
             pw.println();
-            pw.print("  Next kernel wakeup alarm: ");
+            pw.print("Next kernel wakeup alarm: ");
             TimeUtils.formatDuration(mInjector.getNextAlarm(ELAPSED_REALTIME_WAKEUP), pw);
             pw.println();
 
-            pw.print("  Last wakeup: ");
+            pw.print("Last wakeup: ");
             TimeUtils.formatDuration(mLastWakeup, nowELAPSED, pw);
             pw.print(" = ");
             pw.println(mLastWakeup);
-            pw.print("  Last trigger: ");
+
+            pw.print("Last trigger: ");
             TimeUtils.formatDuration(mLastTrigger, nowELAPSED, pw);
             pw.print(" = ");
             pw.println(mLastTrigger);
-            pw.print("  Num time change events: ");
+
+            pw.print("Num time change events: ");
             pw.println(mNumTimeChanged);
 
             pw.println();
-            pw.println("  Next alarm clock information: ");
+            pw.println("Next alarm clock information: ");
+            pw.increaseIndent();
             final TreeSet<Integer> users = new TreeSet<>();
             for (int i = 0; i < mNextAlarmClockForUser.size(); i++) {
                 users.add(mNextAlarmClockForUser.keyAt(i));
@@ -2058,7 +2043,7 @@
                 final AlarmManager.AlarmClockInfo next = mNextAlarmClockForUser.get(user);
                 final long time = next != null ? next.getTriggerTime() : 0;
                 final boolean pendingSend = mPendingSendNextAlarmClockChangedForUser.get(user);
-                pw.print("    user:");
+                pw.print("user:");
                 pw.print(user);
                 pw.print(" pendingSend:");
                 pw.print(pendingSend);
@@ -2072,26 +2057,31 @@
                 }
                 pw.println();
             }
+            pw.decreaseIndent();
+
             if (mAlarmStore.size() > 0) {
                 pw.println();
-                final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ", "  ");
-                mAlarmStore.dump(ipw, nowELAPSED, sdf);
+                mAlarmStore.dump(pw, nowELAPSED, sdf);
             }
             pw.println();
-            pw.println("  Pending user blocked background alarms: ");
+
+            pw.println("Pending user blocked background alarms: ");
+            pw.increaseIndent();
             boolean blocked = false;
             for (int i = 0; i < mPendingBackgroundAlarms.size(); i++) {
                 final ArrayList<Alarm> blockedAlarms = mPendingBackgroundAlarms.valueAt(i);
                 if (blockedAlarms != null && blockedAlarms.size() > 0) {
                     blocked = true;
-                    dumpAlarmList(pw, blockedAlarms, "    ", nowELAPSED, sdf);
+                    dumpAlarmList(pw, blockedAlarms, nowELAPSED, sdf);
                 }
             }
             if (!blocked) {
-                pw.println("    none");
+                pw.println("none");
             }
+            pw.decreaseIndent();
             pw.println();
-            pw.print("  Pending alarms per uid: [");
+
+            pw.print("Pending alarms per uid: [");
             for (int i = 0; i < mAlarmsPerUid.size(); i++) {
                 if (i > 0) {
                     pw.print(", ");
@@ -2103,75 +2093,90 @@
             pw.println("]");
             pw.println();
 
-            mAppWakeupHistory.dump(pw, "  ", nowELAPSED);
+            mAppWakeupHistory.dump(pw, nowELAPSED);
 
             if (mPendingIdleUntil != null || mPendingWhileIdleAlarms.size() > 0) {
                 pw.println();
-                pw.println("    Idle mode state:");
-                pw.print("      Idling until: ");
+                pw.println("Idle mode state:");
+
+                pw.increaseIndent();
+                pw.print("Idling until: ");
                 if (mPendingIdleUntil != null) {
                     pw.println(mPendingIdleUntil);
-                    mPendingIdleUntil.dump(pw, "        ", nowELAPSED, sdf);
+                    mPendingIdleUntil.dump(pw, nowELAPSED, sdf);
                 } else {
                     pw.println("null");
                 }
-                pw.println("      Pending alarms:");
-                dumpAlarmList(pw, mPendingWhileIdleAlarms, "      ", nowELAPSED, sdf);
+                pw.println("Pending alarms:");
+                dumpAlarmList(pw, mPendingWhileIdleAlarms, nowELAPSED, sdf);
+                pw.decreaseIndent();
             }
             if (mNextWakeFromIdle != null) {
                 pw.println();
-                pw.print("  Next wake from idle: ");
+                pw.print("Next wake from idle: ");
                 pw.println(mNextWakeFromIdle);
-                mNextWakeFromIdle.dump(pw, "    ", nowELAPSED, sdf);
+
+                pw.increaseIndent();
+                mNextWakeFromIdle.dump(pw, nowELAPSED, sdf);
+                pw.decreaseIndent();
             }
 
             pw.println();
-            pw.print("  Past-due non-wakeup alarms: ");
+            pw.print("Past-due non-wakeup alarms: ");
             if (mPendingNonWakeupAlarms.size() > 0) {
                 pw.println(mPendingNonWakeupAlarms.size());
-                dumpAlarmList(pw, mPendingNonWakeupAlarms, "    ", nowELAPSED, sdf);
+
+                pw.increaseIndent();
+                dumpAlarmList(pw, mPendingNonWakeupAlarms, nowELAPSED, sdf);
+                pw.decreaseIndent();
             } else {
                 pw.println("(none)");
             }
-            pw.print("    Number of delayed alarms: ");
+            pw.increaseIndent();
+            pw.print("Number of delayed alarms: ");
             pw.print(mNumDelayedAlarms);
             pw.print(", total delay time: ");
             TimeUtils.formatDuration(mTotalDelayTime, pw);
             pw.println();
-            pw.print("    Max delay time: ");
+
+            pw.print("Max delay time: ");
             TimeUtils.formatDuration(mMaxDelayTime, pw);
             pw.print(", max non-interactive time: ");
             TimeUtils.formatDuration(mNonInteractiveTime, pw);
             pw.println();
+            pw.decreaseIndent();
 
             pw.println();
-            pw.print("  Broadcast ref count: ");
+            pw.print("Broadcast ref count: ");
             pw.println(mBroadcastRefCount);
-            pw.print("  PendingIntent send count: ");
+            pw.print("PendingIntent send count: ");
             pw.println(mSendCount);
-            pw.print("  PendingIntent finish count: ");
+            pw.print("PendingIntent finish count: ");
             pw.println(mSendFinishCount);
-            pw.print("  Listener send count: ");
+            pw.print("Listener send count: ");
             pw.println(mListenerCount);
-            pw.print("  Listener finish count: ");
+            pw.print("Listener finish count: ");
             pw.println(mListenerFinishCount);
             pw.println();
 
             if (mInFlight.size() > 0) {
                 pw.println("Outstanding deliveries:");
+                pw.increaseIndent();
                 for (int i = 0; i < mInFlight.size(); i++) {
-                    pw.print("   #");
+                    pw.print("#");
                     pw.print(i);
                     pw.print(": ");
                     pw.println(mInFlight.get(i));
                 }
+                pw.decreaseIndent();
                 pw.println();
             }
 
             if (mLastAllowWhileIdleDispatch.size() > 0) {
-                pw.println("  Last allow while idle dispatch times:");
+                pw.println("Last allow while idle dispatch times:");
+                pw.increaseIndent();
                 for (int i = 0; i < mLastAllowWhileIdleDispatch.size(); i++) {
-                    pw.print("    UID ");
+                    pw.print("UID ");
                     final int uid = mLastAllowWhileIdleDispatch.keyAt(i);
                     UserHandle.formatUid(pw, uid);
                     pw.print(": ");
@@ -2187,9 +2192,10 @@
 
                     pw.println();
                 }
+                pw.decreaseIndent();
             }
 
-            pw.print("  mUseAllowWhileIdleShortTime: [");
+            pw.print("mUseAllowWhileIdleShortTime: [");
             for (int i = 0; i < mUseAllowWhileIdleShortTime.size(); i++) {
                 if (mUseAllowWhileIdleShortTime.valueAt(i)) {
                     UserHandle.formatUid(pw, mUseAllowWhileIdleShortTime.keyAt(i));
@@ -2199,7 +2205,7 @@
             pw.println("]");
             pw.println();
 
-            if (mLog.dump(pw, "  Recent problems", "    ")) {
+            if (mLog.dump(pw, "Recent problems:")) {
                 pw.println();
             }
 
@@ -2242,10 +2248,10 @@
                 }
             }
             if (len > 0) {
-                pw.println("  Top Alarms:");
+                pw.println("Top Alarms:");
+                pw.increaseIndent();
                 for (int i = 0; i < len; i++) {
                     FilterStats fs = topFilters[i];
-                    pw.print("    ");
                     if (fs.nesting > 0) pw.print("*ACTIVE* ");
                     TimeUtils.formatDuration(fs.aggregateTime, pw);
                     pw.print(" running, ");
@@ -2257,20 +2263,22 @@
                     pw.print(":");
                     pw.print(fs.mBroadcastStats.mPackageName);
                     pw.println();
-                    pw.print("      ");
+
+                    pw.increaseIndent();
                     pw.print(fs.mTag);
                     pw.println();
+                    pw.decreaseIndent();
                 }
+                pw.decreaseIndent();
             }
 
-            pw.println(" ");
-            pw.println("  Alarm Stats:");
+            pw.println();
+            pw.println("Alarm Stats:");
             final ArrayList<FilterStats> tmpFilters = new ArrayList<FilterStats>();
             for (int iu = 0; iu < mBroadcastStats.size(); iu++) {
                 ArrayMap<String, BroadcastStats> uidStats = mBroadcastStats.valueAt(iu);
                 for (int ip = 0; ip < uidStats.size(); ip++) {
                     BroadcastStats bs = uidStats.valueAt(ip);
-                    pw.print("  ");
                     if (bs.nesting > 0) pw.print("*ACTIVE* ");
                     UserHandle.formatUid(pw, bs.mUid);
                     pw.print(":");
@@ -2280,14 +2288,15 @@
                     pw.print(" running, ");
                     pw.print(bs.numWakeup);
                     pw.println(" wakeups:");
+
                     tmpFilters.clear();
                     for (int is = 0; is < bs.filterStats.size(); is++) {
                         tmpFilters.add(bs.filterStats.valueAt(is));
                     }
                     Collections.sort(tmpFilters, comparator);
+                    pw.increaseIndent();
                     for (int i = 0; i < tmpFilters.size(); i++) {
                         FilterStats fs = tmpFilters.get(i);
-                        pw.print("    ");
                         if (fs.nesting > 0) pw.print("*ACTIVE* ");
                         TimeUtils.formatDuration(fs.aggregateTime, pw);
                         pw.print(" ");
@@ -2297,28 +2306,32 @@
                         pw.print(" alarms, last ");
                         TimeUtils.formatDuration(fs.lastTime, nowELAPSED, pw);
                         pw.println(":");
-                        pw.print("      ");
+
+                        pw.increaseIndent();
                         pw.print(fs.mTag);
                         pw.println();
+                        pw.decreaseIndent();
                     }
+                    pw.decreaseIndent();
                 }
             }
             pw.println();
-            mStatLogger.dump(pw, "  ");
+            mStatLogger.dump(pw);
 
             if (RECORD_DEVICE_IDLE_ALARMS) {
                 pw.println();
-                pw.println("  Allow while idle dispatches:");
+                pw.println("Allow while idle dispatches:");
+                pw.increaseIndent();
                 for (int i = 0; i < mAllowWhileIdleDispatches.size(); i++) {
                     IdleDispatchEntry ent = mAllowWhileIdleDispatches.get(i);
-                    pw.print("    ");
                     TimeUtils.formatDuration(ent.elapsedRealtime, nowELAPSED, pw);
                     pw.print(": ");
                     UserHandle.formatUid(pw, ent.uid);
                     pw.print(":");
                     pw.println(ent.pkg);
+
+                    pw.increaseIndent();
                     if (ent.op != null) {
-                        pw.print("      ");
                         pw.print(ent.op);
                         pw.print(" / ");
                         pw.print(ent.tag);
@@ -2329,7 +2342,9 @@
                         }
                         pw.println();
                     }
+                    pw.decreaseIndent();
                 }
+                pw.decreaseIndent();
             }
         }
     }
@@ -2563,7 +2578,7 @@
 
     long getNextWakeFromIdleTimeImpl() {
         synchronized (mLock) {
-            return mNextWakeFromIdle != null ? mNextWakeFromIdle.whenElapsed : Long.MAX_VALUE;
+            return mNextWakeFromIdle != null ? mNextWakeFromIdle.getWhenElapsed() : Long.MAX_VALUE;
         }
     }
 
@@ -2784,12 +2799,11 @@
                 restorePending = true;
             }
             if (mNextWakeFromIdle != null && mNextWakeFromIdle.matches(operation, directReceiver)) {
-                mNextWakeFromIdle = null;
-                mAlarmStore.recalculateAlarmDeliveries(alarm -> {
+                mNextWakeFromIdle = mAlarmStore.getNextWakeFromIdleAlarm();
+                mAlarmStore.updateAlarmDeliveries(alarm -> {
                     if (alarm != mPendingIdleUntil) {
                         return false;
                     }
-                    restoreAlarmLocked(alarm, mInjector.getElapsedRealtime());
                     return adjustIdleUntilTime(alarm);
                 });
             }
@@ -2834,15 +2848,14 @@
                 mPendingBackgroundAlarms.removeAt(i);
             }
         }
-        // If we're currently keying off of this app's alarms for doze transitions,
-        // make sure to reset to other triggers.
+        // If we're currently using this app's alarms to come out of doze,
+        // make sure to reset to any remaining WAKE_FROM_IDLE alarms.
         if (mNextWakeFromIdle != null && mNextWakeFromIdle.uid == uid) {
-            mNextWakeFromIdle = null;
-            mAlarmStore.recalculateAlarmDeliveries(alarm -> {
+            mNextWakeFromIdle = mAlarmStore.getNextWakeFromIdleAlarm();
+            mAlarmStore.updateAlarmDeliveries(alarm -> {
                 if (alarm != mPendingIdleUntil) {
                     return false;
                 }
-                restoreAlarmLocked(alarm, mInjector.getElapsedRealtime());
                 return adjustIdleUntilTime(alarm);
             });
         }
@@ -2906,15 +2919,14 @@
                 mPendingBackgroundAlarms.removeAt(i);
             }
         }
-        // If we're currently keying off of this app's alarms for doze transitions,
-        // make sure to reset to other triggers.
+        // If we're currently using this app's alarms to come out of doze,
+        // make sure to reset to any remaining WAKE_FROM_IDLE alarms.
         if (removedNextWakeFromIdle.value) {
-            mNextWakeFromIdle = null;
-            mAlarmStore.recalculateAlarmDeliveries(alarm -> {
+            mNextWakeFromIdle = mAlarmStore.getNextWakeFromIdleAlarm();
+            mAlarmStore.updateAlarmDeliveries(alarm -> {
                 if (alarm != mPendingIdleUntil) {
                     return false;
                 }
-                restoreAlarmLocked(alarm, mInjector.getElapsedRealtime());
                 return adjustIdleUntilTime(alarm);
             });
         }
@@ -3071,31 +3083,11 @@
         }
     }
 
-    private static final String labelForType(int type) {
-        switch (type) {
-            case RTC:
-                return "RTC";
-            case RTC_WAKEUP:
-                return "RTC_WAKEUP";
-            case ELAPSED_REALTIME:
-                return "ELAPSED";
-            case ELAPSED_REALTIME_WAKEUP:
-                return "ELAPSED_WAKEUP";
-        }
-        return "--unknown--";
-    }
-
-    private static final void dumpAlarmList(PrintWriter pw, ArrayList<Alarm> list,
-            String prefix, long nowELAPSED, SimpleDateFormat sdf) {
-        final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, prefix, prefix);
-        dumpAlarmList(ipw, list, nowELAPSED, sdf);
-    }
-
     static final void dumpAlarmList(IndentingPrintWriter ipw, ArrayList<Alarm> list,
             long nowELAPSED, SimpleDateFormat sdf) {
         for (int i = list.size() - 1; i >= 0; i--) {
             final Alarm a = list.get(i);
-            final String label = labelForType(a.type);
+            final String label = Alarm.typeToString(a.type);
             ipw.print(label);
             ipw.print(" #");
             ipw.print(i);
@@ -3125,6 +3117,9 @@
         }
         final String sourcePackage = alarm.sourcePackage;
         final int sourceUid = alarm.creatorUid;
+        if (UserHandle.isCore(sourceUid)) {
+            return false;
+        }
         return (mAppStateTracker != null) &&
                 mAppStateTracker.areAlarmsRestricted(sourceUid, sourcePackage,
                         exemptOnBatterySaver);
@@ -3169,11 +3164,7 @@
                     // Whoops, it hasn't been long enough since the last ALLOW_WHILE_IDLE
                     // alarm went off for this app.  Reschedule the alarm to be in the
                     // correct time period.
-                    alarm.expectedWhenElapsed = alarm.whenElapsed = minTime;
-                    if (alarm.maxWhenElapsed < minTime) {
-                        alarm.maxWhenElapsed = minTime;
-                    }
-                    alarm.expectedMaxWhenElapsed = alarm.maxWhenElapsed;
+                    alarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, minTime);
                     if (RECORD_DEVICE_IDLE_ALARMS) {
                         IdleDispatchEntry ent = new IdleDispatchEntry();
                         ent.uid = alarm.uid;
@@ -3213,12 +3204,11 @@
                 restorePendingWhileIdleAlarmsLocked();
             }
             if (mNextWakeFromIdle == alarm) {
-                mNextWakeFromIdle = null;
-                mAlarmStore.recalculateAlarmDeliveries(a -> {
+                mNextWakeFromIdle = mAlarmStore.getNextWakeFromIdleAlarm();
+                mAlarmStore.updateAlarmDeliveries(a -> {
                     if (a != mPendingIdleUntil) {
                         return false;
                     }
-                    restoreAlarmLocked(a, nowELAPSED);
                     return adjustIdleUntilTime(a);
                 });
             }
@@ -3228,14 +3218,17 @@
             if (alarm.repeatInterval > 0) {
                 // this adjustment will be zero if we're late by
                 // less than one full repeat interval
-                alarm.count += (nowELAPSED - alarm.expectedWhenElapsed) / alarm.repeatInterval;
+                alarm.count += (nowELAPSED - alarm.getPolicyElapsed(REQUESTER_POLICY_INDEX))
+                        / alarm.repeatInterval;
                 // Also schedule its next recurrence
                 final long delta = alarm.count * alarm.repeatInterval;
-                final long nextElapsed = alarm.expectedWhenElapsed + delta;
-                setImplLocked(alarm.type, alarm.when + delta, nextElapsed, alarm.windowLength,
-                        maxTriggerTime(nowELAPSED, nextElapsed, alarm.repeatInterval),
-                        alarm.repeatInterval, alarm.operation, null, null, alarm.flags,
-                        alarm.workSource, alarm.alarmClock, alarm.uid, alarm.packageName);
+                final long nextElapsed = alarm.getPolicyElapsed(REQUESTER_POLICY_INDEX) + delta;
+                final long nextMaxElapsed = maxTriggerTime(nowELAPSED, nextElapsed,
+                        alarm.repeatInterval);
+                setImplLocked(alarm.type, alarm.origWhen + delta, nextElapsed,
+                        nextMaxElapsed - nextElapsed, alarm.repeatInterval, alarm.operation, null,
+                        null, alarm.flags, alarm.workSource, alarm.alarmClock, alarm.uid,
+                        alarm.packageName);
             }
 
             if (alarm.wakeup) {
@@ -3277,7 +3270,7 @@
         }
     }
 
-    static int fuzzForDuration(long duration) {
+    int fuzzForDuration(long duration) {
         if (duration < 15 * 60 * 1000) {
             // If the duration until the time is less than 15 minutes, the maximum fuzz
             // is the duration.
@@ -3480,7 +3473,7 @@
                         FrameworkStatsLog.write(FrameworkStatsLog.WALL_CLOCK_TIME_SHIFTED, nowRTC);
                         removeImpl(null, mTimeTickTrigger);
                         removeImpl(mDateChangeSender, null);
-                        reevaluateRtcAlarms(nowELAPSED);
+                        reevaluateRtcAlarms();
                         mClockReceiver.scheduleTimeTickEvent();
                         mClockReceiver.scheduleDateChangedEvent();
                         synchronized (mLock) {
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmStore.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmStore.java
index 9fdbb8b..7a846b9 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmStore.java
@@ -48,6 +48,15 @@
     ArrayList<Alarm> remove(Predicate<Alarm> whichAlarms);
 
     /**
+     * Gets the earliest alarm with the flag {@link android.app.AlarmManager#FLAG_WAKE_FROM_IDLE}
+     * based on {@link Alarm#getWhenElapsed()}.
+     *
+     * @return An alarm object matching the description above or {@code null} if no such alarm was
+     *         found.
+     */
+    Alarm getNextWakeFromIdleAlarm();
+
+    /**
      * Returns the total number of alarms in this store.
      */
     int size();
@@ -71,7 +80,7 @@
     /**
      * Removes all alarms that are pending delivery at the given time.
      *
-     * @param nowElapsed    The time at which delivery eligibility is evaluated.
+     * @param nowElapsed The time at which delivery eligibility is evaluated.
      * @return The list of alarms pending at the given time.
      */
     ArrayList<Alarm> removePendingAlarms(long nowElapsed);
@@ -82,7 +91,7 @@
      *
      * @return {@code true} if any of the alarm deliveries changed due to this call.
      */
-    boolean recalculateAlarmDeliveries(AlarmDeliveryCalculator deliveryCalculator);
+    boolean updateAlarmDeliveries(AlarmDeliveryCalculator deliveryCalculator);
 
     /**
      * Returns all the alarms in the form of a list.
@@ -97,6 +106,7 @@
      * Primary useful for debugging. Can be called from the
      * {@link android.os.Binder#dump(FileDescriptor PrintWriter, String[]) dump} method of the
      * caller.
+     *
      * @param ipw        The {@link IndentingPrintWriter} to write to.
      * @param nowElapsed the time when the dump is requested in the
      *                   {@link SystemClock#elapsedRealtime()
@@ -112,7 +122,7 @@
 
     /**
      * A functional interface used to update the alarm. Used to describe the update in
-     * {@link #recalculateAlarmDeliveries(AlarmDeliveryCalculator)}
+     * {@link #updateAlarmDeliveries(AlarmDeliveryCalculator)}
      */
     @FunctionalInterface
     interface AlarmDeliveryCalculator {
@@ -125,3 +135,4 @@
         boolean updateAlarmDelivery(Alarm a);
     }
 }
+
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/BatchingAlarmStore.java b/apex/jobscheduler/service/java/com/android/server/alarm/BatchingAlarmStore.java
index 91c0c05..cbfe80b 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/BatchingAlarmStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/BatchingAlarmStore.java
@@ -66,8 +66,8 @@
     };
 
     private static final Comparator<Alarm> sIncreasingTimeOrder = (a1, a2) -> {
-        long when1 = a1.whenElapsed;
-        long when2 = a2.whenElapsed;
+        long when1 = a1.getWhenElapsed();
+        long when2 = a2.getWhenElapsed();
         if (when1 > when2) {
             return 1;
         }
@@ -99,11 +99,28 @@
         }
         if (!removed.isEmpty()) {
             mSize -= removed.size();
+            // Not needed if only whole batches were removed, but keeping existing behavior.
             rebatchAllAlarms();
         }
         return removed;
     }
 
+    @Override
+    public Alarm getNextWakeFromIdleAlarm() {
+        for (final Batch batch : mAlarmBatches) {
+            if ((batch.mFlags & AlarmManager.FLAG_WAKE_FROM_IDLE) == 0) {
+                continue;
+            }
+            for (int i = 0; i < batch.size(); i++) {
+                final Alarm a = batch.get(i);
+                if ((a.flags & AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) {
+                    return a;
+                }
+            }
+        }
+        return null;
+    }
+
     private void rebatchAllAlarms() {
         final long start = mStatLogger.getTime();
         final ArrayList<Batch> oldBatches = (ArrayList<Batch>) mAlarmBatches.clone();
@@ -157,7 +174,7 @@
     }
 
     @Override
-    public boolean recalculateAlarmDeliveries(AlarmDeliveryCalculator deliveryCalculator) {
+    public boolean updateAlarmDeliveries(AlarmDeliveryCalculator deliveryCalculator) {
         boolean changed = false;
         for (final Batch b : mAlarmBatches) {
             for (int i = 0; i < b.size(); i++) {
@@ -204,7 +221,7 @@
 
     private void insertAndBatchAlarm(Alarm alarm) {
         final int whichBatch = ((alarm.flags & AlarmManager.FLAG_STANDALONE) != 0) ? -1
-                : attemptCoalesce(alarm.whenElapsed, alarm.maxWhenElapsed);
+                : attemptCoalesce(alarm.getWhenElapsed(), alarm.getMaxWhenElapsed());
 
         if (whichBatch < 0) {
             addBatch(mAlarmBatches, new Batch(alarm));
@@ -247,8 +264,8 @@
         final ArrayList<Alarm> mAlarms = new ArrayList<>();
 
         Batch(Alarm seed) {
-            mStart = seed.whenElapsed;
-            mEnd = clampPositive(seed.maxWhenElapsed);
+            mStart = seed.getWhenElapsed();
+            mEnd = clampPositive(seed.getMaxWhenElapsed());
             mFlags = seed.flags;
             mAlarms.add(seed);
         }
@@ -276,12 +293,12 @@
             if (DEBUG_BATCH) {
                 Slog.v(TAG, "Adding " + alarm + " to " + this);
             }
-            if (alarm.whenElapsed > mStart) {
-                mStart = alarm.whenElapsed;
+            if (alarm.getWhenElapsed() > mStart) {
+                mStart = alarm.getWhenElapsed();
                 newStart = true;
             }
-            if (alarm.maxWhenElapsed < mEnd) {
-                mEnd = alarm.maxWhenElapsed;
+            if (alarm.getMaxWhenElapsed() < mEnd) {
+                mEnd = alarm.getMaxWhenElapsed();
             }
             mFlags |= alarm.flags;
 
@@ -309,11 +326,11 @@
                         Slog.wtf(TAG, "Removed TIME_TICK alarm");
                     }
                 } else {
-                    if (alarm.whenElapsed > newStart) {
-                        newStart = alarm.whenElapsed;
+                    if (alarm.getWhenElapsed() > newStart) {
+                        newStart = alarm.getWhenElapsed();
                     }
-                    if (alarm.maxWhenElapsed < newEnd) {
-                        newEnd = alarm.maxWhenElapsed;
+                    if (alarm.getMaxWhenElapsed() < newEnd) {
+                        newEnd = alarm.getMaxWhenElapsed();
                     }
                     newFlags |= alarm.flags;
                     i++;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 4512d77..6c14233 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -2673,7 +2673,7 @@
 
             validateJobFlags(job, uid);
 
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
                 return JobSchedulerService.this.scheduleAsPackage(job, null, uid, null, userId,
                         null);
@@ -2701,7 +2701,7 @@
 
             validateJobFlags(job, uid);
 
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
                 return JobSchedulerService.this.scheduleAsPackage(job, work, uid, null, userId,
                         null);
@@ -2732,7 +2732,7 @@
 
             validateJobFlags(job, callerUid);
 
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
                 return JobSchedulerService.this.scheduleAsPackage(job, null, callerUid,
                         packageName, userId, tag);
@@ -2745,7 +2745,7 @@
         public ParceledListSlice<JobInfo> getAllPendingJobs() throws RemoteException {
             final int uid = Binder.getCallingUid();
 
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
                 return new ParceledListSlice<>(JobSchedulerService.this.getPendingJobs(uid));
             } finally {
@@ -2757,7 +2757,7 @@
         public JobInfo getPendingJob(int jobId) throws RemoteException {
             final int uid = Binder.getCallingUid();
 
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
                 return JobSchedulerService.this.getPendingJob(uid, jobId);
             } finally {
@@ -2768,7 +2768,7 @@
         @Override
         public void cancelAll() throws RemoteException {
             final int uid = Binder.getCallingUid();
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
                 JobSchedulerService.this.cancelJobsForUid(uid,
                         "cancelAll() called by app, callingUid=" + uid);
@@ -2781,7 +2781,7 @@
         public void cancel(int jobId) throws RemoteException {
             final int uid = Binder.getCallingUid();
 
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
                 JobSchedulerService.this.cancelJob(uid, jobId, uid);
             } finally {
diff --git a/apex/statsd/framework/java/android/app/StatsManager.java b/apex/statsd/framework/java/android/app/StatsManager.java
index a7d2057..41803cf 100644
--- a/apex/statsd/framework/java/android/app/StatsManager.java
+++ b/apex/statsd/framework/java/android/app/StatsManager.java
@@ -547,7 +547,7 @@
 
         @Override
         public void onPullAtom(int atomTag, IPullAtomResultReceiver resultReceiver) {
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 mExecutor.execute(() -> {
                     List<StatsEvent> data = new ArrayList<>();
diff --git a/api/Android.bp b/api/Android.bp
index e403082..21a7166 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -48,6 +48,7 @@
     name: "frameworks-base-api-current-merged.txt",
     srcs: [
         ":conscrypt.module.public.api{.public.api.txt}",
+        ":framework-appsearch{.public.api.txt}",
         ":framework-graphics{.public.api.txt}",
         ":framework-media{.public.api.txt}",
         ":framework-mediaprovider{.public.api.txt}",
@@ -61,12 +62,26 @@
     out: ["current.txt"],
     tools: ["metalava"],
     cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+    dists: [
+        {
+            targets: ["droidcore"],
+            dir: "api",
+            dest: "current.txt",
+        },
+        {
+            targets: ["sdk", "win_sdk"],
+            dir: "apistubs/android/public/api",
+            dest: "android.txt",
+        },
+    ],
 }
 
 genrule {
     name: "frameworks-base-api-removed-merged.txt",
     srcs: [
         ":conscrypt.module.public.api{.public.removed-api.txt}",
+        ":framework-appsearch{.public.removed-api.txt}",
+        ":framework-graphics{.public.removed-api.txt}",
         ":framework-media{.public.removed-api.txt}",
         ":framework-mediaprovider{.public.removed-api.txt}",
         ":framework-permission{.public.removed-api.txt}",
@@ -79,11 +94,19 @@
     out: ["removed.txt"],
     tools: ["metalava"],
     cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+    dists: [
+        {
+            targets: ["droidcore"],
+            dir: "api",
+            dest: "removed.txt",
+        },
+    ],
 }
 
 genrule {
     name: "frameworks-base-api-system-current-merged.txt",
     srcs: [
+        ":framework-appsearch{.system.api.txt}",
         ":framework-graphics{.system.api.txt}",
         ":framework-media{.system.api.txt}",
         ":framework-mediaprovider{.system.api.txt}",
@@ -97,11 +120,25 @@
     out: ["system-current.txt"],
     tools: ["metalava"],
     cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+    dists: [
+        {
+            targets: ["droidcore"],
+            dir: "api",
+            dest: "system-current.txt",
+        },
+        {
+            targets: ["sdk", "win_sdk"],
+            dir: "apistubs/android/system/api",
+            dest: "android.txt",
+        },
+    ],
 }
 
 genrule {
     name: "frameworks-base-api-system-removed-merged.txt",
     srcs: [
+        ":framework-appsearch{.system.removed-api.txt}",
+        ":framework-graphics{.system.removed-api.txt}",
         ":framework-media{.system.removed-api.txt}",
         ":framework-mediaprovider{.system.removed-api.txt}",
         ":framework-permission{.system.removed-api.txt}",
@@ -114,11 +151,19 @@
     out: ["system-removed.txt"],
     tools: ["metalava"],
     cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+    dists: [
+        {
+            targets: ["droidcore"],
+            dir: "api",
+            dest: "system-removed.txt",
+        },
+    ],
 }
 
 genrule {
     name: "frameworks-base-api-module-lib-current-merged.txt",
     srcs: [
+        ":framework-appsearch{.module-lib.api.txt}",
         ":framework-graphics{.module-lib.api.txt}",
         ":framework-media{.module-lib.api.txt}",
         ":framework-mediaprovider{.module-lib.api.txt}",
@@ -132,11 +177,25 @@
     out: ["module-lib-current.txt"],
     tools: ["metalava"],
     cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+    dists: [
+        {
+            targets: ["droidcore"],
+            dir: "api",
+            dest: "module-lib-current.txt",
+        },
+        {
+            targets: ["sdk", "win_sdk"],
+            dir: "apistubs/android/module-lib/api",
+            dest: "android.txt",
+        },
+    ],
 }
 
 genrule {
     name: "frameworks-base-api-module-lib-removed-merged.txt",
     srcs: [
+        ":framework-appsearch{.module-lib.removed-api.txt}",
+        ":framework-graphics{.module-lib.removed-api.txt}",
         ":framework-media{.module-lib.removed-api.txt}",
         ":framework-mediaprovider{.module-lib.removed-api.txt}",
         ":framework-permission{.module-lib.removed-api.txt}",
@@ -149,4 +208,25 @@
     out: ["module-lib-removed.txt"],
     tools: ["metalava"],
     cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+    dists: [
+        {
+            targets: ["droidcore"],
+            dir: "api",
+            dest: "module-lib-removed.txt",
+        },
+    ],
+}
+
+genrule {
+    name: "combined-removed-dex",
+    srcs: [
+        ":frameworks-base-api-removed-merged.txt",
+        ":frameworks-base-api-system-removed-merged.txt",
+        ":android.car-stubs-docs{.removed-api.txt}",
+        ":android.car-system-stubs-docs{.removed-api.txt}",
+    ],
+    tool_files: ["gen_combined_removed_dex.sh"],
+    tools: ["metalava"],
+    out: ["combined-removed-dex.txt"],
+    cmd: "$(location gen_combined_removed_dex.sh) $(location metalava) $(genDir) $(in) > $(out)",
 }
diff --git a/api/current.txt b/api/current.txt
index 94e90a7..600adf6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1613,6 +1613,7 @@
     field public static final int windowHideAnimation = 16842935; // 0x10100b7
     field public static final int windowIsFloating = 16842839; // 0x1010057
     field public static final int windowIsTranslucent = 16842840; // 0x1010058
+    field public static final int windowLayoutAffinity = 16844313; // 0x1010619
     field public static final int windowLayoutInDisplayCutoutMode = 16844166; // 0x1010586
     field public static final int windowLightNavigationBar = 16844140; // 0x101056c
     field public static final int windowLightStatusBar = 16844000; // 0x10104e0
@@ -2850,6 +2851,7 @@
     method public int describeContents();
     method public int getDisplayId();
     method public int getGestureId();
+    method @NonNull public java.util.List<android.view.MotionEvent> getMotionEvents();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.accessibilityservice.AccessibilityGestureEvent> CREATOR;
   }
@@ -2889,6 +2891,7 @@
     field public static final int GESTURE_2_FINGER_DOUBLE_TAP = 20; // 0x14
     field public static final int GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD = 40; // 0x28
     field public static final int GESTURE_2_FINGER_SINGLE_TAP = 19; // 0x13
+    field public static final int GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD = 43; // 0x2b
     field public static final int GESTURE_2_FINGER_SWIPE_DOWN = 26; // 0x1a
     field public static final int GESTURE_2_FINGER_SWIPE_LEFT = 27; // 0x1b
     field public static final int GESTURE_2_FINGER_SWIPE_RIGHT = 28; // 0x1c
@@ -2897,11 +2900,13 @@
     field public static final int GESTURE_3_FINGER_DOUBLE_TAP = 23; // 0x17
     field public static final int GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD = 41; // 0x29
     field public static final int GESTURE_3_FINGER_SINGLE_TAP = 22; // 0x16
+    field public static final int GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD = 44; // 0x2c
     field public static final int GESTURE_3_FINGER_SWIPE_DOWN = 30; // 0x1e
     field public static final int GESTURE_3_FINGER_SWIPE_LEFT = 31; // 0x1f
     field public static final int GESTURE_3_FINGER_SWIPE_RIGHT = 32; // 0x20
     field public static final int GESTURE_3_FINGER_SWIPE_UP = 29; // 0x1d
     field public static final int GESTURE_3_FINGER_TRIPLE_TAP = 24; // 0x18
+    field public static final int GESTURE_3_FINGER_TRIPLE_TAP_AND_HOLD = 45; // 0x2d
     field public static final int GESTURE_4_FINGER_DOUBLE_TAP = 38; // 0x26
     field public static final int GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD = 42; // 0x2a
     field public static final int GESTURE_4_FINGER_SINGLE_TAP = 37; // 0x25
@@ -2928,6 +2933,7 @@
     field public static final int GESTURE_SWIPE_UP_AND_DOWN = 7; // 0x7
     field public static final int GESTURE_SWIPE_UP_AND_LEFT = 13; // 0xd
     field public static final int GESTURE_SWIPE_UP_AND_RIGHT = 14; // 0xe
+    field public static final int GESTURE_UNKNOWN = 0; // 0x0
     field public static final int GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS = 14; // 0xe
     field public static final int GLOBAL_ACTION_ACCESSIBILITY_BUTTON = 11; // 0xb
     field public static final int GLOBAL_ACTION_ACCESSIBILITY_BUTTON_CHOOSER = 12; // 0xc
@@ -3043,6 +3049,7 @@
     field public static final int FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK = 1024; // 0x400
     field public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 4; // 0x4
     field public static final int FLAG_RETRIEVE_INTERACTIVE_WINDOWS = 64; // 0x40
+    field public static final int FLAG_SEND_MOTION_EVENTS = 16384; // 0x4000
     field public static final int FLAG_SERVICE_HANDLES_DOUBLE_TAP = 2048; // 0x800
     field public int eventTypes;
     field public int feedbackType;
@@ -4376,6 +4383,7 @@
     method @Deprecated public void checkPackage(int, @NonNull String);
     method @Deprecated public void finishOp(@NonNull String, int, @NonNull String);
     method public void finishOp(@NonNull String, int, @NonNull String, @Nullable String);
+    method public void finishProxyOp(@NonNull String, int, @NonNull String, @Nullable String);
     method public boolean isOpActive(@NonNull String, int, @NonNull String);
     method @Deprecated public int noteOp(@NonNull String, int, @NonNull String);
     method public int noteOp(@NonNull String, int, @Nullable String, @Nullable String, @Nullable String);
@@ -4392,6 +4400,8 @@
     method public int startOp(@NonNull String, int, @Nullable String, @Nullable String, @Nullable String);
     method @Deprecated public int startOpNoThrow(@NonNull String, int, @NonNull String);
     method public int startOpNoThrow(@NonNull String, int, @NonNull String, @NonNull String, @Nullable String);
+    method public int startProxyOp(@NonNull String, int, @NonNull String, @Nullable String, @Nullable String);
+    method public int startProxyOpNoThrow(@NonNull String, int, @NonNull String, @Nullable String, @Nullable String);
     method public void startWatchingActive(@NonNull String[], @NonNull java.util.concurrent.Executor, @NonNull android.app.AppOpsManager.OnOpActiveChangedListener);
     method public void startWatchingMode(@NonNull String, @Nullable String, @NonNull android.app.AppOpsManager.OnOpChangedListener);
     method public void startWatchingMode(@NonNull String, @Nullable String, int, @NonNull android.app.AppOpsManager.OnOpChangedListener);
@@ -4895,7 +4905,7 @@
   @Deprecated public class Fragment implements android.content.ComponentCallbacks2 android.view.View.OnCreateContextMenuListener {
     ctor @Deprecated public Fragment();
     method @Deprecated public void dump(String, java.io.FileDescriptor, java.io.PrintWriter, String[]);
-    method @Deprecated public final boolean equals(Object);
+    method @Deprecated public final boolean equals(@Nullable Object);
     method @Deprecated public final android.app.Activity getActivity();
     method @Deprecated public boolean getAllowEnterTransitionOverlap();
     method @Deprecated public boolean getAllowReturnTransitionOverlap();
@@ -6515,6 +6525,7 @@
     method public void dropShellPermissionIdentity();
     method public android.view.accessibility.AccessibilityEvent executeAndWaitForEvent(Runnable, android.app.UiAutomation.AccessibilityEventFilter, long) throws java.util.concurrent.TimeoutException;
     method public android.os.ParcelFileDescriptor executeShellCommand(String);
+    method @NonNull public android.os.ParcelFileDescriptor[] executeShellCommandRw(@NonNull String);
     method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
     method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow();
     method public android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
@@ -9141,6 +9152,7 @@
     method public boolean getIncludeTxPowerLevel();
     method public android.util.SparseArray<byte[]> getManufacturerSpecificData();
     method public java.util.Map<android.os.ParcelUuid,byte[]> getServiceData();
+    method @Nullable public java.util.List<android.os.ParcelUuid> getServiceSolicitationUuids();
     method public java.util.List<android.os.ParcelUuid> getServiceUuids();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.le.AdvertiseData> CREATOR;
@@ -9150,6 +9162,7 @@
     ctor public AdvertiseData.Builder();
     method public android.bluetooth.le.AdvertiseData.Builder addManufacturerData(int, byte[]);
     method public android.bluetooth.le.AdvertiseData.Builder addServiceData(android.os.ParcelUuid, byte[]);
+    method @NonNull public android.bluetooth.le.AdvertiseData.Builder addServiceSolicitationUuid(@NonNull android.os.ParcelUuid);
     method public android.bluetooth.le.AdvertiseData.Builder addServiceUuid(android.os.ParcelUuid);
     method public android.bluetooth.le.AdvertiseData build();
     method public android.bluetooth.le.AdvertiseData.Builder setIncludeDeviceName(boolean);
@@ -10658,12 +10671,15 @@
     field public static final String ACTION_PACKAGE_CHANGED = "android.intent.action.PACKAGE_CHANGED";
     field public static final String ACTION_PACKAGE_DATA_CLEARED = "android.intent.action.PACKAGE_DATA_CLEARED";
     field public static final String ACTION_PACKAGE_FIRST_LAUNCH = "android.intent.action.PACKAGE_FIRST_LAUNCH";
+    field public static final String ACTION_PACKAGE_FULLY_LOADED = "android.intent.action.PACKAGE_FULLY_LOADED";
     field public static final String ACTION_PACKAGE_FULLY_REMOVED = "android.intent.action.PACKAGE_FULLY_REMOVED";
     field @Deprecated public static final String ACTION_PACKAGE_INSTALL = "android.intent.action.PACKAGE_INSTALL";
     field public static final String ACTION_PACKAGE_NEEDS_VERIFICATION = "android.intent.action.PACKAGE_NEEDS_VERIFICATION";
     field public static final String ACTION_PACKAGE_REMOVED = "android.intent.action.PACKAGE_REMOVED";
     field public static final String ACTION_PACKAGE_REPLACED = "android.intent.action.PACKAGE_REPLACED";
     field public static final String ACTION_PACKAGE_RESTARTED = "android.intent.action.PACKAGE_RESTARTED";
+    field public static final String ACTION_PACKAGE_STARTABLE = "android.intent.action.PACKAGE_STARTABLE";
+    field public static final String ACTION_PACKAGE_UNSTARTABLE = "android.intent.action.PACKAGE_UNSTARTABLE";
     field public static final String ACTION_PACKAGE_VERIFIED = "android.intent.action.PACKAGE_VERIFIED";
     field public static final String ACTION_PASTE = "android.intent.action.PASTE";
     field public static final String ACTION_PICK = "android.intent.action.PICK";
@@ -10827,7 +10843,9 @@
     field public static final String EXTRA_TIMEZONE = "time-zone";
     field public static final String EXTRA_TITLE = "android.intent.extra.TITLE";
     field public static final String EXTRA_UID = "android.intent.extra.UID";
+    field public static final String EXTRA_UNSTARTABLE_REASON = "android.intent.extra.UNSTARTABLE_REASON";
     field public static final String EXTRA_USER = "android.intent.extra.USER";
+    field public static final String EXTRA_USER_INITIATED = "android.intent.extra.USER_INITIATED";
     field public static final int FILL_IN_ACTION = 1; // 0x1
     field public static final int FILL_IN_CATEGORIES = 4; // 0x4
     field public static final int FILL_IN_CLIP_DATA = 128; // 0x80
@@ -11454,10 +11472,10 @@
 
   public final class ApkChecksum implements android.os.Parcelable {
     method public int describeContents();
-    method public int getKind();
-    method @Nullable public java.security.cert.Certificate getSourceCertificate() throws java.security.cert.CertificateException;
-    method @Nullable public String getSourcePackageName();
+    method @Nullable public java.security.cert.Certificate getInstallerCertificate() throws java.security.cert.CertificateException;
+    method @Nullable public String getInstallerPackageName();
     method @Nullable public String getSplitName();
+    method public int getType();
     method @NonNull public byte[] getValue();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.ApkChecksum> CREATOR;
@@ -11567,17 +11585,17 @@
   public final class Checksum implements android.os.Parcelable {
     ctor public Checksum(int, @NonNull byte[]);
     method public int describeContents();
-    method public int getKind();
+    method public int getType();
     method @NonNull public byte[] getValue();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.Checksum> CREATOR;
-    field public static final int PARTIAL_MERKLE_ROOT_1M_SHA256 = 32; // 0x20
-    field public static final int PARTIAL_MERKLE_ROOT_1M_SHA512 = 64; // 0x40
-    field @Deprecated public static final int WHOLE_MD5 = 2; // 0x2
-    field public static final int WHOLE_MERKLE_ROOT_4K_SHA256 = 1; // 0x1
-    field @Deprecated public static final int WHOLE_SHA1 = 4; // 0x4
-    field public static final int WHOLE_SHA256 = 8; // 0x8
-    field public static final int WHOLE_SHA512 = 16; // 0x10
+    field public static final int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256 = 32; // 0x20
+    field public static final int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512 = 64; // 0x40
+    field @Deprecated public static final int TYPE_WHOLE_MD5 = 2; // 0x2
+    field public static final int TYPE_WHOLE_MERKLE_ROOT_4K_SHA256 = 1; // 0x1
+    field @Deprecated public static final int TYPE_WHOLE_SHA1 = 4; // 0x4
+    field @Deprecated public static final int TYPE_WHOLE_SHA256 = 8; // 0x8
+    field @Deprecated public static final int TYPE_WHOLE_SHA512 = 16; // 0x10
   }
 
   public class ComponentInfo extends android.content.pm.PackageItemInfo {
@@ -12027,7 +12045,6 @@
     method @Nullable public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
     method @NonNull public CharSequence getBackgroundPermissionOptionLabel();
     method @Nullable public abstract android.content.pm.ChangedPackages getChangedPackages(@IntRange(from=0) int);
-    method public void getChecksums(@NonNull String, boolean, int, @Nullable java.util.List<java.security.cert.Certificate>, @NonNull android.content.IntentSender) throws java.security.cert.CertificateEncodingException, java.io.IOException, android.content.pm.PackageManager.NameNotFoundException;
     method public abstract int getComponentEnabledSetting(@NonNull android.content.ComponentName);
     method @NonNull public abstract android.graphics.drawable.Drawable getDefaultActivityIcon();
     method @Nullable public abstract android.graphics.drawable.Drawable getDrawable(@NonNull String, @DrawableRes int, @Nullable android.content.pm.ApplicationInfo);
@@ -12100,6 +12117,7 @@
     method @Deprecated public abstract void removePackageFromPreferred(@NonNull String);
     method public abstract void removePermission(@NonNull String);
     method @RequiresPermission(value="android.permission.WHITELIST_RESTRICTED_PERMISSIONS", conditional=true) public boolean removeWhitelistedRestrictedPermission(@NonNull String, @NonNull String, int);
+    method public void requestChecksums(@NonNull String, boolean, int, @NonNull java.util.List<java.security.cert.Certificate>, @NonNull android.content.IntentSender) throws java.security.cert.CertificateEncodingException, android.content.pm.PackageManager.NameNotFoundException;
     method @Nullable public abstract android.content.pm.ResolveInfo resolveActivity(@NonNull android.content.Intent, int);
     method @Nullable public abstract android.content.pm.ProviderInfo resolveContentProvider(@NonNull String, int);
     method @Nullable public abstract android.content.pm.ResolveInfo resolveService(@NonNull android.content.Intent, int);
@@ -12247,7 +12265,7 @@
     field @Deprecated public static final int GET_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000
     field public static final int GET_GIDS = 256; // 0x100
     field public static final int GET_INSTRUMENTATION = 16; // 0x10
-    field public static final int GET_INTENT_FILTERS = 32; // 0x20
+    field @Deprecated public static final int GET_INTENT_FILTERS = 32; // 0x20
     field public static final int GET_META_DATA = 128; // 0x80
     field public static final int GET_PERMISSIONS = 4096; // 0x1000
     field public static final int GET_PROVIDERS = 8; // 0x8
@@ -12286,6 +12304,9 @@
     field public static final int SYNCHRONOUS = 2; // 0x2
     field @Nullable public static final java.util.List<java.security.cert.Certificate> TRUST_ALL;
     field @NonNull public static final java.util.List<java.security.cert.Certificate> TRUST_NONE;
+    field public static final int UNSTARTABLE_REASON_CONNECTION_ERROR = 1; // 0x1
+    field public static final int UNSTARTABLE_REASON_INSUFFICIENT_STORAGE = 2; // 0x2
+    field public static final int UNSTARTABLE_REASON_UNKNOWN = 0; // 0x0
     field public static final int VERIFICATION_ALLOW = 1; // 0x1
     field public static final int VERIFICATION_REJECT = -1; // 0xffffffff
     field public static final int VERSION_CODE_HIGHEST = -1; // 0xffffffff
@@ -13056,38 +13077,38 @@
 
   public interface Cursor extends java.io.Closeable {
     method public void close();
-    method public void copyStringToBuffer(int, android.database.CharArrayBuffer);
+    method public void copyStringToBuffer(@IntRange(from=0) int, android.database.CharArrayBuffer);
     method @Deprecated public void deactivate();
-    method public byte[] getBlob(int);
-    method public int getColumnCount();
-    method public int getColumnIndex(String);
-    method public int getColumnIndexOrThrow(String) throws java.lang.IllegalArgumentException;
-    method public String getColumnName(int);
+    method public byte[] getBlob(@IntRange(from=0) int);
+    method @IntRange(from=0) public int getColumnCount();
+    method @IntRange(from=0xffffffff) public int getColumnIndex(String);
+    method @IntRange(from=0) public int getColumnIndexOrThrow(String) throws java.lang.IllegalArgumentException;
+    method public String getColumnName(@IntRange(from=0) int);
     method public String[] getColumnNames();
-    method public int getCount();
-    method public double getDouble(int);
+    method @IntRange(from=0) public int getCount();
+    method public double getDouble(@IntRange(from=0) int);
     method public android.os.Bundle getExtras();
-    method public float getFloat(int);
-    method public int getInt(int);
-    method public long getLong(int);
+    method public float getFloat(@IntRange(from=0) int);
+    method public int getInt(@IntRange(from=0) int);
+    method public long getLong(@IntRange(from=0) int);
     method public android.net.Uri getNotificationUri();
     method @Nullable public default java.util.List<android.net.Uri> getNotificationUris();
-    method public int getPosition();
-    method public short getShort(int);
-    method public String getString(int);
-    method public int getType(int);
+    method @IntRange(from=0xffffffff) public int getPosition();
+    method public short getShort(@IntRange(from=0) int);
+    method public String getString(@IntRange(from=0) int);
+    method public int getType(@IntRange(from=0) int);
     method public boolean getWantsAllOnMoveCalls();
     method public boolean isAfterLast();
     method public boolean isBeforeFirst();
     method public boolean isClosed();
     method public boolean isFirst();
     method public boolean isLast();
-    method public boolean isNull(int);
+    method public boolean isNull(@IntRange(from=0) int);
     method public boolean move(int);
     method public boolean moveToFirst();
     method public boolean moveToLast();
     method public boolean moveToNext();
-    method public boolean moveToPosition(int);
+    method public boolean moveToPosition(@IntRange(from=0xffffffff) int);
     method public boolean moveToPrevious();
     method public void registerContentObserver(android.database.ContentObserver);
     method public void registerDataSetObserver(android.database.DataSetObserver);
@@ -13129,33 +13150,33 @@
     ctor @Deprecated public CursorWindow(boolean);
     method public boolean allocRow();
     method public void clear();
-    method public void copyStringToBuffer(int, int, android.database.CharArrayBuffer);
+    method public void copyStringToBuffer(@IntRange(from=0) int, @IntRange(from=0) int, android.database.CharArrayBuffer);
     method public int describeContents();
     method public void freeLastRow();
-    method public byte[] getBlob(int, int);
-    method public double getDouble(int, int);
-    method public float getFloat(int, int);
-    method public int getInt(int, int);
-    method public long getLong(int, int);
-    method public int getNumRows();
-    method public short getShort(int, int);
-    method public int getStartPosition();
-    method public String getString(int, int);
-    method public int getType(int, int);
-    method @Deprecated public boolean isBlob(int, int);
-    method @Deprecated public boolean isFloat(int, int);
-    method @Deprecated public boolean isLong(int, int);
-    method @Deprecated public boolean isNull(int, int);
-    method @Deprecated public boolean isString(int, int);
+    method public byte[] getBlob(@IntRange(from=0) int, @IntRange(from=0) int);
+    method public double getDouble(@IntRange(from=0) int, @IntRange(from=0) int);
+    method public float getFloat(@IntRange(from=0) int, @IntRange(from=0) int);
+    method public int getInt(@IntRange(from=0) int, @IntRange(from=0) int);
+    method public long getLong(@IntRange(from=0) int, @IntRange(from=0) int);
+    method @IntRange(from=0) public int getNumRows();
+    method public short getShort(@IntRange(from=0) int, @IntRange(from=0) int);
+    method @IntRange(from=0) public int getStartPosition();
+    method public String getString(@IntRange(from=0) int, @IntRange(from=0) int);
+    method public int getType(@IntRange(from=0) int, @IntRange(from=0) int);
+    method @Deprecated public boolean isBlob(@IntRange(from=0) int, @IntRange(from=0) int);
+    method @Deprecated public boolean isFloat(@IntRange(from=0) int, @IntRange(from=0) int);
+    method @Deprecated public boolean isLong(@IntRange(from=0) int, @IntRange(from=0) int);
+    method @Deprecated public boolean isNull(@IntRange(from=0) int, @IntRange(from=0) int);
+    method @Deprecated public boolean isString(@IntRange(from=0) int, @IntRange(from=0) int);
     method public static android.database.CursorWindow newFromParcel(android.os.Parcel);
     method protected void onAllReferencesReleased();
-    method public boolean putBlob(byte[], int, int);
-    method public boolean putDouble(double, int, int);
-    method public boolean putLong(long, int, int);
-    method public boolean putNull(int, int);
-    method public boolean putString(String, int, int);
-    method public boolean setNumColumns(int);
-    method public void setStartPosition(int);
+    method public boolean putBlob(byte[], @IntRange(from=0) int, @IntRange(from=0) int);
+    method public boolean putDouble(double, @IntRange(from=0) int, @IntRange(from=0) int);
+    method public boolean putLong(long, @IntRange(from=0) int, @IntRange(from=0) int);
+    method public boolean putNull(@IntRange(from=0) int, @IntRange(from=0) int);
+    method public boolean putString(String, @IntRange(from=0) int, @IntRange(from=0) int);
+    method public boolean setNumColumns(@IntRange(from=0) int);
+    method public void setStartPosition(@IntRange(from=0) int);
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.database.CursorWindow> CREATOR;
   }
@@ -14356,6 +14377,7 @@
     method public void drawColor(@ColorLong long, @NonNull android.graphics.BlendMode);
     method public void drawDoubleRoundRect(@NonNull android.graphics.RectF, float, float, @NonNull android.graphics.RectF, float, float, @NonNull android.graphics.Paint);
     method public void drawDoubleRoundRect(@NonNull android.graphics.RectF, @NonNull float[], @NonNull android.graphics.RectF, @NonNull float[], @NonNull android.graphics.Paint);
+    method public void drawGlyphs(@NonNull int[], @IntRange(from=0) int, @NonNull float[], @IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.graphics.fonts.Font, @NonNull android.graphics.Paint);
     method public void drawLine(float, float, float, float, @NonNull android.graphics.Paint);
     method public void drawLines(@NonNull @Size(multiple=4) float[], int, int, @NonNull android.graphics.Paint);
     method public void drawLines(@NonNull @Size(multiple=4) float[], @NonNull android.graphics.Paint);
@@ -16514,23 +16536,6 @@
 
 package android.graphics.text {
 
-  public class GlyphStyle {
-    ctor public GlyphStyle(@ColorInt int, @FloatRange(from=0) float, @FloatRange(from=0) float, @FloatRange(from=0) float, int);
-    ctor public GlyphStyle(@NonNull android.graphics.Paint);
-    method public void applyToPaint(@NonNull android.graphics.Paint);
-    method @ColorInt public int getColor();
-    method public int getFlags();
-    method @FloatRange(from=0) public float getFontSize();
-    method @FloatRange(from=0) public float getScaleX();
-    method @FloatRange(from=0) public float getSkewX();
-    method public void setColor(@ColorInt int);
-    method public void setFlags(int);
-    method public void setFontSize(@FloatRange(from=0) float);
-    method public void setFromPaint(@NonNull android.graphics.Paint);
-    method public void setScaleX(@FloatRange(from=0) float);
-    method public void setSkewX(@FloatRange(from=0) float);
-  }
-
   public class LineBreaker {
     method @NonNull public android.graphics.text.LineBreaker.Result computeLineBreaks(@NonNull android.graphics.text.MeasuredText, @NonNull android.graphics.text.LineBreaker.ParagraphConstraints, @IntRange(from=0) int);
     field public static final int BREAK_STRATEGY_BALANCED = 2; // 0x2
@@ -16592,20 +16597,19 @@
   }
 
   public final class PositionedGlyphs {
+    method public float getAdvance();
     method public float getAscent();
     method public float getDescent();
     method @NonNull public android.graphics.fonts.Font getFont(@IntRange(from=0) int);
     method @IntRange(from=0) public int getGlyphId(@IntRange(from=0) int);
-    method public float getOriginX();
-    method public float getOriginY();
-    method public float getPositionX(@IntRange(from=0) int);
-    method public float getPositionY(@IntRange(from=0) int);
-    method @NonNull public android.graphics.text.GlyphStyle getStyle();
-    method public float getTotalAdvance();
+    method public float getGlyphX(@IntRange(from=0) int);
+    method public float getGlyphY(@IntRange(from=0) int);
+    method public float getOffsetX();
+    method public float getOffsetY();
     method @IntRange(from=0) public int glyphCount();
   }
 
-  public class TextShaper {
+  public class TextRunShaper {
     method @NonNull public static android.graphics.text.PositionedGlyphs shapeTextRun(@NonNull char[], int, int, int, int, float, float, boolean, @NonNull android.graphics.Paint);
     method @NonNull public static android.graphics.text.PositionedGlyphs shapeTextRun(@NonNull CharSequence, int, int, int, int, float, float, boolean, @NonNull android.graphics.Paint);
   }
@@ -24081,9 +24085,13 @@
     method @IntRange(from=1, to=java.lang.Integer.MAX_VALUE) public int getMaxUpdates();
     method @FloatRange(from=0, to=java.lang.Float.MAX_VALUE) public float getMinUpdateDistanceMeters();
     method @IntRange(from=0) public long getMinUpdateIntervalMillis();
+    method public int getQuality();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.location.LocationRequest> CREATOR;
     field public static final long PASSIVE_INTERVAL = 9223372036854775807L; // 0x7fffffffffffffffL
+    field public static final int QUALITY_BALANCED_POWER_ACCURACY = 102; // 0x66
+    field public static final int QUALITY_HIGH_ACCURACY = 100; // 0x64
+    field public static final int QUALITY_LOW_POWER = 104; // 0x68
   }
 
   public static final class LocationRequest.Builder {
@@ -24096,6 +24104,7 @@
     method @NonNull public android.location.LocationRequest.Builder setMaxUpdates(@IntRange(from=1, to=java.lang.Integer.MAX_VALUE) int);
     method @NonNull public android.location.LocationRequest.Builder setMinUpdateDistanceMeters(@FloatRange(from=0, to=java.lang.Float.MAX_VALUE) float);
     method @NonNull public android.location.LocationRequest.Builder setMinUpdateIntervalMillis(@IntRange(from=0) long);
+    method @NonNull public android.location.LocationRequest.Builder setQuality(int);
   }
 
   public interface OnNmeaMessageListener {
@@ -25448,6 +25457,7 @@
 
   public static final class MediaCodec.CryptoInfo {
     ctor public MediaCodec.CryptoInfo();
+    method @NonNull public android.media.MediaCodec.CryptoInfo.Pattern getPattern();
     method public void set(int, @NonNull int[], @NonNull int[], @NonNull byte[], @NonNull byte[], int);
     method public void setPattern(android.media.MediaCodec.CryptoInfo.Pattern);
     field public byte[] iv;
@@ -26400,7 +26410,7 @@
     method public boolean containsKey(String);
     method public int describeContents();
     method public android.graphics.Bitmap getBitmap(String);
-    method @IntRange(from=0) public int getBitmapDimensionLimit();
+    method @IntRange(from=1) public int getBitmapDimensionLimit();
     method @NonNull public android.media.MediaDescription getDescription();
     method public long getLong(String);
     method public android.media.Rating getRating(String);
@@ -26450,7 +26460,7 @@
     method public android.media.MediaMetadata.Builder putRating(String, android.media.Rating);
     method public android.media.MediaMetadata.Builder putString(String, String);
     method public android.media.MediaMetadata.Builder putText(String, CharSequence);
-    method @NonNull public android.media.MediaMetadata.Builder setBitmapDimensionLimit(int);
+    method @NonNull public android.media.MediaMetadata.Builder setBitmapDimensionLimit(@IntRange(from=1) int);
   }
 
   @Deprecated public abstract class MediaMetadataEditor {
@@ -30245,9 +30255,12 @@
     method public int describeContents();
     method @NonNull public byte[] getKey();
     method @NonNull public String getName();
+    method @NonNull public static java.util.Set<java.lang.String> getSupportedAlgorithms();
     method public int getTruncationLengthBits();
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final String AUTH_AES_XCBC = "xcbc(aes)";
     field public static final String AUTH_CRYPT_AES_GCM = "rfc4106(gcm(aes))";
+    field public static final String AUTH_CRYPT_CHACHA20_POLY1305 = "rfc7539esp(chacha20,poly1305)";
     field public static final String AUTH_HMAC_MD5 = "hmac(md5)";
     field public static final String AUTH_HMAC_SHA1 = "hmac(sha1)";
     field public static final String AUTH_HMAC_SHA256 = "hmac(sha256)";
@@ -30255,6 +30268,7 @@
     field public static final String AUTH_HMAC_SHA512 = "hmac(sha512)";
     field @NonNull public static final android.os.Parcelable.Creator<android.net.IpSecAlgorithm> CREATOR;
     field public static final String CRYPT_AES_CBC = "cbc(aes)";
+    field public static final String CRYPT_AES_CTR = "rfc3686(ctr(aes))";
   }
 
   public final class IpSecManager {
@@ -31771,6 +31785,7 @@
     method @IntRange(from=0) public int getPriority();
     method public int getPriorityGroup();
     method @Nullable public String getSsid();
+    method public int getSubscriptionId();
     method public boolean isAppInteractionRequired();
     method public boolean isCredentialSharedWithUser();
     method public boolean isEnhancedOpen();
@@ -31799,6 +31814,7 @@
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setPriority(@IntRange(from=0) int);
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setPriorityGroup(int);
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setSsid(@NonNull String);
+    method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setSubscriptionId(int);
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setUntrusted(boolean);
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setWapiEnterpriseConfig(@NonNull android.net.wifi.WifiEnterpriseConfig);
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setWapiPassphrase(@NonNull String);
@@ -31840,6 +31856,7 @@
     method public int getMaxServiceNameLength();
     method public int getMaxServiceSpecificInfoLength();
     method public int getSupportedCipherSuites();
+    method public boolean isInstantCommunicationModeSupported();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.aware.Characteristics> CREATOR;
     field public static final int WIFI_AWARE_CIPHER_SUITE_NCS_SK_128 = 1; // 0x1
@@ -31861,7 +31878,7 @@
     method public void onPublishStarted(@NonNull android.net.wifi.aware.PublishDiscoverySession);
     method public void onServiceDiscovered(android.net.wifi.aware.PeerHandle, byte[], java.util.List<byte[]>);
     method public void onServiceDiscoveredWithinRange(android.net.wifi.aware.PeerHandle, byte[], java.util.List<byte[]>, int);
-    method public void onServiceLost(@NonNull android.net.wifi.aware.PeerHandle);
+    method public void onServiceLost(@NonNull android.net.wifi.aware.PeerHandle, int);
     method public void onSessionConfigFailed();
     method public void onSessionConfigUpdated();
     method public void onSessionTerminated();
@@ -31938,9 +31955,12 @@
     method public android.net.wifi.aware.Characteristics getCharacteristics();
     method public boolean isAvailable();
     method public boolean isDeviceAttached();
+    method public boolean isInstantCommunicationModeEnabled();
     field public static final String ACTION_WIFI_AWARE_STATE_CHANGED = "android.net.wifi.aware.action.WIFI_AWARE_STATE_CHANGED";
     field public static final int WIFI_AWARE_DATA_PATH_ROLE_INITIATOR = 0; // 0x0
     field public static final int WIFI_AWARE_DATA_PATH_ROLE_RESPONDER = 1; // 0x1
+    field public static final int WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE = 1; // 0x1
+    field public static final int WIFI_AWARE_DISCOVERY_LOST_REASON_UNKNOWN = 0; // 0x0
   }
 
   public final class WifiAwareNetworkInfo implements android.os.Parcelable android.net.TransportInfo {
@@ -32080,13 +32100,13 @@
     method public String getFriendlyName();
     method @Nullable public long[] getMatchAllOis();
     method @Nullable public long[] getMatchAnyOis();
-    method @Nullable public String[] getOtherHomePartners();
+    method @NonNull public java.util.Collection<java.lang.String> getOtherHomePartnersList();
     method public long[] getRoamingConsortiumOis();
     method public void setFqdn(String);
     method public void setFriendlyName(String);
     method public void setMatchAllOis(@Nullable long[]);
     method public void setMatchAnyOis(@Nullable long[]);
-    method public void setOtherHomePartners(@Nullable String[]);
+    method public void setOtherHomePartnersList(@NonNull java.util.Collection<java.lang.String>);
     method public void setRoamingConsortiumOis(long[]);
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.HomeSp> CREATOR;
@@ -40377,7 +40397,7 @@
     field public static final android.net.Uri INTERNAL_CONTENT_URI;
   }
 
-  public static final class MediaStore.Audio.Artists.Albums implements android.provider.MediaStore.Audio.AlbumColumns {
+  public static final class MediaStore.Audio.Artists.Albums implements android.provider.BaseColumns android.provider.MediaStore.Audio.AlbumColumns {
     ctor public MediaStore.Audio.Artists.Albums();
     method public static android.net.Uri getContentUri(String, long);
   }
@@ -43285,8 +43305,16 @@
     method @NonNull public java.util.Map<android.view.autofill.AutofillId,android.service.autofill.FieldClassification> getFieldsClassification();
     method @NonNull public java.util.Set<java.lang.String> getIgnoredDatasetIds();
     method @NonNull public java.util.Map<android.view.autofill.AutofillId,java.util.Set<java.lang.String>> getManuallyEnteredField();
+    method public int getNoSaveReason();
     method @NonNull public java.util.Set<java.lang.String> getSelectedDatasetIds();
     method public int getType();
+    field public static final int NO_SAVE_REASON_DATASET_MATCH = 6; // 0x6
+    field public static final int NO_SAVE_REASON_FIELD_VALIDATION_FAILED = 5; // 0x5
+    field public static final int NO_SAVE_REASON_HAS_EMPTY_REQUIRED = 3; // 0x3
+    field public static final int NO_SAVE_REASON_NONE = 0; // 0x0
+    field public static final int NO_SAVE_REASON_NO_SAVE_INFO = 1; // 0x1
+    field public static final int NO_SAVE_REASON_NO_VALUE_CHANGED = 4; // 0x4
+    field public static final int NO_SAVE_REASON_WITH_DELAY_SAVE_FLAG = 2; // 0x2
     field public static final int TYPE_AUTHENTICATION_SELECTED = 2; // 0x2
     field public static final int TYPE_CONTEXT_COMMITTED = 4; // 0x4
     field public static final int TYPE_DATASETS_SHOWN = 5; // 0x5
@@ -44090,11 +44118,13 @@
     method public long getLastAudiblyAlertedMillis();
     method public String getOverrideGroupKey();
     method public int getRank();
+    method @Nullable public android.content.pm.ShortcutInfo getShortcutInfo();
     method @NonNull public java.util.List<android.app.Notification.Action> getSmartActions();
     method @NonNull public java.util.List<java.lang.CharSequence> getSmartReplies();
     method public int getSuppressedVisualEffects();
     method public int getUserSentiment();
     method public boolean isAmbient();
+    method public boolean isConversation();
     method public boolean isSuspended();
     method public boolean matchesInterruptionFilter();
     field public static final int USER_SENTIMENT_NEGATIVE = -1; // 0xffffffff
@@ -47056,6 +47086,7 @@
   }
 
   public static final class CarrierConfigManager.Ims {
+    field public static final String KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL = "ims.ims_single_registration_required_bool";
     field public static final String KEY_PREFIX = "ims.";
     field public static final String KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT = "ims.wifi_off_deferring_time_millis_int";
   }
@@ -48060,6 +48091,7 @@
   }
 
   public class SignalStrength implements android.os.Parcelable {
+    ctor public SignalStrength(@NonNull android.telephony.SignalStrength);
     method public int describeContents();
     method @Deprecated public int getCdmaDbm();
     method @Deprecated public int getCdmaEcio();
@@ -48087,6 +48119,7 @@
     method @NonNull public android.os.Bundle getCarrierConfigValues();
     method @Deprecated public static android.telephony.SmsManager getDefault();
     method public static int getDefaultSmsSubscriptionId();
+    method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getSmsCapacityOnIcc();
     method @Deprecated public static android.telephony.SmsManager getSmsManagerForSubscriptionId(int);
     method @RequiresPermission(android.Manifest.permission.SMS_FINANCIAL_TRANSACTIONS) public void getSmsMessagesForFinancialApp(android.os.Bundle, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.SmsManager.FinancialSmsCallback);
     method @Nullable @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getSmscAddress();
@@ -48429,6 +48462,7 @@
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getDeviceSoftwareVersion();
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>> getEmergencyNumberList();
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>> getEmergencyNumberList(int);
+    method @NonNull @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<java.lang.String> getEquivalentHomePlmns();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String[] getForbiddenPlmns();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getGroupIdLevel1();
     method public String getIccAuthentication(int, int, String);
@@ -48453,7 +48487,7 @@
     method @Deprecated 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(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_COARSE_LOCATION}) public android.telephony.ServiceState getServiceState();
+    method @Nullable @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();
@@ -48476,7 +48510,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getVoiceMailAlphaTag();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getVoiceMailNumber();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getVoiceNetworkType();
-    method public android.net.Uri getVoicemailRingtoneUri(android.telecom.PhoneAccountHandle);
+    method @Nullable public android.net.Uri getVoicemailRingtoneUri(android.telecom.PhoneAccountHandle);
     method public boolean hasCarrierPrivileges();
     method public boolean hasIccCard();
     method @Deprecated public boolean iccCloseLogicalChannel(int);
@@ -48565,6 +48599,9 @@
     field public static final int DATA_ENABLED_REASON_USER = 0; // 0x0
     field public static final int DATA_SUSPENDED = 3; // 0x3
     field public static final int DATA_UNKNOWN = -1; // 0xffffffff
+    field public static final int ERI_FLASH = 2; // 0x2
+    field public static final int ERI_OFF = 1; // 0x1
+    field public static final int ERI_ON = 0; // 0x0
     field public static final String EXTRA_ACTIVE_SIM_SUPPORTED_COUNT = "android.telephony.extra.ACTIVE_SIM_SUPPORTED_COUNT";
     field public static final String EXTRA_CALL_VOICEMAIL_INTENT = "android.telephony.extra.CALL_VOICEMAIL_INTENT";
     field public static final String EXTRA_CARRIER_ID = "android.telephony.extra.CARRIER_ID";
@@ -50041,10 +50078,6 @@
     method @NonNull public android.text.StaticLayout.Builder setUseLineSpacingFromFallbacks(boolean);
   }
 
-  public class StyledTextShaper {
-    method @NonNull public static java.util.List<android.graphics.text.PositionedGlyphs> shapeText(@NonNull CharSequence, int, int, @NonNull android.text.TextDirectionHeuristic, @NonNull android.text.TextPaint);
-  }
-
   public interface TextDirectionHeuristic {
     method public boolean isRtl(char[], int, int);
     method public boolean isRtl(CharSequence, int, int);
@@ -50074,6 +50107,14 @@
     field @Px public float underlineThickness;
   }
 
+  public class TextShaper {
+    method public static void shapeText(@NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.text.TextDirectionHeuristic, @NonNull android.text.TextPaint, @NonNull android.text.TextShaper.GlyphsConsumer);
+  }
+
+  public static interface TextShaper.GlyphsConsumer {
+    method public void accept(@IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.graphics.text.PositionedGlyphs, @NonNull android.text.TextPaint);
+  }
+
   public class TextUtils {
     method @Deprecated public static CharSequence commaEllipsize(CharSequence, android.text.TextPaint, float, String, String);
     method public static CharSequence concat(java.lang.CharSequence...);
@@ -50872,6 +50913,7 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.text.style.SuggestionSpan> CREATOR;
     field public static final int FLAG_AUTO_CORRECTION = 4; // 0x4
     field public static final int FLAG_EASY_CORRECT = 1; // 0x1
+    field public static final int FLAG_GRAMMAR_ERROR = 8; // 0x8
     field public static final int FLAG_MISSPELLED = 2; // 0x2
     field public static final int SUGGESTIONS_MAX_SIZE = 5; // 0x5
     field @Deprecated public static final String SUGGESTION_SPAN_PICKED_AFTER = "after";
@@ -53709,11 +53751,12 @@
     method @Nullable public android.net.Uri getLinkUri();
     method public int getSource();
     field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1
-    field public static final int SOURCE_AUTOFILL = 3; // 0x3
-    field public static final int SOURCE_CLIPBOARD = 0; // 0x0
-    field public static final int SOURCE_DRAG_AND_DROP = 2; // 0x2
-    field public static final int SOURCE_INPUT_METHOD = 1; // 0x1
-    field public static final int SOURCE_PROCESS_TEXT = 4; // 0x4
+    field public static final int SOURCE_APP = 0; // 0x0
+    field public static final int SOURCE_AUTOFILL = 4; // 0x4
+    field public static final int SOURCE_CLIPBOARD = 1; // 0x1
+    field public static final int SOURCE_DRAG_AND_DROP = 3; // 0x3
+    field public static final int SOURCE_INPUT_METHOD = 2; // 0x2
+    field public static final int SOURCE_PROCESS_TEXT = 5; // 0x5
   }
 
   public static final class OnReceiveContentCallback.Payload.Builder {
@@ -57370,6 +57413,7 @@
     method public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int);
     method public android.os.Handler getHandler();
     method public CharSequence getSelectedText(int);
+    method @Nullable public default android.view.inputmethod.SurroundingText getSurroundingText(@IntRange(from=0) int, @IntRange(from=0) int, int);
     method public CharSequence getTextAfterCursor(int, int);
     method public CharSequence getTextBeforeCursor(int, int);
     method public boolean performContextMenuAction(int);
@@ -57578,6 +57622,17 @@
     method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeNameResId(int);
   }
 
+  public final class SurroundingText implements android.os.Parcelable {
+    ctor public SurroundingText(@NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0xffffffff) int);
+    method public int describeContents();
+    method @IntRange(from=0xffffffff) public int getOffset();
+    method @IntRange(from=0) public int getSelectionEnd();
+    method @IntRange(from=0) public int getSelectionStart();
+    method @NonNull public CharSequence getText();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.SurroundingText> CREATOR;
+  }
+
 }
 
 package android.view.inspector {
@@ -58246,6 +58301,7 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.view.textservice.SuggestionsInfo> CREATOR;
     field public static final int RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS = 4; // 0x4
     field public static final int RESULT_ATTR_IN_THE_DICTIONARY = 1; // 0x1
+    field public static final int RESULT_ATTR_LOOKS_LIKE_GRAMMAR_ERROR = 8; // 0x8
     field public static final int RESULT_ATTR_LOOKS_LIKE_TYPO = 2; // 0x2
   }
 
diff --git a/api/gen_combined_removed_dex.sh b/api/gen_combined_removed_dex.sh
new file mode 100755
index 0000000..9225fe8
--- /dev/null
+++ b/api/gen_combined_removed_dex.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+metalava_path="$1"
+tmp_dir="$2"
+shift 2
+
+# Convert each removed.txt to the "dex format" equivalent, and print all output.
+for f in "$@"; do
+    "$metalava_path" --no-banner "$f" --dex-api "${tmp_dir}/tmp"
+    cat "${tmp_dir}/tmp"
+done
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index 19f348c..872f1f2 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -1,10 +1,20 @@
 // Signature format: 2.0
 package android.app {
 
+  public class ActivityManager {
+    method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void addHomeVisibilityListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.HomeVisibilityListener);
+    method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void removeHomeVisibilityListener(@NonNull android.app.HomeVisibilityListener);
+  }
+
   public class AppOpsManager {
     field public static final String OPSTR_NO_ISOLATED_STORAGE = "android:no_isolated_storage";
   }
 
+  public abstract class HomeVisibilityListener {
+    ctor public HomeVisibilityListener();
+    method public abstract void onHomeVisibilityChanged(boolean);
+  }
+
   public class NotificationManager {
     method public boolean hasEnabledNotificationListener(@NonNull String, @NonNull android.os.UserHandle);
     field public static final String ACTION_NOTIFICATION_LISTENER_ENABLED_CHANGED = "android.app.action.NOTIFICATION_LISTENER_ENABLED_CHANGED";
@@ -53,6 +63,10 @@
     field public static final int FLAG_FROM_KEY = 4096; // 0x1000
   }
 
+  public class MediaMetadataRetriever implements java.lang.AutoCloseable {
+    field public static final int METADATA_KEY_VIDEO_CODEC_MIME_TYPE = 40; // 0x28
+  }
+
   @Deprecated public final class MediaParceledListSlice<T extends android.os.Parcelable> implements android.os.Parcelable {
     ctor @Deprecated public MediaParceledListSlice(@NonNull java.util.List<T>);
     method @Deprecated public int describeContents();
@@ -82,13 +96,17 @@
   public final class MediaSessionManager {
     method public void addOnActiveSessionsChangedListener(@NonNull android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, @Nullable android.content.ComponentName, int, @Nullable android.os.Handler);
     method public void dispatchMediaKeyEventAsSystemService(@NonNull android.view.KeyEvent);
-    method public boolean dispatchMediaKeyEventAsSystemService(@NonNull android.media.session.MediaSession.Token, @NonNull android.view.KeyEvent);
+    method public boolean dispatchMediaKeyEventToSessionAsSystemService(@NonNull android.view.KeyEvent, @NonNull android.media.session.MediaSession.Token);
     method public void dispatchVolumeKeyEventAsSystemService(@NonNull android.view.KeyEvent, int);
-    method public void dispatchVolumeKeyEventAsSystemService(@NonNull android.media.session.MediaSession.Token, @NonNull android.view.KeyEvent);
+    method public void dispatchVolumeKeyEventToSessionAsSystemService(@NonNull android.view.KeyEvent, @NonNull android.media.session.MediaSession.Token);
     field public static final int RESULT_MEDIA_KEY_HANDLED = 1; // 0x1
     field public static final int RESULT_MEDIA_KEY_NOT_HANDLED = 0; // 0x0
   }
 
+  public final class PlaybackState implements android.os.Parcelable {
+    method public boolean isActiveState();
+  }
+
 }
 
 package android.net {
@@ -139,8 +157,6 @@
 
   public interface Parcelable {
     method public default int getStability();
-    field public static final int PARCELABLE_STABILITY_LOCAL = 0; // 0x0
-    field public static final int PARCELABLE_STABILITY_VINTF = 1; // 0x1
   }
 
   public class StatsFrameworkInitializer {
@@ -174,6 +190,18 @@
 
 }
 
+package android.telephony {
+
+  public abstract class CellSignalStrength {
+    method public static int getNumSignalStrengthLevels();
+  }
+
+  public class TelephonyManager {
+    method @NonNull public static int[] getAllNetworkTypes();
+  }
+
+}
+
 package android.util {
 
   public class AtomicFile {
diff --git a/api/system-current.txt b/api/system-current.txt
index f2ec297..230ced8 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -38,6 +38,7 @@
     field public static final String BIND_EXTERNAL_STORAGE_SERVICE = "android.permission.BIND_EXTERNAL_STORAGE_SERVICE";
     field public static final String BIND_IMS_SERVICE = "android.permission.BIND_IMS_SERVICE";
     field public static final String BIND_KEYGUARD_APPWIDGET = "android.permission.BIND_KEYGUARD_APPWIDGET";
+    field public static final String BIND_MUSIC_RECOGNITION_SERVICE = "android.permission.BIND_MUSIC_RECOGNITION_SERVICE";
     field public static final String BIND_NETWORK_RECOMMENDATION_SERVICE = "android.permission.BIND_NETWORK_RECOMMENDATION_SERVICE";
     field public static final String BIND_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE";
     field public static final String BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE = "android.permission.BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE";
@@ -120,6 +121,8 @@
     field public static final String MANAGE_DEBUGGING = "android.permission.MANAGE_DEBUGGING";
     field public static final String MANAGE_FACTORY_RESET_PROTECTION = "android.permission.MANAGE_FACTORY_RESET_PROTECTION";
     field public static final String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS";
+    field public static final String MANAGE_MUSIC_RECOGNITION = "android.permission.MANAGE_MUSIC_RECOGNITION";
+    field public static final String MANAGE_NOTIFICATION_LISTENERS = "android.permission.MANAGE_NOTIFICATION_LISTENERS";
     field public static final String MANAGE_ONE_TIME_PERMISSION_SESSIONS = "android.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS";
     field public static final String MANAGE_ROLE_HOLDERS = "android.permission.MANAGE_ROLE_HOLDERS";
     field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS";
@@ -222,6 +225,7 @@
     field public static final String SET_WALLPAPER_COMPONENT = "android.permission.SET_WALLPAPER_COMPONENT";
     field public static final String SHOW_KEYGUARD_MESSAGE = "android.permission.SHOW_KEYGUARD_MESSAGE";
     field public static final String SHUTDOWN = "android.permission.SHUTDOWN";
+    field public static final String START_ACTIVITIES_FROM_BACKGROUND = "android.permission.START_ACTIVITIES_FROM_BACKGROUND";
     field public static final String STATUS_BAR_SERVICE = "android.permission.STATUS_BAR_SERVICE";
     field public static final String STOP_APP_SWITCHES = "android.permission.STOP_APP_SWITCHES";
     field public static final String SUBSTITUTE_NOTIFICATION_APP_NAME = "android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME";
@@ -306,6 +310,7 @@
     field public static final int config_helpPackageNameKey = 17039387; // 0x104001b
     field public static final int config_helpPackageNameValue = 17039388; // 0x104001c
     field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028
+    field public static final int config_systemAutomotiveProjection = 17039402; // 0x104002a
     field public static final int config_systemGallery = 17039399; // 0x1040027
     field public static final int config_systemVideoCall = 17039401; // 0x1040029
   }
@@ -591,7 +596,7 @@
 
   public class BroadcastOptions {
     method public static android.app.BroadcastOptions makeBasic();
-    method @RequiresPermission("android.permission.START_ACTIVITIES_FROM_BACKGROUND") public void setBackgroundActivityStartsAllowed(boolean);
+    method @RequiresPermission(android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND) public void setBackgroundActivityStartsAllowed(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();
@@ -672,8 +677,10 @@
   public class NotificationManager {
     method @NonNull public java.util.List<java.lang.String> getAllowedAssistantAdjustments();
     method @Nullable public android.content.ComponentName getAllowedNotificationAssistant();
+    method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public java.util.List<android.content.ComponentName> getEnabledNotificationListeners();
     method public boolean isNotificationAssistantAccessGranted(@NonNull android.content.ComponentName);
     method public void setNotificationAssistantAccessGranted(@Nullable android.content.ComponentName, boolean);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public void setNotificationListenerAccessGranted(@NonNull android.content.ComponentName, boolean);
     field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL = "android.app.action.CLOSE_NOTIFICATION_HANDLER_PANEL";
     field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_OPEN_NOTIFICATION_HANDLER_PANEL = "android.app.action.OPEN_NOTIFICATION_HANDLER_PANEL";
     field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL = "android.app.action.TOGGLE_NOTIFICATION_HANDLER_PANEL";
@@ -910,6 +917,7 @@
     field public static final int PROVISIONING_TRIGGER_QR_CODE = 2; // 0x2
     field public static final int PROVISIONING_TRIGGER_UNSPECIFIED = 0; // 0x0
     field public static final int STATE_USER_PROFILE_COMPLETE = 4; // 0x4
+    field public static final int STATE_USER_PROFILE_FINALIZED = 5; // 0x5
     field public static final int STATE_USER_SETUP_COMPLETE = 2; // 0x2
     field public static final int STATE_USER_SETUP_FINALIZED = 3; // 0x3
     field public static final int STATE_USER_SETUP_INCOMPLETE = 1; // 0x1
@@ -1446,20 +1454,20 @@
 package android.app.usage {
 
   public final class CacheQuotaHint implements android.os.Parcelable {
-    ctor public CacheQuotaHint(android.app.usage.CacheQuotaHint.Builder);
+    ctor public CacheQuotaHint(@NonNull android.app.usage.CacheQuotaHint.Builder);
     method public int describeContents();
     method public long getQuota();
     method public int getUid();
-    method public android.app.usage.UsageStats getUsageStats();
-    method public String getVolumeUuid();
-    method public void writeToParcel(android.os.Parcel, int);
+    method @Nullable public android.app.usage.UsageStats getUsageStats();
+    method @Nullable public String getVolumeUuid();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.usage.CacheQuotaHint> CREATOR;
     field public static final long QUOTA_NOT_SET = -1L; // 0xffffffffffffffffL
   }
 
   public static final class CacheQuotaHint.Builder {
     ctor public CacheQuotaHint.Builder();
-    ctor public CacheQuotaHint.Builder(android.app.usage.CacheQuotaHint);
+    ctor public CacheQuotaHint.Builder(@NonNull android.app.usage.CacheQuotaHint);
     method @NonNull public android.app.usage.CacheQuotaHint build();
     method @NonNull public android.app.usage.CacheQuotaHint.Builder setQuota(long);
     method @NonNull public android.app.usage.CacheQuotaHint.Builder setUid(int);
@@ -1797,6 +1805,7 @@
     field public static final String APP_PREDICTION_SERVICE = "app_prediction";
     field public static final String BACKUP_SERVICE = "backup";
     field public static final String BATTERY_STATS_SERVICE = "batterystats";
+    field public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 1048576; // 0x100000
     field public static final String BUGREPORT_SERVICE = "bugreport";
     field public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions";
     field public static final String CONTEXTHUB_SERVICE = "contexthub";
@@ -4169,9 +4178,8 @@
     method @Deprecated public long getInterval();
     method @Deprecated public int getNumUpdates();
     method @Deprecated @NonNull public String getProvider();
-    method public int getQuality();
     method @Deprecated public float getSmallestDisplacement();
-    method @Nullable public android.os.WorkSource getWorkSource();
+    method @NonNull public android.os.WorkSource getWorkSource();
     method public boolean isHiddenFromAppOps();
     method public boolean isLocationSettingsIgnored();
     method public boolean isLowPower();
@@ -4188,11 +4196,11 @@
     method @Deprecated @NonNull public android.location.LocationRequest setQuality(int);
     method @Deprecated @NonNull public android.location.LocationRequest setSmallestDisplacement(float);
     method @Deprecated public void setWorkSource(@Nullable android.os.WorkSource);
-    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
-    field public static final int POWER_HIGH = 203; // 0xcb
-    field public static final int POWER_LOW = 201; // 0xc9
+    field @Deprecated public static final int ACCURACY_BLOCK = 102; // 0x66
+    field @Deprecated public static final int ACCURACY_CITY = 104; // 0x68
+    field @Deprecated public static final int ACCURACY_FINE = 100; // 0x64
+    field @Deprecated public static final int POWER_HIGH = 203; // 0xcb
+    field @Deprecated public static final int POWER_LOW = 201; // 0xc9
     field @Deprecated public static final int POWER_NONE = 200; // 0xc8
   }
 
@@ -4200,7 +4208,6 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_APP_OPS_STATS) public android.location.LocationRequest.Builder setHiddenFromAppOps(boolean);
     method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LocationRequest.Builder setLocationSettingsIgnored(boolean);
     method @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public android.location.LocationRequest.Builder setLowPower(boolean);
-    method @NonNull public android.location.LocationRequest.Builder setQuality(int);
     method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.location.LocationRequest.Builder setWorkSource(@Nullable android.os.WorkSource);
   }
 
@@ -4617,6 +4624,58 @@
 
 }
 
+package android.media.musicrecognition {
+
+  public class MusicRecognitionManager {
+    method @RequiresPermission(android.Manifest.permission.MANAGE_MUSIC_RECOGNITION) public void beginStreamingSearch(@NonNull android.media.musicrecognition.RecognitionRequest, @NonNull java.util.concurrent.Executor, @NonNull android.media.musicrecognition.MusicRecognitionManager.RecognitionCallback);
+    field public static final int RECOGNITION_FAILED_AUDIO_UNAVAILABLE = 7; // 0x7
+    field public static final int RECOGNITION_FAILED_NOT_FOUND = 1; // 0x1
+    field public static final int RECOGNITION_FAILED_NO_CONNECTIVITY = 2; // 0x2
+    field public static final int RECOGNITION_FAILED_SERVICE_KILLED = 5; // 0x5
+    field public static final int RECOGNITION_FAILED_SERVICE_UNAVAILABLE = 3; // 0x3
+    field public static final int RECOGNITION_FAILED_TIMEOUT = 6; // 0x6
+    field public static final int RECOGNITION_FAILED_UNKNOWN = -1; // 0xffffffff
+  }
+
+  public static interface MusicRecognitionManager.RecognitionCallback {
+    method public void onAudioStreamClosed();
+    method public void onRecognitionFailed(@NonNull android.media.musicrecognition.RecognitionRequest, int);
+    method public void onRecognitionSucceeded(@NonNull android.media.musicrecognition.RecognitionRequest, @NonNull android.media.MediaMetadata, @Nullable android.os.Bundle);
+  }
+
+  public abstract class MusicRecognitionService extends android.app.Service {
+    ctor public MusicRecognitionService();
+    method public abstract void onRecognize(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @NonNull android.media.musicrecognition.MusicRecognitionService.Callback);
+  }
+
+  public static interface MusicRecognitionService.Callback {
+    method public void onRecognitionFailed(int);
+    method public void onRecognitionSucceeded(@NonNull android.media.MediaMetadata, @Nullable android.os.Bundle);
+  }
+
+  public final class RecognitionRequest implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public android.media.AudioAttributes getAudioAttributes();
+    method @NonNull public android.media.AudioFormat getAudioFormat();
+    method public int getCaptureSession();
+    method public int getIgnoreBeginningFrames();
+    method public int getMaxAudioLengthSeconds();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.media.musicrecognition.RecognitionRequest> CREATOR;
+  }
+
+  public static final class RecognitionRequest.Builder {
+    ctor public RecognitionRequest.Builder();
+    method @NonNull public android.media.musicrecognition.RecognitionRequest build();
+    method @NonNull public android.media.musicrecognition.RecognitionRequest.Builder setAudioAttributes(@NonNull android.media.AudioAttributes);
+    method @NonNull public android.media.musicrecognition.RecognitionRequest.Builder setAudioFormat(@NonNull android.media.AudioFormat);
+    method @NonNull public android.media.musicrecognition.RecognitionRequest.Builder setCaptureSession(int);
+    method @NonNull public android.media.musicrecognition.RecognitionRequest.Builder setIgnoreBeginningFrames(int);
+    method @NonNull public android.media.musicrecognition.RecognitionRequest.Builder setMaxAudioLengthSeconds(int);
+  }
+
+}
+
 package android.media.session {
 
   public final class MediaSessionManager {
@@ -4715,6 +4774,22 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.DvbDeviceInfo> CREATOR;
   }
 
+  public final class TunedInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getAppTag();
+    method public int getAppType();
+    method @Nullable public android.net.Uri getChannelUri();
+    method @NonNull public String getInputId();
+    method public boolean isForeground();
+    method public boolean isRecordingSession();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field public static final int APP_TAG_SELF = 0; // 0x0
+    field public static final int APP_TYPE_NON_SYSTEM = 3; // 0x3
+    field public static final int APP_TYPE_SELF = 1; // 0x1
+    field public static final int APP_TYPE_SYSTEM = 2; // 0x2
+    field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.TunedInfo> CREATOR;
+  }
+
   public final class TvContentRatingSystemInfo implements android.os.Parcelable {
     method public static android.media.tv.TvContentRatingSystemInfo createTvContentRatingSystemInfo(int, android.content.pm.ApplicationInfo);
     method public int describeContents();
@@ -4830,6 +4905,7 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS) public void addBlockedRating(@NonNull android.media.tv.TvContentRating);
     method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public boolean captureFrame(String, android.view.Surface, android.media.tv.TvStreamConfig);
     method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public java.util.List<android.media.tv.TvStreamConfig> getAvailableTvStreamConfigList(String);
+    method @NonNull @RequiresPermission("com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS") public java.util.List<android.media.tv.TunedInfo> getCurrentTunedInfos();
     method @NonNull @RequiresPermission("android.permission.DVB_DEVICE") public java.util.List<android.media.tv.DvbDeviceInfo> getDvbDeviceList();
     method @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE) public java.util.List<android.media.tv.TvInputHardwareInfo> getHardwareList();
     method @RequiresPermission(android.Manifest.permission.READ_CONTENT_RATING_SYSTEMS) public java.util.List<android.media.tv.TvContentRatingSystemInfo> getTvContentRatingSystemList();
@@ -4855,6 +4931,10 @@
     method public abstract void onStreamConfigChanged(android.media.tv.TvStreamConfig[]);
   }
 
+  public abstract static class TvInputManager.TvInputCallback {
+    method public void onCurrentTunedInfosUpdated(@NonNull java.util.List<android.media.tv.TunedInfo>);
+  }
+
   public abstract class TvInputService extends android.app.Service {
     method @Nullable public android.media.tv.TvInputInfo onHardwareAdded(android.media.tv.TvInputHardwareInfo);
     method @Nullable public String onHardwareRemoved(android.media.tv.TvInputHardwareInfo);
@@ -4982,6 +5062,7 @@
     method @Nullable public android.media.tv.tuner.DemuxCapabilities getDemuxCapabilities();
     method @Nullable public android.media.tv.tuner.frontend.FrontendInfo getFrontendInfo();
     method @Nullable public android.media.tv.tuner.frontend.FrontendStatus getFrontendStatus(@NonNull int[]);
+    method public int linkFrontendToCiCam(int);
     method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_DESCRAMBLER) public android.media.tv.tuner.Descrambler openDescrambler();
     method @Nullable public android.media.tv.tuner.dvr.DvrPlayback openDvrPlayback(long, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.dvr.OnPlaybackStatusChangedListener);
     method @Nullable public android.media.tv.tuner.dvr.DvrRecorder openDvrRecorder(long, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.dvr.OnRecordStatusChangedListener);
@@ -4995,9 +5076,14 @@
     method public void setResourceLostListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.Tuner.OnResourceLostListener);
     method public void shareFrontendFromTuner(@NonNull android.media.tv.tuner.Tuner);
     method public int tune(@NonNull android.media.tv.tuner.frontend.FrontendSettings);
+    method public int unlinkFrontendToCiCam(int);
     method public void updateResourcePriority(int, int);
     field public static final int INVALID_AV_SYNC_ID = -1; // 0xffffffff
     field public static final int INVALID_FILTER_ID = -1; // 0xffffffff
+    field public static final long INVALID_FILTER_ID_64BIT = -1L; // 0xffffffffffffffffL
+    field public static final int INVALID_FRONTEND_SETTING_FREQUENCY = -1; // 0xffffffff
+    field public static final int INVALID_LTS_ID = -1; // 0xffffffff
+    field public static final int INVALID_MMTP_RECORD_EVENT_MPT_SEQUENCE_NUM = -1; // 0xffffffff
     field public static final int INVALID_STREAM_ID = 65535; // 0xffff
     field public static final long INVALID_TIMESTAMP = -1L; // 0xffffffffffffffffL
     field public static final int INVALID_TS_PID = 65535; // 0xffff
@@ -5017,15 +5103,22 @@
     method public void onResourceLost(@NonNull android.media.tv.tuner.Tuner);
   }
 
+  public final class TunerVersionChecker {
+    method public static int getTunerVersion();
+    field public static final int TUNER_VERSION_1_0 = 65536; // 0x10000
+    field public static final int TUNER_VERSION_1_1 = 65537; // 0x10001
+    field public static final int TUNER_VERSION_UNKNOWN = 0; // 0x0
+  }
+
 }
 
 package android.media.tv.tuner.dvr {
 
   public class DvrPlayback implements java.lang.AutoCloseable {
-    method public int attachFilter(@NonNull android.media.tv.tuner.filter.Filter);
+    method @Deprecated public int attachFilter(@NonNull android.media.tv.tuner.filter.Filter);
     method public void close();
     method public int configure(@NonNull android.media.tv.tuner.dvr.DvrSettings);
-    method public int detachFilter(@NonNull android.media.tv.tuner.filter.Filter);
+    method @Deprecated public int detachFilter(@NonNull android.media.tv.tuner.filter.Filter);
     method public int flush();
     method public long read(long);
     method public long read(@NonNull byte[], long, long);
@@ -5119,12 +5212,45 @@
 
   public class AvSettings extends android.media.tv.tuner.filter.Settings {
     method @NonNull public static android.media.tv.tuner.filter.AvSettings.Builder builder(int, boolean);
+    method public int getAudioStreamType();
+    method public int getVideoStreamType();
     method public boolean isPassthrough();
+    field public static final int AUDIO_STREAM_TYPE_AAC = 6; // 0x6
+    field public static final int AUDIO_STREAM_TYPE_AC3 = 7; // 0x7
+    field public static final int AUDIO_STREAM_TYPE_AC4 = 9; // 0x9
+    field public static final int AUDIO_STREAM_TYPE_DRA = 15; // 0xf
+    field public static final int AUDIO_STREAM_TYPE_DTS = 10; // 0xa
+    field public static final int AUDIO_STREAM_TYPE_DTS_HD = 11; // 0xb
+    field public static final int AUDIO_STREAM_TYPE_EAC3 = 8; // 0x8
+    field public static final int AUDIO_STREAM_TYPE_MP3 = 2; // 0x2
+    field public static final int AUDIO_STREAM_TYPE_MPEG1 = 3; // 0x3
+    field public static final int AUDIO_STREAM_TYPE_MPEG2 = 4; // 0x4
+    field public static final int AUDIO_STREAM_TYPE_MPEGH = 5; // 0x5
+    field public static final int AUDIO_STREAM_TYPE_OPUS = 13; // 0xd
+    field public static final int AUDIO_STREAM_TYPE_PCM = 1; // 0x1
+    field public static final int AUDIO_STREAM_TYPE_UNDEFINED = 0; // 0x0
+    field public static final int AUDIO_STREAM_TYPE_VORBIS = 14; // 0xe
+    field public static final int AUDIO_STREAM_TYPE_WMA = 12; // 0xc
+    field public static final int VIDEO_STREAM_TYPE_AV1 = 10; // 0xa
+    field public static final int VIDEO_STREAM_TYPE_AVC = 5; // 0x5
+    field public static final int VIDEO_STREAM_TYPE_AVS = 11; // 0xb
+    field public static final int VIDEO_STREAM_TYPE_AVS2 = 12; // 0xc
+    field public static final int VIDEO_STREAM_TYPE_HEVC = 6; // 0x6
+    field public static final int VIDEO_STREAM_TYPE_MPEG1 = 2; // 0x2
+    field public static final int VIDEO_STREAM_TYPE_MPEG2 = 3; // 0x3
+    field public static final int VIDEO_STREAM_TYPE_MPEG4P2 = 4; // 0x4
+    field public static final int VIDEO_STREAM_TYPE_RESERVED = 1; // 0x1
+    field public static final int VIDEO_STREAM_TYPE_UNDEFINED = 0; // 0x0
+    field public static final int VIDEO_STREAM_TYPE_VC1 = 7; // 0x7
+    field public static final int VIDEO_STREAM_TYPE_VP8 = 8; // 0x8
+    field public static final int VIDEO_STREAM_TYPE_VP9 = 9; // 0x9
   }
 
   public static class AvSettings.Builder {
     method @NonNull public android.media.tv.tuner.filter.AvSettings build();
+    method @NonNull public android.media.tv.tuner.filter.AvSettings.Builder setAudioStreamType(int);
     method @NonNull public android.media.tv.tuner.filter.AvSettings.Builder setPassthrough(boolean);
+    method @NonNull public android.media.tv.tuner.filter.AvSettings.Builder setVideoStreamType(int);
   }
 
   public class DownloadEvent extends android.media.tv.tuner.filter.FilterEvent {
@@ -5150,6 +5276,7 @@
     method public int configure(@NonNull android.media.tv.tuner.filter.FilterConfiguration);
     method public int flush();
     method public int getId();
+    method public long getId64Bit();
     method public int read(@NonNull byte[], long, long);
     method public int setDataSource(@Nullable android.media.tv.tuner.filter.Filter);
     method public int start();
@@ -5201,16 +5328,19 @@
     method @NonNull public static android.media.tv.tuner.filter.IpFilterConfiguration.Builder builder();
     method @NonNull @Size(min=4, max=16) public byte[] getDstIpAddress();
     method public int getDstPort();
+    method @IntRange(from=0, to=61439) public int getIpFilterContextId();
     method @NonNull @Size(min=4, max=16) public byte[] getSrcIpAddress();
     method public int getSrcPort();
     method public int getType();
     method public boolean isPassthrough();
+    field public static final int INVALID_IP_FILTER_CONTEXT_ID = -1; // 0xffffffff
   }
 
   public static final class IpFilterConfiguration.Builder {
     method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration build();
     method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setDstIpAddress(@NonNull byte[]);
     method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setDstPort(int);
+    method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setIpFilterContextId(int);
     method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setPassthrough(boolean);
     method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setSettings(@Nullable android.media.tv.tuner.filter.Settings);
     method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setSrcIpAddress(@NonNull byte[]);
@@ -5234,6 +5364,7 @@
     method public boolean isPrivateData();
     method public boolean isPtsPresent();
     method public boolean isSecureMemory();
+    method public void release();
   }
 
   public final class MmtpFilterConfiguration extends android.media.tv.tuner.filter.FilterConfiguration {
@@ -5250,6 +5381,8 @@
 
   public class MmtpRecordEvent extends android.media.tv.tuner.filter.FilterEvent {
     method public long getDataLength();
+    method public int getMpuSequenceNumber();
+    method public long getPts();
     method public int getScHevcIndexMask();
   }
 
@@ -5412,6 +5545,7 @@
   public class TsRecordEvent extends android.media.tv.tuner.filter.FilterEvent {
     method public long getDataLength();
     method public int getPacketId();
+    method public long getPts();
     method public int getScIndexMask();
     method public int getTsIndexMask();
   }
@@ -5427,9 +5561,13 @@
 
   public class AnalogFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings {
     method @NonNull public static android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder builder();
+    method public int getAftFlag();
     method public int getSifStandard();
     method public int getSignalType();
     method public int getType();
+    field public static final int AFT_FLAG_FALSE = 2; // 0x2
+    field public static final int AFT_FLAG_TRUE = 1; // 0x1
+    field public static final int AFT_FLAG_UNDEFINED = 0; // 0x0
     field public static final int SIF_AUTO = 1; // 0x1
     field public static final int SIF_BG = 2; // 0x2
     field public static final int SIF_BG_A2 = 4; // 0x4
@@ -5462,6 +5600,7 @@
 
   public static class AnalogFrontendSettings.Builder {
     method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings build();
+    method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setAftFlag(int);
     method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setFrequency(int);
     method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setSifStandard(int);
     method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setSignalType(int);
@@ -5577,6 +5716,69 @@
     method @NonNull public android.media.tv.tuner.frontend.AtscFrontendSettings.Builder setModulation(int);
   }
 
+  public class DtmbFrontendCapabilities extends android.media.tv.tuner.frontend.FrontendCapabilities {
+    method public int getBandwidthCapability();
+    method public int getCodeRateCapability();
+    method public int getGuardIntervalCapability();
+    method public int getModulationCapability();
+    method public int getTimeInterleaveModeCapability();
+    method public int getTransmissionModeCapability();
+  }
+
+  public class DtmbFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings {
+    method @NonNull public static android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder builder();
+    method public int getBandwidth();
+    method public int getCodeRate();
+    method public int getGuardInterval();
+    method public int getModulation();
+    method public int getTimeInterleaveMode();
+    method public int getTransmissionMode();
+    method public int getType();
+    field public static final int BANDWIDTH_6MHZ = 4; // 0x4
+    field public static final int BANDWIDTH_8MHZ = 2; // 0x2
+    field public static final int BANDWIDTH_AUTO = 1; // 0x1
+    field public static final int BANDWIDTH_UNDEFINED = 0; // 0x0
+    field public static final int CODERATE_2_5 = 2; // 0x2
+    field public static final int CODERATE_3_5 = 4; // 0x4
+    field public static final int CODERATE_4_5 = 8; // 0x8
+    field public static final int CODERATE_AUTO = 1; // 0x1
+    field public static final int CODERATE_UNDEFINED = 0; // 0x0
+    field public static final int GUARD_INTERVAL_AUTO = 1; // 0x1
+    field public static final int GUARD_INTERVAL_PN_420_CONST = 16; // 0x10
+    field public static final int GUARD_INTERVAL_PN_420_VARIOUS = 2; // 0x2
+    field public static final int GUARD_INTERVAL_PN_595_CONST = 4; // 0x4
+    field public static final int GUARD_INTERVAL_PN_945_CONST = 32; // 0x20
+    field public static final int GUARD_INTERVAL_PN_945_VARIOUS = 8; // 0x8
+    field public static final int GUARD_INTERVAL_PN_RESERVED = 64; // 0x40
+    field public static final int GUARD_INTERVAL_UNDEFINED = 0; // 0x0
+    field public static final int MODULATION_CONSTELLATION_16QAM = 8; // 0x8
+    field public static final int MODULATION_CONSTELLATION_32QAM = 16; // 0x10
+    field public static final int MODULATION_CONSTELLATION_4QAM = 2; // 0x2
+    field public static final int MODULATION_CONSTELLATION_4QAM_NR = 4; // 0x4
+    field public static final int MODULATION_CONSTELLATION_64QAM = 32; // 0x20
+    field public static final int MODULATION_CONSTELLATION_AUTO = 1; // 0x1
+    field public static final int MODULATION_CONSTELLATION_UNDEFINED = 0; // 0x0
+    field public static final int TIME_INTERLEAVE_MODE_AUTO = 1; // 0x1
+    field public static final int TIME_INTERLEAVE_MODE_TIMER_INT_240 = 2; // 0x2
+    field public static final int TIME_INTERLEAVE_MODE_TIMER_INT_720 = 4; // 0x4
+    field public static final int TIME_INTERLEAVE_MODE_UNDEFINED = 0; // 0x0
+    field public static final int TRANSMISSION_MODE_AUTO = 1; // 0x1
+    field public static final int TRANSMISSION_MODE_C1 = 2; // 0x2
+    field public static final int TRANSMISSION_MODE_C3780 = 4; // 0x4
+    field public static final int TRANSMISSION_MODE_UNDEFINED = 0; // 0x0
+  }
+
+  public static final class DtmbFrontendSettings.Builder {
+    method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings build();
+    method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setBandwidth(int);
+    method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setCodeRate(int);
+    method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setFrequency(int);
+    method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setGuardInterval(int);
+    method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setModulation(int);
+    method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setTimeInterleaveMode(int);
+    method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setTransmissionMode(int);
+  }
+
   public class DvbcFrontendCapabilities extends android.media.tv.tuner.frontend.FrontendCapabilities {
     method public int getAnnexCapability();
     method public int getFecCapability();
@@ -5591,6 +5793,7 @@
     method public int getOuterFec();
     method public int getSpectralInversion();
     method public int getSymbolRate();
+    method public int getTimeInterleaveMode();
     method public int getType();
     field public static final int ANNEX_A = 1; // 0x1
     field public static final int ANNEX_B = 2; // 0x2
@@ -5606,9 +5809,20 @@
     field public static final int OUTER_FEC_OUTER_FEC_NONE = 1; // 0x1
     field public static final int OUTER_FEC_OUTER_FEC_RS = 2; // 0x2
     field public static final int OUTER_FEC_UNDEFINED = 0; // 0x0
-    field public static final int SPECTRAL_INVERSION_INVERTED = 2; // 0x2
-    field public static final int SPECTRAL_INVERSION_NORMAL = 1; // 0x1
-    field public static final int SPECTRAL_INVERSION_UNDEFINED = 0; // 0x0
+    field @Deprecated public static final int SPECTRAL_INVERSION_INVERTED = 2; // 0x2
+    field @Deprecated public static final int SPECTRAL_INVERSION_NORMAL = 1; // 0x1
+    field @Deprecated public static final int SPECTRAL_INVERSION_UNDEFINED = 0; // 0x0
+    field public static final int TIME_INTERLEAVE_MODE_128_1_0 = 2; // 0x2
+    field public static final int TIME_INTERLEAVE_MODE_128_1_1 = 4; // 0x4
+    field public static final int TIME_INTERLEAVE_MODE_128_2 = 128; // 0x80
+    field public static final int TIME_INTERLEAVE_MODE_128_3 = 256; // 0x100
+    field public static final int TIME_INTERLEAVE_MODE_128_4 = 512; // 0x200
+    field public static final int TIME_INTERLEAVE_MODE_16_8 = 32; // 0x20
+    field public static final int TIME_INTERLEAVE_MODE_32_4 = 16; // 0x10
+    field public static final int TIME_INTERLEAVE_MODE_64_2 = 8; // 0x8
+    field public static final int TIME_INTERLEAVE_MODE_8_16 = 64; // 0x40
+    field public static final int TIME_INTERLEAVE_MODE_AUTO = 1; // 0x1
+    field public static final int TIME_INTERLEAVE_MODE_UNDEFINED = 0; // 0x0
   }
 
   public static class DvbcFrontendSettings.Builder {
@@ -5620,6 +5834,7 @@
     method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setOuterFec(int);
     method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setSpectralInversion(int);
     method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setSymbolRate(int);
+    method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setTimeInterleaveMode(int);
   }
 
   public class DvbsCodeRate {
@@ -5651,10 +5866,12 @@
     method public int getModulation();
     method public int getPilot();
     method public int getRolloff();
+    method public int getScanType();
     method public int getStandard();
     method public int getSymbolRate();
     method public int getType();
     method public int getVcmMode();
+    method public boolean isDiseqcRxMessage();
     field public static final int MODULATION_AUTO = 1; // 0x1
     field public static final int MODULATION_MOD_128APSK = 2048; // 0x800
     field public static final int MODULATION_MOD_16APSK = 256; // 0x100
@@ -5681,6 +5898,11 @@
     field public static final int ROLLOFF_0_35 = 1; // 0x1
     field public static final int ROLLOFF_0_5 = 6; // 0x6
     field public static final int ROLLOFF_UNDEFINED = 0; // 0x0
+    field public static final int SCAN_TYPE_DIRECT = 1; // 0x1
+    field public static final int SCAN_TYPE_DISEQC = 2; // 0x2
+    field public static final int SCAN_TYPE_JESS = 4; // 0x4
+    field public static final int SCAN_TYPE_UNDEFINED = 0; // 0x0
+    field public static final int SCAN_TYPE_UNICABLE = 3; // 0x3
     field public static final int STANDARD_AUTO = 1; // 0x1
     field public static final int STANDARD_S = 2; // 0x2
     field public static final int STANDARD_S2 = 4; // 0x4
@@ -5693,11 +5915,13 @@
   public static class DvbsFrontendSettings.Builder {
     method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings build();
     method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setCodeRate(@Nullable android.media.tv.tuner.frontend.DvbsCodeRate);
+    method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setDiseqcRxMessage(boolean);
     method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setFrequency(int);
     method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setInputStreamId(int);
     method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setModulation(int);
     method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setPilot(int);
     method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setRolloff(int);
+    method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setScanType(int);
     method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setStandard(int);
     method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setSymbolRate(int);
     method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setVcmMode(int);
@@ -5750,10 +5974,14 @@
     field public static final int CODERATE_AUTO = 1; // 0x1
     field public static final int CODERATE_UNDEFINED = 0; // 0x0
     field public static final int CONSTELLATION_16QAM = 4; // 0x4
+    field public static final int CONSTELLATION_16QAM_R = 64; // 0x40
     field public static final int CONSTELLATION_256QAM = 16; // 0x10
+    field public static final int CONSTELLATION_256QAM_R = 256; // 0x100
     field public static final int CONSTELLATION_64QAM = 8; // 0x8
+    field public static final int CONSTELLATION_64QAM_R = 128; // 0x80
     field public static final int CONSTELLATION_AUTO = 1; // 0x1
     field public static final int CONSTELLATION_QPSK = 2; // 0x2
+    field public static final int CONSTELLATION_QPSK_R = 32; // 0x20
     field public static final int CONSTELLATION_UNDEFINED = 0; // 0x0
     field public static final int GUARD_INTERVAL_19_128 = 64; // 0x40
     field public static final int GUARD_INTERVAL_19_256 = 128; // 0x80
@@ -5787,6 +6015,9 @@
     field public static final int TRANSMISSION_MODE_4K = 8; // 0x8
     field public static final int TRANSMISSION_MODE_8K = 4; // 0x4
     field public static final int TRANSMISSION_MODE_AUTO = 1; // 0x1
+    field public static final int TRANSMISSION_MODE_EXTENDED_16K = 256; // 0x100
+    field public static final int TRANSMISSION_MODE_EXTENDED_32K = 512; // 0x200
+    field public static final int TRANSMISSION_MODE_EXTENDED_8K = 128; // 0x80
     field public static final int TRANSMISSION_MODE_UNDEFINED = 0; // 0x0
   }
 
@@ -5824,8 +6055,12 @@
   }
 
   public abstract class FrontendSettings {
+    method public int getEndFrequency();
     method public int getFrequency();
+    method public int getFrontendSpectralInversion();
     method public abstract int getType();
+    method @IntRange(from=1) public void setEndFrequency(int);
+    method public void setSpectralInversion(int);
     field public static final long FEC_11_15 = 4194304L; // 0x400000L
     field public static final long FEC_11_20 = 8388608L; // 0x800000L
     field public static final long FEC_11_45 = 16777216L; // 0x1000000L
@@ -5863,9 +6098,13 @@
     field public static final long FEC_9_20 = 2097152L; // 0x200000L
     field public static final long FEC_AUTO = 1L; // 0x1L
     field public static final long FEC_UNDEFINED = 0L; // 0x0L
+    field public static final int FRONTEND_SPECTRAL_INVERSION_INVERTED = 2; // 0x2
+    field public static final int FRONTEND_SPECTRAL_INVERSION_NORMAL = 1; // 0x1
+    field public static final int FRONTEND_SPECTRAL_INVERSION_UNDEFINED = 0; // 0x0
     field public static final int TYPE_ANALOG = 1; // 0x1
     field public static final int TYPE_ATSC = 2; // 0x2
     field public static final int TYPE_ATSC3 = 3; // 0x3
+    field public static final int TYPE_DTMB = 10; // 0xa
     field public static final int TYPE_DVBC = 4; // 0x4
     field public static final int TYPE_DVBS = 5; // 0x5
     field public static final int TYPE_DVBT = 6; // 0x6
@@ -5878,14 +6117,21 @@
   public class FrontendStatus {
     method public int getAgc();
     method @NonNull public android.media.tv.tuner.frontend.FrontendStatus.Atsc3PlpTuningInfo[] getAtsc3PlpTuningInfo();
+    method public int getBandwidth();
     method public int getBer();
+    method @NonNull public int[] getBers();
+    method @NonNull public int[] getCodeRates();
     method public int getFreqOffset();
+    method public int getGuardInterval();
     method public int getHierarchy();
     method public long getInnerFec();
+    method @NonNull public int[] getInterleaving();
+    method @NonNull public int[] getIsdbtSegment();
     method @NonNull public boolean[] getLayerErrors();
     method public int getLnbVoltage();
     method public int getMer();
     method public int getModulation();
+    method @NonNull public int[] getModulationsExt();
     method public int getPer();
     method public int getPerBer();
     method public int getPlpId();
@@ -5894,23 +6140,34 @@
     method public int getSnr();
     method public int getSpectralInversion();
     method public int getSymbolRate();
+    method public int getSystemId();
+    method public int getTransmissionMode();
+    method @NonNull public int[] getTsDataRate();
+    method public int getUec();
     method public boolean isDemodLocked();
     method public boolean isEwbs();
     method public boolean isLnaOn();
     method public boolean isRfLocked();
     field public static final int FRONTEND_STATUS_TYPE_AGC = 14; // 0xe
     field public static final int FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO = 21; // 0x15
+    field public static final int FRONTEND_STATUS_TYPE_BANDWIDTH = 25; // 0x19
     field public static final int FRONTEND_STATUS_TYPE_BER = 2; // 0x2
+    field public static final int FRONTEND_STATUS_TYPE_BERS = 23; // 0x17
+    field public static final int FRONTEND_STATUS_TYPE_CODERATES = 24; // 0x18
     field public static final int FRONTEND_STATUS_TYPE_DEMOD_LOCK = 0; // 0x0
     field public static final int FRONTEND_STATUS_TYPE_EWBS = 13; // 0xd
     field public static final int FRONTEND_STATUS_TYPE_FEC = 8; // 0x8
     field public static final int FRONTEND_STATUS_TYPE_FREQ_OFFSET = 18; // 0x12
+    field public static final int FRONTEND_STATUS_TYPE_GUARD_INTERVAL = 26; // 0x1a
     field public static final int FRONTEND_STATUS_TYPE_HIERARCHY = 19; // 0x13
+    field public static final int FRONTEND_STATUS_TYPE_INTERLEAVINGS = 30; // 0x1e
+    field public static final int FRONTEND_STATUS_TYPE_ISDBT_SEGMENTS = 31; // 0x1f
     field public static final int FRONTEND_STATUS_TYPE_LAYER_ERROR = 16; // 0x10
     field public static final int FRONTEND_STATUS_TYPE_LNA = 15; // 0xf
     field public static final int FRONTEND_STATUS_TYPE_LNB_VOLTAGE = 11; // 0xb
     field public static final int FRONTEND_STATUS_TYPE_MER = 17; // 0x11
     field public static final int FRONTEND_STATUS_TYPE_MODULATION = 9; // 0x9
+    field public static final int FRONTEND_STATUS_TYPE_MODULATIONS_EXT = 22; // 0x16
     field public static final int FRONTEND_STATUS_TYPE_PER = 3; // 0x3
     field public static final int FRONTEND_STATUS_TYPE_PLP_ID = 12; // 0xc
     field public static final int FRONTEND_STATUS_TYPE_PRE_BER = 4; // 0x4
@@ -5920,6 +6177,10 @@
     field public static final int FRONTEND_STATUS_TYPE_SNR = 1; // 0x1
     field public static final int FRONTEND_STATUS_TYPE_SPECTRAL = 10; // 0xa
     field public static final int FRONTEND_STATUS_TYPE_SYMBOL_RATE = 7; // 0x7
+    field public static final int FRONTEND_STATUS_TYPE_T2_SYSTEM_ID = 29; // 0x1d
+    field public static final int FRONTEND_STATUS_TYPE_TRANSMISSION_MODE = 27; // 0x1b
+    field public static final int FRONTEND_STATUS_TYPE_TS_DATA_RATES = 32; // 0x20
+    field public static final int FRONTEND_STATUS_TYPE_UEC = 28; // 0x1c
   }
 
   public static class FrontendStatus.Atsc3PlpTuningInfo {
@@ -6083,7 +6344,9 @@
     method public void onHierarchyReported(int);
     method public void onInputStreamIdsReported(@NonNull int[]);
     method public void onLocked();
+    method public default void onModulationReported(int);
     method public void onPlpIdsReported(@NonNull int[]);
+    method public default void onPriorityReported(boolean);
     method public void onProgress(@IntRange(from=0, to=100) int);
     method public void onScanStopped();
     method public void onSignalTypeReported(int);
@@ -7238,7 +7501,7 @@
     field public static final int BAND_2GHZ = 1; // 0x1
     field public static final int BAND_5GHZ = 2; // 0x2
     field public static final int BAND_6GHZ = 4; // 0x4
-    field public static final int BAND_ANY = 7; // 0x7
+    field @Deprecated public static final int BAND_ANY = 7; // 0x7
     field public static final int RANDOMIZATION_NONE = 0; // 0x0
     field public static final int RANDOMIZATION_PERSISTENT = 1; // 0x1
   }
@@ -7325,6 +7588,7 @@
     field @Deprecated public int numScorerOverrideAndSwitchedNetwork;
     field @Deprecated public boolean requirePmf;
     field @Deprecated public boolean shared;
+    field @Deprecated public int subscriptionId;
     field @Deprecated public boolean useExternalScores;
   }
 
@@ -7811,6 +8075,10 @@
     method @Deprecated public android.net.NetworkSpecifier createNetworkSpecifierPmk(@NonNull android.net.wifi.aware.PeerHandle, @NonNull byte[]);
   }
 
+  public class WifiAwareManager {
+    method public void enableInstantCommunicationMode(boolean);
+  }
+
   public class WifiAwareSession implements java.lang.AutoCloseable {
     method public android.net.NetworkSpecifier createNetworkSpecifierPmk(int, @NonNull byte[], @NonNull byte[]);
   }
@@ -8544,6 +8812,22 @@
     method public boolean hasSingleFileDescriptor();
   }
 
+  public interface Parcelable {
+    field public static final int PARCELABLE_STABILITY_LOCAL = 0; // 0x0
+    field public static final int PARCELABLE_STABILITY_VINTF = 1; // 0x1
+  }
+
+  public final class ParcelableHolder implements android.os.Parcelable {
+    ctor public ParcelableHolder(int);
+    method public int describeContents();
+    method @Nullable public <T extends android.os.Parcelable> T getParcelable(@NonNull Class<T>);
+    method public int getStability();
+    method public void readFromParcel(@NonNull android.os.Parcel);
+    method public boolean setParcelable(@Nullable android.os.Parcelable);
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.os.ParcelableHolder> CREATOR;
+  }
+
   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();
@@ -10972,6 +11256,25 @@
     field public static final String MBMS_STREAMING_SERVICE_ACTION = "android.telephony.action.EmbmsStreaming";
   }
 
+  public final class ModemActivityInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public android.telephony.ModemActivityInfo getDelta(@NonNull android.telephony.ModemActivityInfo);
+    method public long getIdleTimeMillis();
+    method public static int getNumTxPowerLevels();
+    method public long getReceiveTimeMillis();
+    method public long getSleepTimeMillis();
+    method public long getTimestampMillis();
+    method public long getTransmitDurationMillisAtPowerLevel(int);
+    method @NonNull public android.util.Range<java.lang.Integer> getTransmitPowerRange(int);
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ModemActivityInfo> CREATOR;
+    field public static final int TX_POWER_LEVEL_0 = 0; // 0x0
+    field public static final int TX_POWER_LEVEL_1 = 1; // 0x1
+    field public static final int TX_POWER_LEVEL_2 = 2; // 0x2
+    field public static final int TX_POWER_LEVEL_3 = 3; // 0x3
+    field public static final int TX_POWER_LEVEL_4 = 4; // 0x4
+  }
+
   public final class NetworkRegistrationInfo implements android.os.Parcelable {
     method @Nullable public android.telephony.DataSpecificRegistrationInfo getDataSpecificInfo();
     method public int getRegistrationState();
@@ -11351,7 +11654,6 @@
     method public boolean disableCellBroadcastRange(int, int, int);
     method public boolean enableCellBroadcastRange(int, int, int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getPremiumSmsConsent(@NonNull String);
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getSmsCapacityOnIcc();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void sendMultipartTextMessageWithoutPersisting(String, String, java.util.List<java.lang.String>, java.util.List<android.app.PendingIntent>, java.util.List<android.app.PendingIntent>);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setPremiumSmsConsent(@NonNull String, int);
     field public static final int PREMIUM_SMS_CONSENT_ALWAYS_ALLOW = 3; // 0x3
@@ -11452,6 +11754,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCarrierPrivilegeStatus(int);
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<java.lang.String> getCarrierPrivilegedPackagesForAllActiveSubscriptions();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.CarrierRestrictionRules getCarrierRestrictionRules();
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCdmaEnhancedRoamingIndicatorIconIndex();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn(int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin();
@@ -11466,7 +11769,6 @@
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDeviceSoftwareVersion(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean getEmergencyCallbackMode();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEmergencyNumberDbVersion();
-    method @NonNull @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<java.lang.String> getEquivalentHomePlmns();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimDomain();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimIst();
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.Map<java.lang.Integer,java.lang.Integer> getLogicalToPhysicalSlotMapping();
@@ -11500,6 +11802,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean isIccLockEnabled();
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isLteCdmaEvdoGsmWcdmaEnabled();
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isMobileDataPolicyEnabled(int);
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isOpportunisticNetworkEnabled();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isPotentialEmergencyNumber(@NonNull String);
@@ -11530,6 +11833,7 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataActivationState(int);
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMobileDataPolicyEnabledStatus(int, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setPreferredNetworkTypeBitmask(long);
@@ -11580,6 +11884,8 @@
     field public static final int INVALID_EMERGENCY_NUMBER_DB_VERSION = -1; // 0xffffffff
     field public static final int KEY_TYPE_EPDG = 1; // 0x1
     field public static final int KEY_TYPE_WLAN = 2; // 0x2
+    field public static final int MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL = 1; // 0x1
+    field public static final int MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED = 2; // 0x2
     field public static final long NETWORK_TYPE_BITMASK_1xRTT = 64L; // 0x40L
     field public static final long NETWORK_TYPE_BITMASK_CDMA = 8L; // 0x8L
     field public static final long NETWORK_TYPE_BITMASK_EDGE = 2L; // 0x2L
@@ -12182,6 +12488,10 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsExternalCallState> CREATOR;
   }
 
+  public class ImsManager {
+    method @NonNull public android.telephony.ims.SipDelegateManager getSipDelegateManager(int);
+  }
+
   public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
     method @Deprecated @NonNull @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getFeatureState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>) throws android.telephony.ims.ImsException;
@@ -12217,10 +12527,13 @@
     method public void disableIms(int);
     method public void enableIms(int);
     method public android.telephony.ims.stub.ImsConfigImplBase getConfig(int);
+    method public long getImsServiceCapabilities();
     method public android.telephony.ims.stub.ImsRegistrationImplBase getRegistration(int);
+    method @Nullable public android.telephony.ims.stub.SipTransportImplBase getSipTransport(int);
     method public final void onUpdateSupportedImsFeatures(android.telephony.ims.stub.ImsFeatureConfiguration) throws android.os.RemoteException;
     method public android.telephony.ims.stub.ImsFeatureConfiguration querySupportedImsFeatures();
     method public void readyForFeatureCreation();
+    field public static final long CAPABILITY_SIP_DELEGATE_CREATION = 2L; // 0x2L
   }
 
   public final class ImsSsData implements android.os.Parcelable {
@@ -12466,6 +12779,10 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
   }
 
+  public class SipDelegateManager {
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSupported() throws android.telephony.ims.ImsException;
+  }
+
 }
 
 package android.telephony.ims.feature {
@@ -12715,6 +13032,10 @@
     method public int updateColr(int);
   }
 
+  public class SipTransportImplBase {
+    ctor public SipTransportImplBase(@NonNull java.util.concurrent.Executor);
+  }
+
 }
 
 package android.telephony.mbms {
@@ -13022,7 +13343,7 @@
     method @Nullable public String findProxyForUrl(@NonNull String);
     method @NonNull public static android.webkit.PacProcessor getInstance();
     method @Nullable public default android.net.Network getNetwork();
-    method public default void releasePacProcessor();
+    method public default void release();
     method public default void setNetwork(@Nullable android.net.Network);
     method public boolean setProxyScript(@NonNull String);
   }
diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt
index 3a9f49c..773ecd03 100644
--- a/api/system-lint-baseline.txt
+++ b/api/system-lint-baseline.txt
@@ -159,8 +159,6 @@
     
 MissingNullability: android.telephony.ModemActivityInfo#writeToParcel(android.os.Parcel, int) parameter #0:
     
-MissingNullability: android.telephony.ModemActivityInfo.TransmitPower#toString():
-    
 MissingNullability: android.telephony.NetworkService#onUnbind(android.content.Intent) parameter #0:
     
 MissingNullability: android.telephony.SmsCbCmasInfo#toString():
@@ -229,6 +227,8 @@
     
 MutableBareField: android.net.wifi.WifiConfiguration#shared:
     
+MutableBareField: android.net.wifi.WifiConfiguration#subscriptionId:
+    Bare field subscriptionId must be marked final, or moved behind accessors if mutable
 MutableBareField: android.net.wifi.WifiScanner.ScanSettings#type:
     
 
diff --git a/api/test-current.txt b/api/test-current.txt
index 576cbacb..c92fb6c 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -14,6 +14,7 @@
     field public static final String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES";
     field public static final String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS";
     field public static final String MANAGE_CRATES = "android.permission.MANAGE_CRATES";
+    field public static final String MANAGE_NOTIFICATION_LISTENERS = "android.permission.MANAGE_NOTIFICATION_LISTENERS";
     field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS";
     field public static final String NETWORK_SETTINGS = "android.permission.NETWORK_SETTINGS";
     field public static final String NETWORK_STACK = "android.permission.NETWORK_STACK";
@@ -23,6 +24,7 @@
     field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
     field public static final String RESET_APP_ERRORS = "android.permission.RESET_APP_ERRORS";
     field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
+    field public static final String TEST_BIOMETRIC = "android.permission.TEST_BIOMETRIC";
     field public static final String TEST_MANAGE_ROLLBACKS = "android.permission.TEST_MANAGE_ROLLBACKS";
     field public static final String UPGRADE_RUNTIME_PERMISSIONS = "android.permission.UPGRADE_RUNTIME_PERMISSIONS";
     field public static final String WRITE_DEVICE_CONFIG = "android.permission.WRITE_DEVICE_CONFIG";
@@ -44,6 +46,7 @@
     field public static final int config_defaultAssistant = 17039393; // 0x1040021
     field public static final int config_defaultDialer = 17039395; // 0x1040023
     field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028
+    field public static final int config_systemAutomotiveProjection = 17039402; // 0x104002a
     field public static final int config_systemGallery = 17039399; // 0x1040027
     field public static final int config_systemVideoCall = 17039401; // 0x1040029
   }
@@ -78,20 +81,15 @@
   }
 
   public class ActivityManager {
-    method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void addOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener, int);
+    method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void addHomeVisibilityListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.HomeVisibilityListener);
     method public void alwaysShowUnsupportedCompileSdkWarning(android.content.ComponentName);
-    method @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) public void forceStopPackage(String);
-    method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getPackageImportance(String);
     method public long getTotalRam();
-    method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getUidImportance(int);
-    method @RequiresPermission("android.permission.INJECT_EVENTS") public void holdLock(int);
+    method @RequiresPermission(android.Manifest.permission.INJECT_EVENTS) public void holdLock(int);
     method public static boolean isHighEndGfx();
-    method @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) public void killProcessesWhenImperceptible(@NonNull int[], @NonNull String);
-    method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener);
+    method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void removeHomeVisibilityListener(@NonNull android.app.HomeVisibilityListener);
     method @RequiresPermission(android.Manifest.permission.RESET_APP_ERRORS) public void resetAppErrors();
     method public static void resumeAppSwitches() throws android.os.RemoteException;
     method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public void scheduleApplicationInfoChanged(java.util.List<java.lang.String>, int);
-    method @RequiresPermission("android.permission.MANAGE_USERS") public boolean switchUser(@NonNull android.os.UserHandle);
     field public static final int PROCESS_CAPABILITY_ALL = 7; // 0x7
     field public static final int PROCESS_CAPABILITY_ALL_EXPLICIT = 1; // 0x1
     field public static final int PROCESS_CAPABILITY_ALL_IMPLICIT = 6; // 0x6
@@ -101,10 +99,6 @@
     field public static final int PROCESS_CAPABILITY_NONE = 0; // 0x0
   }
 
-  public static interface ActivityManager.OnUidImportanceListener {
-    method public void onUidImportance(int, int);
-  }
-
   public static class ActivityManager.RunningAppProcessInfo implements android.os.Parcelable {
     field public static final int IMPORTANCE_CANT_SAVE_STATE_PRE_26 = 170; // 0xaa
   }
@@ -136,14 +130,13 @@
   public class ActivityTaskManager {
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void clearLaunchParamsForPackages(java.util.List<java.lang.String>);
     method public static boolean currentUiModeSupportsErrorDialogs(@NonNull android.content.Context);
-    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void moveTaskToStack(int, int, boolean);
-    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public boolean moveTopActivityToPinnedStack(int, android.graphics.Rect);
-    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void removeStacksInWindowingModes(int[]) throws java.lang.SecurityException;
-    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void removeStacksWithActivityTypes(int[]) throws java.lang.SecurityException;
+    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void moveTaskToRootTask(int, int, boolean);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public boolean moveTopActivityToPinnedRootTask(int, @NonNull android.graphics.Rect);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void removeRootTasksInWindowingModes(@NonNull int[]);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void removeRootTasksWithActivityTypes(@NonNull int[]);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void requestPictureInPictureMode(@NonNull android.os.IBinder);
-    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void resizeDockedStack(android.graphics.Rect, android.graphics.Rect);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void resizePrimarySplitScreen(@NonNull android.graphics.Rect, @NonNull android.graphics.Rect);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void resizeTask(int, android.graphics.Rect);
-    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void setDisplayToSingleTaskInstance(int);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public boolean setTaskWindowingMode(int, int, boolean) throws java.lang.SecurityException;
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public boolean setTaskWindowingModeSplitScreenPrimary(int, int, boolean, boolean, android.graphics.Rect, boolean) throws java.lang.SecurityException;
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void startSystemLockTaskMode(int);
@@ -159,7 +152,7 @@
   public class ActivityView extends android.view.ViewGroup {
     ctor public ActivityView(android.content.Context);
     ctor public ActivityView(android.content.Context, android.util.AttributeSet);
-    ctor public ActivityView(@NonNull android.content.Context, @NonNull android.util.AttributeSet, int, boolean, boolean);
+    ctor public ActivityView(@NonNull android.content.Context, @NonNull android.util.AttributeSet, int, boolean);
     method public int getVirtualDisplayId();
     method public void onLayout(boolean, int, int, int, int);
     method public void onLocationChanged();
@@ -179,7 +172,6 @@
     method @NonNull public android.app.ActivityView.Builder setAttributeSet(@Nullable android.util.AttributeSet);
     method @NonNull public android.app.ActivityView.Builder setDefaultStyle(int);
     method @NonNull public android.app.ActivityView.Builder setDisableSurfaceViewBackgroundLayer(boolean);
-    method @NonNull public android.app.ActivityView.Builder setSingleInstance(boolean);
     method @NonNull public android.app.ActivityView.Builder setUsePublicVirtualDisplay(boolean);
     method @NonNull public android.app.ActivityView.Builder setUseTrustedDisplay(boolean);
   }
@@ -200,15 +192,10 @@
   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 @Nullable @RequiresPermission("android.permission.GET_APP_OPS_STATS") public android.app.RuntimeAppOpAccessMessage collectRuntimeAppOpAccessMessage();
-    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.MANAGE_APPOPS") 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 @NonNull @RequiresPermission("android.permission.GET_APP_OPS_STATS") public java.util.List<android.app.AppOpsManager.PackageOps> getOpsForPackage(int, @NonNull String, @Nullable java.lang.String...);
     method public boolean isOperationActive(int, int, String);
     method @RequiresPermission("android.permission.MANAGE_APPOPS") public void offsetHistory(long);
-    method public static int opToDefaultMode(@NonNull String);
     method public static String opToPermission(int);
     method public static int permissionToOpCode(String);
     method @RequiresPermission("android.permission.MANAGE_APPOPS") public void rebootHistory(long);
@@ -216,8 +203,6 @@
     method @RequiresPermission("android.permission.MANAGE_APPOPS") public void resetHistoryParameters();
     method @RequiresPermission("android.permission.MANAGE_APPOPS") public void setHistoryParameters(int, long, int);
     method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setMode(int, int, String, int);
-    method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setMode(@NonNull String, int, @Nullable String, int);
-    method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setUidMode(@NonNull String, int, int);
     method public static int strOpToOp(@NonNull String);
     field public static final int HISTORICAL_MODE_DISABLED = 0; // 0x0
     field public static final int HISTORICAL_MODE_ENABLED_ACTIVE = 1; // 0x1
@@ -225,225 +210,19 @@
     field public static final String KEY_BG_STATE_SETTLE_TIME = "bg_state_settle_time";
     field public static final String KEY_FG_SERVICE_STATE_SETTLE_TIME = "fg_service_state_settle_time";
     field public static final String KEY_TOP_STATE_SETTLE_TIME = "top_state_settle_time";
-    field public static final String OPSTR_ACCEPT_HANDOVER = "android:accept_handover";
-    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";
-    field public static final String OPSTR_ASSIST_STRUCTURE = "android:assist_structure";
-    field public static final String OPSTR_AUDIO_ACCESSIBILITY_VOLUME = "android:audio_accessibility_volume";
-    field public static final String OPSTR_AUDIO_ALARM_VOLUME = "android:audio_alarm_volume";
-    field public static final String OPSTR_AUDIO_BLUETOOTH_VOLUME = "android:audio_bluetooth_volume";
-    field public static final String OPSTR_AUDIO_MASTER_VOLUME = "android:audio_master_volume";
-    field public static final String OPSTR_AUDIO_MEDIA_VOLUME = "android:audio_media_volume";
-    field public static final String OPSTR_AUDIO_NOTIFICATION_VOLUME = "android:audio_notification_volume";
-    field public static final String OPSTR_AUDIO_RING_VOLUME = "android:audio_ring_volume";
-    field public static final String OPSTR_AUDIO_VOICE_VOLUME = "android:audio_voice_volume";
-    field public static final String OPSTR_BIND_ACCESSIBILITY_SERVICE = "android:bind_accessibility_service";
-    field public static final String OPSTR_CHANGE_WIFI_STATE = "android:change_wifi_state";
-    field public static final String OPSTR_GET_ACCOUNTS = "android:get_accounts";
-    field public static final String OPSTR_GPS = "android:gps";
-    field public static final String OPSTR_INSTANT_APP_START_FOREGROUND = "android:instant_app_start_foreground";
-    field public static final String OPSTR_LEGACY_STORAGE = "android:legacy_storage";
-    field public static final String OPSTR_MANAGE_EXTERNAL_STORAGE = "android:manage_external_storage";
-    field public static final String OPSTR_MANAGE_IPSEC_TUNNELS = "android:manage_ipsec_tunnels";
-    field public static final String OPSTR_MUTE_MICROPHONE = "android:mute_microphone";
-    field public static final String OPSTR_NEIGHBORING_CELLS = "android:neighboring_cells";
-    field public static final String OPSTR_PLAY_AUDIO = "android:play_audio";
-    field public static final String OPSTR_POST_NOTIFICATION = "android:post_notification";
-    field public static final String OPSTR_PROJECT_MEDIA = "android:project_media";
-    field public static final String OPSTR_READ_CLIPBOARD = "android:read_clipboard";
-    field public static final String OPSTR_READ_ICC_SMS = "android:read_icc_sms";
-    field public static final String OPSTR_READ_MEDIA_AUDIO = "android:read_media_audio";
-    field public static final String OPSTR_READ_MEDIA_IMAGES = "android:read_media_images";
-    field public static final String OPSTR_READ_MEDIA_VIDEO = "android:read_media_video";
-    field public static final String OPSTR_RECEIVE_EMERGENCY_BROADCAST = "android:receive_emergency_broadcast";
-    field public static final String OPSTR_REQUEST_DELETE_PACKAGES = "android:request_delete_packages";
-    field public static final String OPSTR_REQUEST_INSTALL_PACKAGES = "android:request_install_packages";
-    field public static final String OPSTR_RUN_ANY_IN_BACKGROUND = "android:run_any_in_background";
-    field public static final String OPSTR_RUN_IN_BACKGROUND = "android:run_in_background";
-    field public static final String OPSTR_START_FOREGROUND = "android:start_foreground";
-    field public static final String OPSTR_TAKE_AUDIO_FOCUS = "android:take_audio_focus";
-    field public static final String OPSTR_TAKE_MEDIA_BUTTONS = "android:take_media_buttons";
-    field public static final String OPSTR_TOAST_WINDOW = "android:toast_window";
-    field public static final String OPSTR_TURN_SCREEN_ON = "android:turn_screen_on";
-    field public static final String OPSTR_VIBRATE = "android:vibrate";
-    field public static final String OPSTR_WAKE_LOCK = "android:wake_lock";
-    field public static final String OPSTR_WIFI_SCAN = "android:wifi_scan";
-    field public static final String OPSTR_WRITE_CLIPBOARD = "android:write_clipboard";
-    field public static final String OPSTR_WRITE_ICC_SMS = "android:write_icc_sms";
-    field public static final String OPSTR_WRITE_MEDIA_AUDIO = "android:write_media_audio";
-    field public static final String OPSTR_WRITE_MEDIA_IMAGES = "android:write_media_images";
-    field public static final String OPSTR_WRITE_MEDIA_VIDEO = "android:write_media_video";
-    field public static final String OPSTR_WRITE_SMS = "android:write_sms";
-    field public static final String OPSTR_WRITE_WALLPAPER = "android:write_wallpaper";
     field public static final int OP_COARSE_LOCATION = 0; // 0x0
-    field public static final int OP_FLAGS_ALL = 31; // 0x1f
-    field public static final int OP_FLAG_SELF = 1; // 0x1
-    field public static final int OP_FLAG_TRUSTED_PROXIED = 8; // 0x8
-    field public static final int OP_FLAG_TRUSTED_PROXY = 2; // 0x2
-    field public static final int OP_FLAG_UNTRUSTED_PROXIED = 16; // 0x10
-    field public static final int OP_FLAG_UNTRUSTED_PROXY = 4; // 0x4
     field public static final int OP_RECORD_AUDIO = 27; // 0x1b
     field public static final int OP_START_FOREGROUND = 76; // 0x4c
     field public static final int OP_SYSTEM_ALERT_WINDOW = 24; // 0x18
     field public static final long SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE = 151105954L; // 0x901b1a2L
-    field public static final int UID_STATE_BACKGROUND = 600; // 0x258
-    field public static final int UID_STATE_CACHED = 700; // 0x2bc
-    field public static final int UID_STATE_FOREGROUND = 500; // 0x1f4
-    field public static final int UID_STATE_FOREGROUND_SERVICE = 400; // 0x190
-    field @Deprecated public static final int UID_STATE_FOREGROUND_SERVICE_LOCATION = 300; // 0x12c
-    field public static final int UID_STATE_PERSISTENT = 100; // 0x64
-    field public static final int UID_STATE_TOP = 200; // 0xc8
-  }
-
-  public static final class AppOpsManager.AttributedHistoricalOps implements android.os.Parcelable {
-    method public int describeContents();
-    method @Nullable public android.app.AppOpsManager.HistoricalOp getOp(@NonNull String);
-    method @NonNull public android.app.AppOpsManager.HistoricalOp getOpAt(@IntRange(from=0) int);
-    method @IntRange(from=0) public int getOpCount();
-    method @Nullable public String getTag();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.AttributedHistoricalOps> CREATOR;
-  }
-
-  public static final class AppOpsManager.AttributedOpEntry implements android.os.Parcelable {
-    method public int describeContents();
-    method public long getLastAccessBackgroundTime(int);
-    method public long getLastAccessForegroundTime(int);
-    method public long getLastAccessTime(int);
-    method public long getLastAccessTime(int, int, int);
-    method public long getLastBackgroundDuration(int);
-    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastBackgroundProxyInfo(int);
-    method public long getLastDuration(int);
-    method public long getLastDuration(int, int, int);
-    method public long getLastForegroundDuration(int);
-    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastForegroundProxyInfo(int);
-    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int);
-    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int, int, int);
-    method public long getLastRejectBackgroundTime(int);
-    method public long getLastRejectForegroundTime(int);
-    method public long getLastRejectTime(int);
-    method public long getLastRejectTime(int, int, int);
-    method public boolean isRunning();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.AttributedOpEntry> CREATOR;
-  }
-
-  public static final class AppOpsManager.HistoricalOp implements android.os.Parcelable {
-    method public int describeContents();
-    method public long getAccessCount(int, int, int);
-    method public long getAccessDuration(int, int, int);
-    method public long getBackgroundAccessCount(int);
-    method public long getBackgroundAccessDuration(int);
-    method public long getBackgroundRejectCount(int);
-    method public long getForegroundAccessCount(int);
-    method public long getForegroundAccessDuration(int);
-    method public long getForegroundRejectCount(int);
-    method @NonNull public String getOpName();
-    method public long getRejectCount(int, int, int);
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalOp> CREATOR;
   }
 
   public static final class AppOpsManager.HistoricalOps implements android.os.Parcelable {
     ctor public AppOpsManager.HistoricalOps(long, long);
-    method public int describeContents();
-    method public long getBeginTimeMillis();
-    method public long getEndTimeMillis();
-    method @IntRange(from=0) public int getUidCount();
-    method @Nullable public android.app.AppOpsManager.HistoricalUidOps getUidOps(int);
-    method @NonNull public android.app.AppOpsManager.HistoricalUidOps getUidOpsAt(@IntRange(from=0) int);
     method public void increaseAccessCount(int, int, @NonNull String, @Nullable String, int, int, long);
     method public void increaseAccessDuration(int, int, @NonNull String, @Nullable String, int, int, long);
     method public void increaseRejectCount(int, int, @NonNull String, @Nullable String, int, int, long);
     method public void offsetBeginAndEndTime(long);
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull 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 setAttributionTag(@Nullable String);
-    method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setFlags(int);
-    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.AttributedHistoricalOps getAttributedOps(@NonNull String);
-    method @NonNull public android.app.AppOpsManager.AttributedHistoricalOps getAttributedOpsAt(@IntRange(from=0) int);
-    method @IntRange(from=0) public int getAttributedOpsCount();
-    method @Nullable public android.app.AppOpsManager.HistoricalOp getOp(@NonNull String);
-    method @NonNull public android.app.AppOpsManager.HistoricalOp getOpAt(@IntRange(from=0) int);
-    method @IntRange(from=0) public int getOpCount();
-    method @NonNull public String getPackageName();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalPackageOps> CREATOR;
-  }
-
-  public static final class AppOpsManager.HistoricalUidOps implements android.os.Parcelable {
-    method public int describeContents();
-    method @IntRange(from=0) public int getPackageCount();
-    method @Nullable public android.app.AppOpsManager.HistoricalPackageOps getPackageOps(@NonNull String);
-    method @NonNull public android.app.AppOpsManager.HistoricalPackageOps getPackageOpsAt(@IntRange(from=0) int);
-    method public int getUid();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalUidOps> CREATOR;
-  }
-
-  public static final class AppOpsManager.OpEntry implements android.os.Parcelable {
-    method public int describeContents();
-    method @NonNull public java.util.Map<java.lang.String,android.app.AppOpsManager.AttributedOpEntry> getAttributedOpEntries();
-    method @Deprecated public long getDuration();
-    method public long getLastAccessBackgroundTime(int);
-    method public long getLastAccessForegroundTime(int);
-    method public long getLastAccessTime(int);
-    method public long getLastAccessTime(int, int, int);
-    method public long getLastBackgroundDuration(int);
-    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastBackgroundProxyInfo(int);
-    method public long getLastDuration(int);
-    method public long getLastDuration(int, int, int);
-    method public long getLastForegroundDuration(int);
-    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastForegroundProxyInfo(int);
-    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int);
-    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int, int, int);
-    method public long getLastRejectBackgroundTime(int);
-    method public long getLastRejectForegroundTime(int);
-    method public long getLastRejectTime(int);
-    method public long getLastRejectTime(int, int, int);
-    method public int getMode();
-    method @NonNull public String getOpStr();
-    method @Deprecated @Nullable public String getProxyPackageName();
-    method @Deprecated @Nullable public String getProxyPackageName(int, int);
-    method @Deprecated public int getProxyUid();
-    method @Deprecated public int getProxyUid(int, int);
-    method public boolean isRunning();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.OpEntry> CREATOR;
-  }
-
-  public static final class AppOpsManager.OpEventProxyInfo implements android.os.Parcelable {
-    method public int describeContents();
-    method @Nullable public String getAttributionTag();
-    method @Nullable public String getPackageName();
-    method @IntRange(from=0) public int getUid();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.OpEventProxyInfo> CREATOR;
-  }
-
-  public static final class AppOpsManager.PackageOps implements android.os.Parcelable {
-    method public int describeContents();
-    method @NonNull public java.util.List<android.app.AppOpsManager.OpEntry> getOps();
-    method @NonNull public String getPackageName();
-    method public int getUid();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.PackageOps> CREATOR;
   }
 
   public class DownloadManager {
@@ -451,10 +230,15 @@
   }
 
   public class DreamManager {
-    method @RequiresPermission("android.permission.READ_DREAM_STATE") public boolean isDreaming();
-    method @RequiresPermission("android.permission.WRITE_DREAM_STATE") public void setActiveDream(@NonNull android.content.ComponentName);
-    method @RequiresPermission("android.permission.WRITE_DREAM_STATE") public void startDream(@NonNull android.content.ComponentName);
-    method @RequiresPermission("android.permission.WRITE_DREAM_STATE") public void stopDream();
+    method @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE) public boolean isDreaming();
+    method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void setActiveDream(@NonNull android.content.ComponentName);
+    method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void startDream(@NonNull android.content.ComponentName);
+    method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void stopDream();
+  }
+
+  public abstract class HomeVisibilityListener {
+    ctor public HomeVisibilityListener();
+    method public abstract void onHomeVisibilityChanged(boolean);
   }
 
   public final class NotificationChannel implements android.os.Parcelable {
@@ -463,7 +247,6 @@
     method public boolean isImportanceLockedByCriticalDeviceFunction();
     method public boolean isImportanceLockedByOEM();
     method public void lockFields(int);
-    method public void setBlockable(boolean);
     method public void setDeleted(boolean);
     method public void setDemoted(boolean);
     method public void setFgServiceShown(boolean);
@@ -471,7 +254,6 @@
     method public void setImportanceLockedByOEM(boolean);
     method public void setImportantConversation(boolean);
     method public void setOriginalImportance(int);
-    field public static final int USER_LOCKED_SOUND = 32; // 0x20
   }
 
   public final class NotificationChannelGroup implements android.os.Parcelable {
@@ -483,12 +265,8 @@
   public class NotificationManager {
     method public void allowAssistantAdjustment(String);
     method public void disallowAssistantAdjustment(String);
-    method @NonNull public java.util.List<java.lang.String> getAllowedAssistantAdjustments();
-    method @Nullable public android.content.ComponentName getAllowedNotificationAssistant();
     method public android.content.ComponentName getEffectsSuppressor();
-    method public boolean isNotificationAssistantAccessGranted(@NonNull android.content.ComponentName);
     method public boolean matchesCallFilter(android.os.Bundle);
-    method public void setNotificationAssistantAccessGranted(@Nullable android.content.ComponentName, boolean);
     method public void updateNotificationChannel(@NonNull String, int, @NonNull android.app.NotificationChannel);
   }
 
@@ -498,36 +276,12 @@
     method public android.graphics.Rect getSourceRectHint();
   }
 
-  public final class RuntimeAppOpAccessMessage implements android.os.Parcelable {
-    ctor public RuntimeAppOpAccessMessage(@IntRange(from=0L) int, @IntRange(from=0L) int, @NonNull String, @Nullable String, @NonNull String, int);
-    method public int describeContents();
-    method @Nullable public String getAttributionTag();
-    method @NonNull public String getMessage();
-    method @NonNull public String getOp();
-    method @NonNull public String getPackageName();
-    method public int getSamplingStrategy();
-    method @IntRange(from=0L) public int getUid();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.app.RuntimeAppOpAccessMessage> CREATOR;
-  }
-
   public class StatusBarManager {
     method public void collapsePanels();
     method public void expandNotificationsPanel();
-    method @NonNull @RequiresPermission(android.Manifest.permission.STATUS_BAR) public android.app.StatusBarManager.DisableInfo getDisableInfo();
-    method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSetup(boolean);
     method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setExpansionDisabledForSimNetworkLock(boolean);
   }
 
-  public static final class StatusBarManager.DisableInfo {
-    method public boolean areAllComponentsEnabled();
-    method public boolean isNavigateToHomeDisabled();
-    method public boolean isNotificationPeekingDisabled();
-    method public boolean isRecentsDisabled();
-    method public boolean isSearchDisabled();
-    method public boolean isStatusBarExpansionDisabled();
-  }
-
   public class TaskInfo {
     method @NonNull public android.content.res.Configuration getConfiguration();
     method @NonNull public android.window.WindowContainerToken getToken();
@@ -539,7 +293,6 @@
 
   public final class UiAutomation {
     method public void destroy();
-    method @NonNull public android.os.ParcelFileDescriptor[] executeShellCommandRw(@NonNull String);
     method @NonNull public android.os.ParcelFileDescriptor[] executeShellCommandRwe(@NonNull String);
     method @Deprecated public boolean grantRuntimePermission(String, String, android.os.UserHandle);
     method @Deprecated public boolean revokeRuntimePermission(String, String, android.os.UserHandle);
@@ -547,14 +300,12 @@
   }
 
   public class UiModeManager {
-    method @RequiresPermission("android.permission.ENTER_CAR_MODE_PRIORITIZED") public void enableCarMode(@IntRange(from=0) int, int);
     method public boolean isNightModeLocked();
     method public boolean isUiModeLocked();
   }
 
   public class WallpaperManager {
     method @Nullable public android.graphics.Bitmap getBitmap();
-    method @RequiresPermission("android.permission.SET_WALLPAPER_COMPONENT") public boolean setWallpaperComponent(android.content.ComponentName);
     method public boolean shouldEnableWideColorGamut();
     method @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) public boolean wallpaperSupportsWcg(int);
   }
@@ -599,18 +350,13 @@
 package android.app.admin {
 
   public class DevicePolicyManager {
-    method @Nullable public CharSequence getDeviceOwnerOrganizationName();
     method public long getLastBugReportRequestTime();
     method public long getLastNetworkLogRetrievalTime();
     method public long getLastSecurityLogRetrievalTime();
     method public java.util.List<java.lang.String> getOwnerInstalledCaCerts(@NonNull android.os.UserHandle);
     method public boolean isCurrentInputMethodSetByOwner();
-    method public boolean isDeviceManaged();
     method public boolean isFactoryResetProtectionPolicySupported();
-    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_DATA_SHARING_RESTRICTION_APPLIED = "android.app.action.DATA_SHARING_RESTRICTION_APPLIED";
-    field public static final String EXTRA_RESTRICTION = "android.app.extra.RESTRICTION";
   }
 
   public static final class SecurityLog.SecurityEvent implements android.os.Parcelable {
@@ -619,26 +365,6 @@
 
 }
 
-package android.app.assist {
-
-  public static class AssistStructure.ViewNode {
-    ctor public AssistStructure.ViewNode();
-  }
-
-}
-
-package android.app.backup {
-
-  public class BackupManager {
-    method @RequiresPermission("android.permission.BACKUP") public android.content.Intent getConfigurationIntent(String);
-    method @RequiresPermission("android.permission.BACKUP") public android.content.Intent getDataManagementIntent(String);
-    method @Nullable @RequiresPermission("android.permission.BACKUP") public CharSequence getDataManagementIntentLabel(@NonNull String);
-    method @Deprecated @Nullable @RequiresPermission("android.permission.BACKUP") public String getDataManagementLabel(@NonNull String);
-    method @RequiresPermission("android.permission.BACKUP") public String getDestinationString(String);
-  }
-
-}
-
 package android.app.blob {
 
   public class BlobStoreManager {
@@ -661,122 +387,21 @@
 
 package android.app.prediction {
 
-  public final class AppPredictionContext implements android.os.Parcelable {
-    method public int describeContents();
-    method @Nullable public android.os.Bundle getExtras();
-    method @NonNull public String getPackageName();
-    method @IntRange(from=0) public int getPredictedTargetCount();
-    method @NonNull public String getUiSurface();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.app.prediction.AppPredictionContext> CREATOR;
-  }
-
-  public static final class AppPredictionContext.Builder {
-    ctor public AppPredictionContext.Builder(@NonNull android.content.Context);
-    method @NonNull public android.app.prediction.AppPredictionContext build();
-    method @NonNull public android.app.prediction.AppPredictionContext.Builder setExtras(@Nullable android.os.Bundle);
-    method @NonNull public android.app.prediction.AppPredictionContext.Builder setPredictedTargetCount(@IntRange(from=0) int);
-    method @NonNull public android.app.prediction.AppPredictionContext.Builder setUiSurface(@NonNull String);
-  }
-
-  public final class AppPredictionManager {
-    method @NonNull public android.app.prediction.AppPredictor createAppPredictionSession(@NonNull android.app.prediction.AppPredictionContext);
-  }
-
-  public final class AppPredictionSessionId implements android.os.Parcelable {
-    method public int describeContents();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.app.prediction.AppPredictionSessionId> CREATOR;
-  }
-
   public final class AppPredictor {
-    method public void destroy();
     method public android.app.prediction.AppPredictionSessionId getSessionId();
-    method public void notifyAppTargetEvent(@NonNull android.app.prediction.AppTargetEvent);
-    method public void notifyLaunchLocationShown(@NonNull String, @NonNull java.util.List<android.app.prediction.AppTargetId>);
-    method public void registerPredictionUpdates(@NonNull java.util.concurrent.Executor, @NonNull android.app.prediction.AppPredictor.Callback);
-    method public void requestPredictionUpdate();
-    method @Nullable public void sortTargets(@NonNull java.util.List<android.app.prediction.AppTarget>, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.List<android.app.prediction.AppTarget>>);
-    method public void unregisterPredictionUpdates(@NonNull android.app.prediction.AppPredictor.Callback);
-  }
-
-  public static interface AppPredictor.Callback {
-    method public void onTargetsAvailable(@NonNull java.util.List<android.app.prediction.AppTarget>);
-  }
-
-  public final class AppTarget implements android.os.Parcelable {
-    method public int describeContents();
-    method @Nullable public String getClassName();
-    method @NonNull public android.app.prediction.AppTargetId getId();
-    method @NonNull public String getPackageName();
-    method @IntRange(from=0) public int getRank();
-    method @Nullable public android.content.pm.ShortcutInfo getShortcutInfo();
-    method @NonNull public android.os.UserHandle getUser();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.app.prediction.AppTarget> CREATOR;
-  }
-
-  public static final class AppTarget.Builder {
-    ctor public AppTarget.Builder(@NonNull android.app.prediction.AppTargetId, @NonNull String, @NonNull android.os.UserHandle);
-    ctor public AppTarget.Builder(@NonNull android.app.prediction.AppTargetId, @NonNull android.content.pm.ShortcutInfo);
-    method @NonNull public android.app.prediction.AppTarget build();
-    method @NonNull public android.app.prediction.AppTarget.Builder setClassName(@NonNull String);
-    method @NonNull public android.app.prediction.AppTarget.Builder setRank(@IntRange(from=0) int);
-  }
-
-  public final class AppTargetEvent implements android.os.Parcelable {
-    method public int describeContents();
-    method public int getAction();
-    method @Nullable public String getLaunchLocation();
-    method @Nullable public android.app.prediction.AppTarget getTarget();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final int ACTION_DISMISS = 2; // 0x2
-    field public static final int ACTION_LAUNCH = 1; // 0x1
-    field public static final int ACTION_PIN = 3; // 0x3
-    field public static final int ACTION_UNPIN = 4; // 0x4
-    field @NonNull public static final android.os.Parcelable.Creator<android.app.prediction.AppTargetEvent> CREATOR;
-  }
-
-  public static final class AppTargetEvent.Builder {
-    ctor public AppTargetEvent.Builder(@Nullable android.app.prediction.AppTarget, int);
-    method @NonNull public android.app.prediction.AppTargetEvent build();
-    method @NonNull public android.app.prediction.AppTargetEvent.Builder setLaunchLocation(@Nullable String);
-  }
-
-  public final class AppTargetId implements android.os.Parcelable {
-    ctor public AppTargetId(@NonNull String);
-    method public int describeContents();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.app.prediction.AppTargetId> CREATOR;
   }
 
 }
 
 package android.app.role {
 
-  public interface OnRoleHoldersChangedListener {
-    method public void onRoleHoldersChanged(@NonNull String, @NonNull android.os.UserHandle);
-  }
-
   public class RoleControllerManager {
-    method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void isApplicationVisibleForRole(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
-    method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void isRoleVisible(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void isApplicationVisibleForRole(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void isRoleVisible(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
   }
 
   public final class RoleManager {
-    method @RequiresPermission("android.permission.OBSERVE_ROLE_HOLDERS") public void addOnRoleHoldersChangedListenerAsUser(@NonNull java.util.concurrent.Executor, @NonNull android.app.role.OnRoleHoldersChangedListener, @NonNull android.os.UserHandle);
-    method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void addRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
-    method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean addRoleHolderFromController(@NonNull String, @NonNull String);
-    method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void clearRoleHoldersAsUser(@NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
-    method @NonNull @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public java.util.List<java.lang.String> getHeldRolesFromController(@NonNull String);
-    method @NonNull @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public java.util.List<java.lang.String> getRoleHolders(@NonNull String);
-    method @NonNull @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public java.util.List<java.lang.String> getRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle);
     method @Nullable public String getSmsRoleHolder(int);
-    method @RequiresPermission("android.permission.OBSERVE_ROLE_HOLDERS") public void removeOnRoleHoldersChangedListenerAsUser(@NonNull android.app.role.OnRoleHoldersChangedListener, @NonNull android.os.UserHandle);
-    method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void removeRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
-    method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean removeRoleHolderFromController(@NonNull String, @NonNull String);
-    method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public void setRoleNamesFromController(@NonNull java.util.List<java.lang.String>);
-    field public static final int MANAGE_HOLDERS_FLAG_DONT_KILL_APP = 1; // 0x1
   }
 
 }
@@ -795,6 +420,10 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_CRATES) @WorkerThread public java.util.Collection<android.os.storage.CrateInfo> queryCratesForUser(@NonNull java.util.UUID, @NonNull android.os.UserHandle) throws java.io.IOException, android.content.pm.PackageManager.NameNotFoundException;
   }
 
+  public final class UsageStats implements android.os.Parcelable {
+    ctor public UsageStats();
+  }
+
   public final class UsageStatsManager {
     method public void forceUsageSourceSettingRead();
   }
@@ -809,23 +438,8 @@
 
 }
 
-package android.companion {
-
-  public final class CompanionDeviceManager {
-    method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public boolean isDeviceAssociatedForWifiConnection(@NonNull String, @NonNull android.net.MacAddress, @NonNull android.os.UserHandle);
-  }
-
-}
-
 package android.content {
 
-  public class ApexEnvironment {
-    method @NonNull public static android.content.ApexEnvironment getApexEnvironment(@NonNull String);
-    method @NonNull public java.io.File getCredentialProtectedDataDirForUser(@NonNull android.os.UserHandle);
-    method @NonNull public java.io.File getDeviceProtectedDataDir();
-    method @NonNull public java.io.File getDeviceProtectedDataDirForUser(@NonNull android.os.UserHandle);
-  }
-
   public final class AutofillOptions implements android.os.Parcelable {
     ctor public AutofillOptions(int, boolean);
     method public int describeContents();
@@ -861,36 +475,21 @@
     method @NonNull public static android.os.UserHandle getUserHandleFromUri(@NonNull android.net.Uri);
   }
 
-  public class ContentProviderClient implements java.lang.AutoCloseable {
-    method @RequiresPermission(android.Manifest.permission.REMOVE_TASKS) public void setDetectNotResponding(long);
-  }
-
   public abstract class ContentResolver {
-    method @NonNull public static android.net.Uri decodeFromFile(@NonNull java.io.File);
-    method @NonNull public static java.io.File encodeToFile(@NonNull android.net.Uri);
     method public static String[] getSyncAdapterPackagesForAuthorityAsUser(String, int);
   }
 
   public abstract class Context {
-    method @NonNull public android.content.Context createContextAsUser(@NonNull android.os.UserHandle, int);
-    method @NonNull public android.content.Context createPackageContextAsUser(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
     method @NonNull public java.io.File getCrateDir(@NonNull String);
     method public abstract int getDisplayId();
     method public android.os.UserHandle getUser();
     method public int getUserId();
     method public void setAutofillOptions(@Nullable android.content.AutofillOptions);
     method public void setContentCaptureOptions(@Nullable android.content.ContentCaptureOptions);
-    method @RequiresPermission("android.permission.INTERACT_ACROSS_USERS") public void startActivityAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.os.UserHandle);
-    field public static final String APP_INTEGRITY_SERVICE = "app_integrity";
-    field public static final String BUGREPORT_SERVICE = "bugreport";
     field public static final String CONTENT_CAPTURE_MANAGER_SERVICE = "content_capture";
     field public static final String DEVICE_IDLE_CONTROLLER = "deviceidle";
     field public static final String DREAM_SERVICE = "dream";
-    field public static final String ETHERNET_SERVICE = "ethernet";
-    field public static final String PERMISSION_SERVICE = "permission";
     field public static final String POWER_WHITELIST_MANAGER = "power_whitelist";
-    field public static final String ROLLBACK_SERVICE = "rollback";
-    field public static final String STATUS_BAR_SERVICE = "statusbar";
     field public static final String TEST_NETWORK_SERVICE = "test_network";
   }
 
@@ -898,75 +497,13 @@
     method public int getDisplayId();
   }
 
-  public class Intent implements java.lang.Cloneable android.os.Parcelable {
-    field @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public static final String ACTION_MANAGE_DEFAULT_APP = "android.intent.action.MANAGE_DEFAULT_APP";
-    field public static final String ACTION_ROLLBACK_COMMITTED = "android.intent.action.ROLLBACK_COMMITTED";
-    field public static final String EXTRA_ORIGINATING_UID = "android.intent.extra.ORIGINATING_UID";
-    field public static final String EXTRA_ROLE_NAME = "android.intent.extra.ROLE_NAME";
-  }
-
 }
 
 package android.content.integrity {
 
   public class AppIntegrityManager {
     method @NonNull public android.content.integrity.RuleSet getCurrentRuleSet();
-    method @NonNull public String getCurrentRuleSetProvider();
-    method @NonNull public String getCurrentRuleSetVersion();
     method @NonNull public java.util.List<java.lang.String> getWhitelistedRuleProviders();
-    method public void updateRuleSet(@NonNull android.content.integrity.RuleSet, @NonNull android.content.IntentSender);
-    field public static final String EXTRA_STATUS = "android.content.integrity.extra.STATUS";
-    field public static final int STATUS_FAILURE = 1; // 0x1
-    field public static final int STATUS_SUCCESS = 0; // 0x0
-  }
-
-  public abstract class IntegrityFormula {
-    method @NonNull public static android.content.integrity.IntegrityFormula all(@NonNull android.content.integrity.IntegrityFormula...);
-    method @NonNull public static android.content.integrity.IntegrityFormula any(@NonNull android.content.integrity.IntegrityFormula...);
-    method @NonNull public static android.content.integrity.IntegrityFormula not(@NonNull android.content.integrity.IntegrityFormula);
-  }
-
-  public static final class IntegrityFormula.Application {
-    method @NonNull public static android.content.integrity.IntegrityFormula certificatesContain(@NonNull String);
-    method @NonNull public static android.content.integrity.IntegrityFormula isPreInstalled();
-    method @NonNull public static android.content.integrity.IntegrityFormula packageNameEquals(@NonNull String);
-    method @NonNull public static android.content.integrity.IntegrityFormula versionCodeEquals(@NonNull long);
-    method @NonNull public static android.content.integrity.IntegrityFormula versionCodeGreaterThan(@NonNull long);
-    method @NonNull public static android.content.integrity.IntegrityFormula versionCodeGreaterThanOrEqualTo(@NonNull long);
-  }
-
-  public static final class IntegrityFormula.Installer {
-    method @NonNull public static android.content.integrity.IntegrityFormula certificatesContain(@NonNull String);
-    method @NonNull public static android.content.integrity.IntegrityFormula notAllowedByManifest();
-    method @NonNull public static android.content.integrity.IntegrityFormula packageNameEquals(@NonNull String);
-  }
-
-  public static final class IntegrityFormula.SourceStamp {
-    method @NonNull public static android.content.integrity.IntegrityFormula notTrusted();
-    method @NonNull public static android.content.integrity.IntegrityFormula stampCertificateHashEquals(@NonNull String);
-  }
-
-  public final class Rule implements android.os.Parcelable {
-    ctor public Rule(@NonNull android.content.integrity.IntegrityFormula, int);
-    method public int describeContents();
-    method public int getEffect();
-    method @NonNull public android.content.integrity.IntegrityFormula getFormula();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.content.integrity.Rule> CREATOR;
-    field public static final int DENY = 0; // 0x0
-    field public static final int FORCE_ALLOW = 1; // 0x1
-  }
-
-  public class RuleSet {
-    method @NonNull public java.util.List<android.content.integrity.Rule> getRules();
-    method @NonNull public String getVersion();
-  }
-
-  public static class RuleSet.Builder {
-    ctor public RuleSet.Builder();
-    method @NonNull public android.content.integrity.RuleSet.Builder addRules(@NonNull java.util.List<android.content.integrity.Rule>);
-    method @NonNull public android.content.integrity.RuleSet build();
-    method @NonNull public android.content.integrity.RuleSet.Builder setVersion(@NonNull String);
   }
 
 }
@@ -983,95 +520,38 @@
     method public boolean isSystemApp();
     field public static final int PRIVATE_FLAG_PRIVILEGED = 8; // 0x8
     field public int privateFlags;
-    field public int targetSandboxVersion;
   }
 
   public class LauncherApps {
     ctor public LauncherApps(android.content.Context);
   }
 
-  public static class PackageInstaller.SessionInfo implements android.os.Parcelable {
-    method public int getAutoRevokePermissionsMode();
-    method public int getRollbackDataPolicy();
-    method @NonNull public java.util.Set<java.lang.String> getWhitelistedRestrictedPermissions();
-  }
-
   public static class PackageInstaller.SessionParams implements android.os.Parcelable {
-    method public void setEnableRollback(boolean);
-    method public void setEnableRollback(boolean, int);
-    method @RequiresPermission("android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS") public void setGrantedRuntimePermissions(String[]);
-    method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setInstallAsApex();
-    method public void setInstallAsInstantApp(boolean);
     method public void setInstallerPackageName(@Nullable String);
-    method public void setRequestDowngrade(boolean);
-    method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setStaged();
   }
 
   public abstract class PackageManager {
-    method @RequiresPermission("android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS") public abstract void addOnPermissionsChangeListener(@NonNull android.content.pm.PackageManager.OnPermissionsChangedListener);
-    method public abstract boolean arePermissionsIndividuallyControlled();
     method @Nullable public String getContentCaptureServicePackageName();
-    method @Nullable @RequiresPermission("android.permission.INTERACT_ACROSS_USERS_FULL") public abstract String getDefaultBrowserPackageNameAsUser(int);
     method @Nullable public String getDefaultTextClassifierPackageName();
-    method @Nullable public String getIncidentReportApproverPackageName();
     method public abstract int getInstallReason(@NonNull String, @NonNull android.os.UserHandle);
     method @NonNull public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplicationsAsUser(int, int);
-    method @NonNull @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 @NonNull public abstract String getPermissionControllerPackageName();
-    method @RequiresPermission(anyOf={"android.permission.GRANT_RUNTIME_PERMISSIONS", "android.permission.REVOKE_RUNTIME_PERMISSIONS", "android.permission.GET_RUNTIME_PERMISSIONS"}) public abstract int getPermissionFlags(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
     method @NonNull public abstract String getServicesSystemSharedLibraryPackageName();
     method @NonNull public abstract String getSharedSystemSharedLibraryPackageName();
     method @Nullable public String getSystemTextClassifierPackageName();
     method @Nullable 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.INJECT_EVENTS") public void holdLock(int);
-    method @RequiresPermission("android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS") public abstract void removeOnPermissionsChangeListener(@NonNull android.content.pm.PackageManager.OnPermissionsChangedListener);
-    method @RequiresPermission("android.permission.REVOKE_RUNTIME_PERMISSIONS") public abstract void revokeRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
-    method @RequiresPermission("android.permission.REVOKE_RUNTIME_PERMISSIONS") public void revokeRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle, @NonNull String);
-    method @RequiresPermission(anyOf={"android.permission.GRANT_RUNTIME_PERMISSIONS", "android.permission.REVOKE_RUNTIME_PERMISSIONS"}) public abstract void updatePermissionFlags(@NonNull String, @NonNull String, int, int, @NonNull android.os.UserHandle);
+    method @RequiresPermission(android.Manifest.permission.INJECT_EVENTS) public void holdLock(int);
     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_APPLY_RESTRICTION = 16384; // 0x4000
-    field public static final int FLAG_PERMISSION_GRANTED_BY_DEFAULT = 32; // 0x20
-    field public static final int FLAG_PERMISSION_GRANTED_BY_ROLE = 32768; // 0x8000
-    field public static final int FLAG_PERMISSION_ONE_TIME = 65536; // 0x10000
-    field public static final int FLAG_PERMISSION_POLICY_FIXED = 4; // 0x4
-    field public static final int FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT = 2048; // 0x800
-    field public static final int FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT = 262144; // 0x40000
-    field public static final int FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT = 4096; // 0x1000
-    field public static final int FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT = 8192; // 0x2000
-    field public static final int FLAG_PERMISSION_REVIEW_REQUIRED = 64; // 0x40
-    field public static final int FLAG_PERMISSION_REVOKED_COMPAT = 8; // 0x8
-    field @Deprecated 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_SYSTEM_FIXED = 16; // 0x10
-    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 int MODULE_APEX_NAME = 1; // 0x1
     field public static final String SYSTEM_SHARED_LIBRARY_SERVICES = "android.ext.services";
     field public static final String SYSTEM_SHARED_LIBRARY_SHARED = "android.ext.shared";
   }
 
-  public static interface PackageManager.OnPermissionsChangedListener {
-    method public void onPermissionsChanged(int);
-  }
-
   public class PermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
-    field public static final int FLAG_REMOVED = 2; // 0x2
-    field public static final int PROTECTION_FLAG_APP_PREDICTOR = 2097152; // 0x200000
-    field public static final int PROTECTION_FLAG_COMPANION = 8388608; // 0x800000
-    field public static final int PROTECTION_FLAG_CONFIGURATOR = 524288; // 0x80000
-    field public static final int PROTECTION_FLAG_DOCUMENTER = 262144; // 0x40000
-    field public static final int PROTECTION_FLAG_INCIDENT_REPORT_APPROVER = 1048576; // 0x100000
-    field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000
-    field public static final int PROTECTION_FLAG_RETAIL_DEMO = 16777216; // 0x1000000
-    field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
     field public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 32768; // 0x8000
-    field public static final int PROTECTION_FLAG_WELLBEING = 131072; // 0x20000
-    field @Nullable public final String backgroundPermission;
   }
 
   public final class ProviderInfoList implements android.os.Parcelable {
@@ -1110,40 +590,11 @@
 
 package android.content.rollback {
 
-  public final class PackageRollbackInfo implements android.os.Parcelable {
-    method public int describeContents();
-    method @NonNull public String getPackageName();
-    method @NonNull public android.content.pm.VersionedPackage getVersionRolledBackFrom();
-    method @NonNull public android.content.pm.VersionedPackage getVersionRolledBackTo();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.content.rollback.PackageRollbackInfo> CREATOR;
-  }
-
-  public final class RollbackInfo implements android.os.Parcelable {
-    method public int describeContents();
-    method @NonNull public java.util.List<android.content.pm.VersionedPackage> getCausePackages();
-    method public int getCommittedSessionId();
-    method @NonNull public java.util.List<android.content.rollback.PackageRollbackInfo> getPackages();
-    method public int getRollbackId();
-    method public boolean isStaged();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.content.rollback.RollbackInfo> CREATOR;
-  }
-
   public final class RollbackManager {
     method @RequiresPermission(android.Manifest.permission.TEST_MANAGE_ROLLBACKS) public void blockRollbackManager(long);
-    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_ROLLBACKS, android.Manifest.permission.TEST_MANAGE_ROLLBACKS}) public void commitRollback(int, @NonNull java.util.List<android.content.pm.VersionedPackage>, @NonNull android.content.IntentSender);
     method @RequiresPermission(android.Manifest.permission.TEST_MANAGE_ROLLBACKS) public void expireRollbackForPackage(@NonNull String);
-    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_ROLLBACKS, android.Manifest.permission.TEST_MANAGE_ROLLBACKS}) public java.util.List<android.content.rollback.RollbackInfo> getAvailableRollbacks();
-    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_ROLLBACKS, android.Manifest.permission.TEST_MANAGE_ROLLBACKS}) public java.util.List<android.content.rollback.RollbackInfo> getRecentlyCommittedRollbacks();
     method @RequiresPermission(android.Manifest.permission.TEST_MANAGE_ROLLBACKS) public void reloadPersistedData();
-    field public static final String EXTRA_STATUS = "android.content.rollback.extra.STATUS";
-    field public static final String EXTRA_STATUS_MESSAGE = "android.content.rollback.extra.STATUS_MESSAGE";
     field public static final String PROPERTY_ROLLBACK_LIFETIME_MILLIS = "rollback_lifetime_in_millis";
-    field public static final int STATUS_FAILURE = 1; // 0x1
-    field public static final int STATUS_FAILURE_INSTALL = 3; // 0x3
-    field public static final int STATUS_FAILURE_ROLLBACK_UNAVAILABLE = 2; // 0x2
-    field public static final int STATUS_SUCCESS = 0; // 0x0
   }
 
 }
@@ -1224,15 +675,36 @@
 
 }
 
-package android.hardware.camera2 {
+package android.hardware.biometrics {
 
-  public abstract class CameraDevice implements java.lang.AutoCloseable {
-    method @Deprecated public abstract void createCustomCaptureSession(android.hardware.camera2.params.InputConfiguration, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>, int, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
-    field public static final int SESSION_OPERATION_MODE_CONSTRAINED_HIGH_SPEED = 1; // 0x1
-    field public static final int SESSION_OPERATION_MODE_NORMAL = 0; // 0x0
-    field public static final int SESSION_OPERATION_MODE_VENDOR_START = 32768; // 0x8000
+  public class BiometricManager {
+    method @NonNull @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public android.hardware.biometrics.BiometricTestSession createTestSession(int);
+    method @NonNull @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public java.util.List<android.hardware.biometrics.SensorProperties> getSensorProperties();
   }
 
+  public class BiometricTestSession implements java.lang.AutoCloseable {
+    method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void acceptAuthentication(int);
+    method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void cleanupInternalState(int);
+    method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void close();
+    method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void finishEnroll(int);
+    method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void notifyAcquired(int, int);
+    method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void notifyError(int, int);
+    method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void rejectAuthentication(int);
+    method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void startEnroll(int);
+  }
+
+  public class SensorProperties {
+    method public int getSensorId();
+    method public int getSensorStrength();
+    field public static final int STRENGTH_CONVENIENCE = 0; // 0x0
+    field public static final int STRENGTH_STRONG = 2; // 0x2
+    field public static final int STRENGTH_WEAK = 1; // 0x1
+  }
+
+}
+
+package android.hardware.camera2 {
+
   public final class CameraManager {
     method public String[] getCameraIdListNoLazy() throws android.hardware.camera2.CameraAccessException;
   }
@@ -1241,15 +713,6 @@
 
 package android.hardware.display {
 
-  public final class AmbientBrightnessDayStats implements android.os.Parcelable {
-    method public int describeContents();
-    method public float[] getBucketBoundaries();
-    method public java.time.LocalDate getLocalDate();
-    method public float[] getStats();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.display.AmbientBrightnessDayStats> CREATOR;
-  }
-
   public class AmbientDisplayConfiguration {
     ctor public AmbientDisplayConfiguration(android.content.Context);
     method public boolean alwaysOnAvailable();
@@ -1257,70 +720,8 @@
     method public boolean alwaysOnEnabled(int);
   }
 
-  public final class BrightnessChangeEvent implements android.os.Parcelable {
-    method public int describeContents();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessChangeEvent> CREATOR;
-    field public final float batteryLevel;
-    field public final float brightness;
-    field public final long colorSampleDuration;
-    field public final int colorTemperature;
-    field @Nullable public final long[] colorValueBuckets;
-    field public final boolean isDefaultBrightnessConfig;
-    field public final boolean isUserSetBrightness;
-    field public final float lastBrightness;
-    field public final long[] luxTimestamps;
-    field public final float[] luxValues;
-    field public final boolean nightMode;
-    field public final String packageName;
-    field public final float powerBrightnessFactor;
-    field public final long timeStamp;
-  }
-
-  public final class BrightnessConfiguration implements android.os.Parcelable {
-    method public int describeContents();
-    method @Nullable public android.hardware.display.BrightnessCorrection getCorrectionByCategory(int);
-    method @Nullable public android.hardware.display.BrightnessCorrection getCorrectionByPackageName(@NonNull String);
-    method public android.util.Pair<float[],float[]> getCurve();
-    method public float getShortTermModelLowerLuxMultiplier();
-    method public long getShortTermModelTimeoutMillis();
-    method public float getShortTermModelUpperLuxMultiplier();
-    method public boolean shouldCollectColorSamples();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessConfiguration> CREATOR;
-    field public static final long SHORT_TERM_TIMEOUT_UNSET = -1L; // 0xffffffffffffffffL
-  }
-
-  public static class BrightnessConfiguration.Builder {
-    ctor public BrightnessConfiguration.Builder(float[], float[]);
-    method @NonNull public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByCategory(int, @NonNull android.hardware.display.BrightnessCorrection);
-    method @NonNull public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByPackageName(@NonNull String, @NonNull android.hardware.display.BrightnessCorrection);
-    method @NonNull public android.hardware.display.BrightnessConfiguration build();
-    method public int getMaxCorrectionsByCategory();
-    method public int getMaxCorrectionsByPackageName();
-    method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setDescription(@Nullable String);
-    method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setShortTermModelLowerLuxMultiplier(@FloatRange(from=0.0f) float);
-    method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setShortTermModelTimeoutMillis(long);
-    method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setShortTermModelUpperLuxMultiplier(@FloatRange(from=0.0f) float);
-    method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setShouldCollectColorSamples(boolean);
-  }
-
-  public final class BrightnessCorrection implements android.os.Parcelable {
-    method @FloatRange(from=0.0) public float apply(@FloatRange(from=0.0) float);
-    method @NonNull public static android.hardware.display.BrightnessCorrection createScaleAndTranslateLog(float, float);
-    method public int describeContents();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessCorrection> CREATOR;
-  }
-
   public final class DisplayManager {
-    method @RequiresPermission("android.permission.ACCESS_AMBIENT_LIGHT_STATS") public java.util.List<android.hardware.display.AmbientBrightnessDayStats> getAmbientBrightnessStats();
-    method @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public android.hardware.display.BrightnessConfiguration getBrightnessConfiguration();
-    method @RequiresPermission(android.Manifest.permission.BRIGHTNESS_SLIDER_USAGE) public java.util.List<android.hardware.display.BrightnessChangeEvent> getBrightnessEvents();
-    method @Nullable @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public android.hardware.display.BrightnessConfiguration getDefaultBrightnessConfiguration();
-    method public android.graphics.Point getStableDisplaySize();
     method public boolean isMinimalPostProcessingRequested(int);
-    method @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public void setBrightnessConfiguration(android.hardware.display.BrightnessConfiguration);
     method @RequiresPermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS) public void setShouldAlwaysRespectAppRequestedMode(boolean);
     method @RequiresPermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS) public boolean shouldAlwaysRespectAppRequestedMode();
     field public static final int VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 512; // 0x200
@@ -1329,99 +730,17 @@
 
 }
 
-package android.hardware.hdmi {
+package android.hardware.fingerprint {
 
-  public final class HdmiControlManager {
-    method @Nullable public android.hardware.hdmi.HdmiSwitchClient getSwitchClient();
-    method @RequiresPermission("android.permission.HDMI_CEC") public void setStandbyMode(boolean);
-    field public static final String ACTION_OSD_MESSAGE = "android.hardware.hdmi.action.OSD_MESSAGE";
-    field public static final int AVR_VOLUME_MUTED = 101; // 0x65
-    field public static final int CLEAR_TIMER_STATUS_CEC_DISABLE = 162; // 0xa2
-    field public static final int CLEAR_TIMER_STATUS_CHECK_RECORDER_CONNECTION = 160; // 0xa0
-    field public static final int CLEAR_TIMER_STATUS_FAIL_TO_CLEAR_SELECTED_SOURCE = 161; // 0xa1
-    field public static final int CLEAR_TIMER_STATUS_TIMER_CLEARED = 128; // 0x80
-    field public static final int CLEAR_TIMER_STATUS_TIMER_NOT_CLEARED_NO_INFO_AVAILABLE = 2; // 0x2
-    field public static final int CLEAR_TIMER_STATUS_TIMER_NOT_CLEARED_NO_MATCHING = 1; // 0x1
-    field public static final int CLEAR_TIMER_STATUS_TIMER_NOT_CLEARED_RECORDING = 0; // 0x0
-    field public static final int CONTROL_STATE_CHANGED_REASON_SETTING = 1; // 0x1
-    field public static final int CONTROL_STATE_CHANGED_REASON_STANDBY = 3; // 0x3
-    field public static final int CONTROL_STATE_CHANGED_REASON_START = 0; // 0x0
-    field public static final int CONTROL_STATE_CHANGED_REASON_WAKEUP = 2; // 0x2
-    field public static final int DEVICE_EVENT_ADD_DEVICE = 1; // 0x1
-    field public static final int DEVICE_EVENT_REMOVE_DEVICE = 2; // 0x2
-    field public static final int DEVICE_EVENT_UPDATE_DEVICE = 3; // 0x3
-    field public static final String EXTRA_MESSAGE_EXTRA_PARAM1 = "android.hardware.hdmi.extra.MESSAGE_EXTRA_PARAM1";
-    field public static final String EXTRA_MESSAGE_ID = "android.hardware.hdmi.extra.MESSAGE_ID";
-    field public static final int ONE_TOUCH_RECORD_ALREADY_RECORDING = 18; // 0x12
-    field public static final int ONE_TOUCH_RECORD_CEC_DISABLED = 51; // 0x33
-    field public static final int ONE_TOUCH_RECORD_CHECK_RECORDER_CONNECTION = 49; // 0x31
-    field public static final int ONE_TOUCH_RECORD_DISALLOW_TO_COPY = 13; // 0xd
-    field public static final int ONE_TOUCH_RECORD_DISALLOW_TO_FUTHER_COPIES = 14; // 0xe
-    field public static final int ONE_TOUCH_RECORD_FAIL_TO_RECORD_DISPLAYED_SCREEN = 50; // 0x32
-    field public static final int ONE_TOUCH_RECORD_INVALID_EXTERNAL_PHYSICAL_ADDRESS = 10; // 0xa
-    field public static final int ONE_TOUCH_RECORD_INVALID_EXTERNAL_PLUG_NUMBER = 9; // 0x9
-    field public static final int ONE_TOUCH_RECORD_MEDIA_PROBLEM = 21; // 0x15
-    field public static final int ONE_TOUCH_RECORD_MEDIA_PROTECTED = 19; // 0x13
-    field public static final int ONE_TOUCH_RECORD_NOT_ENOUGH_SPACE = 22; // 0x16
-    field public static final int ONE_TOUCH_RECORD_NO_MEDIA = 16; // 0x10
-    field public static final int ONE_TOUCH_RECORD_NO_OR_INSUFFICIENT_CA_ENTITLEMENTS = 12; // 0xc
-    field public static final int ONE_TOUCH_RECORD_NO_SOURCE_SIGNAL = 20; // 0x14
-    field public static final int ONE_TOUCH_RECORD_OTHER_REASON = 31; // 0x1f
-    field public static final int ONE_TOUCH_RECORD_PARENT_LOCK_ON = 23; // 0x17
-    field public static final int ONE_TOUCH_RECORD_PLAYING = 17; // 0x11
-    field public static final int ONE_TOUCH_RECORD_PREVIOUS_RECORDING_IN_PROGRESS = 48; // 0x30
-    field public static final int ONE_TOUCH_RECORD_RECORDING_ALREADY_TERMINATED = 27; // 0x1b
-    field public static final int ONE_TOUCH_RECORD_RECORDING_ANALOGUE_SERVICE = 3; // 0x3
-    field public static final int ONE_TOUCH_RECORD_RECORDING_CURRENTLY_SELECTED_SOURCE = 1; // 0x1
-    field public static final int ONE_TOUCH_RECORD_RECORDING_DIGITAL_SERVICE = 2; // 0x2
-    field public static final int ONE_TOUCH_RECORD_RECORDING_EXTERNAL_INPUT = 4; // 0x4
-    field public static final int ONE_TOUCH_RECORD_RECORDING_TERMINATED_NORMALLY = 26; // 0x1a
-    field public static final int ONE_TOUCH_RECORD_UNABLE_ANALOGUE_SERVICE = 6; // 0x6
-    field public static final int ONE_TOUCH_RECORD_UNABLE_DIGITAL_SERVICE = 5; // 0x5
-    field public static final int ONE_TOUCH_RECORD_UNABLE_SELECTED_SERVICE = 7; // 0x7
-    field public static final int ONE_TOUCH_RECORD_UNSUPPORTED_CA = 11; // 0xb
-    field public static final int OSD_MESSAGE_ARC_CONNECTED_INVALID_PORT = 1; // 0x1
-    field public static final int OSD_MESSAGE_AVR_VOLUME_CHANGED = 2; // 0x2
-    field public static final int POWER_STATUS_ON = 0; // 0x0
-    field public static final int POWER_STATUS_STANDBY = 1; // 0x1
-    field public static final int POWER_STATUS_TRANSIENT_TO_ON = 2; // 0x2
-    field public static final int POWER_STATUS_TRANSIENT_TO_STANDBY = 3; // 0x3
-    field public static final int POWER_STATUS_UNKNOWN = -1; // 0xffffffff
-    field @Deprecated public static final int RESULT_ALREADY_IN_PROGRESS = 4; // 0x4
-    field public static final int RESULT_COMMUNICATION_FAILED = 7; // 0x7
-    field public static final int RESULT_EXCEPTION = 5; // 0x5
-    field public static final int RESULT_INCORRECT_MODE = 6; // 0x6
-    field public static final int RESULT_SOURCE_NOT_AVAILABLE = 2; // 0x2
-    field public static final int RESULT_SUCCESS = 0; // 0x0
-    field public static final int RESULT_TARGET_NOT_AVAILABLE = 3; // 0x3
-    field public static final int RESULT_TIMEOUT = 1; // 0x1
-    field public static final int TIMER_RECORDING_RESULT_EXTRA_CEC_DISABLED = 3; // 0x3
-    field public static final int TIMER_RECORDING_RESULT_EXTRA_CHECK_RECORDER_CONNECTION = 1; // 0x1
-    field public static final int TIMER_RECORDING_RESULT_EXTRA_FAIL_TO_RECORD_SELECTED_SOURCE = 2; // 0x2
-    field public static final int TIMER_RECORDING_RESULT_EXTRA_NO_ERROR = 0; // 0x0
-    field public static final int TIMER_RECORDING_TYPE_ANALOGUE = 2; // 0x2
-    field public static final int TIMER_RECORDING_TYPE_DIGITAL = 1; // 0x1
-    field public static final int TIMER_RECORDING_TYPE_EXTERNAL = 3; // 0x3
-    field public static final int TIMER_STATUS_MEDIA_INFO_NOT_PRESENT = 2; // 0x2
-    field public static final int TIMER_STATUS_MEDIA_INFO_PRESENT_NOT_PROTECTED = 0; // 0x0
-    field public static final int TIMER_STATUS_MEDIA_INFO_PRESENT_PROTECTED = 1; // 0x1
-    field public static final int TIMER_STATUS_NOT_PROGRAMMED_CA_NOT_SUPPORTED = 6; // 0x6
-    field public static final int TIMER_STATUS_NOT_PROGRAMMED_CLOCK_FAILURE = 10; // 0xa
-    field public static final int TIMER_STATUS_NOT_PROGRAMMED_DATE_OUT_OF_RANGE = 2; // 0x2
-    field public static final int TIMER_STATUS_NOT_PROGRAMMED_DUPLICATED = 14; // 0xe
-    field public static final int TIMER_STATUS_NOT_PROGRAMMED_INVALID_EXTERNAL_PHYSICAL_NUMBER = 5; // 0x5
-    field public static final int TIMER_STATUS_NOT_PROGRAMMED_INVALID_EXTERNAL_PLUG_NUMBER = 4; // 0x4
-    field public static final int TIMER_STATUS_NOT_PROGRAMMED_INVALID_SEQUENCE = 3; // 0x3
-    field public static final int TIMER_STATUS_NOT_PROGRAMMED_NO_CA_ENTITLEMENTS = 7; // 0x7
-    field public static final int TIMER_STATUS_NOT_PROGRAMMED_NO_FREE_TIME = 1; // 0x1
-    field public static final int TIMER_STATUS_NOT_PROGRAMMED_PARENTAL_LOCK_ON = 9; // 0x9
-    field public static final int TIMER_STATUS_NOT_PROGRAMMED_UNSUPPORTED_RESOLUTION = 8; // 0x8
-    field public static final int TIMER_STATUS_PROGRAMMED_INFO_ENOUGH_SPACE = 8; // 0x8
-    field public static final int TIMER_STATUS_PROGRAMMED_INFO_MIGHT_NOT_ENOUGH_SPACE = 11; // 0xb
-    field public static final int TIMER_STATUS_PROGRAMMED_INFO_NOT_ENOUGH_SPACE = 9; // 0x9
-    field public static final int TIMER_STATUS_PROGRAMMED_INFO_NO_MEDIA_INFO = 10; // 0xa
+  @Deprecated public class FingerprintManager {
+    method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public android.hardware.biometrics.BiometricTestSession createTestSession(int);
+    method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public java.util.List<android.hardware.biometrics.SensorProperties> getSensorProperties();
   }
 
+}
+
+package android.hardware.hdmi {
+
   public final class HdmiControlServiceWrapper {
     ctor public HdmiControlServiceWrapper();
     method @NonNull public android.hardware.hdmi.HdmiControlManager createHdmiControlManager();
@@ -1430,68 +749,24 @@
     field public static final int DEVICE_PURE_CEC_SWITCH = 6; // 0x6
   }
 
-  public final class HdmiPortInfo implements android.os.Parcelable {
-    ctor public HdmiPortInfo(int, int, int, boolean, boolean, boolean);
-    method public int describeContents();
-    method public int getAddress();
-    method public int getId();
-    method public int getType();
-    method public boolean isArcSupported();
-    method public boolean isCecSupported();
-    method public boolean isMhlSupported();
-    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.hdmi.HdmiPortInfo> CREATOR;
-    field public static final int PORT_INPUT = 0; // 0x0
-    field public static final int PORT_OUTPUT = 1; // 0x1
-  }
+}
 
-  public class HdmiSwitchClient {
-    method public int getDeviceType();
-    method @NonNull public java.util.List<android.hardware.hdmi.HdmiPortInfo> getPortInfo();
-    method public void sendKeyEvent(int, boolean);
-    method public void sendVendorCommand(int, byte[], boolean);
+package android.hardware.input {
+
+  public final class InputManager {
+    method public int getBlockUntrustedTouchesMode(@NonNull android.content.Context);
+    method public float getMaximumObscuringOpacityForTouch(@NonNull android.content.Context);
+    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setBlockUntrustedTouchesMode(@NonNull android.content.Context, int);
+    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setMaximumObscuringOpacityForTouch(@NonNull android.content.Context, float);
+    field public static final long BLOCK_UNTRUSTED_TOUCHES = 158002302L; // 0x96aec7eL
   }
 
 }
 
 package android.hardware.lights {
 
-  public final class Light implements android.os.Parcelable {
-    method public int describeContents();
-    method public int getId();
-    method public int getOrdinal();
-    method public int getType();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.lights.Light> CREATOR;
-  }
-
-  public final class LightState implements android.os.Parcelable {
-    ctor public LightState(@ColorInt int);
-    method public int describeContents();
-    method @ColorInt public int getColor();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.lights.LightState> CREATOR;
-  }
-
   public final class LightsManager {
     method @NonNull @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public android.hardware.lights.LightState getLightState(@NonNull android.hardware.lights.Light);
-    method @NonNull @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public java.util.List<android.hardware.lights.Light> getLights();
-    method @NonNull @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public android.hardware.lights.LightsManager.LightsSession openSession();
-    field public static final int LIGHT_TYPE_MICROPHONE = 8; // 0x8
-  }
-
-  public final class LightsManager.LightsSession implements java.lang.AutoCloseable {
-    method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void close();
-    method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void requestLights(@NonNull android.hardware.lights.LightsRequest);
-  }
-
-  public final class LightsRequest {
-  }
-
-  public static final class LightsRequest.Builder {
-    ctor public LightsRequest.Builder();
-    method @NonNull public android.hardware.lights.LightsRequest build();
-    method @NonNull public android.hardware.lights.LightsRequest.Builder clearLight(@NonNull android.hardware.lights.Light);
-    method @NonNull public android.hardware.lights.LightsRequest.Builder setLight(@NonNull android.hardware.lights.Light, @NonNull android.hardware.lights.LightState);
   }
 
 }
@@ -1522,84 +797,16 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.KeyphraseMetadata> CREATOR;
   }
 
-  public class SoundTrigger {
-    field public static final int RECOGNITION_MODE_GENERIC = 8; // 0x8
-    field public static final int RECOGNITION_MODE_USER_AUTHENTICATION = 4; // 0x4
-    field public static final int RECOGNITION_MODE_USER_IDENTIFICATION = 2; // 0x2
-    field public static final int RECOGNITION_MODE_VOICE_TRIGGER = 1; // 0x1
-    field public static final int STATUS_OK = 0; // 0x0
-  }
-
-  public static final class SoundTrigger.Keyphrase implements android.os.Parcelable {
-    ctor public SoundTrigger.Keyphrase(int, int, @NonNull java.util.Locale, @NonNull String, @Nullable int[]);
-    method public int getId();
-    method @NonNull public java.util.Locale getLocale();
-    method public int getRecognitionModes();
-    method @NonNull public String getText();
-    method @NonNull public int[] getUsers();
-    method @NonNull public static android.hardware.soundtrigger.SoundTrigger.Keyphrase readFromParcel(@NonNull android.os.Parcel);
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.SoundTrigger.Keyphrase> CREATOR;
-  }
-
-  public static final class SoundTrigger.KeyphraseSoundModel extends android.hardware.soundtrigger.SoundTrigger.SoundModel implements android.os.Parcelable {
-    ctor public SoundTrigger.KeyphraseSoundModel(@NonNull java.util.UUID, @NonNull java.util.UUID, @Nullable byte[], @Nullable android.hardware.soundtrigger.SoundTrigger.Keyphrase[], int);
-    ctor public SoundTrigger.KeyphraseSoundModel(@NonNull java.util.UUID, @NonNull java.util.UUID, @Nullable byte[], @Nullable android.hardware.soundtrigger.SoundTrigger.Keyphrase[]);
-    method @NonNull public android.hardware.soundtrigger.SoundTrigger.Keyphrase[] getKeyphrases();
-    method @NonNull public static android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel readFromParcel(@NonNull android.os.Parcel);
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel> CREATOR;
-  }
-
   public static final class SoundTrigger.ModelParamRange implements android.os.Parcelable {
     ctor public SoundTrigger.ModelParamRange(int, int);
-    method public int getEnd();
-    method public int getStart();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.SoundTrigger.ModelParamRange> CREATOR;
   }
 
   public static final class SoundTrigger.ModuleProperties implements android.os.Parcelable {
     ctor public SoundTrigger.ModuleProperties(int, @NonNull String, @NonNull String, @NonNull String, int, @NonNull String, int, int, int, int, boolean, int, boolean, int, boolean, int);
-    method public int describeContents();
-    method public int getAudioCapabilities();
-    method @NonNull public String getDescription();
-    method public int getId();
-    method @NonNull public String getImplementor();
-    method public int getMaxBufferMillis();
-    method public int getMaxKeyphrases();
-    method public int getMaxSoundModels();
-    method public int getMaxUsers();
-    method public int getPowerConsumptionMw();
-    method public int getRecognitionModes();
-    method @NonNull public String getSupportedModelArch();
-    method @NonNull public java.util.UUID getUuid();
-    method public int getVersion();
-    method public boolean isCaptureTransitionSupported();
-    method public boolean isConcurrentCaptureSupported();
-    method public boolean isTriggerReturnedInEvent();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final int AUDIO_CAPABILITY_ECHO_CANCELLATION = 1; // 0x1
-    field public static final int AUDIO_CAPABILITY_NOISE_SUPPRESSION = 2; // 0x2
-    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.SoundTrigger.ModuleProperties> CREATOR;
   }
 
   public static class SoundTrigger.RecognitionEvent {
     ctor public SoundTrigger.RecognitionEvent(int, int, boolean, int, int, int, boolean, @NonNull android.media.AudioFormat, @Nullable byte[]);
-    method @Nullable public android.media.AudioFormat getCaptureFormat();
-    method public int getCaptureSession();
-    method public byte[] getData();
-    method public boolean isCaptureAvailable();
-  }
-
-  public static class SoundTrigger.SoundModel {
-    method @NonNull public byte[] getData();
-    method public int getType();
-    method @NonNull public java.util.UUID getUuid();
-    method @NonNull public java.util.UUID getVendorUuid();
-    method public int getVersion();
-    field public static final int TYPE_GENERIC_SOUND = 1; // 0x1
-    field public static final int TYPE_KEYPHRASE = 0; // 0x0
   }
 
 }
@@ -1698,60 +905,17 @@
     method public void setType(int);
   }
 
-  public class Location implements android.os.Parcelable {
-    method public void makeComplete();
-    field @Deprecated public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation";
-  }
-
   public class LocationManager {
     method @NonNull public String[] getBackgroundThrottlingWhitelist();
-    method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void getCurrentLocation(@NonNull android.location.LocationRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.location.Location>);
     method @NonNull public String[] getIgnoreSettingsWhitelist();
-    method @Deprecated @Nullable @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public java.util.List<java.lang.String> getProviderPackages(@NonNull String);
-    method @NonNull public java.util.List<android.location.LocationRequest> getTestProviderCurrentRequests(String);
-    method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.location.LocationListener, @Nullable android.os.Looper);
-    method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener);
-    method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.app.PendingIntent);
-    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setLocationEnabledForUser(boolean, @NonNull android.os.UserHandle);
+    method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public java.util.List<java.lang.String> getProviderPackages(@NonNull String);
     field public static final String FUSED_PROVIDER = "fused";
   }
 
-  public final class LocationRequest implements android.os.Parcelable {
-    method @Nullable public android.os.WorkSource getWorkSource();
-    method public boolean isHiddenFromAppOps();
-    method public boolean isLocationSettingsIgnored();
-    method public boolean isLowPower();
-    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
-    field public static final int POWER_HIGH = 203; // 0xcb
-    field public static final int POWER_LOW = 201; // 0xc9
-  }
-
-  public static final class LocationRequest.Builder {
-    method @NonNull @RequiresPermission("android.permission.UPDATE_APP_OPS_STATS") public android.location.LocationRequest.Builder setHiddenFromAppOps(boolean);
-    method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LocationRequest.Builder setLocationSettingsIgnored(boolean);
-    method @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public android.location.LocationRequest.Builder setLowPower(boolean);
-    method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.location.LocationRequest.Builder setWorkSource(@Nullable android.os.WorkSource);
-  }
-
 }
 
 package android.media {
 
-  public final class AudioFocusInfo implements android.os.Parcelable {
-    method public int describeContents();
-    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 @NonNull public String getPackageName();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.media.AudioFocusInfo> CREATOR;
-  }
-
   public final class AudioFocusRequest {
     method @Nullable public android.media.AudioManager.OnAudioFocusChangeListener getOnAudioFocusChangeListener();
   }
@@ -1764,13 +928,7 @@
   }
 
   public class AudioManager {
-    method @RequiresPermission("android.permission.MODIFY_AUDIO_ROUTING") public int dispatchAudioFocusChange(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
     method public boolean hasRegisteredDynamicPolicy();
-    method @RequiresPermission("android.permission.MODIFY_AUDIO_ROUTING") public int registerAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
-    method @RequiresPermission("android.permission.MODIFY_AUDIO_ROUTING") public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
-    method @RequiresPermission("android.permission.MODIFY_AUDIO_ROUTING") public void unregisterAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
-    method @RequiresPermission("android.permission.MODIFY_AUDIO_ROUTING") public void unregisterAudioPolicyAsync(@NonNull android.media.audiopolicy.AudioPolicy);
-    field public static final int SUCCESS = 0; // 0x0
   }
 
   public static final class AudioRecord.MetricsConstants {
@@ -1826,68 +984,6 @@
     method @NonNull public String getOriginalId();
   }
 
-  public final class MediaTranscodeManager {
-    method @NonNull public android.media.MediaTranscodeManager.TranscodingJob enqueueRequest(@NonNull android.media.MediaTranscodeManager.TranscodingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaTranscodeManager.OnTranscodingFinishedListener) throws java.io.FileNotFoundException, android.media.MediaTranscodingException.ServiceNotAvailableException;
-    field public static final int PRIORITY_REALTIME = 1; // 0x1
-    field public static final int TRANSCODING_TYPE_VIDEO = 1; // 0x1
-  }
-
-  @java.lang.FunctionalInterface public static interface MediaTranscodeManager.OnTranscodingFinishedListener {
-    method public void onTranscodingFinished(@NonNull android.media.MediaTranscodeManager.TranscodingJob);
-  }
-
-  public static final class MediaTranscodeManager.TranscodingJob {
-    method public void cancel();
-    method public int getJobId();
-    method @IntRange(from=0, to=100) public int getProgress();
-    method public int getResult();
-    method public int getStatus();
-    method public void setOnProgressUpdateListener(@NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener);
-    method public void setOnProgressUpdateListener(int, @NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener);
-    field public static final int RESULT_CANCELED = 4; // 0x4
-    field public static final int RESULT_ERROR = 3; // 0x3
-    field public static final int RESULT_NONE = 1; // 0x1
-    field public static final int RESULT_SUCCESS = 2; // 0x2
-    field public static final int STATUS_FINISHED = 3; // 0x3
-    field public static final int STATUS_PAUSED = 4; // 0x4
-    field public static final int STATUS_PENDING = 1; // 0x1
-    field public static final int STATUS_RUNNING = 2; // 0x2
-  }
-
-  @java.lang.FunctionalInterface public static interface MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener {
-    method public void onProgressUpdate(@NonNull android.media.MediaTranscodeManager.TranscodingJob, @IntRange(from=0, to=100) int);
-  }
-
-  public static final class MediaTranscodeManager.TranscodingRequest {
-    method public int getClientPid();
-    method public int getClientUid();
-    method @NonNull public android.net.Uri getDestinationUri();
-    method public int getPriority();
-    method @NonNull public android.net.Uri getSourceUri();
-    method public int getType();
-    method @Nullable public android.media.MediaFormat getVideoTrackFormat();
-  }
-
-  public static final class MediaTranscodeManager.TranscodingRequest.Builder {
-    ctor public MediaTranscodeManager.TranscodingRequest.Builder();
-    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest build();
-    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setClientPid(int);
-    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setClientUid(int);
-    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setDestinationUri(@NonNull android.net.Uri);
-    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setPriority(int);
-    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setSourceUri(@NonNull android.net.Uri);
-    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setType(int);
-    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setVideoTrackFormat(@NonNull android.media.MediaFormat);
-  }
-
-  public static class MediaTranscodeManager.TranscodingRequest.MediaFormatResolver {
-    ctor public MediaTranscodeManager.TranscodingRequest.MediaFormatResolver();
-    method @Nullable public android.media.MediaFormat resolveVideoFormat();
-    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.MediaFormatResolver setSourceVideoFormatHint(@NonNull android.media.MediaFormat);
-    method public boolean shouldTranscode();
-    field public static final String CAPS_SUPPORTS_HEVC = "support-hevc";
-  }
-
   public final class PlaybackParams implements android.os.Parcelable {
     method public int getAudioStretchMode();
     method public android.media.PlaybackParams setAudioStretchMode(int);
@@ -1936,90 +1032,8 @@
 
 package android.media.audiopolicy {
 
-  public class AudioMix {
-    method public int getMixState();
-    field public static final int MIX_STATE_DISABLED = -1; // 0xffffffff
-    field public static final int MIX_STATE_IDLE = 0; // 0x0
-    field public static final int MIX_STATE_MIXING = 1; // 0x1
-    field public static final int ROUTE_FLAG_LOOP_BACK = 2; // 0x2
-    field public static final int ROUTE_FLAG_RENDER = 1; // 0x1
-  }
-
-  public static class AudioMix.Builder {
-    ctor public AudioMix.Builder(android.media.audiopolicy.AudioMixingRule) throws java.lang.IllegalArgumentException;
-    method public android.media.audiopolicy.AudioMix build() throws java.lang.IllegalArgumentException;
-    method public android.media.audiopolicy.AudioMix.Builder setDevice(@NonNull android.media.AudioDeviceInfo) throws java.lang.IllegalArgumentException;
-    method public android.media.audiopolicy.AudioMix.Builder setFormat(android.media.AudioFormat) throws java.lang.IllegalArgumentException;
-    method public android.media.audiopolicy.AudioMix.Builder setRouteFlags(int) throws java.lang.IllegalArgumentException;
-  }
-
-  public class AudioMixingRule {
-    field public static final int RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET = 2; // 0x2
-    field public static final int RULE_MATCH_ATTRIBUTE_USAGE = 1; // 0x1
-    field public static final int RULE_MATCH_UID = 4; // 0x4
-    field public static final int RULE_MATCH_USERID = 8; // 0x8
-  }
-
-  public static class AudioMixingRule.Builder {
-    ctor public AudioMixingRule.Builder();
-    method public android.media.audiopolicy.AudioMixingRule.Builder addMixRule(int, Object) throws java.lang.IllegalArgumentException;
-    method public android.media.audiopolicy.AudioMixingRule.Builder addRule(android.media.AudioAttributes, int) throws java.lang.IllegalArgumentException;
-    method @NonNull public android.media.audiopolicy.AudioMixingRule.Builder allowPrivilegedPlaybackCapture(boolean);
-    method public android.media.audiopolicy.AudioMixingRule build();
-    method public android.media.audiopolicy.AudioMixingRule.Builder excludeMixRule(int, Object) throws java.lang.IllegalArgumentException;
-    method public android.media.audiopolicy.AudioMixingRule.Builder excludeRule(android.media.AudioAttributes, int) throws java.lang.IllegalArgumentException;
-  }
-
-  public class AudioPolicy {
-    method public int attachMixes(@NonNull java.util.List<android.media.audiopolicy.AudioMix>);
-    method public android.media.AudioRecord createAudioRecordSink(android.media.audiopolicy.AudioMix) throws java.lang.IllegalArgumentException;
-    method public android.media.AudioTrack createAudioTrackSource(android.media.audiopolicy.AudioMix) throws java.lang.IllegalArgumentException;
-    method public int detachMixes(@NonNull java.util.List<android.media.audiopolicy.AudioMix>);
-    method public int getFocusDuckingBehavior();
-    method public int getStatus();
-    method public boolean removeUidDeviceAffinity(int);
-    method public boolean removeUserIdDeviceAffinity(int);
-    method public int setFocusDuckingBehavior(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException;
-    method public void setRegistration(String);
-    method public boolean setUidDeviceAffinity(int, @NonNull java.util.List<android.media.AudioDeviceInfo>);
-    method public boolean setUserIdDeviceAffinity(int, @NonNull java.util.List<android.media.AudioDeviceInfo>);
-    method public String toLogFriendlyString();
-    field public static final int FOCUS_POLICY_DUCKING_DEFAULT = 0; // 0x0
-    field public static final int FOCUS_POLICY_DUCKING_IN_APP = 0; // 0x0
-    field public static final int FOCUS_POLICY_DUCKING_IN_POLICY = 1; // 0x1
-    field public static final int POLICY_STATUS_REGISTERED = 2; // 0x2
-    field public static final int POLICY_STATUS_UNREGISTERED = 1; // 0x1
-  }
-
-  public abstract static class AudioPolicy.AudioPolicyFocusListener {
-    ctor public AudioPolicy.AudioPolicyFocusListener();
-    method public void onAudioFocusAbandon(android.media.AudioFocusInfo);
-    method public void onAudioFocusGrant(android.media.AudioFocusInfo, int);
-    method public void onAudioFocusLoss(android.media.AudioFocusInfo, boolean);
-    method public void onAudioFocusRequest(android.media.AudioFocusInfo, int);
-  }
-
-  public abstract static class AudioPolicy.AudioPolicyStatusListener {
-    ctor public AudioPolicy.AudioPolicyStatusListener();
-    method public void onMixStateUpdate(android.media.audiopolicy.AudioMix);
-    method public void onStatusChange();
-  }
-
-  public abstract static class AudioPolicy.AudioPolicyVolumeCallback {
-    ctor public AudioPolicy.AudioPolicyVolumeCallback();
-    method public void onVolumeAdjustment(int);
-  }
-
   public static class AudioPolicy.Builder {
-    ctor public AudioPolicy.Builder(android.content.Context);
-    method @NonNull public android.media.audiopolicy.AudioPolicy.Builder addMix(@NonNull android.media.audiopolicy.AudioMix) throws java.lang.IllegalArgumentException;
-    method @NonNull public android.media.audiopolicy.AudioPolicy build();
-    method public void setAudioPolicyFocusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener);
-    method public void setAudioPolicyStatusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyStatusListener);
-    method @NonNull public android.media.audiopolicy.AudioPolicy.Builder setAudioPolicyVolumeCallback(@NonNull android.media.audiopolicy.AudioPolicy.AudioPolicyVolumeCallback);
-    method @NonNull public android.media.audiopolicy.AudioPolicy.Builder setIsAudioFocusPolicy(boolean);
     method @NonNull public android.media.audiopolicy.AudioPolicy.Builder setIsTestFocusPolicy(boolean);
-    method @NonNull public android.media.audiopolicy.AudioPolicy.Builder setLooper(@NonNull android.os.Looper) throws java.lang.IllegalArgumentException;
   }
 
 }
@@ -2033,232 +1047,38 @@
 
 }
 
-package android.metrics {
+package android.media.tv.tuner {
 
-  public class LogMaker {
-    ctor public LogMaker(int);
-    ctor public LogMaker(Object[]);
-    method public android.metrics.LogMaker addTaggedData(int, Object);
-    method public android.metrics.LogMaker clearCategory();
-    method public android.metrics.LogMaker clearPackageName();
-    method public android.metrics.LogMaker clearSubtype();
-    method public android.metrics.LogMaker clearTaggedData(int);
-    method public android.metrics.LogMaker clearType();
-    method public void deserialize(Object[]);
-    method public int getCategory();
-    method public long getCounterBucket();
-    method public String getCounterName();
-    method public int getCounterValue();
-    method public String getPackageName();
-    method public int getProcessId();
-    method public int getSubtype();
-    method public Object getTaggedData(int);
-    method public long getTimestamp();
-    method public int getType();
-    method public int getUid();
-    method public boolean isLongCounterBucket();
-    method public boolean isSubsetOf(android.metrics.LogMaker);
-    method public boolean isValidValue(Object);
-    method public Object[] serialize();
-    method public android.metrics.LogMaker setCategory(int);
-    method public android.metrics.LogMaker setPackageName(String);
-    method public android.metrics.LogMaker setSubtype(int);
-    method public android.metrics.LogMaker setType(int);
-  }
-
-  public class MetricsReader {
-    ctor public MetricsReader();
-    method public void checkpoint();
-    method public boolean hasNext();
-    method public android.metrics.LogMaker next();
-    method public void read(long);
-    method public void reset();
+  public final class TunerVersionChecker {
+    method public static int getMajorVersion(int);
+    method public static int getMinorVersion(int);
+    method public static boolean isHigherOrEqualVersionTo(int);
+    method public static boolean supportTunerVersion(int);
   }
 
 }
 
 package android.net {
 
-  public class CaptivePortal implements android.os.Parcelable {
-    method public void logEvent(int, @NonNull String);
-    method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void reevaluateNetwork();
-    method public void useNetwork();
-    field public static final int APP_REQUEST_REEVALUATION_REQUIRED = 100; // 0x64
-    field public static final int APP_RETURN_DISMISSED = 0; // 0x0
-    field public static final int APP_RETURN_UNWANTED = 1; // 0x1
-    field public static final int APP_RETURN_WANTED_AS_IS = 2; // 0x2
-  }
-
-  public final class CaptivePortalData implements android.os.Parcelable {
-    method public int describeContents();
-    method public long getByteLimit();
-    method public long getExpiryTimeMillis();
-    method public long getRefreshTimeMillis();
-    method @Nullable public android.net.Uri getUserPortalUrl();
-    method @Nullable public android.net.Uri getVenueInfoUrl();
-    method public boolean isCaptive();
-    method public boolean isSessionExtendable();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.CaptivePortalData> CREATOR;
-  }
-
-  public static class CaptivePortalData.Builder {
-    ctor public CaptivePortalData.Builder();
-    ctor public CaptivePortalData.Builder(@Nullable android.net.CaptivePortalData);
-    method @NonNull public android.net.CaptivePortalData build();
-    method @NonNull public android.net.CaptivePortalData.Builder setBytesRemaining(long);
-    method @NonNull public android.net.CaptivePortalData.Builder setCaptive(boolean);
-    method @NonNull public android.net.CaptivePortalData.Builder setExpiryTime(long);
-    method @NonNull public android.net.CaptivePortalData.Builder setRefreshTime(long);
-    method @NonNull public android.net.CaptivePortalData.Builder setSessionExtendable(boolean);
-    method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri);
-    method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri);
-  }
-
   public class ConnectivityManager {
     method @RequiresPermission(anyOf={"android.permission.MANAGE_TEST_NETWORKS", android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle);
-    method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(@NonNull android.net.Network, @NonNull 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";
   }
 
   public class EthernetManager {
-    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public android.net.EthernetManager.TetheredInterfaceRequest requestTetheredInterface(@NonNull java.util.concurrent.Executor, @NonNull android.net.EthernetManager.TetheredInterfaceCallback);
     method public void setIncludeTestInterfaces(boolean);
   }
 
-  public static interface EthernetManager.TetheredInterfaceCallback {
-    method public void onAvailable(@NonNull String);
-    method public void onUnavailable();
-  }
-
-  public static class EthernetManager.TetheredInterfaceRequest {
-    method public void release();
-  }
-
-  public final class IpPrefix implements android.os.Parcelable {
-    ctor public IpPrefix(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int);
-    ctor public IpPrefix(@NonNull String);
-  }
-
   public final class IpSecManager {
     field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0
   }
 
-  public class LinkAddress implements android.os.Parcelable {
-    ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int, int, int);
-    ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int, int, int, long, long);
-    ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int);
-    ctor public LinkAddress(@NonNull String);
-    ctor public LinkAddress(@NonNull String, int, int);
-    method public long getDeprecationTime();
-    method public long getExpirationTime();
-    method public boolean isGlobalPreferred();
-    method public boolean isIpv4();
-    method public boolean isIpv6();
-    method public boolean isSameAddressAs(@Nullable android.net.LinkAddress);
-  }
-
-  public final class LinkProperties implements android.os.Parcelable {
-    ctor public LinkProperties(@Nullable android.net.LinkProperties);
-    ctor public LinkProperties(@Nullable android.net.LinkProperties, boolean);
-    method public boolean addDnsServer(@NonNull java.net.InetAddress);
-    method public boolean addLinkAddress(@NonNull android.net.LinkAddress);
-    method @Nullable public android.net.Uri getCaptivePortalApiUrl();
-    method @Nullable public android.net.CaptivePortalData getCaptivePortalData();
-    method @NonNull public java.util.List<java.net.InetAddress> getPcscfServers();
-    method @Nullable public String getTcpBufferSizes();
-    method @NonNull public java.util.List<java.net.InetAddress> getValidatedPrivateDnsServers();
-    method public boolean hasGlobalIpv6Address();
-    method public boolean hasIpv4Address();
-    method public boolean hasIpv6DefaultRoute();
-    method public boolean isIpv4Provisioned();
-    method public boolean isIpv6Provisioned();
-    method public boolean isProvisioned();
-    method public boolean isReachable(@NonNull java.net.InetAddress);
-    method public boolean removeDnsServer(@NonNull java.net.InetAddress);
-    method public boolean removeLinkAddress(@NonNull android.net.LinkAddress);
-    method public boolean removeRoute(@NonNull android.net.RouteInfo);
-    method public void setCaptivePortalApiUrl(@Nullable android.net.Uri);
-    method public void setCaptivePortalData(@Nullable android.net.CaptivePortalData);
-    method public void setPcscfServers(@NonNull java.util.Collection<java.net.InetAddress>);
-    method public void setPrivateDnsServerName(@Nullable String);
-    method public void setTcpBufferSizes(@Nullable String);
-    method public void setUsePrivateDns(boolean);
-    method public void setValidatedPrivateDnsServers(@NonNull java.util.Collection<java.net.InetAddress>);
-  }
-
-  public class Network implements android.os.Parcelable {
-    ctor public Network(@NonNull android.net.Network);
-    method public int getNetId();
-    method @NonNull public android.net.Network getPrivateDnsBypassingCopy();
-  }
-
   public final class NetworkCapabilities implements android.os.Parcelable {
-    method @NonNull public int[] getAdministratorUids();
     method public int[] getCapabilities();
-    method @Nullable public String getSsid();
-    method @NonNull public int[] getTransportTypes();
-    method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities);
     field public static final int TRANSPORT_TEST = 7; // 0x7
   }
 
-  public static final class NetworkCapabilities.Builder {
-    ctor public NetworkCapabilities.Builder();
-    ctor public NetworkCapabilities.Builder(@NonNull android.net.NetworkCapabilities);
-    method @NonNull public android.net.NetworkCapabilities.Builder addCapability(int);
-    method @NonNull public android.net.NetworkCapabilities.Builder addTransportType(int);
-    method @NonNull public android.net.NetworkCapabilities build();
-    method @NonNull public android.net.NetworkCapabilities.Builder removeCapability(int);
-    method @NonNull public android.net.NetworkCapabilities.Builder removeTransportType(int);
-    method @NonNull @RequiresPermission("android.permission.NETWORK_FACTORY") public android.net.NetworkCapabilities.Builder setAdministratorUids(@NonNull int[]);
-    method @NonNull public android.net.NetworkCapabilities.Builder setLinkDownstreamBandwidthKbps(int);
-    method @NonNull public android.net.NetworkCapabilities.Builder setLinkUpstreamBandwidthKbps(int);
-    method @NonNull public android.net.NetworkCapabilities.Builder setNetworkSpecifier(@Nullable android.net.NetworkSpecifier);
-    method @NonNull @RequiresPermission("android.permission.NETWORK_FACTORY") public android.net.NetworkCapabilities.Builder setOwnerUid(int);
-    method @NonNull @RequiresPermission("android.permission.NETWORK_FACTORY") public android.net.NetworkCapabilities.Builder setRequestorPackageName(@Nullable String);
-    method @NonNull @RequiresPermission("android.permission.NETWORK_FACTORY") public android.net.NetworkCapabilities.Builder setRequestorUid(int);
-    method @NonNull @RequiresPermission("android.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP") public android.net.NetworkCapabilities.Builder setSignalStrength(int);
-    method @NonNull @RequiresPermission("android.permission.NETWORK_FACTORY") public android.net.NetworkCapabilities.Builder setSsid(@Nullable String);
-    method @NonNull public android.net.NetworkCapabilities.Builder setTransportInfo(@Nullable android.net.TransportInfo);
-  }
-
   public class NetworkStack {
-    method @Nullable public static android.os.IBinder getService();
     method public static void setServiceForTest(@Nullable android.os.IBinder);
-    field public static final String PERMISSION_MAINLINE_NETWORK_STACK = "android.permission.MAINLINE_NETWORK_STACK";
-  }
-
-  public final class RouteInfo implements android.os.Parcelable {
-    ctor public RouteInfo(@Nullable android.net.IpPrefix, @Nullable java.net.InetAddress, @Nullable String, int);
-    method public int getType();
-    field public static final int RTN_THROW = 9; // 0x9
-    field public static final int RTN_UNICAST = 1; // 0x1
-    field public static final int RTN_UNREACHABLE = 7; // 0x7
-  }
-
-  public final class StaticIpConfiguration implements android.os.Parcelable {
-    ctor public StaticIpConfiguration();
-    ctor public StaticIpConfiguration(@Nullable android.net.StaticIpConfiguration);
-    method public void addDnsServer(@NonNull java.net.InetAddress);
-    method public void clear();
-    method public int describeContents();
-    method @NonNull public java.util.List<java.net.InetAddress> getDnsServers();
-    method @Nullable public String getDomains();
-    method @Nullable public java.net.InetAddress getGateway();
-    method @Nullable public android.net.LinkAddress getIpAddress();
-    method @NonNull public java.util.List<android.net.RouteInfo> getRoutes(@Nullable String);
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.StaticIpConfiguration> CREATOR;
-  }
-
-  public static final class StaticIpConfiguration.Builder {
-    ctor public StaticIpConfiguration.Builder();
-    method @NonNull public android.net.StaticIpConfiguration build();
-    method @NonNull public android.net.StaticIpConfiguration.Builder setDnsServers(@NonNull Iterable<java.net.InetAddress>);
-    method @NonNull public android.net.StaticIpConfiguration.Builder setDomains(@Nullable String);
-    method @NonNull public android.net.StaticIpConfiguration.Builder setGateway(@Nullable java.net.InetAddress);
-    method @NonNull public android.net.StaticIpConfiguration.Builder setIpAddress(@Nullable android.net.LinkAddress);
   }
 
   public final class TestNetworkInterface implements android.os.Parcelable {
@@ -2277,100 +1097,6 @@
     method public void teardownTestNetwork(@NonNull android.net.Network);
   }
 
-  public final class TetheredClient implements android.os.Parcelable {
-    ctor public TetheredClient(@NonNull android.net.MacAddress, @NonNull java.util.Collection<android.net.TetheredClient.AddressInfo>, int);
-    method public int describeContents();
-    method @NonNull public java.util.List<android.net.TetheredClient.AddressInfo> getAddresses();
-    method @NonNull public android.net.MacAddress getMacAddress();
-    method public int getTetheringType();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient> CREATOR;
-  }
-
-  public static final class TetheredClient.AddressInfo implements android.os.Parcelable {
-    method public int describeContents();
-    method @NonNull public android.net.LinkAddress getAddress();
-    method @Nullable public String getHostname();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient.AddressInfo> CREATOR;
-  }
-
-  public class TetheringManager {
-    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback);
-    method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener);
-    method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(@NonNull android.net.TetheringManager.TetheringRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
-    method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering();
-    method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int);
-    method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback);
-    field public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED";
-    field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY";
-    field public static final String EXTRA_ACTIVE_TETHER = "tetherArray";
-    field public static final String EXTRA_AVAILABLE_TETHER = "availableArray";
-    field public static final String EXTRA_ERRORED_TETHER = "erroredArray";
-    field public static final int TETHERING_BLUETOOTH = 2; // 0x2
-    field public static final int TETHERING_ETHERNET = 5; // 0x5
-    field public static final int TETHERING_INVALID = -1; // 0xffffffff
-    field public static final int TETHERING_NCM = 4; // 0x4
-    field public static final int TETHERING_USB = 1; // 0x1
-    field public static final int TETHERING_WIFI = 0; // 0x0
-    field public static final int TETHERING_WIFI_P2P = 3; // 0x3
-    field public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; // 0xc
-    field public static final int TETHER_ERROR_DISABLE_FORWARDING_ERROR = 9; // 0x9
-    field public static final int TETHER_ERROR_ENABLE_FORWARDING_ERROR = 8; // 0x8
-    field public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; // 0xd
-    field public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; // 0xa
-    field public static final int TETHER_ERROR_INTERNAL_ERROR = 5; // 0x5
-    field public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; // 0xf
-    field public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; // 0xe
-    field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0
-    field public static final int TETHER_ERROR_PROVISIONING_FAILED = 11; // 0xb
-    field public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; // 0x2
-    field public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; // 0x6
-    field public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; // 0x4
-    field public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; // 0x1
-    field public static final int TETHER_ERROR_UNKNOWN_TYPE = 16; // 0x10
-    field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3
-    field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7
-    field public static final int TETHER_HARDWARE_OFFLOAD_FAILED = 2; // 0x2
-    field public static final int TETHER_HARDWARE_OFFLOAD_STARTED = 1; // 0x1
-    field public static final int TETHER_HARDWARE_OFFLOAD_STOPPED = 0; // 0x0
-  }
-
-  public static interface TetheringManager.OnTetheringEntitlementResultListener {
-    method public void onTetheringEntitlementResult(int);
-  }
-
-  public static interface TetheringManager.StartTetheringCallback {
-    method public default void onTetheringFailed(int);
-    method public default void onTetheringStarted();
-  }
-
-  public static interface TetheringManager.TetheringEventCallback {
-    method public default void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>);
-    method public default void onError(@NonNull String, int);
-    method public default void onOffloadStatusChanged(int);
-    method public default void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>);
-    method public default void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>);
-    method public default void onTetheringSupported(boolean);
-    method public default void onUpstreamChanged(@Nullable android.net.Network);
-  }
-
-  public static class TetheringManager.TetheringRequest {
-    method @Nullable public android.net.LinkAddress getClientStaticIpv4Address();
-    method @Nullable public android.net.LinkAddress getLocalIpv4Address();
-    method public boolean getShouldShowEntitlementUi();
-    method public int getTetheringType();
-    method public boolean isExemptFromEntitlementCheck();
-  }
-
-  public static class TetheringManager.TetheringRequest.Builder {
-    ctor public TetheringManager.TetheringRequest.Builder(int);
-    method @NonNull public android.net.TetheringManager.TetheringRequest build();
-    method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean);
-    method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setShouldShowEntitlementUi(boolean);
-    method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress);
-  }
-
   public class TrafficStats {
     method public static long getLoopbackRxBytes();
     method public static long getLoopbackRxPackets();
@@ -2380,228 +1106,8 @@
 
 }
 
-package android.net.apf {
-
-  public final class ApfCapabilities implements android.os.Parcelable {
-    ctor public ApfCapabilities(int, int, int);
-    method public int describeContents();
-    method public static boolean getApfDrop8023Frames();
-    method @NonNull public static int[] getApfEtherTypeBlackList();
-    method public boolean hasDataAccess();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.net.apf.ApfCapabilities> CREATOR;
-    field public final int apfPacketFormat;
-    field public final int apfVersionSupported;
-    field public final int maximumApfProgramSize;
-  }
-
-}
-
-package android.net.metrics {
-
-  public final class ApfProgramEvent implements android.net.metrics.IpConnectivityLog.Event {
-  }
-
-  public static final class ApfProgramEvent.Builder {
-    ctor public ApfProgramEvent.Builder();
-    method @NonNull public android.net.metrics.ApfProgramEvent build();
-    method @NonNull public android.net.metrics.ApfProgramEvent.Builder setActualLifetime(long);
-    method @NonNull public android.net.metrics.ApfProgramEvent.Builder setCurrentRas(int);
-    method @NonNull public android.net.metrics.ApfProgramEvent.Builder setFilteredRas(int);
-    method @NonNull public android.net.metrics.ApfProgramEvent.Builder setFlags(boolean, boolean);
-    method @NonNull public android.net.metrics.ApfProgramEvent.Builder setLifetime(long);
-    method @NonNull public android.net.metrics.ApfProgramEvent.Builder setProgramLength(int);
-  }
-
-  public final class ApfStats implements android.net.metrics.IpConnectivityLog.Event {
-  }
-
-  public static final class ApfStats.Builder {
-    ctor public ApfStats.Builder();
-    method @NonNull public android.net.metrics.ApfStats build();
-    method @NonNull public android.net.metrics.ApfStats.Builder setDroppedRas(int);
-    method @NonNull public android.net.metrics.ApfStats.Builder setDurationMs(long);
-    method @NonNull public android.net.metrics.ApfStats.Builder setMatchingRas(int);
-    method @NonNull public android.net.metrics.ApfStats.Builder setMaxProgramSize(int);
-    method @NonNull public android.net.metrics.ApfStats.Builder setParseErrors(int);
-    method @NonNull public android.net.metrics.ApfStats.Builder setProgramUpdates(int);
-    method @NonNull public android.net.metrics.ApfStats.Builder setProgramUpdatesAll(int);
-    method @NonNull public android.net.metrics.ApfStats.Builder setProgramUpdatesAllowingMulticast(int);
-    method @NonNull public android.net.metrics.ApfStats.Builder setReceivedRas(int);
-    method @NonNull public android.net.metrics.ApfStats.Builder setZeroLifetimeRas(int);
-  }
-
-  public final class DhcpClientEvent implements android.net.metrics.IpConnectivityLog.Event {
-  }
-
-  public static final class DhcpClientEvent.Builder {
-    ctor public DhcpClientEvent.Builder();
-    method @NonNull public android.net.metrics.DhcpClientEvent build();
-    method @NonNull public android.net.metrics.DhcpClientEvent.Builder setDurationMs(int);
-    method @NonNull public android.net.metrics.DhcpClientEvent.Builder setMsg(String);
-  }
-
-  public final class DhcpErrorEvent implements android.net.metrics.IpConnectivityLog.Event {
-    ctor public DhcpErrorEvent(int);
-    method public static int errorCodeWithOption(int, int);
-    field public static final int BOOTP_TOO_SHORT = 67174400; // 0x4010000
-    field public static final int BUFFER_UNDERFLOW = 83951616; // 0x5010000
-    field public static final int DHCP_BAD_MAGIC_COOKIE = 67239936; // 0x4020000
-    field public static final int DHCP_ERROR = 4; // 0x4
-    field public static final int DHCP_INVALID_OPTION_LENGTH = 67305472; // 0x4030000
-    field public static final int DHCP_NO_COOKIE = 67502080; // 0x4060000
-    field public static final int DHCP_NO_MSG_TYPE = 67371008; // 0x4040000
-    field public static final int DHCP_UNKNOWN_MSG_TYPE = 67436544; // 0x4050000
-    field public static final int L2_ERROR = 1; // 0x1
-    field public static final int L2_TOO_SHORT = 16842752; // 0x1010000
-    field public static final int L2_WRONG_ETH_TYPE = 16908288; // 0x1020000
-    field public static final int L3_ERROR = 2; // 0x2
-    field public static final int L3_INVALID_IP = 33751040; // 0x2030000
-    field public static final int L3_NOT_IPV4 = 33685504; // 0x2020000
-    field public static final int L3_TOO_SHORT = 33619968; // 0x2010000
-    field public static final int L4_ERROR = 3; // 0x3
-    field public static final int L4_NOT_UDP = 50397184; // 0x3010000
-    field public static final int L4_WRONG_PORT = 50462720; // 0x3020000
-    field public static final int MISC_ERROR = 5; // 0x5
-    field public static final int PARSING_ERROR = 84082688; // 0x5030000
-    field public static final int RECEIVE_ERROR = 84017152; // 0x5020000
-  }
-
-  public class IpConnectivityLog {
-    ctor public IpConnectivityLog();
-    method public boolean log(long, @NonNull android.net.metrics.IpConnectivityLog.Event);
-    method public boolean log(@NonNull String, @NonNull android.net.metrics.IpConnectivityLog.Event);
-    method public boolean log(@NonNull android.net.Network, @NonNull int[], @NonNull android.net.metrics.IpConnectivityLog.Event);
-    method public boolean log(int, @NonNull int[], @NonNull android.net.metrics.IpConnectivityLog.Event);
-    method public boolean log(@NonNull android.net.metrics.IpConnectivityLog.Event);
-  }
-
-  public static interface IpConnectivityLog.Event extends android.os.Parcelable {
-  }
-
-  public final class IpManagerEvent implements android.net.metrics.IpConnectivityLog.Event {
-    ctor public IpManagerEvent(int, long);
-    field public static final int COMPLETE_LIFECYCLE = 3; // 0x3
-    field public static final int ERROR_INTERFACE_NOT_FOUND = 8; // 0x8
-    field public static final int ERROR_INVALID_PROVISIONING = 7; // 0x7
-    field public static final int ERROR_STARTING_IPREACHABILITYMONITOR = 6; // 0x6
-    field public static final int ERROR_STARTING_IPV4 = 4; // 0x4
-    field public static final int ERROR_STARTING_IPV6 = 5; // 0x5
-    field public static final int PROVISIONING_FAIL = 2; // 0x2
-    field public static final int PROVISIONING_OK = 1; // 0x1
-  }
-
-  public final class IpReachabilityEvent implements android.net.metrics.IpConnectivityLog.Event {
-    ctor public IpReachabilityEvent(int);
-    field public static final int NUD_FAILED = 512; // 0x200
-    field public static final int NUD_FAILED_ORGANIC = 1024; // 0x400
-    field public static final int PROBE = 256; // 0x100
-    field public static final int PROVISIONING_LOST = 768; // 0x300
-    field public static final int PROVISIONING_LOST_ORGANIC = 1280; // 0x500
-  }
-
-  public final class NetworkEvent implements android.net.metrics.IpConnectivityLog.Event {
-    ctor public NetworkEvent(int, long);
-    ctor public NetworkEvent(int);
-    field public static final int NETWORK_CAPTIVE_PORTAL_FOUND = 4; // 0x4
-    field public static final int NETWORK_CONNECTED = 1; // 0x1
-    field public static final int NETWORK_CONSECUTIVE_DNS_TIMEOUT_FOUND = 12; // 0xc
-    field public static final int NETWORK_DISCONNECTED = 7; // 0x7
-    field public static final int NETWORK_FIRST_VALIDATION_PORTAL_FOUND = 10; // 0xa
-    field public static final int NETWORK_FIRST_VALIDATION_SUCCESS = 8; // 0x8
-    field public static final int NETWORK_LINGER = 5; // 0x5
-    field public static final int NETWORK_PARTIAL_CONNECTIVITY = 13; // 0xd
-    field public static final int NETWORK_REVALIDATION_PORTAL_FOUND = 11; // 0xb
-    field public static final int NETWORK_REVALIDATION_SUCCESS = 9; // 0x9
-    field public static final int NETWORK_UNLINGER = 6; // 0x6
-    field public static final int NETWORK_VALIDATED = 2; // 0x2
-    field public static final int NETWORK_VALIDATION_FAILED = 3; // 0x3
-  }
-
-  public final class RaEvent implements android.net.metrics.IpConnectivityLog.Event {
-  }
-
-  public static final class RaEvent.Builder {
-    ctor public RaEvent.Builder();
-    method @NonNull public android.net.metrics.RaEvent build();
-    method @NonNull public android.net.metrics.RaEvent.Builder updateDnsslLifetime(long);
-    method @NonNull public android.net.metrics.RaEvent.Builder updatePrefixPreferredLifetime(long);
-    method @NonNull public android.net.metrics.RaEvent.Builder updatePrefixValidLifetime(long);
-    method @NonNull public android.net.metrics.RaEvent.Builder updateRdnssLifetime(long);
-    method @NonNull public android.net.metrics.RaEvent.Builder updateRouteInfoLifetime(long);
-    method @NonNull public android.net.metrics.RaEvent.Builder updateRouterLifetime(long);
-  }
-
-  public final class ValidationProbeEvent implements android.net.metrics.IpConnectivityLog.Event {
-    method @NonNull public static String getProbeName(int);
-    field public static final int DNS_FAILURE = 0; // 0x0
-    field public static final int DNS_SUCCESS = 1; // 0x1
-    field public static final int PROBE_DNS = 0; // 0x0
-    field public static final int PROBE_FALLBACK = 4; // 0x4
-    field public static final int PROBE_HTTP = 1; // 0x1
-    field public static final int PROBE_HTTPS = 2; // 0x2
-    field public static final int PROBE_PAC = 3; // 0x3
-    field public static final int PROBE_PRIVDNS = 5; // 0x5
-  }
-
-  public static final class ValidationProbeEvent.Builder {
-    ctor public ValidationProbeEvent.Builder();
-    method @NonNull public android.net.metrics.ValidationProbeEvent build();
-    method @NonNull public android.net.metrics.ValidationProbeEvent.Builder setDurationMs(long);
-    method @NonNull public android.net.metrics.ValidationProbeEvent.Builder setProbeType(int, boolean);
-    method @NonNull public android.net.metrics.ValidationProbeEvent.Builder setReturnCode(int);
-  }
-
-}
-
-package android.net.util {
-
-  public final class SocketUtils {
-    method public static void bindSocketToInterface(@NonNull java.io.FileDescriptor, @NonNull String) throws android.system.ErrnoException;
-    method public static void closeSocket(@Nullable java.io.FileDescriptor) throws java.io.IOException;
-    method @NonNull public static java.net.SocketAddress makeNetlinkSocketAddress(int, int);
-    method @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, int);
-    method @Deprecated @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, @NonNull byte[]);
-    method @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, int, @NonNull byte[]);
-  }
-
-}
-
 package android.os {
 
-  public class BatteryManager {
-    method @RequiresPermission("android.permission.POWER_SAVER") public boolean setChargingStateUpdateDelayMillis(int);
-  }
-
-  public final class BugreportManager {
-    method @RequiresPermission(android.Manifest.permission.DUMP) public void cancelBugreport();
-    method @RequiresPermission(android.Manifest.permission.DUMP) public void requestBugreport(@NonNull android.os.BugreportParams, @Nullable CharSequence, @Nullable CharSequence);
-    method @RequiresPermission(android.Manifest.permission.DUMP) public void startBugreport(@NonNull android.os.ParcelFileDescriptor, @Nullable android.os.ParcelFileDescriptor, @NonNull android.os.BugreportParams, @NonNull java.util.concurrent.Executor, @NonNull android.os.BugreportManager.BugreportCallback);
-  }
-
-  public abstract static class BugreportManager.BugreportCallback {
-    ctor public BugreportManager.BugreportCallback();
-    method public void onError(int);
-    method public void onFinished();
-    method public void onProgress(@FloatRange(from=0.0f, to=100.0f) float);
-    field public static final int BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS = 5; // 0x5
-    field public static final int BUGREPORT_ERROR_INVALID_INPUT = 1; // 0x1
-    field public static final int BUGREPORT_ERROR_RUNTIME = 2; // 0x2
-    field public static final int BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT = 4; // 0x4
-    field public static final int BUGREPORT_ERROR_USER_DENIED_CONSENT = 3; // 0x3
-  }
-
-  public final class BugreportParams {
-    ctor public BugreportParams(int);
-    method public int getMode();
-    field public static final int BUGREPORT_MODE_FULL = 0; // 0x0
-    field public static final int BUGREPORT_MODE_INTERACTIVE = 1; // 0x1
-    field public static final int BUGREPORT_MODE_REMOTE = 2; // 0x2
-    field public static final int BUGREPORT_MODE_TELEPHONY = 4; // 0x4
-    field public static final int BUGREPORT_MODE_WEAR = 3; // 0x3
-    field public static final int BUGREPORT_MODE_WIFI = 5; // 0x5
-  }
-
   public class Build {
     method public static boolean is64BitAbi(String);
     field public static final boolean IS_EMULATOR;
@@ -2614,18 +1120,12 @@
   }
 
   public class DeviceIdleManager {
-    method @RequiresPermission("android.permission.DEVICE_POWER") public void endIdle(@NonNull String);
     method @NonNull public String[] getSystemPowerWhitelist();
     method @NonNull public String[] getSystemPowerWhitelistExceptIdle();
   }
 
   public class Environment {
     method public static java.io.File buildPath(java.io.File, java.lang.String...);
-    method @NonNull public static java.io.File getOdmDirectory();
-    method @NonNull public static java.io.File getOemDirectory();
-    method @NonNull public static java.io.File getProductDirectory();
-    method @NonNull public static java.io.File getSystemExtDirectory();
-    method @NonNull public static java.io.File getVendorDirectory();
   }
 
   public final class FileUtils {
@@ -2634,228 +1134,11 @@
     method @NonNull public static byte[] digest(@NonNull java.io.InputStream, @NonNull String) throws java.io.IOException, java.security.NoSuchAlgorithmException;
   }
 
-  public class HidlMemory implements java.io.Closeable {
-    ctor public HidlMemory(@NonNull String, @IntRange(from=0) long, @Nullable android.os.NativeHandle);
-    method public void close() throws java.io.IOException;
-    method @NonNull public android.os.HidlMemory dup() throws java.io.IOException;
-    method protected void finalize();
-    method @Nullable public android.os.NativeHandle getHandle();
-    method @NonNull public String getName();
-    method public long getSize();
-    method @Nullable public android.os.NativeHandle releaseHandle();
-  }
-
-  public abstract class HwBinder implements android.os.IHwBinder {
-    ctor public HwBinder();
-    method public static final void configureRpcThreadpool(long, boolean);
-    method public static void enableInstrumentation();
-    method public static final android.os.IHwBinder getService(String, String) throws java.util.NoSuchElementException, android.os.RemoteException;
-    method public static final android.os.IHwBinder getService(String, String, boolean) throws java.util.NoSuchElementException, android.os.RemoteException;
-    method public static final void joinRpcThreadpool();
-    method public abstract void onTransact(int, android.os.HwParcel, android.os.HwParcel, int) throws android.os.RemoteException;
-    method public final void registerService(String) throws android.os.RemoteException;
-    method public final void transact(int, android.os.HwParcel, android.os.HwParcel, int) throws android.os.RemoteException;
-  }
-
-  public class HwBlob {
-    ctor public HwBlob(int);
-    method public final void copyToBoolArray(long, boolean[], int);
-    method public final void copyToDoubleArray(long, double[], int);
-    method public final void copyToFloatArray(long, float[], int);
-    method public final void copyToInt16Array(long, short[], int);
-    method public final void copyToInt32Array(long, int[], int);
-    method public final void copyToInt64Array(long, long[], int);
-    method public final void copyToInt8Array(long, byte[], int);
-    method public final boolean getBool(long);
-    method public final double getDouble(long);
-    method public final long getFieldHandle(long);
-    method public final float getFloat(long);
-    method public final short getInt16(long);
-    method public final int getInt32(long);
-    method public final long getInt64(long);
-    method public final byte getInt8(long);
-    method public final String getString(long);
-    method public final long handle();
-    method public final void putBlob(long, android.os.HwBlob);
-    method public final void putBool(long, boolean);
-    method public final void putBoolArray(long, boolean[]);
-    method public final void putDouble(long, double);
-    method public final void putDoubleArray(long, double[]);
-    method public final void putFloat(long, float);
-    method public final void putFloatArray(long, float[]);
-    method public final void putHidlMemory(long, @NonNull android.os.HidlMemory);
-    method public final void putInt16(long, short);
-    method public final void putInt16Array(long, short[]);
-    method public final void putInt32(long, int);
-    method public final void putInt32Array(long, int[]);
-    method public final void putInt64(long, long);
-    method public final void putInt64Array(long, long[]);
-    method public final void putInt8(long, byte);
-    method public final void putInt8Array(long, byte[]);
-    method public final void putNativeHandle(long, @Nullable android.os.NativeHandle);
-    method public final void putString(long, String);
-    method public static Boolean[] wrapArray(@NonNull boolean[]);
-    method public static Long[] wrapArray(@NonNull long[]);
-    method public static Byte[] wrapArray(@NonNull byte[]);
-    method public static Short[] wrapArray(@NonNull short[]);
-    method public static Integer[] wrapArray(@NonNull int[]);
-    method public static Float[] wrapArray(@NonNull float[]);
-    method public static Double[] wrapArray(@NonNull double[]);
-  }
-
-  public class HwParcel {
-    ctor public HwParcel();
-    method public final void enforceInterface(String);
-    method public final boolean readBool();
-    method public final java.util.ArrayList<java.lang.Boolean> readBoolVector();
-    method public final android.os.HwBlob readBuffer(long);
-    method public final double readDouble();
-    method public final java.util.ArrayList<java.lang.Double> readDoubleVector();
-    method public final android.os.HwBlob readEmbeddedBuffer(long, long, long, boolean);
-    method @NonNull @Nullable public final android.os.HidlMemory readEmbeddedHidlMemory(long, long, long);
-    method @Nullable public final android.os.NativeHandle readEmbeddedNativeHandle(long, long);
-    method public final float readFloat();
-    method public final java.util.ArrayList<java.lang.Float> readFloatVector();
-    method @NonNull public final android.os.HidlMemory readHidlMemory();
-    method public final short readInt16();
-    method public final java.util.ArrayList<java.lang.Short> readInt16Vector();
-    method public final int readInt32();
-    method public final java.util.ArrayList<java.lang.Integer> readInt32Vector();
-    method public final long readInt64();
-    method public final java.util.ArrayList<java.lang.Long> readInt64Vector();
-    method public final byte readInt8();
-    method public final java.util.ArrayList<java.lang.Byte> readInt8Vector();
-    method @Nullable public final android.os.NativeHandle readNativeHandle();
-    method @NonNull public final java.util.ArrayList<android.os.NativeHandle> readNativeHandleVector();
-    method public final String readString();
-    method public final java.util.ArrayList<java.lang.String> readStringVector();
-    method public final android.os.IHwBinder readStrongBinder();
-    method public final void release();
-    method public final void releaseTemporaryStorage();
-    method public final void send();
-    method public final void verifySuccess();
-    method public final void writeBool(boolean);
-    method public final void writeBoolVector(java.util.ArrayList<java.lang.Boolean>);
-    method public final void writeBuffer(android.os.HwBlob);
-    method public final void writeDouble(double);
-    method public final void writeDoubleVector(java.util.ArrayList<java.lang.Double>);
-    method public final void writeFloat(float);
-    method public final void writeFloatVector(java.util.ArrayList<java.lang.Float>);
-    method public final void writeHidlMemory(@NonNull android.os.HidlMemory);
-    method public final void writeInt16(short);
-    method public final void writeInt16Vector(java.util.ArrayList<java.lang.Short>);
-    method public final void writeInt32(int);
-    method public final void writeInt32Vector(java.util.ArrayList<java.lang.Integer>);
-    method public final void writeInt64(long);
-    method public final void writeInt64Vector(java.util.ArrayList<java.lang.Long>);
-    method public final void writeInt8(byte);
-    method public final void writeInt8Vector(java.util.ArrayList<java.lang.Byte>);
-    method public final void writeInterfaceToken(String);
-    method public final void writeNativeHandle(@Nullable android.os.NativeHandle);
-    method public final void writeNativeHandleVector(@NonNull java.util.ArrayList<android.os.NativeHandle>);
-    method public final void writeStatus(int);
-    method public final void writeString(String);
-    method public final void writeStringVector(java.util.ArrayList<java.lang.String>);
-    method public final void writeStrongBinder(android.os.IHwBinder);
-    field public static final int STATUS_SUCCESS = 0; // 0x0
-  }
-
-  public interface IHwBinder {
-    method public boolean linkToDeath(android.os.IHwBinder.DeathRecipient, long);
-    method public android.os.IHwInterface queryLocalInterface(String);
-    method public void transact(int, android.os.HwParcel, android.os.HwParcel, int) throws android.os.RemoteException;
-    method public boolean unlinkToDeath(android.os.IHwBinder.DeathRecipient);
-  }
-
-  public static interface IHwBinder.DeathRecipient {
-    method public void serviceDied(long);
-  }
-
-  public interface IHwInterface {
-    method public android.os.IHwBinder asBinder();
-  }
-
-  public class IncidentManager {
-    method @RequiresPermission(android.Manifest.permission.APPROVE_INCIDENT_REPORTS) public void approveReport(android.net.Uri);
-    method @RequiresPermission("android.permission.REQUEST_INCIDENT_REPORT_APPROVAL") public void cancelAuthorization(android.os.IncidentManager.AuthListener);
-    method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void deleteIncidentReports(android.net.Uri);
-    method @RequiresPermission(android.Manifest.permission.APPROVE_INCIDENT_REPORTS) public void denyReport(android.net.Uri);
-    method @Nullable @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public android.os.IncidentManager.IncidentReport getIncidentReport(android.net.Uri);
-    method @NonNull @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public java.util.List<android.net.Uri> getIncidentReportList(String);
-    method @RequiresPermission(android.Manifest.permission.APPROVE_INCIDENT_REPORTS) public java.util.List<android.os.IncidentManager.PendingReport> getPendingReports();
-    method public void registerSection(int, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.IncidentManager.DumpCallback);
-    method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void reportIncident(android.os.IncidentReportArgs);
-    method @RequiresPermission("android.permission.REQUEST_INCIDENT_REPORT_APPROVAL") public void requestAuthorization(int, String, int, android.os.IncidentManager.AuthListener);
-    method public void unregisterSection(int);
-    field public static final int FLAG_CONFIRMATION_DIALOG = 1; // 0x1
-    field public static final int PRIVACY_POLICY_AUTO = 200; // 0xc8
-    field public static final int PRIVACY_POLICY_EXPLICIT = 100; // 0x64
-    field public static final int PRIVACY_POLICY_LOCAL = 0; // 0x0
-  }
-
-  public static class IncidentManager.AuthListener {
-    ctor public IncidentManager.AuthListener();
-    method public void onReportApproved();
-    method public void onReportDenied();
-  }
-
-  public static class IncidentManager.DumpCallback {
-    ctor public IncidentManager.DumpCallback();
-    method public void onDumpSection(int, @NonNull java.io.OutputStream);
-  }
-
-  public static class IncidentManager.IncidentReport implements java.io.Closeable android.os.Parcelable {
-    ctor public IncidentManager.IncidentReport(android.os.Parcel);
-    method public void close();
-    method public int describeContents();
-    method public java.io.InputStream getInputStream() throws java.io.IOException;
-    method public long getPrivacyPolicy();
-    method public long getTimestamp();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.os.IncidentManager.IncidentReport> CREATOR;
-  }
-
-  public static class IncidentManager.PendingReport {
-    ctor public IncidentManager.PendingReport(@NonNull android.net.Uri);
-    method public int getFlags();
-    method @NonNull public String getRequestingPackage();
-    method public long getTimestamp();
-    method @NonNull public android.net.Uri getUri();
-  }
-
-  public final class IncidentReportArgs implements android.os.Parcelable {
-    ctor public IncidentReportArgs();
-    ctor public IncidentReportArgs(android.os.Parcel);
-    method public void addHeader(byte[]);
-    method public void addSection(int);
-    method public boolean containsSection(int);
-    method public int describeContents();
-    method public boolean isAll();
-    method public void readFromParcel(android.os.Parcel);
-    method public int sectionCount();
-    method public void setAll(boolean);
-    method public void setPrivacyPolicy(int);
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.os.IncidentReportArgs> CREATOR;
-  }
-
   public final class MessageQueue {
     method public int postSyncBarrier();
     method public void removeSyncBarrier(int);
   }
 
-  public final class NativeHandle implements java.io.Closeable {
-    ctor public NativeHandle();
-    ctor public NativeHandle(@NonNull java.io.FileDescriptor, boolean);
-    ctor public NativeHandle(@NonNull java.io.FileDescriptor[], @NonNull int[], boolean);
-    method public void close() throws java.io.IOException;
-    method @NonNull public android.os.NativeHandle dup() throws java.io.IOException;
-    method @NonNull public java.io.FileDescriptor getFileDescriptor();
-    method @NonNull public java.io.FileDescriptor[] getFileDescriptors();
-    method @NonNull public int[] getInts();
-    method public boolean hasSingleFileDescriptor();
-  }
-
   public final class Parcel {
     method public boolean allowSquashing();
     method public int readExceptionCode();
@@ -2867,24 +1150,7 @@
   }
 
   public final class PowerManager {
-    method @RequiresPermission("android.permission.POWER_SAVER") public int getPowerSaveModeTrigger();
-    method @RequiresPermission("android.permission.DEVICE_POWER") public void setBatteryDischargePrediction(@NonNull java.time.Duration, boolean);
-    method @RequiresPermission("android.permission.POWER_SAVER") public boolean setDynamicPowerSaveHint(boolean, int);
-    method @RequiresPermission(anyOf={"android.permission.DEVICE_POWER", "android.permission.POWER_SAVER"}) public boolean setPowerSaveModeEnabled(boolean);
     field public static final String ACTION_ENHANCED_DISCHARGE_PREDICTION_CHANGED = "android.os.action.ENHANCED_DISCHARGE_PREDICTION_CHANGED";
-    field public static final int POWER_SAVE_MODE_TRIGGER_DYNAMIC = 1; // 0x1
-    field public static final int POWER_SAVE_MODE_TRIGGER_PERCENTAGE = 0; // 0x0
-  }
-
-  public class PowerWhitelistManager {
-    method @RequiresPermission("android.permission.DEVICE_POWER") public void addToWhitelist(@NonNull String);
-    method @RequiresPermission("android.permission.DEVICE_POWER") public void addToWhitelist(@NonNull java.util.List<java.lang.String>);
-    method @RequiresPermission("android.permission.DEVICE_POWER") public void removeFromWhitelist(@NonNull String);
-    method @RequiresPermission("android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST") public void whitelistAppTemporarily(@NonNull String, long);
-    method @RequiresPermission("android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST") public long whitelistAppTemporarilyForEvent(@NonNull String, int, @NonNull String);
-    field public static final int EVENT_MMS = 2; // 0x2
-    field public static final int EVENT_SMS = 1; // 0x1
-    field public static final int EVENT_UNSPECIFIED = 0; // 0x0
   }
 
   public class Process {
@@ -2896,19 +1162,6 @@
     field public static final int NUM_UIDS_PER_APP_ZYGOTE = 100; // 0x64
   }
 
-  public final class RemoteCallback implements android.os.Parcelable {
-    ctor public RemoteCallback(android.os.RemoteCallback.OnResultListener);
-    ctor public RemoteCallback(@NonNull android.os.RemoteCallback.OnResultListener, @Nullable android.os.Handler);
-    method public int describeContents();
-    method public void sendResult(@Nullable android.os.Bundle);
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.os.RemoteCallback> CREATOR;
-  }
-
-  public static interface RemoteCallback.OnResultListener {
-    method public void onResult(@Nullable android.os.Bundle);
-  }
-
   public final class StrictMode {
     method public static void conditionallyCheckInstanceCounts();
     method public static void setViolationLogger(android.os.StrictMode.ViolationLogger);
@@ -2946,44 +1199,23 @@
     method @NonNull public android.os.StrictMode.VmPolicy.Builder permitIncorrectContextUse();
   }
 
-  public class SystemConfigManager {
-    method @NonNull @RequiresPermission("android.permission.READ_CARRIER_APP_INFO") public java.util.Set<java.lang.String> getDisabledUntilUsedPreinstalledCarrierApps();
-    method @NonNull @RequiresPermission("android.permission.READ_CARRIER_APP_INFO") public java.util.Map<java.lang.String,java.util.List<java.lang.String>> getDisabledUntilUsedPreinstalledCarrierAssociatedApps();
-  }
-
-  public class SystemProperties {
-    method @NonNull public static String get(@NonNull String);
-    method @NonNull public static String get(@NonNull String, @Nullable String);
-    method public static boolean getBoolean(@NonNull String, boolean);
-    method public static int getInt(@NonNull String, int);
-    method public static long getLong(@NonNull String, long);
-  }
-
   public final class UserHandle implements android.os.Parcelable {
-    method public static int getAppId(int);
-    method public int getIdentifier();
     method public static int getUid(int, int);
     method public static int getUserId(int);
     method public static boolean isApp(int);
-    method public static int myUserId();
-    method public static android.os.UserHandle of(int);
-    field @NonNull public static final android.os.UserHandle ALL;
-    field @NonNull public static final android.os.UserHandle CURRENT;
     field public static final int MIN_SECONDARY_USER_ID = 10; // 0xa
-    field @NonNull public static final android.os.UserHandle SYSTEM;
     field public static final int USER_ALL = -1; // 0xffffffff
     field public static final int USER_NULL = -10000; // 0xffffd8f0
     field public static final int USER_SYSTEM = 0; // 0x0
   }
 
   public class UserManager {
-    method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.CREATE_USERS"}) public boolean hasBaseUserRestriction(@NonNull String, @NonNull android.os.UserHandle);
+    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean hasBaseUserRestriction(@NonNull String, @NonNull android.os.UserHandle);
     method public static boolean isSplitSystemUser();
-    field public static final String ACTION_USER_RESTRICTIONS_CHANGED = "android.os.action.USER_RESTRICTIONS_CHANGED";
   }
 
   public final class VibrationAttributes implements android.os.Parcelable {
-    method @Deprecated @NonNull public android.media.AudioAttributes getAudioAttributes();
+    method public int getAudioUsage();
   }
 
   public static final class VibrationAttributes.Builder {
@@ -3036,17 +1268,6 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.os.VibrationEffect.Waveform> CREATOR;
   }
 
-  public abstract class Vibrator {
-    method @RequiresPermission("android.permission.ACCESS_VIBRATOR_STATE") public void addVibratorStateListener(@NonNull android.os.Vibrator.OnVibratorStateChangedListener);
-    method @RequiresPermission("android.permission.ACCESS_VIBRATOR_STATE") public void addVibratorStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.os.Vibrator.OnVibratorStateChangedListener);
-    method @RequiresPermission("android.permission.ACCESS_VIBRATOR_STATE") public boolean isVibrating();
-    method @RequiresPermission("android.permission.ACCESS_VIBRATOR_STATE") public void removeVibratorStateListener(@NonNull android.os.Vibrator.OnVibratorStateChangedListener);
-  }
-
-  public static interface Vibrator.OnVibratorStateChangedListener {
-    method public void onVibratorStateChanged(boolean);
-  }
-
   public class VintfObject {
     method public static String[] getHalNamesAndVersions();
     method public static String getSepolicyVersion();
@@ -3066,16 +1287,10 @@
   }
 
   public class WorkSource implements android.os.Parcelable {
-    ctor public WorkSource(int);
     method public boolean add(int);
     method public boolean add(int, String);
     method @Deprecated public android.os.WorkSource addReturningNewbs(android.os.WorkSource);
-    method @Nullable public String getPackageName(int);
-    method public int getUid(int);
-    method public boolean isEmpty();
     method @Deprecated public android.os.WorkSource[] setReturningDiffs(android.os.WorkSource);
-    method public int size();
-    method @NonNull public android.os.WorkSource withoutNames();
   }
 
 }
@@ -3135,36 +1350,6 @@
 
 }
 
-package android.os.image {
-
-  public class DynamicSystemClient {
-    ctor public DynamicSystemClient(@NonNull android.content.Context);
-    method @RequiresPermission("android.permission.INSTALL_DYNAMIC_SYSTEM") public void bind();
-    method public void setOnStatusChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.os.image.DynamicSystemClient.OnStatusChangedListener);
-    method public void setOnStatusChangedListener(@NonNull android.os.image.DynamicSystemClient.OnStatusChangedListener);
-    method @RequiresPermission("android.permission.INSTALL_DYNAMIC_SYSTEM") public void start(@NonNull android.net.Uri, long);
-    method @RequiresPermission("android.permission.INSTALL_DYNAMIC_SYSTEM") public void start(@NonNull android.net.Uri, long, long);
-    method @RequiresPermission("android.permission.INSTALL_DYNAMIC_SYSTEM") public void unbind();
-    field public static final int CAUSE_ERROR_EXCEPTION = 6; // 0x6
-    field public static final int CAUSE_ERROR_INVALID_URL = 4; // 0x4
-    field public static final int CAUSE_ERROR_IO = 3; // 0x3
-    field public static final int CAUSE_ERROR_IPC = 5; // 0x5
-    field public static final int CAUSE_INSTALL_CANCELLED = 2; // 0x2
-    field public static final int CAUSE_INSTALL_COMPLETED = 1; // 0x1
-    field public static final int CAUSE_NOT_SPECIFIED = 0; // 0x0
-    field public static final int STATUS_IN_PROGRESS = 2; // 0x2
-    field public static final int STATUS_IN_USE = 4; // 0x4
-    field public static final int STATUS_NOT_STARTED = 1; // 0x1
-    field public static final int STATUS_READY = 3; // 0x3
-    field public static final int STATUS_UNKNOWN = 0; // 0x0
-  }
-
-  public static interface DynamicSystemClient.OnStatusChangedListener {
-    method public void onStatusChanged(int, int, long, @Nullable Throwable);
-  }
-
-}
-
 package android.os.storage {
 
   public final class CrateInfo implements android.os.Parcelable {
@@ -3178,10 +1363,6 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.os.storage.CrateInfo> CREATOR;
   }
 
-  public class StorageManager {
-    method public static boolean hasIsolatedStorage();
-  }
-
   public final class StorageVolume implements android.os.Parcelable {
     method public String getPath();
   }
@@ -3198,17 +1379,9 @@
 package android.permission {
 
   public final class PermissionControllerManager {
-    method @RequiresPermission(anyOf={"android.permission.GRANT_RUNTIME_PERMISSIONS", "android.permission.RESTORE_RUNTIME_PERMISSIONS"}) public void applyStagedRuntimePermissionBackup(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
-    method @RequiresPermission("android.permission.GET_RUNTIME_PERMISSIONS") public void countPermissionApps(@NonNull java.util.List<java.lang.String>, int, @NonNull android.permission.PermissionControllerManager.OnCountPermissionAppsResultCallback, @Nullable android.os.Handler);
-    method @RequiresPermission("android.permission.GET_RUNTIME_PERMISSIONS") public void getAppPermissions(@NonNull String, @NonNull android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, @Nullable android.os.Handler);
-    method @RequiresPermission("android.permission.GET_RUNTIME_PERMISSIONS") public void getRuntimePermissionBackup(@NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<byte[]>);
-    method @RequiresPermission("android.permission.REVOKE_RUNTIME_PERMISSIONS") public void revokeRuntimePermission(@NonNull String, @NonNull String);
-    method @RequiresPermission("android.permission.REVOKE_RUNTIME_PERMISSIONS") public void revokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull java.util.concurrent.Executor, @NonNull android.permission.PermissionControllerManager.OnRevokeRuntimePermissionsCallback);
-    method @RequiresPermission(anyOf={"android.permission.GRANT_RUNTIME_PERMISSIONS", "android.permission.RESTORE_RUNTIME_PERMISSIONS"}) public void stageAndApplyRuntimePermissionsBackup(@NonNull byte[], @NonNull android.os.UserHandle);
-    field public static final int COUNT_ONLY_WHEN_GRANTED = 1; // 0x1
-    field public static final int COUNT_WHEN_SYSTEM = 2; // 0x2
-    field public static final int REASON_INSTALLER_POLICY_VIOLATION = 2; // 0x2
-    field public static final int REASON_MALWARE = 1; // 0x1
+    method @RequiresPermission(android.Manifest.permission.GET_RUNTIME_PERMISSIONS) public void countPermissionApps(@NonNull java.util.List<java.lang.String>, int, @NonNull android.permission.PermissionControllerManager.OnCountPermissionAppsResultCallback, @Nullable android.os.Handler);
+    method @RequiresPermission(android.Manifest.permission.GET_RUNTIME_PERMISSIONS) public void getAppPermissions(@NonNull String, @NonNull android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, @Nullable android.os.Handler);
+    method @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) public void revokeRuntimePermission(@NonNull String, @NonNull String);
   }
 
   public static interface PermissionControllerManager.OnCountPermissionAppsResultCallback {
@@ -3219,33 +1392,6 @@
     method public void onGetAppPermissions(@NonNull java.util.List<android.permission.RuntimePermissionPresentationInfo>);
   }
 
-  public abstract static class PermissionControllerManager.OnRevokeRuntimePermissionsCallback {
-    ctor public PermissionControllerManager.OnRevokeRuntimePermissionsCallback();
-    method public abstract void onRevokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>);
-  }
-
-  public final class PermissionManager {
-    method @IntRange(from=0) @RequiresPermission(anyOf={"android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY", android.Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS}) public int getRuntimePermissionsVersion();
-    method @NonNull public java.util.List<android.permission.PermissionManager.SplitPermissionInfo> getSplitPermissions();
-    method @RequiresPermission(anyOf={"android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY", android.Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS}) public void setRuntimePermissionsVersion(@IntRange(from=0) int);
-  }
-
-  public static final class PermissionManager.SplitPermissionInfo {
-    method @NonNull public java.util.List<java.lang.String> getNewPermissions();
-    method @NonNull public String getSplitPermission();
-    method public int getTargetSdk();
-  }
-
-  public final class RuntimePermissionPresentationInfo implements android.os.Parcelable {
-    ctor public RuntimePermissionPresentationInfo(@NonNull CharSequence, boolean, boolean);
-    method public int describeContents();
-    method @NonNull public CharSequence getLabel();
-    method public boolean isGranted();
-    method public boolean isStandard();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.permission.RuntimePermissionPresentationInfo> CREATOR;
-  }
-
 }
 
 package android.print {
@@ -3293,86 +1439,24 @@
   }
 
   public final class DeviceConfig {
-    method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static void addOnPropertiesChangedListener(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.provider.DeviceConfig.OnPropertiesChangedListener);
-    method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static boolean getBoolean(@NonNull String, @NonNull String, boolean);
-    method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static float getFloat(@NonNull String, @NonNull String, float);
-    method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static int getInt(@NonNull String, @NonNull String, int);
-    method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static long getLong(@NonNull String, @NonNull String, long);
-    method @NonNull @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static android.provider.DeviceConfig.Properties getProperties(@NonNull String, @NonNull java.lang.String...);
-    method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static String getProperty(@NonNull String, @NonNull String);
-    method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static String getString(@NonNull String, @NonNull String, @Nullable String);
-    method public static void removeOnPropertiesChangedListener(@NonNull android.provider.DeviceConfig.OnPropertiesChangedListener);
-    method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static void resetToDefaults(int, @Nullable String);
-    method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean setProperties(@NonNull android.provider.DeviceConfig.Properties) throws android.provider.DeviceConfig.BadConfigException;
-    method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean setProperty(@NonNull String, @NonNull String, @Nullable String, boolean);
     field public static final String NAMESPACE_ALARM_MANAGER = "alarm_manager";
     field public static final String NAMESPACE_ANDROID = "android";
-    field public static final String NAMESPACE_AUTOFILL = "autofill";
-    field public static final String NAMESPACE_BIOMETRICS = "biometrics";
-    field public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture";
     field public static final String NAMESPACE_DEVICE_IDLE = "device_idle";
     field public static final String NAMESPACE_JOB_SCHEDULER = "jobscheduler";
-    field public static final String NAMESPACE_PERMISSIONS = "permissions";
-    field public static final String NAMESPACE_PRIVACY = "privacy";
-    field public static final String NAMESPACE_ROLLBACK = "rollback";
-    field public static final String NAMESPACE_ROLLBACK_BOOT = "rollback_boot";
-  }
-
-  public static class DeviceConfig.BadConfigException extends java.lang.Exception {
-    ctor public DeviceConfig.BadConfigException();
-  }
-
-  public static interface DeviceConfig.OnPropertiesChangedListener {
-    method public void onPropertiesChanged(@NonNull android.provider.DeviceConfig.Properties);
-  }
-
-  public static class DeviceConfig.Properties {
-    method public boolean getBoolean(@NonNull String, boolean);
-    method public float getFloat(@NonNull String, float);
-    method public int getInt(@NonNull String, int);
-    method @NonNull public java.util.Set<java.lang.String> getKeyset();
-    method public long getLong(@NonNull String, long);
-    method @NonNull public String getNamespace();
-    method @Nullable public String getString(@NonNull String, @Nullable String);
-  }
-
-  public static final class DeviceConfig.Properties.Builder {
-    ctor public DeviceConfig.Properties.Builder(@NonNull String);
-    method @NonNull public android.provider.DeviceConfig.Properties build();
-    method @NonNull public android.provider.DeviceConfig.Properties.Builder setBoolean(@NonNull String, boolean);
-    method @NonNull public android.provider.DeviceConfig.Properties.Builder setFloat(@NonNull String, float);
-    method @NonNull public android.provider.DeviceConfig.Properties.Builder setInt(@NonNull String, int);
-    method @NonNull public android.provider.DeviceConfig.Properties.Builder setLong(@NonNull String, long);
-    method @NonNull public android.provider.DeviceConfig.Properties.Builder setString(@NonNull String, @Nullable String);
-  }
-
-  public final class DocumentsContract {
-    method public static boolean isManageMode(@NonNull android.net.Uri);
-    method @NonNull public static android.net.Uri setManageMode(@NonNull android.net.Uri);
-  }
-
-  public final class MediaStore {
-    method @NonNull @WorkerThread public static android.net.Uri scanFile(@NonNull android.content.ContentResolver, @NonNull java.io.File);
-    method @WorkerThread public static void scanVolume(@NonNull android.content.ContentResolver, @NonNull String);
-    method @WorkerThread public static void waitForIdle(@NonNull android.content.ContentResolver);
   }
 
   public final class Settings {
-    field public static final String ACTION_ENTERPRISE_PRIVACY_SETTINGS = "android.settings.ENTERPRISE_PRIVACY_SETTINGS";
-    field public static final String ACTION_MANAGE_APP_OVERLAY_PERMISSION = "android.settings.MANAGE_APP_OVERLAY_PERMISSION";
-    field public static final String ACTION_REQUEST_ENABLE_CONTENT_CAPTURE = "android.settings.REQUEST_ENABLE_CONTENT_CAPTURE";
-    field public static final String ACTION_TETHER_PROVISIONING_UI = "android.settings.TETHER_PROVISIONING_UI";
     field public static final int RESET_MODE_PACKAGE_DEFAULTS = 1; // 0x1
   }
 
   public static final class Settings.Global extends android.provider.Settings.NameValueTable {
     field public static final String APP_OPS_CONSTANTS = "app_ops_constants";
-    field public static final String AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES = "autofill_compat_mode_allowed_packages";
     field public static final String AUTOMATIC_POWER_SAVE_MODE = "automatic_power_save_mode";
     field public static final String BATTERY_SAVER_CONSTANTS = "battery_saver_constants";
     field public static final String DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD = "dynamic_power_savings_disable_threshold";
     field public static final String DYNAMIC_POWER_SAVINGS_ENABLED = "dynamic_power_savings_enabled";
     field public static final String HIDDEN_API_BLACKLIST_EXEMPTIONS = "hidden_api_blacklist_exemptions";
+    field public static final String HIDDEN_API_POLICY = "hidden_api_policy";
     field public static final String HIDE_ERROR_DIALOGS = "hide_error_dialogs";
     field public static final String LOCATION_GLOBAL_KILL_SWITCH = "location_global_kill_switch";
     field public static final String LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST = "location_ignore_settings_package_whitelist";
@@ -3381,32 +1465,24 @@
     field public static final String NOTIFICATION_BUBBLES = "notification_bubbles";
     field public static final String OVERLAY_DISPLAY_DEVICES = "overlay_display_devices";
     field public static final String SHOW_FIRST_CRASH_DIALOG = "show_first_crash_dialog";
-    field public static final String TETHER_OFFLOAD_DISABLED = "tether_offload_disabled";
     field public static final String USE_OPEN_WIFI_PACKAGE = "use_open_wifi_package";
   }
 
   public static final class Settings.Secure extends android.provider.Settings.NameValueTable {
-    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static void resetToDefaults(@NonNull android.content.ContentResolver, @Nullable String);
     field public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED = "accessibility_display_magnification_enabled";
+    field public static final String ACCESSIBILITY_MAGNIFICATION_CAPABILITY = "accessibility_magnification_capability";
+    field public static final String ACCESSIBILITY_MAGNIFICATION_MODE = "accessibility_magnification_mode";
+    field public static final int ACCESSIBILITY_MAGNIFICATION_MODE_ALL = 3; // 0x3
+    field public static final int ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN = 1; // 0x1
+    field public static final int ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW = 2; // 0x2
     field public static final String ACCESSIBILITY_SHORTCUT_TARGET_SERVICE = "accessibility_shortcut_target_service";
     field public static final String ANR_SHOW_BACKGROUND = "anr_show_background";
-    field public static final String AUTOFILL_FEATURE_FIELD_CLASSIFICATION = "autofill_field_classification";
     field public static final String AUTOFILL_SERVICE = "autofill_service";
-    field public static final String AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT = "autofill_user_data_max_category_count";
-    field public static final String AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE = "autofill_user_data_max_field_classification_size";
-    field public static final String AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE = "autofill_user_data_max_user_data_size";
-    field public static final String AUTOFILL_USER_DATA_MAX_VALUE_LENGTH = "autofill_user_data_max_value_length";
-    field public static final String AUTOFILL_USER_DATA_MIN_VALUE_LENGTH = "autofill_user_data_min_value_length";
     field public static final String CONTENT_CAPTURE_ENABLED = "content_capture_enabled";
     field public static final String DISABLED_PRINT_SERVICES = "disabled_print_services";
-    field public static final String DOZE_ALWAYS_ON = "doze_always_on";
     field @Deprecated public static final String ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES = "enabled_notification_policy_access_packages";
     field public static final String ENABLED_VR_LISTENERS = "enabled_vr_listeners";
     field public static final String IMMERSIVE_MODE_CONFIRMATIONS = "immersive_mode_confirmations";
-    field public static final String LOCATION_ACCESS_CHECK_DELAY_MILLIS = "location_access_check_delay_millis";
-    field public static final String LOCATION_ACCESS_CHECK_INTERVAL_MILLIS = "location_access_check_interval_millis";
-    field public static final String LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS = "lock_screen_allow_private_notifications";
-    field public static final String LOCK_SCREEN_SHOW_NOTIFICATIONS = "lock_screen_show_notifications";
     field public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component";
     field public static final String NOTIFICATION_BADGING = "notification_badging";
     field public static final String POWER_MENU_LOCKED_SHOW_CONTENT = "power_menu_locked_show_content";
@@ -3415,45 +1491,9 @@
     field public static final String SHOW_FIRST_CRASH_DIALOG_DEV_OPTION = "show_first_crash_dialog_dev_option";
     field public static final String SHOW_IME_WITH_HARD_KEYBOARD = "show_ime_with_hard_keyboard";
     field @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static final String SYNC_PARENT_SOUNDS = "sync_parent_sounds";
-    field public static final String USER_SETUP_COMPLETE = "user_setup_complete";
     field public static final String VOICE_INTERACTION_SERVICE = "voice_interaction_service";
   }
 
-  public static final class Telephony.CellBroadcasts implements android.provider.BaseColumns {
-    field public static final String CID = "cid";
-    field public static final String CMAS_CATEGORY = "cmas_category";
-    field public static final String CMAS_CERTAINTY = "cmas_certainty";
-    field public static final String CMAS_MESSAGE_CLASS = "cmas_message_class";
-    field public static final String CMAS_RESPONSE_TYPE = "cmas_response_type";
-    field public static final String CMAS_SEVERITY = "cmas_severity";
-    field public static final String CMAS_URGENCY = "cmas_urgency";
-    field @NonNull public static final android.net.Uri CONTENT_URI;
-    field public static final String DATA_CODING_SCHEME = "dcs";
-    field public static final String DEFAULT_SORT_ORDER = "date DESC";
-    field public static final String DELIVERY_TIME = "date";
-    field public static final String ETWS_IS_PRIMARY = "etws_is_primary";
-    field public static final String ETWS_WARNING_TYPE = "etws_warning_type";
-    field public static final String GEOGRAPHICAL_SCOPE = "geo_scope";
-    field public static final String GEOMETRIES = "geometries";
-    field public static final String LAC = "lac";
-    field public static final String LANGUAGE_CODE = "language";
-    field public static final String LOCATION_CHECK_TIME = "location_check_time";
-    field public static final String MAXIMUM_WAIT_TIME = "maximum_wait_time";
-    field public static final String MESSAGE_BODY = "body";
-    field public static final String MESSAGE_BROADCASTED = "message_broadcasted";
-    field public static final String MESSAGE_DISPLAYED = "message_displayed";
-    field public static final String MESSAGE_FORMAT = "format";
-    field @NonNull @RequiresPermission(android.Manifest.permission.READ_CELL_BROADCASTS) public static final android.net.Uri MESSAGE_HISTORY_URI;
-    field public static final String MESSAGE_PRIORITY = "priority";
-    field public static final String MESSAGE_READ = "read";
-    field public static final String PLMN = "plmn";
-    field public static final String RECEIVED_TIME = "received_time";
-    field public static final String SERIAL_NUMBER = "serial_number";
-    field public static final String SERVICE_CATEGORY = "service_category";
-    field public static final String SLOT_INDEX = "slot_index";
-    field public static final String SUBSCRIPTION_ID = "sub_id";
-  }
-
   public static final class Telephony.Sms.Intents {
     field public static final String SMS_CARRIER_PROVISION_ACTION = "android.provider.Telephony.SMS_CARRIER_PROVISION";
   }
@@ -3475,19 +1515,6 @@
 
 package android.security.keystore {
 
-  public abstract class AttestationUtils {
-    method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static java.security.cert.X509Certificate[] attestDeviceIds(android.content.Context, @NonNull int[], @NonNull byte[]) throws android.security.keystore.DeviceIdAttestationException;
-    field public static final int ID_TYPE_IMEI = 2; // 0x2
-    field public static final int ID_TYPE_MEID = 3; // 0x3
-    field public static final int ID_TYPE_SERIAL = 1; // 0x1
-    field public static final int USE_INDIVIDUAL_ATTESTATION = 4; // 0x4
-  }
-
-  public class DeviceIdAttestationException extends java.lang.Exception {
-    ctor public DeviceIdAttestationException(@Nullable String);
-    ctor public DeviceIdAttestationException(@Nullable String, @Nullable Throwable);
-  }
-
   public static final class KeyGenParameterSpec.Builder {
     method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUniqueIdIncluded(boolean);
   }
@@ -3502,37 +1529,8 @@
 
 }
 
-package android.service.appprediction {
-
-  public abstract class AppPredictionService extends android.app.Service {
-    ctor public AppPredictionService();
-    method @MainThread public abstract void onAppTargetEvent(@NonNull android.app.prediction.AppPredictionSessionId, @NonNull android.app.prediction.AppTargetEvent);
-    method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
-    method public void onCreatePredictionSession(@NonNull android.app.prediction.AppPredictionContext, @NonNull android.app.prediction.AppPredictionSessionId);
-    method @MainThread public void onDestroyPredictionSession(@NonNull android.app.prediction.AppPredictionSessionId);
-    method @MainThread public abstract void onLaunchLocationShown(@NonNull android.app.prediction.AppPredictionSessionId, @NonNull String, @NonNull java.util.List<android.app.prediction.AppTargetId>);
-    method @MainThread public abstract void onRequestPredictionUpdate(@NonNull android.app.prediction.AppPredictionSessionId);
-    method @MainThread public abstract void onSortAppTargets(@NonNull android.app.prediction.AppPredictionSessionId, @NonNull java.util.List<android.app.prediction.AppTarget>, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<java.util.List<android.app.prediction.AppTarget>>);
-    method @MainThread public void onStartPredictionUpdates();
-    method @MainThread public void onStopPredictionUpdates();
-    method public final void updatePredictions(@NonNull android.app.prediction.AppPredictionSessionId, @NonNull java.util.List<android.app.prediction.AppTarget>);
-  }
-
-}
-
 package android.service.autofill {
 
-  public abstract class AutofillFieldClassificationService extends android.app.Service {
-    ctor public AutofillFieldClassificationService();
-    method public android.os.IBinder onBind(android.content.Intent);
-    field public static final String REQUIRED_ALGORITHM_CREDIT_CARD = "CREDIT_CARD";
-    field public static final String REQUIRED_ALGORITHM_EDIT_DISTANCE = "EDIT_DISTANCE";
-    field public static final String REQUIRED_ALGORITHM_EXACT_MATCH = "EXACT_MATCH";
-    field public static final String SERVICE_INTERFACE = "android.service.autofill.AutofillFieldClassificationService";
-    field public static final String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS = "android.autofill.field_classification.available_algorithms";
-    field public static final String SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM = "android.autofill.field_classification.default_algorithm";
-  }
-
   public final class CharSequenceTransformation extends android.service.autofill.InternalTransformation implements android.os.Parcelable android.service.autofill.Transformation {
     method public void apply(@NonNull android.service.autofill.ValueFinder, @NonNull android.widget.RemoteViews, int) throws java.lang.Exception;
   }
@@ -3555,11 +1553,6 @@
     method @Nullable public android.util.SparseArray<android.service.autofill.InternalOnClickAction> getActions();
   }
 
-  public static final class Dataset.Builder {
-    ctor public Dataset.Builder(@NonNull android.service.autofill.InlinePresentation);
-    method @NonNull public android.service.autofill.Dataset.Builder setFieldInlinePresentation(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.service.autofill.InlinePresentation);
-  }
-
   public final class DateTransformation extends android.service.autofill.InternalTransformation implements android.os.Parcelable android.service.autofill.Transformation {
     method public void apply(@NonNull android.service.autofill.ValueFinder, @NonNull android.widget.RemoteViews, int) throws java.lang.Exception;
   }
@@ -3576,15 +1569,6 @@
     method public void apply(@NonNull android.service.autofill.ValueFinder, @NonNull android.widget.RemoteViews, int) throws java.lang.Exception;
   }
 
-  public abstract class InlineSuggestionRenderService extends android.app.Service {
-    ctor public InlineSuggestionRenderService();
-    method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
-    method @NonNull public android.os.Bundle onGetInlineSuggestionsRendererInfo();
-    method @Nullable public android.view.View onRenderSuggestion(@NonNull android.service.autofill.InlinePresentation, int, int);
-    method public final void startIntentSender(@NonNull android.content.IntentSender);
-    field public static final String SERVICE_INTERFACE = "android.service.autofill.InlineSuggestionRenderService";
-  }
-
   public abstract class InternalOnClickAction implements android.service.autofill.OnClickAction android.os.Parcelable {
     ctor public InternalOnClickAction();
     method public abstract void onClick(@NonNull android.view.ViewGroup);
@@ -3634,26 +1618,6 @@
 
 package android.service.autofill.augmented {
 
-  public abstract class AugmentedAutofillService extends android.app.Service {
-    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 @Nullable public final android.service.autofill.FillEventHistory getFillEventHistory();
-    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);
-    method public final boolean requestAutofill(@NonNull android.content.ComponentName, @NonNull android.view.autofill.AutofillId);
-    field public static final String SERVICE_INTERFACE = "android.service.autofill.augmented.AugmentedAutofillService";
-  }
-
-  public final class FillCallback {
-    method public void onSuccess(@Nullable android.service.autofill.augmented.FillResponse);
-  }
-
-  public final class FillController {
-    method public void autofill(@NonNull java.util.List<android.util.Pair<android.view.autofill.AutofillId,android.view.autofill.AutofillValue>>);
-  }
-
   public final class FillRequest {
     method @NonNull public android.content.ComponentName getActivityComponent();
     method @NonNull public android.view.autofill.AutofillId getFocusedId();
@@ -3663,181 +1627,14 @@
     method public int getTaskId();
   }
 
-  public final class FillResponse {
-  }
-
-  public static final class FillResponse.Builder {
-    ctor public FillResponse.Builder();
-    method @NonNull public android.service.autofill.augmented.FillResponse build();
-    method @NonNull public android.service.autofill.augmented.FillResponse.Builder setClientState(@NonNull android.os.Bundle);
-    method @NonNull public android.service.autofill.augmented.FillResponse.Builder setFillWindow(@NonNull android.service.autofill.augmented.FillWindow);
-    method @NonNull public android.service.autofill.augmented.FillResponse.Builder setInlineSuggestions(@NonNull java.util.List<android.service.autofill.Dataset>);
-  }
-
-  public final class FillWindow implements java.lang.AutoCloseable {
-    ctor public FillWindow();
-    method public void destroy();
-    method public boolean update(@NonNull android.service.autofill.augmented.PresentationParams.Area, @NonNull android.view.View, long);
-  }
-
-  public abstract class PresentationParams {
-    method @Nullable public android.service.autofill.augmented.PresentationParams.Area getSuggestionArea();
-  }
-
-  public abstract static class PresentationParams.Area {
-    method @NonNull public android.graphics.Rect getBounds();
-  }
-
-}
-
-package android.service.contentcapture {
-
-  public final class ActivityEvent implements android.os.Parcelable {
-    method public int describeContents();
-    method @NonNull public android.content.ComponentName getComponentName();
-    method public int getEventType();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.service.contentcapture.ActivityEvent> CREATOR;
-    field public static final int TYPE_ACTIVITY_DESTROYED = 24; // 0x18
-    field public static final int TYPE_ACTIVITY_PAUSED = 2; // 0x2
-    field public static final int TYPE_ACTIVITY_RESUMED = 1; // 0x1
-    field public static final int TYPE_ACTIVITY_STOPPED = 23; // 0x17
-  }
-
-  public abstract class ContentCaptureService extends android.app.Service {
-    ctor public ContentCaptureService();
-    method public final void disableSelf();
-    method public void onActivityEvent(@NonNull android.service.contentcapture.ActivityEvent);
-    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);
-    method public void onCreateContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureContext, @NonNull android.view.contentcapture.ContentCaptureSessionId);
-    method public void onDataRemovalRequest(@NonNull android.view.contentcapture.DataRemovalRequest);
-    method public void onDataShareRequest(@NonNull android.view.contentcapture.DataShareRequest, @NonNull android.service.contentcapture.DataShareCallback);
-    method public void onDestroyContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureSessionId);
-    method public void onDisconnected();
-    method public final void setContentCaptureConditions(@NonNull String, @Nullable java.util.Set<android.view.contentcapture.ContentCaptureCondition>);
-    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";
-    field public static final String SERVICE_META_DATA = "android.content_capture";
-  }
-
-  public interface DataShareCallback {
-    method public void onAccept(@NonNull java.util.concurrent.Executor, @NonNull android.service.contentcapture.DataShareReadAdapter);
-    method public void onReject();
-  }
-
-  public interface DataShareReadAdapter {
-    method public void onError(int);
-    method public void onStart(@NonNull android.os.ParcelFileDescriptor);
-  }
-
-  public final class SnapshotData implements android.os.Parcelable {
-    method public int describeContents();
-    method @Nullable public android.app.assist.AssistContent getAssistContent();
-    method @NonNull public android.os.Bundle getAssistData();
-    method @NonNull public android.app.assist.AssistStructure getAssistStructure();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.service.contentcapture.SnapshotData> CREATOR;
-  }
-
 }
 
 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(@NonNull String, @NonNull String, @NonNull android.os.Bundle, @NonNull CharSequence, @NonNull android.os.UserHandle);
-    method public int describeContents();
-    method @NonNull public CharSequence getExplanation();
-    method @NonNull public String getKey();
-    method @NonNull public String getPackage();
-    method @NonNull public android.os.Bundle getSignals();
-    method public int getUser();
-    method @NonNull public android.os.UserHandle getUserHandle();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull 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_RANKING_SCORE = "key_ranking_score";
-    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";
-  }
-
   @Deprecated public abstract class ConditionProviderService extends android.app.Service {
     method @Deprecated public boolean isBound();
   }
 
-  public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService {
-    ctor public NotificationAssistantService();
-    method public final void adjustNotification(@NonNull android.service.notification.Adjustment);
-    method public final void adjustNotifications(@NonNull java.util.List<android.service.notification.Adjustment>);
-    method public void onActionInvoked(@NonNull String, @NonNull android.app.Notification.Action, int);
-    method public void onAllowedAdjustmentsChanged();
-    method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent);
-    method public void onNotificationDirectReplied(@NonNull String);
-    method @Nullable public abstract android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification);
-    method @Nullable public android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification, @NonNull android.app.NotificationChannel);
-    method public void onNotificationExpansionChanged(@NonNull String, boolean, boolean);
-    method public abstract void onNotificationSnoozedUntilContext(@NonNull android.service.notification.StatusBarNotification, @NonNull String);
-    method public void onNotificationVisibilityChanged(@NonNull String, boolean);
-    method public void onNotificationsSeen(@NonNull java.util.List<java.lang.String>);
-    method public void onPanelHidden();
-    method public void onPanelRevealed(int);
-    method public void onSuggestedReplySent(@NonNull String, @NonNull CharSequence, int);
-    method public final void unsnoozeNotification(@NonNull 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
-  }
-
-  public abstract class NotificationListenerService extends android.app.Service {
-    method public void onNotificationRemoved(@NonNull android.service.notification.StatusBarNotification, @NonNull android.service.notification.NotificationListenerService.RankingMap, @NonNull android.service.notification.NotificationStats, int);
-  }
-
-  public final class NotificationStats implements android.os.Parcelable {
-    ctor public NotificationStats();
-    method public int describeContents();
-    method public int getDismissalSentiment();
-    method public int getDismissalSurface();
-    method public boolean hasDirectReplied();
-    method public boolean hasExpanded();
-    method public boolean hasInteracted();
-    method public boolean hasSeen();
-    method public boolean hasSnoozed();
-    method public boolean hasViewedSettings();
-    method public void setDirectReplied();
-    method public void setDismissalSentiment(int);
-    method public void setDismissalSurface(int);
-    method public void setExpanded();
-    method public void setSeen();
-    method public void setSnoozed();
-    method public void setViewedSettings();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.service.notification.NotificationStats> CREATOR;
-    field public static final int DISMISSAL_AOD = 2; // 0x2
-    field public static final int DISMISSAL_NOT_DISMISSED = -1; // 0xffffffff
-    field public static final int DISMISSAL_OTHER = 0; // 0x0
-    field public static final int DISMISSAL_PEEK = 1; // 0x1
-    field public static final int DISMISSAL_SHADE = 3; // 0x3
-    field public static final int DISMISS_SENTIMENT_NEGATIVE = 0; // 0x0
-    field public static final int DISMISS_SENTIMENT_NEUTRAL = 1; // 0x1
-    field public static final int DISMISS_SENTIMENT_POSITIVE = 2; // 0x2
-    field public static final int DISMISS_SENTIMENT_UNKNOWN = -1000; // 0xfffffc18
-  }
-
-  public final class SnoozeCriterion implements android.os.Parcelable {
-    ctor public SnoozeCriterion(String, CharSequence, CharSequence);
-    ctor protected SnoozeCriterion(android.os.Parcel);
-    method public int describeContents();
-    method public CharSequence getConfirmation();
-    method public CharSequence getExplanation();
-    method public String getId();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.service.notification.SnoozeCriterion> CREATOR;
-  }
-
 }
 
 package android.service.quickaccesswallet {
@@ -3878,67 +1675,16 @@
 
 }
 
-package android.service.textclassifier {
-
-  public abstract class TextClassifierService extends android.app.Service {
-    ctor public TextClassifierService();
-    method @NonNull public static android.view.textclassifier.TextClassifier getDefaultTextClassifierImplementation(@NonNull android.content.Context);
-    method @Deprecated public final android.view.textclassifier.TextClassifier getLocalTextClassifier();
-    method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
-    method @MainThread public abstract void onClassifyText(@Nullable android.view.textclassifier.TextClassificationSessionId, @NonNull android.view.textclassifier.TextClassification.Request, @NonNull android.os.CancellationSignal, @NonNull android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextClassification>);
-    method public void onConnected();
-    method @MainThread public void onCreateTextClassificationSession(@NonNull android.view.textclassifier.TextClassificationContext, @NonNull android.view.textclassifier.TextClassificationSessionId);
-    method @MainThread public void onDestroyTextClassificationSession(@NonNull android.view.textclassifier.TextClassificationSessionId);
-    method @MainThread public void onDetectLanguage(@Nullable android.view.textclassifier.TextClassificationSessionId, @NonNull android.view.textclassifier.TextLanguage.Request, @NonNull android.os.CancellationSignal, @NonNull android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextLanguage>);
-    method public void onDisconnected();
-    method @MainThread public abstract void onGenerateLinks(@Nullable android.view.textclassifier.TextClassificationSessionId, @NonNull android.view.textclassifier.TextLinks.Request, @NonNull android.os.CancellationSignal, @NonNull android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextLinks>);
-    method @Deprecated @MainThread public void onSelectionEvent(@Nullable android.view.textclassifier.TextClassificationSessionId, @NonNull android.view.textclassifier.SelectionEvent);
-    method @MainThread public void onSuggestConversationActions(@Nullable android.view.textclassifier.TextClassificationSessionId, @NonNull android.view.textclassifier.ConversationActions.Request, @NonNull android.os.CancellationSignal, @NonNull android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.ConversationActions>);
-    method @MainThread public abstract void onSuggestSelection(@Nullable android.view.textclassifier.TextClassificationSessionId, @NonNull android.view.textclassifier.TextSelection.Request, @NonNull android.os.CancellationSignal, @NonNull android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextSelection>);
-    method @MainThread public void onTextClassifierEvent(@Nullable android.view.textclassifier.TextClassificationSessionId, @NonNull android.view.textclassifier.TextClassifierEvent);
-    field public static final String SERVICE_INTERFACE = "android.service.textclassifier.TextClassifierService";
-  }
-
-  public static interface TextClassifierService.Callback<T> {
-    method public void onFailure(@NonNull CharSequence);
-    method public void onSuccess(T);
-  }
-
-}
-
 package android.service.watchdog {
 
   public abstract class ExplicitHealthCheckService extends android.app.Service {
-    ctor public ExplicitHealthCheckService();
-    method public final void notifyHealthCheckPassed(@NonNull String);
-    method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
-    method public abstract void onCancelHealthCheck(@NonNull String);
-    method @NonNull public abstract java.util.List<java.lang.String> onGetRequestedPackages();
-    method @NonNull public abstract java.util.List<android.service.watchdog.ExplicitHealthCheckService.PackageConfig> onGetSupportedPackages();
-    method public abstract void onRequestHealthCheck(@NonNull String);
     method public void setCallback(@Nullable android.os.RemoteCallback);
-    field public static final String BIND_PERMISSION = "android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE";
-    field public static final String SERVICE_INTERFACE = "android.service.watchdog.ExplicitHealthCheckService";
-  }
-
-  public static final class ExplicitHealthCheckService.PackageConfig implements android.os.Parcelable {
-    ctor public ExplicitHealthCheckService.PackageConfig(@NonNull String, long);
-    method public int describeContents();
-    method public long getHealthCheckTimeoutMillis();
-    method @NonNull public String getPackageName();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.service.watchdog.ExplicitHealthCheckService.PackageConfig> CREATOR;
   }
 
 }
 
 package android.telecom {
 
-  public final class Call {
-    method public void enterBackgroundAudioProcessing();
-    method public void exitBackgroundAudioProcessing(boolean);
-  }
-
   public static class Call.Details {
     method public String getTelecomCallId();
   }
@@ -3947,42 +1693,6 @@
     ctor public CallAudioState(boolean, int, int, @Nullable android.bluetooth.BluetoothDevice, @NonNull java.util.Collection<android.bluetooth.BluetoothDevice>);
   }
 
-  public static class CallScreeningService.CallResponse.Builder {
-    method @NonNull @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT) public android.telecom.CallScreeningService.CallResponse.Builder setShouldScreenCallViaAudioProcessing(boolean);
-  }
-
-  public abstract class Conference extends android.telecom.Conferenceable {
-    method public android.telecom.Connection getPrimaryConnection();
-    method @NonNull public final String getTelecomCallId();
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setAddress(@NonNull android.net.Uri, int);
-    method public final void setCallerDisplayName(@NonNull String, int);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setConferenceState(boolean);
-  }
-
-  public abstract class Connection extends android.telecom.Conferenceable {
-    method @IntRange(from=0) public final long getConnectTimeMillis();
-    method public final long getConnectionStartElapsedRealtimeMillis();
-    method @Nullable public android.telecom.PhoneAccountHandle getPhoneAccountHandle();
-    method @Nullable public final String getTelecomCallId();
-    method public final void resetConnectionTime();
-    method public void setCallDirection(int);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setConnectTimeMillis(@IntRange(from=0) long);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setConnectionStartElapsedRealtimeMillis(long);
-    method public void setPhoneAccountHandle(@NonNull android.telecom.PhoneAccountHandle);
-    method public void setTelecomCallId(@NonNull String);
-    field public static final int CAPABILITY_CONFERENCE_HAS_NO_CHILDREN = 2097152; // 0x200000
-    field public static final int CAPABILITY_SPEED_UP_MT_AUDIO = 262144; // 0x40000
-    field public static final String EXTRA_DISABLE_ADD_CALL = "android.telecom.extra.DISABLE_ADD_CALL";
-    field public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 1; // 0x1
-    field public static final int PROPERTY_GENERIC_CONFERENCE = 2; // 0x2
-    field public static final int PROPERTY_IS_DOWNGRADED_CONFERENCE = 64; // 0x40
-    field public static final int PROPERTY_REMOTELY_HOSTED = 2048; // 0x800
-  }
-
-  public final class ConnectionRequest implements android.os.Parcelable {
-    method @Nullable public String getTelecomCallId();
-  }
-
   public static final class ConnectionRequest.Builder {
     ctor public ConnectionRequest.Builder();
     method @NonNull public android.telecom.ConnectionRequest build();
@@ -3998,53 +1708,11 @@
     method @NonNull public android.telecom.ConnectionRequest.Builder setVideoState(int);
   }
 
-  public static class PhoneAccount.Builder {
-    method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telecom.PhoneAccount.Builder setGroupId(@NonNull String);
-  }
-
-  public class PhoneAccountSuggestionService extends android.app.Service {
-    ctor public PhoneAccountSuggestionService();
-    method public void onAccountSuggestionRequest(@NonNull String);
-    method public android.os.IBinder onBind(android.content.Intent);
-    method public final void suggestPhoneAccounts(@NonNull String, @NonNull java.util.List<android.telecom.PhoneAccountSuggestion>);
-    field public static final String SERVICE_INTERFACE = "android.telecom.PhoneAccountSuggestionService";
-  }
-
-  public class TelecomManager {
-    method @NonNull public android.content.Intent createLaunchEmergencyDialerIntent(@Nullable String);
-    method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<android.telecom.PhoneAccountHandle> getCallCapablePhoneAccounts(boolean);
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCurrentTtyMode();
-    method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDefaultDialerPackage(@NonNull android.os.UserHandle);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isInEmergencyCall();
-    method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging();
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUserSelectedOutgoingPhoneAccount(@Nullable android.telecom.PhoneAccountHandle);
-    field public static final String ACTION_CURRENT_TTY_MODE_CHANGED = "android.telecom.action.CURRENT_TTY_MODE_CHANGED";
-    field public static final String ACTION_TTY_PREFERRED_MODE_CHANGED = "android.telecom.action.TTY_PREFERRED_MODE_CHANGED";
-    field public static final String EXTRA_CURRENT_TTY_MODE = "android.telecom.extra.CURRENT_TTY_MODE";
-    field public static final String EXTRA_TTY_PREFERRED_MODE = "android.telecom.extra.TTY_PREFERRED_MODE";
-    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
-    field public static final int TTY_MODE_VCO = 3; // 0x3
-  }
-
 }
 
 package android.telephony {
 
-  public final class AccessNetworkConstants {
-    field public static final int TRANSPORT_TYPE_INVALID = -1; // 0xffffffff
-  }
-
-  public static final class AccessNetworkConstants.NgranBands {
-    method public static int getFrequencyRangeGroup(int);
-    field public static final int FREQUENCY_RANGE_GROUP_1 = 1; // 0x1
-    field public static final int FREQUENCY_RANGE_GROUP_2 = 2; // 0x2
-    field public static final int FREQUENCY_RANGE_GROUP_UNKNOWN = 0; // 0x0
-  }
-
   public final class BarringInfo implements android.os.Parcelable {
-    ctor public BarringInfo();
     ctor public BarringInfo(@Nullable android.telephony.CellIdentity, @NonNull android.util.SparseArray<android.telephony.BarringInfo.BarringServiceInfo>);
   }
 
@@ -4052,57 +1720,6 @@
     ctor public BarringInfo.BarringServiceInfo(int, boolean, int, int);
   }
 
-  public final class CallQuality implements android.os.Parcelable {
-    ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int);
-    ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int, boolean, boolean, boolean);
-    method public int describeContents();
-    method public int getAverageRelativeJitter();
-    method public int getAverageRoundTripTime();
-    method public int getCallDuration();
-    method public int getCodecType();
-    method public int getDownlinkCallQualityLevel();
-    method public int getMaxRelativeJitter();
-    method public int getNumRtpPacketsNotReceived();
-    method public int getNumRtpPacketsReceived();
-    method public int getNumRtpPacketsTransmitted();
-    method public int getNumRtpPacketsTransmittedLost();
-    method public int getUplinkCallQualityLevel();
-    method public boolean isIncomingSilenceDetectedAtCallSetup();
-    method public boolean isOutgoingSilenceDetectedAtCallSetup();
-    method public boolean isRtpInactivityDetected();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final int CALL_QUALITY_BAD = 4; // 0x4
-    field public static final int CALL_QUALITY_EXCELLENT = 0; // 0x0
-    field public static final int CALL_QUALITY_FAIR = 2; // 0x2
-    field public static final int CALL_QUALITY_GOOD = 1; // 0x1
-    field public static final int CALL_QUALITY_NOT_AVAILABLE = 5; // 0x5
-    field public static final int CALL_QUALITY_POOR = 3; // 0x3
-    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CallQuality> CREATOR;
-  }
-
-  public class CarrierConfigManager {
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void overrideConfig(int, @Nullable android.os.PersistableBundle);
-  }
-
-  public final class DataSpecificRegistrationInfo implements android.os.Parcelable {
-    method public int describeContents();
-    method @NonNull public android.telephony.LteVopsSupportInfo getLteVopsSupportInfo();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.DataSpecificRegistrationInfo> CREATOR;
-  }
-
-  public final class LteVopsSupportInfo implements android.os.Parcelable {
-    ctor public LteVopsSupportInfo(int, int);
-    method public int describeContents();
-    method public int getEmcBearerSupport();
-    method public int getVopsSupport();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.LteVopsSupportInfo> CREATOR;
-    field public static final int LTE_STATUS_NOT_AVAILABLE = 1; // 0x1
-    field public static final int LTE_STATUS_NOT_SUPPORTED = 3; // 0x3
-    field public static final int LTE_STATUS_SUPPORTED = 2; // 0x2
-  }
-
   public class MbmsDownloadSession implements java.lang.AutoCloseable {
     field public static final String MBMS_DOWNLOAD_SERVICE_OVERRIDE_METADATA = "mbms-download-service-override";
   }
@@ -4115,52 +1732,16 @@
     field public static final String MBMS_STREAMING_SERVICE_OVERRIDE_METADATA = "mbms-streaming-service-override";
   }
 
-  public final class NetworkRegistrationInfo implements android.os.Parcelable {
-    method @Nullable public android.telephony.DataSpecificRegistrationInfo getDataSpecificInfo();
-    method public int getRegistrationState();
-    method public int getRejectCause();
-    method public int getRoamingType();
-    method public boolean isEmergencyEnabled();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final int REGISTRATION_STATE_DENIED = 3; // 0x3
-    field public static final int REGISTRATION_STATE_HOME = 1; // 0x1
-    field public static final int REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING = 0; // 0x0
-    field public static final int REGISTRATION_STATE_NOT_REGISTERED_SEARCHING = 2; // 0x2
-    field public static final int REGISTRATION_STATE_ROAMING = 5; // 0x5
-    field public static final int REGISTRATION_STATE_UNKNOWN = 4; // 0x4
-  }
-
-  public static final class NetworkRegistrationInfo.Builder {
-    ctor public NetworkRegistrationInfo.Builder();
-    method @NonNull public android.telephony.NetworkRegistrationInfo build();
-    method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setAccessNetworkTechnology(int);
-    method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setAvailableServices(@NonNull java.util.List<java.lang.Integer>);
-    method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setCellIdentity(@Nullable android.telephony.CellIdentity);
-    method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setDomain(int);
-    method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setEmergencyOnly(boolean);
-    method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setRegisteredPlmn(@Nullable String);
-    method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setRegistrationState(int);
-    method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setRejectCause(int);
-    method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setTransportType(int);
+  public final class ModemActivityInfo implements android.os.Parcelable {
+    ctor public ModemActivityInfo(long, int, int, @NonNull int[], int);
+    method public boolean isValid();
   }
 
   public class PhoneNumberUtils {
     method public static int getMinMatchForTest();
-    method @NonNull public static String getUsernameFromUriNumber(@NonNull String);
-    method public static boolean isUriNumber(@Nullable String);
-    method public static boolean isVoiceMailNumber(@NonNull android.content.Context, int, @Nullable String);
     method public static void setMinMatchForTest(int);
   }
 
-  public class PhoneStateListener {
-    method @Deprecated public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber);
-    method public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int);
-    method @Deprecated public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber);
-    method public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber, int);
-    field @RequiresPermission("android.permission.READ_ACTIVE_EMERGENCY_SESSION") public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 268435456; // 0x10000000
-    field @RequiresPermission("android.permission.READ_ACTIVE_EMERGENCY_SESSION") public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000
-  }
-
   public final class PreciseDataConnectionState implements android.os.Parcelable {
     ctor @Deprecated public PreciseDataConnectionState(int, int, int, @NonNull String, @Nullable android.net.LinkProperties, int);
   }
@@ -4186,40 +1767,16 @@
     field public static final int SMS_CATEGORY_STANDARD_SHORT_CODE = 2; // 0x2
   }
 
-  public class SubscriptionManager {
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultVoiceSubscriptionId(int);
-    field @NonNull public static final android.net.Uri ADVANCED_CALLING_ENABLED_CONTENT_URI;
-    field @NonNull public static final android.net.Uri VT_ENABLED_CONTENT_URI;
-    field @NonNull public static final android.net.Uri WFC_ENABLED_CONTENT_URI;
-    field @NonNull public static final android.net.Uri WFC_MODE_CONTENT_URI;
-    field @NonNull public static final android.net.Uri WFC_ROAMING_ENABLED_CONTENT_URI;
-    field @NonNull public static final android.net.Uri WFC_ROAMING_MODE_CONTENT_URI;
-  }
-
   public class TelephonyManager {
     method public int addDevicePolicyOverrideApn(@NonNull android.content.Context, @NonNull android.telephony.data.ApnSetting);
-    method public int checkCarrierPrivilegesForPackage(String);
-    method @Nullable @RequiresPermission("android.permission.INTERACT_ACROSS_USERS") public android.content.ComponentName getAndUpdateDefaultRespondViaMessageApplication();
     method public int getCarrierIdListVersion();
-    method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
-    method @Nullable @RequiresPermission("android.permission.INTERACT_ACROSS_USERS") public android.content.ComponentName getDefaultRespondViaMessageApplication();
     method @NonNull public java.util.List<android.telephony.data.ApnSetting> getDevicePolicyOverrideApns(@NonNull android.content.Context);
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEmergencyNumberDbVersion();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getLine1AlphaTag();
     method public android.util.Pair<java.lang.Integer,java.lang.Integer> getRadioHalVersion();
     method public boolean modifyDevicePolicyOverrideApn(@NonNull android.content.Context, int, @NonNull android.telephony.data.ApnSetting);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void refreshUiccProfile();
-    method @RequiresPermission("android.permission.READ_ACTIVE_EMERGENCY_SESSION") public void resetOtaEmergencyNumberDbFilePath();
     method @Deprecated public void setCarrierTestOverride(String, String, String, String, String, String, String);
     method public void setCarrierTestOverride(String, String, String, String, String, String, String, String, String);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSystemSelectionChannels(@NonNull java.util.List<android.telephony.RadioAccessSpecifier>, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSystemSelectionChannels(@NonNull java.util.List<android.telephony.RadioAccessSpecifier>);
-    method @RequiresPermission("android.permission.READ_ACTIVE_EMERGENCY_SESSION") public void updateOtaEmergencyNumberDbFilePath(@NonNull android.os.ParcelFileDescriptor);
-    field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe
-    field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
-    field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
-    field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff
-    field public static final int INVALID_EMERGENCY_NUMBER_DB_VERSION = -1; // 0xffffffff
     field public static final int UNKNOWN_CARRIER_ID_LIST_VERSION = -1; // 0xffffffff
   }
 
@@ -4235,835 +1792,18 @@
 
 package android.telephony.ims {
 
-  public final class ImsCallForwardInfo implements android.os.Parcelable {
-    ctor public ImsCallForwardInfo(int, int, int, int, @NonNull String, int);
-    method public int describeContents();
-    method public int getCondition();
-    method public String getNumber();
-    method public int getServiceClass();
-    method public int getStatus();
-    method public int getTimeSeconds();
-    method public int getToA();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final int CDIV_CF_REASON_ALL = 4; // 0x4
-    field public static final int CDIV_CF_REASON_ALL_CONDITIONAL = 5; // 0x5
-    field public static final int CDIV_CF_REASON_BUSY = 1; // 0x1
-    field public static final int CDIV_CF_REASON_NOT_LOGGED_IN = 6; // 0x6
-    field public static final int CDIV_CF_REASON_NOT_REACHABLE = 3; // 0x3
-    field public static final int CDIV_CF_REASON_NO_REPLY = 2; // 0x2
-    field public static final int CDIV_CF_REASON_UNCONDITIONAL = 0; // 0x0
-    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsCallForwardInfo> CREATOR;
-    field public static final int STATUS_ACTIVE = 1; // 0x1
-    field public static final int STATUS_NOT_ACTIVE = 0; // 0x0
-    field public static final int TYPE_OF_ADDRESS_INTERNATIONAL = 145; // 0x91
-    field public static final int TYPE_OF_ADDRESS_UNKNOWN = 129; // 0x81
-  }
-
   public final class ImsCallProfile implements android.os.Parcelable {
-    ctor public ImsCallProfile();
-    ctor public ImsCallProfile(int, int);
-    ctor public ImsCallProfile(int, int, android.os.Bundle, android.telephony.ims.ImsStreamMediaProfile);
-    method public int describeContents();
-    method public String getCallExtra(String);
-    method public String getCallExtra(String, String);
-    method public boolean getCallExtraBoolean(String);
-    method public boolean getCallExtraBoolean(String, boolean);
-    method public int getCallExtraInt(String);
-    method public int getCallExtraInt(String, int);
-    method public android.os.Bundle getCallExtras();
-    method public int getCallType();
-    method public static int getCallTypeFromVideoState(int);
-    method public int getCallerNumberVerificationStatus();
-    method public int getEmergencyCallRouting();
-    method public int getEmergencyServiceCategories();
-    method @NonNull public java.util.List<java.lang.String> getEmergencyUrns();
-    method public android.telephony.ims.ImsStreamMediaProfile getMediaProfile();
-    method @NonNull public android.os.Bundle getProprietaryCallExtras();
-    method public int getRestrictCause();
-    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();
-    method public static int presentationToOir(int);
-    method public void setCallExtra(String, String);
-    method public void setCallExtraBoolean(String, boolean);
-    method public void setCallExtraInt(String, int);
-    method public void setCallRestrictCause(int);
-    method public void setCallerNumberVerificationStatus(int);
-    method public void setEmergencyCallRouting(int);
-    method public void setEmergencyCallTesting(boolean);
-    method public void setEmergencyServiceCategories(int);
-    method public void setEmergencyUrns(@NonNull 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);
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final int CALL_RESTRICT_CAUSE_DISABLED = 2; // 0x2
-    field public static final int CALL_RESTRICT_CAUSE_HD = 3; // 0x3
-    field public static final int CALL_RESTRICT_CAUSE_NONE = 0; // 0x0
-    field public static final int CALL_RESTRICT_CAUSE_RAT = 1; // 0x1
-    field public static final int CALL_TYPE_VIDEO_N_VOICE = 3; // 0x3
-    field public static final int CALL_TYPE_VOICE = 2; // 0x2
-    field public static final int CALL_TYPE_VOICE_N_VIDEO = 1; // 0x1
-    field public static final int CALL_TYPE_VS = 8; // 0x8
-    field public static final int CALL_TYPE_VS_RX = 10; // 0xa
-    field public static final int CALL_TYPE_VS_TX = 9; // 0x9
-    field public static final int CALL_TYPE_VT = 4; // 0x4
-    field public static final int CALL_TYPE_VT_NODIR = 7; // 0x7
-    field public static final int CALL_TYPE_VT_RX = 6; // 0x6
-    field public static final int CALL_TYPE_VT_TX = 5; // 0x5
-    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsCallProfile> CREATOR;
-    field public static final int DIALSTRING_NORMAL = 0; // 0x0
-    field public static final int DIALSTRING_SS_CONF = 1; // 0x1
-    field public static final int DIALSTRING_USSD = 2; // 0x2
-    field public static final String EXTRA_ADDITIONAL_CALL_INFO = "AdditionalCallInfo";
-    field public static final String EXTRA_ADDITIONAL_SIP_INVITE_FIELDS = "android.telephony.ims.extra.ADDITIONAL_SIP_INVITE_FIELDS";
-    field public static final String EXTRA_CALL_DISCONNECT_CAUSE = "android.telephony.ims.extra.CALL_DISCONNECT_CAUSE";
-    field public static final String EXTRA_CALL_NETWORK_TYPE = "android.telephony.ims.extra.CALL_NETWORK_TYPE";
-    field @Deprecated public static final String EXTRA_CALL_RAT_TYPE = "CallRadioTech";
-    field public static final String EXTRA_CHILD_NUMBER = "ChildNum";
-    field public static final String EXTRA_CNA = "cna";
-    field public static final String EXTRA_CNAP = "cnap";
-    field public static final String EXTRA_CODEC = "Codec";
-    field public static final String EXTRA_DIALSTRING = "dialstring";
-    field public static final String EXTRA_DISPLAY_TEXT = "DisplayText";
-    field public static final String EXTRA_EMERGENCY_CALL = "e_call";
-    field public static final String EXTRA_FORWARDED_NUMBER = "android.telephony.ims.extra.FORWARDED_NUMBER";
-    field public static final String EXTRA_IS_CALL_PULL = "CallPull";
     field public static final String EXTRA_OEM_EXTRAS = "android.telephony.ims.extra.OEM_EXTRAS";
-    field public static final String EXTRA_OI = "oi";
-    field public static final String EXTRA_OIR = "oir";
-    field public static final String EXTRA_REMOTE_URI = "remote_uri";
-    field public static final String EXTRA_USSD = "ussd";
-    field public static final int OIR_DEFAULT = 0; // 0x0
-    field public static final int OIR_PRESENTATION_NOT_RESTRICTED = 2; // 0x2
-    field public static final int OIR_PRESENTATION_PAYPHONE = 4; // 0x4
-    field public static final int OIR_PRESENTATION_RESTRICTED = 1; // 0x1
-    field public static final int OIR_PRESENTATION_UNKNOWN = 3; // 0x3
-    field public static final int SERVICE_TYPE_EMERGENCY = 2; // 0x2
-    field public static final int SERVICE_TYPE_NONE = 0; // 0x0
-    field public static final int SERVICE_TYPE_NORMAL = 1; // 0x1
-    field public static final int VERIFICATION_STATUS_FAILED = 2; // 0x2
-    field public static final int VERIFICATION_STATUS_NOT_VERIFIED = 0; // 0x0
-    field public static final int VERIFICATION_STATUS_PASSED = 1; // 0x1
-  }
-
-  public class ImsCallSessionListener {
-    method public void callQualityChanged(@NonNull android.telephony.CallQuality);
-    method public void callSessionConferenceExtendFailed(android.telephony.ims.ImsReasonInfo);
-    method public void callSessionConferenceExtendReceived(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile);
-    method public void callSessionConferenceExtended(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile);
-    method public void callSessionConferenceStateUpdated(android.telephony.ims.ImsConferenceState);
-    method @Deprecated public void callSessionHandover(int, int, android.telephony.ims.ImsReasonInfo);
-    method @Deprecated public void callSessionHandoverFailed(int, int, android.telephony.ims.ImsReasonInfo);
-    method public void callSessionHeld(android.telephony.ims.ImsCallProfile);
-    method public void callSessionHoldFailed(android.telephony.ims.ImsReasonInfo);
-    method public void callSessionHoldReceived(android.telephony.ims.ImsCallProfile);
-    method public void callSessionInitiated(android.telephony.ims.ImsCallProfile);
-    method public void callSessionInitiatedFailed(android.telephony.ims.ImsReasonInfo);
-    method public void callSessionInviteParticipantsRequestDelivered();
-    method public void callSessionInviteParticipantsRequestFailed(android.telephony.ims.ImsReasonInfo);
-    method @Deprecated public void callSessionMayHandover(int, int);
-    method public void callSessionMergeComplete(android.telephony.ims.stub.ImsCallSessionImplBase);
-    method public void callSessionMergeFailed(android.telephony.ims.ImsReasonInfo);
-    method public void callSessionMergeStarted(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile);
-    method public void callSessionMultipartyStateChanged(boolean);
-    method public void callSessionProgressing(android.telephony.ims.ImsStreamMediaProfile);
-    method public void callSessionRemoveParticipantsRequestDelivered();
-    method public void callSessionRemoveParticipantsRequestFailed(android.telephony.ims.ImsReasonInfo);
-    method public void callSessionResumeFailed(android.telephony.ims.ImsReasonInfo);
-    method public void callSessionResumeReceived(android.telephony.ims.ImsCallProfile);
-    method public void callSessionResumed(android.telephony.ims.ImsCallProfile);
-    method public void callSessionRttAudioIndicatorChanged(@NonNull android.telephony.ims.ImsStreamMediaProfile);
-    method public void callSessionRttMessageReceived(String);
-    method public void callSessionRttModifyRequestReceived(android.telephony.ims.ImsCallProfile);
-    method public void callSessionRttModifyResponseReceived(int);
-    method public void callSessionSuppServiceReceived(android.telephony.ims.ImsSuppServiceNotification);
-    method public void callSessionTerminated(android.telephony.ims.ImsReasonInfo);
-    method public void callSessionTtyModeReceived(int);
-    method public void callSessionUpdateFailed(android.telephony.ims.ImsReasonInfo);
-    method public void callSessionUpdateReceived(android.telephony.ims.ImsCallProfile);
-    method public void callSessionUpdated(android.telephony.ims.ImsCallProfile);
-    method public void callSessionUssdMessageReceived(int, String);
-    method public void onHandover(int, int, @Nullable android.telephony.ims.ImsReasonInfo);
-    method public void onHandoverFailed(int, int, @NonNull android.telephony.ims.ImsReasonInfo);
-    method public void onMayHandover(int, int);
-  }
-
-  public final class ImsConferenceState implements android.os.Parcelable {
-    method public int describeContents();
-    method public static int getConnectionStateForStatus(String);
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsConferenceState> CREATOR;
-    field public static final String DISPLAY_TEXT = "display-text";
-    field public static final String ENDPOINT = "endpoint";
-    field public static final String SIP_STATUS_CODE = "sipstatuscode";
-    field public static final String STATUS = "status";
-    field public static final String STATUS_ALERTING = "alerting";
-    field public static final String STATUS_CONNECTED = "connected";
-    field public static final String STATUS_CONNECT_FAIL = "connect-fail";
-    field public static final String STATUS_DIALING_IN = "dialing-in";
-    field public static final String STATUS_DIALING_OUT = "dialing-out";
-    field public static final String STATUS_DISCONNECTED = "disconnected";
-    field public static final String STATUS_DISCONNECTING = "disconnecting";
-    field public static final String STATUS_MUTED_VIA_FOCUS = "muted-via-focus";
-    field public static final String STATUS_ON_HOLD = "on-hold";
-    field public static final String STATUS_PENDING = "pending";
-    field public static final String STATUS_SEND_ONLY = "sendonly";
-    field public static final String STATUS_SEND_RECV = "sendrecv";
-    field public static final String USER = "user";
-    field public final java.util.HashMap<java.lang.String,android.os.Bundle> mParticipants;
-  }
-
-  public final class ImsException extends java.lang.Exception {
-    ctor public ImsException(@Nullable String);
-    ctor public ImsException(@Nullable String, int);
-    ctor public ImsException(@Nullable String, int, @Nullable Throwable);
-  }
-
-  public final class ImsExternalCallState implements android.os.Parcelable {
-    ctor public ImsExternalCallState(@NonNull String, @NonNull android.net.Uri, @Nullable android.net.Uri, boolean, int, int, boolean);
-    method public int describeContents();
-    method @NonNull public android.net.Uri getAddress();
-    method public int getCallId();
-    method public int getCallState();
-    method public int getCallType();
-    method @Nullable public android.net.Uri getLocalAddress();
-    method public boolean isCallHeld();
-    method public boolean isCallPullable();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final int CALL_STATE_CONFIRMED = 1; // 0x1
-    field public static final int CALL_STATE_TERMINATED = 2; // 0x2
-    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsExternalCallState> CREATOR;
-  }
-
-  public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
-    method @Deprecated @NonNull @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int);
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getFeatureState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>) throws android.telephony.ims.ImsException;
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getVoWiFiRoamingModeSetting();
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAvailable(int, int);
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isCapable(int, int);
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void isSupported(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>) throws android.telephony.ims.ImsException;
-    method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback) throws android.telephony.ims.ImsException;
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setAdvancedCallingSettingEnabled(boolean);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setRttCapabilitySetting(boolean);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiModeSetting(int);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiNonPersistent(boolean, int);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiRoamingModeSetting(int);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiRoamingSettingEnabled(boolean);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiSettingEnabled(boolean);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVtSettingEnabled(boolean);
-    method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback);
-  }
-
-  @Deprecated public static class ImsMmTelManager.RegistrationCallback extends android.telephony.ims.RegistrationManager.RegistrationCallback {
-    ctor @Deprecated public ImsMmTelManager.RegistrationCallback();
-  }
-
-  public class ImsService extends android.app.Service {
-    ctor public ImsService();
-    method public android.telephony.ims.feature.MmTelFeature createMmTelFeature(int);
-    method public android.telephony.ims.feature.RcsFeature createRcsFeature(int);
-    method public void disableIms(int);
-    method public void enableIms(int);
-    method public android.telephony.ims.stub.ImsConfigImplBase getConfig(int);
-    method public android.telephony.ims.stub.ImsRegistrationImplBase getRegistration(int);
-    method public final void onUpdateSupportedImsFeatures(android.telephony.ims.stub.ImsFeatureConfiguration) throws android.os.RemoteException;
-    method public android.telephony.ims.stub.ImsFeatureConfiguration querySupportedImsFeatures();
-    method public void readyForFeatureCreation();
-  }
-
-  public final class ImsSsData implements android.os.Parcelable {
-    ctor public ImsSsData(int, int, int, int, int);
-    method public int describeContents();
-    method @Nullable public java.util.List<android.telephony.ims.ImsCallForwardInfo> getCallForwardInfo();
-    method public int getRequestType();
-    method public int getResult();
-    method public int getServiceClass();
-    method public int getServiceType();
-    method @NonNull public java.util.List<android.telephony.ims.ImsSsInfo> getSuppServiceInfo();
-    method public int getTeleserviceType();
-    method public boolean isTypeBarring();
-    method public boolean isTypeCf();
-    method public boolean isTypeClip();
-    method public boolean isTypeClir();
-    method public boolean isTypeColp();
-    method public boolean isTypeColr();
-    method public boolean isTypeCw();
-    method public boolean isTypeIcb();
-    method public boolean isTypeInterrogation();
-    method public boolean isTypeUnConditional();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsSsData> CREATOR;
-    field public static final int RESULT_SUCCESS = 0; // 0x0
-    field public static final int SERVICE_CLASS_DATA = 2; // 0x2
-    field public static final int SERVICE_CLASS_DATA_CIRCUIT_ASYNC = 32; // 0x20
-    field public static final int SERVICE_CLASS_DATA_CIRCUIT_SYNC = 16; // 0x10
-    field public static final int SERVICE_CLASS_DATA_PACKET_ACCESS = 64; // 0x40
-    field public static final int SERVICE_CLASS_DATA_PAD = 128; // 0x80
-    field public static final int SERVICE_CLASS_FAX = 4; // 0x4
-    field public static final int SERVICE_CLASS_NONE = 0; // 0x0
-    field public static final int SERVICE_CLASS_SMS = 8; // 0x8
-    field public static final int SERVICE_CLASS_VOICE = 1; // 0x1
-    field public static final int SS_ACTIVATION = 0; // 0x0
-    field public static final int SS_ALL_BARRING = 18; // 0x12
-    field public static final int SS_ALL_DATA_TELESERVICES = 3; // 0x3
-    field public static final int SS_ALL_TELESERVICES_EXCEPT_SMS = 5; // 0x5
-    field public static final int SS_ALL_TELESEVICES = 1; // 0x1
-    field public static final int SS_ALL_TELE_AND_BEARER_SERVICES = 0; // 0x0
-    field public static final int SS_BAIC = 16; // 0x10
-    field public static final int SS_BAIC_ROAMING = 17; // 0x11
-    field public static final int SS_BAOC = 13; // 0xd
-    field public static final int SS_BAOIC = 14; // 0xe
-    field public static final int SS_BAOIC_EXC_HOME = 15; // 0xf
-    field public static final int SS_CFU = 0; // 0x0
-    field public static final int SS_CFUT = 6; // 0x6
-    field public static final int SS_CF_ALL = 4; // 0x4
-    field public static final int SS_CF_ALL_CONDITIONAL = 5; // 0x5
-    field public static final int SS_CF_BUSY = 1; // 0x1
-    field public static final int SS_CF_NOT_REACHABLE = 3; // 0x3
-    field public static final int SS_CF_NO_REPLY = 2; // 0x2
-    field public static final int SS_CLIP = 7; // 0x7
-    field public static final int SS_CLIR = 8; // 0x8
-    field public static final int SS_CNAP = 11; // 0xb
-    field public static final int SS_COLP = 9; // 0x9
-    field public static final int SS_COLR = 10; // 0xa
-    field public static final int SS_DEACTIVATION = 1; // 0x1
-    field public static final int SS_ERASURE = 4; // 0x4
-    field public static final int SS_INCOMING_BARRING = 20; // 0x14
-    field public static final int SS_INCOMING_BARRING_ANONYMOUS = 22; // 0x16
-    field public static final int SS_INCOMING_BARRING_DN = 21; // 0x15
-    field public static final int SS_INTERROGATION = 2; // 0x2
-    field public static final int SS_OUTGOING_BARRING = 19; // 0x13
-    field public static final int SS_REGISTRATION = 3; // 0x3
-    field public static final int SS_SMS_SERVICES = 4; // 0x4
-    field public static final int SS_TELEPHONY = 2; // 0x2
-    field public static final int SS_WAIT = 12; // 0xc
-  }
-
-  public static final class ImsSsData.Builder {
-    ctor public ImsSsData.Builder(int, int, int, int, int);
-    method @NonNull public android.telephony.ims.ImsSsData build();
-    method @NonNull public android.telephony.ims.ImsSsData.Builder setCallForwardingInfo(@NonNull java.util.List<android.telephony.ims.ImsCallForwardInfo>);
-    method @NonNull public android.telephony.ims.ImsSsData.Builder setSuppServiceInfo(@NonNull java.util.List<android.telephony.ims.ImsSsInfo>);
-  }
-
-  public final class ImsSsInfo implements android.os.Parcelable {
-    ctor @Deprecated public ImsSsInfo(int, @Nullable String);
-    method public int describeContents();
-    method public int getClirInterrogationStatus();
-    method public int getClirOutgoingState();
-    method @Deprecated public String getIcbNum();
-    method @Nullable public String getIncomingCommunicationBarringNumber();
-    method public int getProvisionStatus();
-    method public int getStatus();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final int CLIR_OUTGOING_DEFAULT = 0; // 0x0
-    field public static final int CLIR_OUTGOING_INVOCATION = 1; // 0x1
-    field public static final int CLIR_OUTGOING_SUPPRESSION = 2; // 0x2
-    field public static final int CLIR_STATUS_NOT_PROVISIONED = 0; // 0x0
-    field public static final int CLIR_STATUS_PROVISIONED_PERMANENT = 1; // 0x1
-    field public static final int CLIR_STATUS_TEMPORARILY_ALLOWED = 4; // 0x4
-    field public static final int CLIR_STATUS_TEMPORARILY_RESTRICTED = 3; // 0x3
-    field public static final int CLIR_STATUS_UNKNOWN = 2; // 0x2
-    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsSsInfo> CREATOR;
-    field public static final int DISABLED = 0; // 0x0
-    field public static final int ENABLED = 1; // 0x1
-    field public static final int NOT_REGISTERED = -1; // 0xffffffff
-    field public static final int SERVICE_NOT_PROVISIONED = 0; // 0x0
-    field public static final int SERVICE_PROVISIONED = 1; // 0x1
-    field public static final int SERVICE_PROVISIONING_UNKNOWN = -1; // 0xffffffff
-  }
-
-  public static final class ImsSsInfo.Builder {
-    ctor public ImsSsInfo.Builder(int);
-    method @NonNull public android.telephony.ims.ImsSsInfo build();
-    method @NonNull public android.telephony.ims.ImsSsInfo.Builder setClirInterrogationStatus(int);
-    method @NonNull public android.telephony.ims.ImsSsInfo.Builder setClirOutgoingState(int);
-    method @NonNull public android.telephony.ims.ImsSsInfo.Builder setIncomingCommunicationBarringNumber(@NonNull String);
-    method @NonNull public android.telephony.ims.ImsSsInfo.Builder setProvisionStatus(int);
-  }
-
-  public final class ImsStreamMediaProfile implements android.os.Parcelable {
-    ctor public ImsStreamMediaProfile(int, int, int, int, int);
-    method public void copyFrom(android.telephony.ims.ImsStreamMediaProfile);
-    method public int describeContents();
-    method public int getAudioDirection();
-    method public int getAudioQuality();
-    method public int getRttMode();
-    method public int getVideoDirection();
-    method public int getVideoQuality();
-    method public boolean isReceivingRttAudio();
-    method public boolean isRttCall();
-    method public void setReceivingRttAudio(boolean);
-    method public void setRttMode(int);
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final int AUDIO_QUALITY_AMR = 1; // 0x1
-    field public static final int AUDIO_QUALITY_AMR_WB = 2; // 0x2
-    field public static final int AUDIO_QUALITY_EVRC = 4; // 0x4
-    field public static final int AUDIO_QUALITY_EVRC_B = 5; // 0x5
-    field public static final int AUDIO_QUALITY_EVRC_NW = 7; // 0x7
-    field public static final int AUDIO_QUALITY_EVRC_WB = 6; // 0x6
-    field public static final int AUDIO_QUALITY_EVS_FB = 20; // 0x14
-    field public static final int AUDIO_QUALITY_EVS_NB = 17; // 0x11
-    field public static final int AUDIO_QUALITY_EVS_SWB = 19; // 0x13
-    field public static final int AUDIO_QUALITY_EVS_WB = 18; // 0x12
-    field public static final int AUDIO_QUALITY_G711A = 13; // 0xd
-    field public static final int AUDIO_QUALITY_G711AB = 15; // 0xf
-    field public static final int AUDIO_QUALITY_G711U = 11; // 0xb
-    field public static final int AUDIO_QUALITY_G722 = 14; // 0xe
-    field public static final int AUDIO_QUALITY_G723 = 12; // 0xc
-    field public static final int AUDIO_QUALITY_G729 = 16; // 0x10
-    field public static final int AUDIO_QUALITY_GSM_EFR = 8; // 0x8
-    field public static final int AUDIO_QUALITY_GSM_FR = 9; // 0x9
-    field public static final int AUDIO_QUALITY_GSM_HR = 10; // 0xa
-    field public static final int AUDIO_QUALITY_NONE = 0; // 0x0
-    field public static final int AUDIO_QUALITY_QCELP13K = 3; // 0x3
-    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsStreamMediaProfile> CREATOR;
-    field public static final int DIRECTION_INACTIVE = 0; // 0x0
-    field public static final int DIRECTION_INVALID = -1; // 0xffffffff
-    field public static final int DIRECTION_RECEIVE = 1; // 0x1
-    field public static final int DIRECTION_SEND = 2; // 0x2
-    field public static final int DIRECTION_SEND_RECEIVE = 3; // 0x3
-    field public static final int RTT_MODE_DISABLED = 0; // 0x0
-    field public static final int RTT_MODE_FULL = 1; // 0x1
-    field public static final int VIDEO_QUALITY_NONE = 0; // 0x0
-    field public static final int VIDEO_QUALITY_QCIF = 1; // 0x1
-    field public static final int VIDEO_QUALITY_QVGA_LANDSCAPE = 2; // 0x2
-    field public static final int VIDEO_QUALITY_QVGA_PORTRAIT = 4; // 0x4
-    field public static final int VIDEO_QUALITY_VGA_LANDSCAPE = 8; // 0x8
-    field public static final int VIDEO_QUALITY_VGA_PORTRAIT = 16; // 0x10
-  }
-
-  public final class ImsSuppServiceNotification implements android.os.Parcelable {
-    ctor public ImsSuppServiceNotification(int, int, int, int, String, String[]);
-    method public int describeContents();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsSuppServiceNotification> CREATOR;
-    field public final int code;
-    field public final String[] history;
-    field public final int index;
-    field public final int notificationType;
-    field public final String number;
-    field public final int type;
-  }
-
-  public class ImsUtListener {
-    method public void onLineIdentificationSupplementaryServiceResponse(int, @NonNull android.telephony.ims.ImsSsInfo);
-    method public void onSupplementaryServiceIndication(android.telephony.ims.ImsSsData);
-    method public void onUtConfigurationCallBarringQueried(int, android.telephony.ims.ImsSsInfo[]);
-    method public void onUtConfigurationCallForwardQueried(int, android.telephony.ims.ImsCallForwardInfo[]);
-    method public void onUtConfigurationCallWaitingQueried(int, android.telephony.ims.ImsSsInfo[]);
-    method @Deprecated public void onUtConfigurationQueried(int, android.os.Bundle);
-    method public void onUtConfigurationQueryFailed(int, android.telephony.ims.ImsReasonInfo);
-    method public void onUtConfigurationUpdateFailed(int, android.telephony.ims.ImsReasonInfo);
-    method public void onUtConfigurationUpdated(int);
-    field @Deprecated public static final String BUNDLE_KEY_CLIR = "queryClir";
-    field @Deprecated public static final String BUNDLE_KEY_SSINFO = "imsSsInfo";
-  }
-
-  public abstract class ImsVideoCallProvider {
-    ctor public ImsVideoCallProvider();
-    method public void changeCallDataUsage(long);
-    method public void changeCameraCapabilities(android.telecom.VideoProfile.CameraCapabilities);
-    method public void changePeerDimensions(int, int);
-    method public void changeVideoQuality(int);
-    method public void handleCallSessionEvent(int);
-    method public abstract void onRequestCallDataUsage();
-    method public abstract void onRequestCameraCapabilities();
-    method public abstract void onSendSessionModifyRequest(android.telecom.VideoProfile, android.telecom.VideoProfile);
-    method public abstract void onSendSessionModifyResponse(android.telecom.VideoProfile);
-    method public abstract void onSetCamera(String);
-    method public void onSetCamera(String, int);
-    method public abstract void onSetDeviceOrientation(int);
-    method public abstract void onSetDisplaySurface(android.view.Surface);
-    method public abstract void onSetPauseImage(android.net.Uri);
-    method public abstract void onSetPreviewSurface(android.view.Surface);
-    method public abstract void onSetZoom(float);
-    method public void receiveSessionModifyRequest(android.telecom.VideoProfile);
-    method public void receiveSessionModifyResponse(int, android.telecom.VideoProfile, android.telecom.VideoProfile);
-  }
-
-  public class ProvisioningManager {
-    method @NonNull public static android.telephony.ims.ProvisioningManager createForSubscriptionId(int);
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public int getProvisioningIntValue(int);
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean getProvisioningStatusForCapability(int, int);
-    method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public String getProvisioningStringValue(int);
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean getRcsProvisioningStatusForCapability(int);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean);
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerProvisioningChangedCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.Callback) throws android.telephony.ims.ImsException;
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningIntValue(int, int);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setProvisioningStatusForCapability(int, int, boolean);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean);
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback);
-    field public static final int KEY_VOICE_OVER_WIFI_ENTITLEMENT_ID = 67; // 0x43
-    field public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27; // 0x1b
-    field public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26; // 0x1a
-    field public static final int PROVISIONING_VALUE_DISABLED = 0; // 0x0
-    field public static final int PROVISIONING_VALUE_ENABLED = 1; // 0x1
-    field public static final String STRING_QUERY_RESULT_ERROR_GENERIC = "STRING_QUERY_RESULT_ERROR_GENERIC";
-    field public static final String STRING_QUERY_RESULT_ERROR_NOT_READY = "STRING_QUERY_RESULT_ERROR_NOT_READY";
-  }
-
-  public static class ProvisioningManager.Callback {
-    ctor public ProvisioningManager.Callback();
-    method public void onProvisioningIntChanged(int, int);
-    method public void onProvisioningStringChanged(int, @NonNull String);
-  }
-
-  public class RcsUceAdapter {
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
   }
 
 }
 
 package android.telephony.ims.feature {
 
-  public final class CapabilityChangeRequest implements android.os.Parcelable {
-    method public void addCapabilitiesToDisableForTech(int, int);
-    method public void addCapabilitiesToEnableForTech(int, int);
-    method public int describeContents();
-    method public java.util.List<android.telephony.ims.feature.CapabilityChangeRequest.CapabilityPair> getCapabilitiesToDisable();
-    method public java.util.List<android.telephony.ims.feature.CapabilityChangeRequest.CapabilityPair> getCapabilitiesToEnable();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.feature.CapabilityChangeRequest> CREATOR;
-  }
-
-  public static class CapabilityChangeRequest.CapabilityPair {
-    ctor public CapabilityChangeRequest.CapabilityPair(int, int);
-    method public int getCapability();
-    method public int getRadioTech();
-  }
-
-  public abstract class ImsFeature {
-    ctor public ImsFeature();
-    method public abstract void changeEnabledCapabilities(android.telephony.ims.feature.CapabilityChangeRequest, android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
-    method public int getFeatureState();
-    method public final int getSlotIndex();
-    method public abstract void onFeatureReady();
-    method public abstract void onFeatureRemoved();
-    method public final void setFeatureState(int);
-    field public static final int CAPABILITY_ERROR_GENERIC = -1; // 0xffffffff
-    field public static final int CAPABILITY_SUCCESS = 0; // 0x0
-    field public static final int FEATURE_EMERGENCY_MMTEL = 0; // 0x0
-    field public static final int FEATURE_MMTEL = 1; // 0x1
-    field public static final int FEATURE_RCS = 2; // 0x2
-    field public static final int STATE_INITIALIZING = 1; // 0x1
-    field public static final int STATE_READY = 2; // 0x2
-    field public static final int STATE_UNAVAILABLE = 0; // 0x0
-  }
-
   @Deprecated public static class ImsFeature.Capabilities {
     field @Deprecated protected int mCapabilities;
   }
 
-  protected static class ImsFeature.CapabilityCallbackProxy {
-    method public void onChangeCapabilityConfigurationError(int, int, int);
-  }
-
-  public class MmTelFeature extends android.telephony.ims.feature.ImsFeature {
-    ctor public MmTelFeature();
-    method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
-    method @Nullable public android.telephony.ims.ImsCallProfile createCallProfile(int, int);
-    method @Nullable public android.telephony.ims.stub.ImsCallSessionImplBase createCallSession(@NonNull android.telephony.ims.ImsCallProfile);
-    method @NonNull public android.telephony.ims.stub.ImsEcbmImplBase getEcbm();
-    method @NonNull public android.telephony.ims.stub.ImsMultiEndpointImplBase getMultiEndpoint();
-    method @NonNull public android.telephony.ims.stub.ImsSmsImplBase getSmsImplementation();
-    method @NonNull public android.telephony.ims.stub.ImsUtImplBase getUt();
-    method public final void notifyCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.MmTelFeature.MmTelCapabilities);
-    method public final void notifyIncomingCall(@NonNull android.telephony.ims.stub.ImsCallSessionImplBase, @NonNull android.os.Bundle);
-    method public final void notifyRejectedCall(@NonNull android.telephony.ims.ImsCallProfile, @NonNull android.telephony.ims.ImsReasonInfo);
-    method public final void notifyVoiceMessageCountUpdate(int);
-    method public void onFeatureReady();
-    method public void onFeatureRemoved();
-    method public boolean queryCapabilityConfiguration(int, int);
-    method @NonNull public final android.telephony.ims.feature.MmTelFeature.MmTelCapabilities queryCapabilityStatus();
-    method public void setUiTtyMode(int, @Nullable android.os.Message);
-    method public int shouldProcessCall(@NonNull String[]);
-    field public static final String EXTRA_IS_UNKNOWN_CALL = "android.telephony.ims.feature.extra.IS_UNKNOWN_CALL";
-    field public static final String EXTRA_IS_USSD = "android.telephony.ims.feature.extra.IS_USSD";
-    field public static final int PROCESS_CALL_CSFB = 1; // 0x1
-    field public static final int PROCESS_CALL_IMS = 0; // 0x0
-  }
-
-  public static class MmTelFeature.MmTelCapabilities extends android.telephony.ims.feature.ImsFeature.Capabilities {
-    ctor public MmTelFeature.MmTelCapabilities();
-    ctor @Deprecated public MmTelFeature.MmTelCapabilities(android.telephony.ims.feature.ImsFeature.Capabilities);
-    ctor public MmTelFeature.MmTelCapabilities(int);
-    method public final void addCapabilities(int);
-    method public final void removeCapabilities(int);
-  }
-
-  public class RcsFeature extends android.telephony.ims.feature.ImsFeature {
-    ctor public RcsFeature();
-    method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
-    method public void onFeatureReady();
-    method public void onFeatureRemoved();
-  }
-
-}
-
-package android.telephony.ims.stub {
-
-  public class ImsCallSessionImplBase implements java.lang.AutoCloseable {
-    ctor public ImsCallSessionImplBase();
-    method public void accept(int, android.telephony.ims.ImsStreamMediaProfile);
-    method public void close();
-    method public void deflect(String);
-    method public void extendToConference(String[]);
-    method public String getCallId();
-    method public android.telephony.ims.ImsCallProfile getCallProfile();
-    method public android.telephony.ims.ImsVideoCallProvider getImsVideoCallProvider();
-    method public android.telephony.ims.ImsCallProfile getLocalCallProfile();
-    method public String getProperty(String);
-    method public android.telephony.ims.ImsCallProfile getRemoteCallProfile();
-    method public int getState();
-    method public void hold(android.telephony.ims.ImsStreamMediaProfile);
-    method public void inviteParticipants(String[]);
-    method public boolean isInCall();
-    method public boolean isMultiparty();
-    method public void merge();
-    method public void reject(int);
-    method public void removeParticipants(String[]);
-    method public void resume(android.telephony.ims.ImsStreamMediaProfile);
-    method public void sendDtmf(char, android.os.Message);
-    method public void sendRttMessage(String);
-    method public void sendRttModifyRequest(android.telephony.ims.ImsCallProfile);
-    method public void sendRttModifyResponse(boolean);
-    method public void sendUssd(String);
-    method public void setListener(android.telephony.ims.ImsCallSessionListener);
-    method public void setMute(boolean);
-    method public void start(String, android.telephony.ims.ImsCallProfile);
-    method public void startConference(String[], android.telephony.ims.ImsCallProfile);
-    method public void startDtmf(char);
-    method public void stopDtmf();
-    method public void terminate(int);
-    method public void update(int, android.telephony.ims.ImsStreamMediaProfile);
-    field public static final int USSD_MODE_NOTIFY = 0; // 0x0
-    field public static final int USSD_MODE_REQUEST = 1; // 0x1
-  }
-
-  public static class ImsCallSessionImplBase.State {
-    method public static String toString(int);
-    field public static final int ESTABLISHED = 4; // 0x4
-    field public static final int ESTABLISHING = 3; // 0x3
-    field public static final int IDLE = 0; // 0x0
-    field public static final int INITIATED = 1; // 0x1
-    field public static final int INVALID = -1; // 0xffffffff
-    field public static final int NEGOTIATING = 2; // 0x2
-    field public static final int REESTABLISHING = 6; // 0x6
-    field public static final int RENEGOTIATING = 5; // 0x5
-    field public static final int TERMINATED = 8; // 0x8
-    field public static final int TERMINATING = 7; // 0x7
-  }
-
-  public class ImsConfigImplBase {
-    ctor public ImsConfigImplBase();
-    method public int getConfigInt(int);
-    method public String getConfigString(int);
-    method public final void notifyProvisionedValueChanged(int, int);
-    method public final void notifyProvisionedValueChanged(int, String);
-    method public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean);
-    method public int setConfig(int, int);
-    method public int setConfig(int, String);
-    field public static final int CONFIG_RESULT_FAILED = 1; // 0x1
-    field public static final int CONFIG_RESULT_SUCCESS = 0; // 0x0
-    field public static final int CONFIG_RESULT_UNKNOWN = -1; // 0xffffffff
-  }
-
-  public class ImsEcbmImplBase {
-    ctor public ImsEcbmImplBase();
-    method public final void enteredEcbm();
-    method public void exitEmergencyCallbackMode();
-    method public final void exitedEcbm();
-  }
-
-  public final class ImsFeatureConfiguration implements android.os.Parcelable {
-    method public int describeContents();
-    method public java.util.Set<android.telephony.ims.stub.ImsFeatureConfiguration.FeatureSlotPair> getServiceFeatures();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.stub.ImsFeatureConfiguration> CREATOR;
-  }
-
-  public static class ImsFeatureConfiguration.Builder {
-    ctor public ImsFeatureConfiguration.Builder();
-    method public android.telephony.ims.stub.ImsFeatureConfiguration.Builder addFeature(int, int);
-    method public android.telephony.ims.stub.ImsFeatureConfiguration build();
-  }
-
-  public static final class ImsFeatureConfiguration.FeatureSlotPair {
-    ctor public ImsFeatureConfiguration.FeatureSlotPair(int, int);
-    field public final int featureType;
-    field public final int slotId;
-  }
-
-  public class ImsMultiEndpointImplBase {
-    ctor public ImsMultiEndpointImplBase();
-    method public final void onImsExternalCallStateUpdate(java.util.List<android.telephony.ims.ImsExternalCallState>);
-    method public void requestImsExternalCallStateInfo();
-  }
-
-  public class ImsRegistrationImplBase {
-    ctor public ImsRegistrationImplBase();
-    method public final void onDeregistered(android.telephony.ims.ImsReasonInfo);
-    method public final void onRegistered(int);
-    method public final void onRegistering(int);
-    method public final void onSubscriberAssociatedUriChanged(android.net.Uri[]);
-    method public final void onTechnologyChangeFailed(int, android.telephony.ims.ImsReasonInfo);
-    field public static final int REGISTRATION_TECH_IWLAN = 1; // 0x1
-    field public static final int REGISTRATION_TECH_LTE = 0; // 0x0
-    field public static final int REGISTRATION_TECH_NONE = -1; // 0xffffffff
-  }
-
-  public class ImsSmsImplBase {
-    ctor public ImsSmsImplBase();
-    method public void acknowledgeSms(int, @IntRange(from=0, to=65535) int, int);
-    method public void acknowledgeSmsReport(int, @IntRange(from=0, to=65535) int, int);
-    method public String getSmsFormat();
-    method public void onReady();
-    method @Deprecated public final void onSendSmsResult(int, @IntRange(from=0, to=65535) int, int, int) throws java.lang.RuntimeException;
-    method public final void onSendSmsResultError(int, @IntRange(from=0, to=65535) int, int, int, int) throws java.lang.RuntimeException;
-    method public final void onSendSmsResultSuccess(int, @IntRange(from=0, to=65535) int) throws java.lang.RuntimeException;
-    method public final void onSmsReceived(int, String, byte[]) throws java.lang.RuntimeException;
-    method @Deprecated public final void onSmsStatusReportReceived(int, @IntRange(from=0, to=65535) int, String, byte[]) throws java.lang.RuntimeException;
-    method public final void onSmsStatusReportReceived(int, String, byte[]) throws java.lang.RuntimeException;
-    method public void sendSms(int, @IntRange(from=0, to=65535) int, String, String, boolean, byte[]);
-    field public static final int DELIVER_STATUS_ERROR_GENERIC = 2; // 0x2
-    field public static final int DELIVER_STATUS_ERROR_NO_MEMORY = 3; // 0x3
-    field public static final int DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED = 4; // 0x4
-    field public static final int DELIVER_STATUS_OK = 1; // 0x1
-    field public static final int RESULT_NO_NETWORK_ERROR = -1; // 0xffffffff
-    field public static final int SEND_STATUS_ERROR = 2; // 0x2
-    field public static final int SEND_STATUS_ERROR_FALLBACK = 4; // 0x4
-    field public static final int SEND_STATUS_ERROR_RETRY = 3; // 0x3
-    field public static final int SEND_STATUS_OK = 1; // 0x1
-    field public static final int STATUS_REPORT_STATUS_ERROR = 2; // 0x2
-    field public static final int STATUS_REPORT_STATUS_OK = 1; // 0x1
-  }
-
-  public class ImsUtImplBase {
-    ctor public ImsUtImplBase();
-    method public void close();
-    method public int queryCallBarring(int);
-    method public int queryCallBarringForServiceClass(int, int);
-    method public int queryCallForward(int, String);
-    method public int queryCallWaiting();
-    method public int queryClip();
-    method public int queryClir();
-    method public int queryColp();
-    method public int queryColr();
-    method public void setListener(android.telephony.ims.ImsUtListener);
-    method public int transact(android.os.Bundle);
-    method public int updateCallBarring(int, int, String[]);
-    method public int updateCallBarringForServiceClass(int, int, String[], int);
-    method public int updateCallForward(int, int, String, int, int);
-    method public int updateCallWaiting(boolean, int);
-    method public int updateClip(boolean);
-    method public int updateClir(int);
-    method public int updateColp(boolean);
-    method public int updateColr(int);
-  }
-
-}
-
-package android.telephony.mbms {
-
-  public static class DownloadRequest.Builder {
-    method public android.telephony.mbms.DownloadRequest.Builder setServiceId(String);
-  }
-
-  public final class FileInfo implements android.os.Parcelable {
-    ctor public FileInfo(android.net.Uri, String);
-  }
-
-  public final class FileServiceInfo extends android.telephony.mbms.ServiceInfo implements android.os.Parcelable {
-    ctor public FileServiceInfo(java.util.Map<java.util.Locale,java.lang.String>, String, java.util.List<java.util.Locale>, String, java.util.Date, java.util.Date, java.util.List<android.telephony.mbms.FileInfo>);
-  }
-
-  public final class StreamingServiceInfo extends android.telephony.mbms.ServiceInfo implements android.os.Parcelable {
-    ctor public StreamingServiceInfo(java.util.Map<java.util.Locale,java.lang.String>, String, java.util.List<java.util.Locale>, String, java.util.Date, java.util.Date);
-  }
-
-  public final class UriPathPair implements android.os.Parcelable {
-    method public int describeContents();
-    method public android.net.Uri getContentUri();
-    method public android.net.Uri getFilePathUri();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.mbms.UriPathPair> CREATOR;
-  }
-
-}
-
-package android.telephony.mbms.vendor {
-
-  public class MbmsDownloadServiceBase extends android.os.Binder implements android.os.IInterface {
-    ctor public MbmsDownloadServiceBase();
-    method public int addProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener) throws android.os.RemoteException;
-    method public int addServiceAnnouncement(int, @NonNull byte[]);
-    method public int addStatusListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStatusListener) throws android.os.RemoteException;
-    method public android.os.IBinder asBinder();
-    method public int cancelDownload(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
-    method public void dispose(int) throws android.os.RemoteException;
-    method public int download(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
-    method public int initialize(int, android.telephony.mbms.MbmsDownloadSessionCallback) throws android.os.RemoteException;
-    method @NonNull public java.util.List<android.telephony.mbms.DownloadRequest> listPendingDownloads(int) throws android.os.RemoteException;
-    method public void onAppCallbackDied(int, int);
-    method public boolean onTransact(int, android.os.Parcel, android.os.Parcel, int) throws android.os.RemoteException;
-    method public int removeProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener) throws android.os.RemoteException;
-    method public int removeStatusListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStatusListener) throws android.os.RemoteException;
-    method public int requestDownloadState(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo) throws android.os.RemoteException;
-    method public int requestUpdateFileServices(int, java.util.List<java.lang.String>) throws android.os.RemoteException;
-    method public int resetDownloadKnowledge(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
-    method public int setTempFileRootDirectory(int, String) throws android.os.RemoteException;
-  }
-
-  public class MbmsGroupCallServiceBase extends android.app.Service {
-    ctor public MbmsGroupCallServiceBase();
-    method public void dispose(int) throws android.os.RemoteException;
-    method public int initialize(@NonNull android.telephony.mbms.MbmsGroupCallSessionCallback, int) throws android.os.RemoteException;
-    method public void onAppCallbackDied(int, int);
-    method public android.os.IBinder onBind(android.content.Intent);
-    method public int startGroupCall(int, long, @NonNull java.util.List<java.lang.Integer>, @NonNull java.util.List<java.lang.Integer>, @NonNull android.telephony.mbms.GroupCallCallback);
-    method public void stopGroupCall(int, long);
-    method public void updateGroupCall(int, long, @NonNull java.util.List<java.lang.Integer>, @NonNull java.util.List<java.lang.Integer>);
-  }
-
-  public class MbmsStreamingServiceBase extends android.os.Binder implements android.os.IInterface {
-    ctor public MbmsStreamingServiceBase();
-    method public android.os.IBinder asBinder();
-    method public void dispose(int) throws android.os.RemoteException;
-    method @Nullable public android.net.Uri getPlaybackUri(int, String) throws android.os.RemoteException;
-    method public int initialize(android.telephony.mbms.MbmsStreamingSessionCallback, int) throws android.os.RemoteException;
-    method public void onAppCallbackDied(int, int);
-    method public boolean onTransact(int, android.os.Parcel, android.os.Parcel, int) throws android.os.RemoteException;
-    method public int requestUpdateStreamingServices(int, java.util.List<java.lang.String>) throws android.os.RemoteException;
-    method public int startStreaming(int, String, android.telephony.mbms.StreamingServiceCallback) throws android.os.RemoteException;
-    method public void stopStreaming(int, String) throws android.os.RemoteException;
-  }
-
-  public class VendorUtils {
-    ctor public VendorUtils();
-    method public static android.content.ComponentName getAppReceiverFromPackageName(android.content.Context, String);
-    field public static final String ACTION_CLEANUP = "android.telephony.mbms.action.CLEANUP";
-    field public static final String ACTION_DOWNLOAD_RESULT_INTERNAL = "android.telephony.mbms.action.DOWNLOAD_RESULT_INTERNAL";
-    field public static final String ACTION_FILE_DESCRIPTOR_REQUEST = "android.telephony.mbms.action.FILE_DESCRIPTOR_REQUEST";
-    field public static final String EXTRA_FD_COUNT = "android.telephony.mbms.extra.FD_COUNT";
-    field public static final String EXTRA_FINAL_URI = "android.telephony.mbms.extra.FINAL_URI";
-    field public static final String EXTRA_FREE_URI_LIST = "android.telephony.mbms.extra.FREE_URI_LIST";
-    field public static final String EXTRA_PAUSED_LIST = "android.telephony.mbms.extra.PAUSED_LIST";
-    field public static final String EXTRA_PAUSED_URI_LIST = "android.telephony.mbms.extra.PAUSED_URI_LIST";
-    field public static final String EXTRA_SERVICE_ID = "android.telephony.mbms.extra.SERVICE_ID";
-    field public static final String EXTRA_TEMP_FILES_IN_USE = "android.telephony.mbms.extra.TEMP_FILES_IN_USE";
-    field public static final String EXTRA_TEMP_FILE_ROOT = "android.telephony.mbms.extra.TEMP_FILE_ROOT";
-    field public static final String EXTRA_TEMP_LIST = "android.telephony.mbms.extra.TEMP_LIST";
-  }
-
 }
 
 package android.text {
@@ -5102,7 +1842,6 @@
     field public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid";
     field public static final String PERSIST_PREFIX = "persist.sys.fflag.override.";
     field public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press";
-    field public static final String SEAMLESS_TRANSFER = "settings_seamless_transfer";
     field public static final String SETTINGS_WIFITRACKER2 = "settings_wifitracker2";
   }
 
@@ -5295,7 +2034,7 @@
   }
 
   public interface WindowManager extends android.view.ViewManager {
-    method @RequiresPermission("android.permission.INJECT_EVENTS") public default void holdLock(int);
+    method @RequiresPermission(android.Manifest.permission.INJECT_EVENTS) public default void holdLock(int);
     method public default void setShouldShowIme(int, boolean);
     method public default void setShouldShowSystemDecors(int, boolean);
     method public default void setShouldShowWithInsecureKeyguard(int, boolean);
@@ -5316,11 +2055,8 @@
 
   public final class AccessibilityManager {
     method public void addAccessibilityServicesStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener, @Nullable android.os.Handler);
-    method @NonNull @RequiresPermission("android.permission.MANAGE_ACCESSIBILITY") public java.util.List<java.lang.String> getAccessibilityShortcutTargets(int);
-    method @RequiresPermission("android.permission.MANAGE_ACCESSIBILITY") public void performAccessibilityShortcut();
-    method @RequiresPermission("android.permission.MANAGE_ACCESSIBILITY") public void registerSystemAction(@NonNull android.app.RemoteAction, int);
+    method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public java.util.List<java.lang.String> getAccessibilityShortcutTargets(int);
     method public void removeAccessibilityServicesStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener);
-    method @RequiresPermission("android.permission.MANAGE_ACCESSIBILITY") public void unregisterSystemAction(int);
   }
 
   public static interface AccessibilityManager.AccessibilityServicesStateChangeListener {
@@ -5366,7 +2102,6 @@
   }
 
   public final class AutofillManager {
-    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
@@ -5377,42 +2112,7 @@
 
 package android.view.contentcapture {
 
-  public final class ContentCaptureContext implements android.os.Parcelable {
-    method @Nullable public android.content.ComponentName getActivityComponent();
-    method public int getDisplayId();
-    method public int getFlags();
-    method @Nullable public android.view.contentcapture.ContentCaptureSessionId getParentSessionId();
-    method public int getTaskId();
-    field public static final int FLAG_DISABLED_BY_APP = 1; // 0x1
-    field public static final int FLAG_DISABLED_BY_FLAG_SECURE = 2; // 0x2
-    field public static final int FLAG_RECONNECTED = 4; // 0x4
-  }
-
-  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();
-    method @Nullable public android.graphics.Insets getInsets();
-    method @Nullable public CharSequence getText();
-    method public int getType();
-    method @Nullable public android.view.contentcapture.ViewNode getViewNode();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull 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_SESSION_PAUSED = 8; // 0x8
-    field public static final int TYPE_SESSION_RESUMED = 7; // 0x7
-    field public static final int TYPE_VIEW_APPEARED = 1; // 0x1
-    field public static final int TYPE_VIEW_DISAPPEARED = 2; // 0x2
-    field public static final int TYPE_VIEW_INSETS_CHANGED = 9; // 0x9
-    field public static final int TYPE_VIEW_TEXT_CHANGED = 3; // 0x3
-    field public static final int TYPE_VIEW_TREE_APPEARED = 5; // 0x5
-    field public static final int TYPE_VIEW_TREE_APPEARING = 4; // 0x4
-  }
-
   public final class ContentCaptureManager {
-    method public boolean isContentCaptureFeatureEnabled();
     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";
@@ -5425,7 +2125,6 @@
   }
 
   public final class ViewNode extends android.app.assist.AssistStructure.ViewNode {
-    method @Nullable public android.view.autofill.AutofillId getParentAutofillId();
     method @Nullable public static android.view.contentcapture.ViewNode readFromParcel(@NonNull android.os.Parcel);
     method public static void writeToParcel(@NonNull android.os.Parcel, @Nullable android.view.contentcapture.ViewNode, int);
   }
@@ -5663,6 +2362,15 @@
     field public static final int FEATURE_WINDOW_TOKENS = 2; // 0x2
   }
 
+  public final class TaskAppearedInfo implements android.os.Parcelable {
+    ctor public TaskAppearedInfo(@NonNull android.app.ActivityManager.RunningTaskInfo, @NonNull android.view.SurfaceControl);
+    method public int describeContents();
+    method @NonNull public android.view.SurfaceControl getLeash();
+    method @NonNull public android.app.ActivityManager.RunningTaskInfo getTaskInfo();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.window.TaskAppearedInfo> CREATOR;
+  }
+
   public class TaskOrganizer extends android.window.WindowOrganizer {
     ctor public TaskOrganizer();
     method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public android.app.ActivityManager.RunningTaskInfo createRootTask(int, int);
@@ -5674,10 +2382,10 @@
     method @BinderThread public void onTaskAppeared(@NonNull android.app.ActivityManager.RunningTaskInfo, @NonNull android.view.SurfaceControl);
     method @BinderThread public void onTaskInfoChanged(@NonNull android.app.ActivityManager.RunningTaskInfo);
     method @BinderThread public void onTaskVanished(@NonNull android.app.ActivityManager.RunningTaskInfo);
-    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public final void registerOrganizer();
+    method @CallSuper @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public java.util.List<android.window.TaskAppearedInfo> registerOrganizer();
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void setInterceptBackPressedOnTaskRoot(@NonNull android.window.WindowContainerToken, boolean);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void setLaunchRoot(int, @NonNull android.window.WindowContainerToken);
-    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public final void unregisterOrganizer();
+    method @CallSuper @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void unregisterOrganizer();
   }
 
   public final class WindowContainerToken implements android.os.Parcelable {
diff --git a/api/test-lint-baseline.txt b/api/test-lint-baseline.txt
index 91a09e3..0440d1a 100644
--- a/api/test-lint-baseline.txt
+++ b/api/test-lint-baseline.txt
@@ -2511,6 +2511,8 @@
     
 NoSettingsProvider: android.provider.Settings.Global#HIDDEN_API_BLACKLIST_EXEMPTIONS:
     
+NoSettingsProvider: android.provider.Settings.Global#HIDDEN_API_POLICY:
+    
 NoSettingsProvider: android.provider.Settings.Global#HIDE_ERROR_DIALOGS:
     
 NoSettingsProvider: android.provider.Settings.Global#LOCATION_GLOBAL_KILL_SWITCH:
@@ -2530,6 +2532,16 @@
 NoSettingsProvider: android.provider.Settings.Global#USE_OPEN_WIFI_PACKAGE:
     
 NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED:
+
+NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_CAPABILITY:
+
+NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE:
+
+NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_ALL:
+
+NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN:
+
+NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW:
     
 NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_SHORTCUT_TARGET_SERVICE:
     
diff --git a/api/test-removed.txt b/api/test-removed.txt
index e47f6ed..d802177 100644
--- a/api/test-removed.txt
+++ b/api/test-removed.txt
@@ -1,10 +1 @@
 // Signature format: 2.0
-package android.app.prediction {
-
-  public static final class AppTarget.Builder {
-    method @Deprecated @NonNull public android.app.prediction.AppTarget.Builder setTarget(@NonNull String, @NonNull android.os.UserHandle);
-    method @Deprecated @NonNull public android.app.prediction.AppTarget.Builder setTarget(@NonNull android.content.pm.ShortcutInfo);
-  }
-
-}
-
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.h b/cmds/idmap2/idmap2d/Idmap2Service.h
index 1a44519..1d2090c 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.h
+++ b/cmds/idmap2/idmap2d/Idmap2Service.h
@@ -22,6 +22,8 @@
 #include <android-base/unique_fd.h>
 #include <binder/BinderService.h>
 
+#include <string>
+
 #include "android/os/BnIdmap2.h"
 
 namespace android::os {
diff --git a/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h b/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h
index ff45b14..bf31cbf 100644
--- a/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h
+++ b/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h
@@ -36,12 +36,11 @@
   void visit(const IdmapData::Header& header) override;
 
  private:
-  void Write(const void* value, size_t length);
   void Write8(uint8_t value);
   void Write16(uint16_t value);
   void Write32(uint32_t value);
   void WriteString256(const StringPiece& value);
-  void WriteString(const std::string& value);
+  void WriteString(const StringPiece& value);
   std::ostream& stream_;
 };
 
diff --git a/cmds/idmap2/include/idmap2/Idmap.h b/cmds/idmap2/include/idmap2/Idmap.h
index 0f05592..a35fad9 100644
--- a/cmds/idmap2/include/idmap2/Idmap.h
+++ b/cmds/idmap2/include/idmap2/Idmap.h
@@ -17,48 +17,45 @@
 /*
  * # idmap file format (current version)
  *
- * idmap             := header data*
- * header            := magic version target_crc overlay_crc target_path overlay_path debug_info
- * data              := data_header data_block*
- * data_header       := target_package_id types_count
- * data_block        := target_type overlay_type entry_count entry_offset entry*
- * overlay_path      := string256
- * target_path       := string256
- * debug_info        := string
- * string            := <uint32_t> <uint8_t>+ '\0'+
- * entry             := <uint32_t>
- * entry_count       := <uint16_t>
- * entry_offset      := <uint16_t>
- * magic             := <uint32_t>
- * overlay_crc       := <uint32_t>
- * overlay_type      := <uint16_t>
- * string256         := <uint8_t>[256]
- * target_crc        := <uint32_t>
- * target_package_id := <uint16_t>
- * target_type       := <uint16_t>
- * types_count       := <uint16_t>
- * version           := <uint32_t>
+ * idmap                      := header data*
+ * header                     := magic version target_crc overlay_crc fulfilled_policies
+ *                               enforce_overlayable target_path overlay_path debug_info
+ * data                       := data_header target_entry* target_inline_entry* overlay_entry*
+ *                               string_pool
+ * data_header                := target_package_id overlay_package_id padding(2) target_entry_count
+ *                               target_inline_entry_count overlay_entry_count string_pool_index
+ * target_entry               := target_id overlay_id
+ * target_inline_entry        := target_id Res_value::size padding(1) Res_value::type
+ *                               Res_value::value
+ * overlay_entry              := overlay_id target_id
  *
- *
- * # idmap file format changelog
- * ## v1
- * - Identical to idmap v1.
- *
- * ## v2
- * - Entries are no longer separated by type into type specific data blocks.
- * - Added overlay-indexed target resource id lookup capabilities.
- * - Target and overlay entries are stored as a sparse array in the data block. The target entries
- *   array maps from target resource id to overlay data type and value and the array is sorted by
- *   target resource id. The overlay entries array maps from overlay resource id to target resource
- *   id and the array is sorted by overlay resource id. It is important for both arrays to be sorted
- *   to allow for O(log(number_of_overlaid_resources)) performance when looking up resource
- *   mappings at runtime.
- * - Idmap can now encode a type and value to override a resource without needing a table entry.
- * - A string pool block is included to retrieve the value of strings that do not have a resource
- *   table entry.
- *
- * ## v3
- * - Add 'debug' block to IdmapHeader.
+ * debug_info                 := string
+ * enforce_overlayable        := <uint32_t>
+ * fulfilled_policies         := <uint32_t>
+ * magic                      := <uint32_t>
+ * overlay_crc                := <uint32_t>
+ * overlay_entry_count        := <uint32_t>
+ * overlay_id                 := <uint32_t>
+ * overlay_package_id         := <uint8_t>
+ * overlay_path               := string256
+ * padding(n)                 := <uint8_t>[n]
+ * Res_value::size            := <uint16_t>
+ * Res_value::type            := <uint8_t>
+ * Res_value::value           := <uint32_t>
+ * string                     := <uint32_t> <uint8_t>+ padding(n)
+ * string256                  := <uint8_t>[256]
+ * string_pool                := string
+ * string_pool_index          := <uint32_t>
+ * string_pool_length         := <uint32_t>
+ * target_crc                 := <uint32_t>
+ * target_entry_count         := <uint32_t>
+ * target_inline_entry_count  := <uint32_t>
+ * target_id                  := <uint32_t>
+ * target_package_id          := <uint8_t>
+ * target_path                := string256
+ * value_type                 := <uint8_t>
+ * value_data                 := <uint32_t>
+ * version                    := <uint32_t>
  */
 
 #ifndef IDMAP2_INCLUDE_IDMAP2_IDMAP_H_
@@ -183,6 +180,10 @@
       return target_entry_count;
     }
 
+    inline uint32_t GetTargetInlineEntryCount() const {
+      return target_entry_inline_count;
+    }
+
     inline uint32_t GetOverlayEntryCount() const {
       return overlay_entry_count;
     }
@@ -191,19 +192,15 @@
       return string_pool_index_offset;
     }
 
-    inline uint32_t GetStringPoolLength() const {
-      return string_pool_len;
-    }
-
     void accept(Visitor* v) const;
 
    private:
     PackageId target_package_id_;
     PackageId overlay_package_id_;
     uint32_t target_entry_count;
+    uint32_t target_entry_inline_count;
     uint32_t overlay_entry_count;
     uint32_t string_pool_index_offset;
-    uint32_t string_pool_len;
     Header() = default;
 
     friend Idmap;
@@ -213,8 +210,12 @@
 
   struct TargetEntry {
     ResourceId target_id;
-    TargetValue::DataType data_type;
-    TargetValue::DataValue data_value;
+    ResourceId overlay_id;
+  };
+
+  struct TargetInlineEntry {
+    ResourceId target_id;
+    TargetValue value;
   };
 
   struct OverlayEntry {
@@ -227,20 +228,24 @@
   static Result<std::unique_ptr<const IdmapData>> FromResourceMapping(
       const ResourceMapping& resource_mapping);
 
-  inline const std::unique_ptr<const Header>& GetHeader() const {
+  const std::unique_ptr<const Header>& GetHeader() const {
     return header_;
   }
 
-  inline const std::vector<TargetEntry>& GetTargetEntries() const {
+  const std::vector<TargetEntry>& GetTargetEntries() const {
     return target_entries_;
   }
 
-  inline const std::vector<OverlayEntry>& GetOverlayEntries() const {
+  const std::vector<TargetInlineEntry>& GetTargetInlineEntries() const {
+    return target_inline_entries_;
+  }
+
+  const std::vector<OverlayEntry>& GetOverlayEntries() const {
     return overlay_entries_;
   }
 
-  inline const void* GetStringPoolData() const {
-    return string_pool_.get();
+  const std::string& GetStringPoolData() const {
+    return string_pool_data_;
   }
 
   void accept(Visitor* v) const;
@@ -251,8 +256,9 @@
 
   std::unique_ptr<const Header> header_;
   std::vector<TargetEntry> target_entries_;
+  std::vector<TargetInlineEntry> target_inline_entries_;
   std::vector<OverlayEntry> overlay_entries_;
-  std::unique_ptr<uint8_t[]> string_pool_;
+  std::string string_pool_data_;
 
   friend Idmap;
   DISALLOW_COPY_AND_ASSIGN(IdmapData);
@@ -304,6 +310,10 @@
   virtual void visit(const IdmapData::Header& header) = 0;
 };
 
+inline size_t CalculatePadding(size_t data_length) {
+  return (4 - (data_length % 4)) % 4;
+}
+
 }  // namespace android::idmap2
 
 #endif  // IDMAP2_INCLUDE_IDMAP2_IDMAP_H_
diff --git a/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h b/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h
index 5dcf217..2b4c761 100644
--- a/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h
+++ b/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h
@@ -41,8 +41,9 @@
 
  private:
   std::ostream& stream_;
-  std::unique_ptr<const ApkAssets> target_apk_;
   AssetManager2 target_am_;
+  AssetManager2 overlay_am_;
+  std::vector<std::unique_ptr<const ApkAssets>> apk_assets_;
 };
 
 }  // namespace idmap2
diff --git a/cmds/idmap2/include/idmap2/RawPrintVisitor.h b/cmds/idmap2/include/idmap2/RawPrintVisitor.h
index 92c1864..58edc99 100644
--- a/cmds/idmap2/include/idmap2/RawPrintVisitor.h
+++ b/cmds/idmap2/include/idmap2/RawPrintVisitor.h
@@ -45,11 +45,9 @@
   void print(uint16_t value, const char* fmt, ...);
   void print(uint32_t value, const char* fmt, ...);
   void print(const std::string& value, size_t encoded_size, const char* fmt, ...);
-  void print_raw(uint32_t length, const char* fmt, ...);
 
   std::ostream& stream_;
-  std::unique_ptr<const ApkAssets> target_apk_;
-  std::unique_ptr<const ApkAssets> overlay_apk_;
+  std::vector<std::unique_ptr<const ApkAssets>> apk_assets_;
   AssetManager2 target_am_;
   AssetManager2 overlay_am_;
   size_t offset_;
diff --git a/cmds/idmap2/include/idmap2/ResourceMapping.h b/cmds/idmap2/include/idmap2/ResourceMapping.h
index 5869409..0a58ec4 100644
--- a/cmds/idmap2/include/idmap2/ResourceMapping.h
+++ b/cmds/idmap2/include/idmap2/ResourceMapping.h
@@ -41,7 +41,7 @@
   DataValue data_value;
 };
 
-using TargetResourceMap = std::map<ResourceId, TargetValue>;
+using TargetResourceMap = std::map<ResourceId, std::variant<ResourceId, TargetValue>>;
 using OverlayResourceMap = std::map<ResourceId, ResourceId>;
 
 class ResourceMapping {
@@ -56,7 +56,7 @@
                                                bool enforce_overlayable, LogInfo& log_info);
 
   // Retrieves the mapping of target resource id to overlay value.
-  inline TargetResourceMap GetTargetToOverlayMap() const {
+  inline const TargetResourceMap& GetTargetToOverlayMap() const {
     return target_map_;
   }
 
@@ -81,19 +81,24 @@
   }
 
   // Retrieves the raw string pool data from the xml referenced in android:resourcesMap.
-  inline const std::pair<const uint8_t*, uint32_t> GetStringPoolData() const {
-    return std::make_pair(string_pool_data_.get(), string_pool_data_length_);
+  inline const StringPiece GetStringPoolData() const {
+    return StringPiece(reinterpret_cast<const char*>(string_pool_data_.get()),
+                       string_pool_data_length_);
   }
 
  private:
   ResourceMapping() = default;
 
-  // Apps a mapping of target resource id to the type and value of the data that overlays the
-  // target resource. The data_type is the runtime format of the data value (see
-  // Res_value::dataType). If rewrite_overlay_reference is `true` then references to an overlay
+  // Maps a target resource id to an overlay resource id.
+  // If rewrite_overlay_reference is `true` then references to the overlay
   // resource should appear as a reference to its corresponding target resource at runtime.
+  Result<Unit> AddMapping(ResourceId target_resource, ResourceId overlay_resource,
+                          bool rewrite_overlay_reference);
+
+  // Maps a target resource id to a data type and value combination.
+  // The `data_type` is the runtime format of the data value (see Res_value::dataType).
   Result<Unit> AddMapping(ResourceId target_resource, TargetValue::DataType data_type,
-                          TargetValue::DataValue data_value, bool rewrite_overlay_reference);
+                          TargetValue::DataValue data_value);
 
   // Removes the overlay value mapping for the target resource.
   void RemoveMapping(ResourceId target_resource);
diff --git a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
index 255212a..726f6c5 100644
--- a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
+++ b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
@@ -24,10 +24,6 @@
 
 namespace android::idmap2 {
 
-void BinaryStreamVisitor::Write(const void* value, size_t length) {
-  stream_.write(reinterpret_cast<const char*>(value), length);
-}
-
 void BinaryStreamVisitor::Write8(uint8_t value) {
   stream_.write(reinterpret_cast<char*>(&value), sizeof(uint8_t));
 }
@@ -49,11 +45,11 @@
   stream_.write(buf, sizeof(buf));
 }
 
-void BinaryStreamVisitor::WriteString(const std::string& value) {
-  // pad with null to nearest word boundary; include at least one terminating null
-  size_t padding_size = 4 - (value.size() % 4);
-  Write32(value.size() + padding_size);
-  stream_.write(value.c_str(), value.size());
+void BinaryStreamVisitor::WriteString(const StringPiece& value) {
+  // pad with null to nearest word boundary;
+  size_t padding_size = CalculatePadding(value.size());
+  Write32(value.size());
+  stream_.write(value.data(), value.size());
   stream_.write("\0\0\0\0", padding_size);
 }
 
@@ -67,7 +63,7 @@
   Write32(header.GetTargetCrc());
   Write32(header.GetOverlayCrc());
   Write32(header.GetFulfilledPolicies());
-  Write8(static_cast<uint8_t>(header.GetEnforceOverlayable()));
+  Write32(static_cast<uint8_t>(header.GetEnforceOverlayable()));
   WriteString256(header.GetTargetPath());
   WriteString256(header.GetOverlayPath());
   WriteString(header.GetDebugInfo());
@@ -76,8 +72,16 @@
 void BinaryStreamVisitor::visit(const IdmapData& data) {
   for (const auto& target_entry : data.GetTargetEntries()) {
     Write32(target_entry.target_id);
-    Write8(target_entry.data_type);
-    Write32(target_entry.data_value);
+    Write32(target_entry.overlay_id);
+  }
+
+  static constexpr uint16_t kValueSize = 8U;
+  for (const auto& target_entry : data.GetTargetInlineEntries()) {
+    Write32(target_entry.target_id);
+    Write16(kValueSize);
+    Write8(0U);  // padding
+    Write8(target_entry.value.data_type);
+    Write32(target_entry.value.data_value);
   }
 
   for (const auto& overlay_entry : data.GetOverlayEntries()) {
@@ -85,16 +89,18 @@
     Write32(overlay_entry.target_id);
   }
 
-  Write(data.GetStringPoolData(), data.GetHeader()->GetStringPoolLength());
+  WriteString(data.GetStringPoolData());
 }
 
 void BinaryStreamVisitor::visit(const IdmapData::Header& header) {
   Write8(header.GetTargetPackageId());
   Write8(header.GetOverlayPackageId());
+  Write8(0U);  // padding
+  Write8(0U);  // padding
   Write32(header.GetTargetEntryCount());
+  Write32(header.GetTargetInlineEntryCount());
   Write32(header.GetOverlayEntryCount());
   Write32(header.GetStringPoolIndexOffset());
-  Write32(header.GetStringPoolLength());
 }
 
 }  // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp
index 23c25a7..1129413 100644
--- a/cmds/idmap2/libidmap2/Idmap.cpp
+++ b/cmds/idmap2/libidmap2/Idmap.cpp
@@ -51,19 +51,19 @@
   return false;
 }
 
-bool WARN_UNUSED Read32(std::istream& stream, uint32_t* out) {
-  uint32_t value;
-  if (stream.read(reinterpret_cast<char*>(&value), sizeof(uint32_t))) {
-    *out = dtohl(value);
+bool WARN_UNUSED Read16(std::istream& stream, uint16_t* out) {
+  uint16_t value;
+  if (stream.read(reinterpret_cast<char*>(&value), sizeof(uint16_t))) {
+    *out = dtohs(value);
     return true;
   }
   return false;
 }
 
-bool WARN_UNUSED ReadBuffer(std::istream& stream, std::unique_ptr<uint8_t[]>* out, size_t length) {
-  auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[length]);
-  if (stream.read(reinterpret_cast<char*>(buffer.get()), length)) {
-    *out = std::move(buffer);
+bool WARN_UNUSED Read32(std::istream& stream, uint32_t* out) {
+  uint32_t value;
+  if (stream.read(reinterpret_cast<char*>(&value), sizeof(uint32_t))) {
+    *out = dtohl(value);
     return true;
   }
   return false;
@@ -95,8 +95,11 @@
   if (!stream.read(buf.data(), size)) {
     return Error("failed to read string of size %u", size);
   }
-  // buf is guaranteed to be null terminated (with enough nulls to end on a word boundary)
-  buf.resize(strlen(buf.c_str()));
+  uint32_t padding_size = CalculatePadding(size);
+  std::string padding(padding_size, '\0');
+  if (!stream.read(padding.data(), padding_size)) {
+    return Error("failed to read string padding of size %u", padding_size);
+  }
   return buf;
 }
 
@@ -112,16 +115,16 @@
 
 std::unique_ptr<const IdmapHeader> IdmapHeader::FromBinaryStream(std::istream& stream) {
   std::unique_ptr<IdmapHeader> idmap_header(new IdmapHeader());
-  uint8_t enforce_overlayable;
+  uint32_t enforce_overlayable;
   if (!Read32(stream, &idmap_header->magic_) || !Read32(stream, &idmap_header->version_) ||
       !Read32(stream, &idmap_header->target_crc_) || !Read32(stream, &idmap_header->overlay_crc_) ||
-      !Read32(stream, &idmap_header->fulfilled_policies_) || !Read8(stream, &enforce_overlayable) ||
-      !ReadString256(stream, idmap_header->target_path_) ||
+      !Read32(stream, &idmap_header->fulfilled_policies_) ||
+      !Read32(stream, &enforce_overlayable) || !ReadString256(stream, idmap_header->target_path_) ||
       !ReadString256(stream, idmap_header->overlay_path_)) {
     return nullptr;
   }
 
-  idmap_header->enforce_overlayable_ = static_cast<bool>(enforce_overlayable);
+  idmap_header->enforce_overlayable_ = enforce_overlayable != 0U;
 
   auto debug_str = ReadString(stream);
   if (!debug_str) {
@@ -207,12 +210,13 @@
 std::unique_ptr<const IdmapData::Header> IdmapData::Header::FromBinaryStream(std::istream& stream) {
   std::unique_ptr<IdmapData::Header> idmap_data_header(new IdmapData::Header());
 
+  uint8_t padding;
   if (!Read8(stream, &idmap_data_header->target_package_id_) ||
-      !Read8(stream, &idmap_data_header->overlay_package_id_) ||
-      !Read32(stream, &idmap_data_header->target_entry_count) ||
+      !Read8(stream, &idmap_data_header->overlay_package_id_) || !Read8(stream, &padding) ||
+      !Read8(stream, &padding) || !Read32(stream, &idmap_data_header->target_entry_count) ||
+      !Read32(stream, &idmap_data_header->target_entry_inline_count) ||
       !Read32(stream, &idmap_data_header->overlay_entry_count) ||
-      !Read32(stream, &idmap_data_header->string_pool_index_offset) ||
-      !Read32(stream, &idmap_data_header->string_pool_len)) {
+      !Read32(stream, &idmap_data_header->string_pool_index_offset)) {
     return nullptr;
   }
 
@@ -225,14 +229,27 @@
   if (!data->header_) {
     return nullptr;
   }
+
   // Read the mapping of target resource id to overlay resource value.
   for (size_t i = 0; i < data->header_->GetTargetEntryCount(); i++) {
     TargetEntry target_entry{};
-    if (!Read32(stream, &target_entry.target_id) || !Read8(stream, &target_entry.data_type) ||
-        !Read32(stream, &target_entry.data_value)) {
+    if (!Read32(stream, &target_entry.target_id) || !Read32(stream, &target_entry.overlay_id)) {
       return nullptr;
     }
-    data->target_entries_.emplace_back(target_entry);
+    data->target_entries_.push_back(target_entry);
+  }
+
+  // Read the mapping of target resource id to inline overlay values.
+  uint8_t unused1;
+  uint16_t unused2;
+  for (size_t i = 0; i < data->header_->GetTargetInlineEntryCount(); i++) {
+    TargetInlineEntry target_entry{};
+    if (!Read32(stream, &target_entry.target_id) || !Read16(stream, &unused2) ||
+        !Read8(stream, &unused1) || !Read8(stream, &target_entry.value.data_type) ||
+        !Read32(stream, &target_entry.value.data_value)) {
+      return nullptr;
+    }
+    data->target_inline_entries_.push_back(target_entry);
   }
 
   // Read the mapping of overlay resource id to target resource id.
@@ -245,9 +262,11 @@
   }
 
   // Read raw string pool bytes.
-  if (!ReadBuffer(stream, &data->string_pool_, data->header_->string_pool_len)) {
+  auto string_pool_data = ReadString(stream);
+  if (!string_pool_data) {
     return nullptr;
   }
+  data->string_pool_data_ = std::move(*string_pool_data);
 
   return std::move(data);
 }
@@ -290,27 +309,28 @@
   }
 
   std::unique_ptr<IdmapData> data(new IdmapData());
-  for (const auto& mappings : resource_mapping.GetTargetToOverlayMap()) {
-    data->target_entries_.emplace_back(IdmapData::TargetEntry{
-        mappings.first, mappings.second.data_type, mappings.second.data_value});
+  data->string_pool_data_ = resource_mapping.GetStringPoolData().to_string();
+  for (const auto& mapping : resource_mapping.GetTargetToOverlayMap()) {
+    if (auto overlay_resource = std::get_if<ResourceId>(&mapping.second)) {
+      data->target_entries_.push_back({mapping.first, *overlay_resource});
+    } else {
+      data->target_inline_entries_.push_back(
+          {mapping.first, std::get<TargetValue>(mapping.second)});
+    }
   }
 
-  for (const auto& mappings : resource_mapping.GetOverlayToTargetMap()) {
-    data->overlay_entries_.emplace_back(IdmapData::OverlayEntry{mappings.first, mappings.second});
+  for (const auto& mapping : resource_mapping.GetOverlayToTargetMap()) {
+    data->overlay_entries_.emplace_back(IdmapData::OverlayEntry{mapping.first, mapping.second});
   }
 
   std::unique_ptr<IdmapData::Header> data_header(new IdmapData::Header());
   data_header->target_package_id_ = resource_mapping.GetTargetPackageId();
   data_header->overlay_package_id_ = resource_mapping.GetOverlayPackageId();
   data_header->target_entry_count = static_cast<uint32_t>(data->target_entries_.size());
+  data_header->target_entry_inline_count =
+      static_cast<uint32_t>(data->target_inline_entries_.size());
   data_header->overlay_entry_count = static_cast<uint32_t>(data->overlay_entries_.size());
   data_header->string_pool_index_offset = resource_mapping.GetStringPoolOffset();
-
-  const auto string_pool_data = resource_mapping.GetStringPoolData();
-  data_header->string_pool_len = string_pool_data.second;
-  data->string_pool_ = std::unique_ptr<uint8_t[]>(new uint8_t[data_header->string_pool_len]);
-  memcpy(data->string_pool_.get(), string_pool_data.first, data_header->string_pool_len);
-
   data->header_ = std::move(data_header);
   return {std::move(data)};
 }
diff --git a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
index 63ee8a6..a93202a 100644
--- a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
@@ -38,6 +38,7 @@
   stream_ << "Paths:" << std::endl
           << TAB "target apk path  : " << header.GetTargetPath() << std::endl
           << TAB "overlay apk path : " << header.GetOverlayPath() << std::endl;
+
   const std::string& debug = header.GetDebugInfo();
   if (!debug.empty()) {
     std::istringstream debug_stream(debug);
@@ -48,10 +49,16 @@
     }
   }
 
-  target_apk_ = ApkAssets::Load(header.GetTargetPath().to_string());
-  if (target_apk_) {
+  if (auto target_apk_ = ApkAssets::Load(header.GetTargetPath().to_string())) {
     target_am_.SetApkAssets({target_apk_.get()});
+    apk_assets_.push_back(std::move(target_apk_));
   }
+
+  if (auto overlay_apk = ApkAssets::Load(header.GetOverlayPath().to_string())) {
+    overlay_am_.SetApkAssets({overlay_apk.get()});
+    apk_assets_.push_back(std::move(overlay_apk));
+  }
+
   stream_ << "Mapping:" << std::endl;
 }
 
@@ -59,34 +66,56 @@
 }
 
 void PrettyPrintVisitor::visit(const IdmapData& data) {
+  static constexpr const char* kUnknownResourceName = "???";
+
   const bool target_package_loaded = !target_am_.GetApkAssets().empty();
-  const ResStringPool string_pool(data.GetStringPoolData(),
-                                  data.GetHeader()->GetStringPoolLength());
+  const bool overlay_package_loaded = !overlay_am_.GetApkAssets().empty();
+
+  const ResStringPool string_pool(data.GetStringPoolData().data(), data.GetStringPoolData().size());
   const size_t string_pool_offset = data.GetHeader()->GetStringPoolIndexOffset();
 
-  for (auto& target_entry : data.GetTargetEntries()) {
-    stream_ << TAB << base::StringPrintf("0x%08x ->", target_entry.target_id);
-
-    if (target_entry.data_type != Res_value::TYPE_REFERENCE &&
-        target_entry.data_type != Res_value::TYPE_DYNAMIC_REFERENCE) {
-      stream_ << " " << utils::DataTypeToString(target_entry.data_type);
-    }
-
-    if (target_entry.data_type == Res_value::TYPE_STRING) {
-      stream_ << " \""
-              << string_pool.string8ObjectAt(target_entry.data_value - string_pool_offset).c_str()
-              << "\"";
-    } else {
-      stream_ << " " << base::StringPrintf("0x%08x", target_entry.data_value);
-    }
-
+  for (const auto& target_entry : data.GetTargetEntries()) {
+    std::string target_name = kUnknownResourceName;
     if (target_package_loaded) {
-      Result<std::string> name = utils::ResToTypeEntryName(target_am_, target_entry.target_id);
-      if (name) {
-        stream_ << " " << *name;
+      if (auto name = utils::ResToTypeEntryName(target_am_, target_entry.target_id)) {
+        target_name = *name;
       }
     }
-    stream_ << std::endl;
+
+    std::string overlay_name = kUnknownResourceName;
+    if (overlay_package_loaded) {
+      if (auto name = utils::ResToTypeEntryName(overlay_am_, target_entry.overlay_id)) {
+        overlay_name = *name;
+      }
+    }
+
+    stream_ << TAB
+            << base::StringPrintf("0x%08x -> 0x%08x (%s -> %s)", target_entry.target_id,
+                                  target_entry.overlay_id, target_name.c_str(),
+                                  overlay_name.c_str())
+            << std::endl;
+  }
+
+  for (auto& target_entry : data.GetTargetInlineEntries()) {
+    stream_ << TAB << base::StringPrintf("0x%08x -> ", target_entry.target_id)
+            << utils::DataTypeToString(target_entry.value.data_type);
+
+    size_t unused;
+    if (target_entry.value.data_type == Res_value::TYPE_STRING) {
+      auto str = string_pool.stringAt(target_entry.value.data_value - string_pool_offset, &unused);
+      stream_ << " \"" << StringPiece16(str) << "\"";
+    } else {
+      stream_ << " " << base::StringPrintf("0x%08x", target_entry.value.data_value);
+    }
+
+    std::string target_name = kUnknownResourceName;
+    if (target_package_loaded) {
+      if (auto name = utils::ResToTypeEntryName(target_am_, target_entry.target_id)) {
+        target_name = *name;
+      }
+    }
+
+    stream_ << " (" << target_name << ")" << std::endl;
   }
 }
 
diff --git a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
index 3f62a2a..82f5d26 100644
--- a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
@@ -30,15 +30,6 @@
 using android::ApkAssets;
 using android::idmap2::policy::PoliciesToDebugString;
 
-namespace {
-
-size_t StringSizeWhenEncoded(const std::string& s) {
-  size_t null_bytes = 4 - (s.size() % 4);
-  return sizeof(uint32_t) + s.size() + null_bytes;
-}
-
-}  // namespace
-
 namespace android::idmap2 {
 
 void RawPrintVisitor::visit(const Idmap& idmap ATTRIBUTE_UNUSED) {
@@ -51,19 +42,24 @@
   print(header.GetOverlayCrc(), "overlay crc");
   print(header.GetFulfilledPolicies(), "fulfilled policies: %s",
         PoliciesToDebugString(header.GetFulfilledPolicies()).c_str());
-  print(static_cast<uint8_t>(header.GetEnforceOverlayable()), "enforce overlayable");
+  print(static_cast<uint32_t>(header.GetEnforceOverlayable()), "enforce overlayable");
   print(header.GetTargetPath().to_string(), kIdmapStringLength, "target path");
   print(header.GetOverlayPath().to_string(), kIdmapStringLength, "overlay path");
-  print("...", StringSizeWhenEncoded(header.GetDebugInfo()), "debug info");
 
-  target_apk_ = ApkAssets::Load(header.GetTargetPath().to_string());
+  uint32_t debug_info_size = header.GetDebugInfo().size();
+  print(debug_info_size, "debug info size");
+  print("...", debug_info_size + CalculatePadding(debug_info_size), "debug info");
+
+  auto target_apk_ = ApkAssets::Load(header.GetTargetPath().to_string());
   if (target_apk_) {
     target_am_.SetApkAssets({target_apk_.get()});
+    apk_assets_.push_back(std::move(target_apk_));
   }
 
-  overlay_apk_ = ApkAssets::Load(header.GetOverlayPath().to_string());
+  auto overlay_apk_ = ApkAssets::Load(header.GetOverlayPath().to_string());
   if (overlay_apk_) {
     overlay_am_.SetApkAssets({overlay_apk_.get()});
+    apk_assets_.push_back(std::move(overlay_apk_));
   }
 }
 
@@ -82,18 +78,44 @@
       print(target_entry.target_id, "target id");
     }
 
-    print(target_entry.data_type, "type: %s",
-          utils::DataTypeToString(target_entry.data_type).data());
-
     Result<std::string> overlay_name(Error(""));
-    if (overlay_package_loaded && (target_entry.data_type == Res_value::TYPE_REFERENCE ||
-                                   target_entry.data_type == Res_value::TYPE_DYNAMIC_REFERENCE)) {
-      overlay_name = utils::ResToTypeEntryName(overlay_am_, target_entry.data_value);
+    if (overlay_package_loaded) {
+      overlay_name = utils::ResToTypeEntryName(overlay_am_, target_entry.overlay_id);
     }
     if (overlay_name) {
-      print(target_entry.data_value, "value: %s", overlay_name->c_str());
+      print(target_entry.overlay_id, "overlay id: %s", overlay_name->c_str());
     } else {
-      print(target_entry.data_value, "value");
+      print(target_entry.overlay_id, "overlay id");
+    }
+  }
+
+  for (auto& target_entry : data.GetTargetInlineEntries()) {
+    Result<std::string> target_name(Error(""));
+    if (target_package_loaded) {
+      target_name = utils::ResToTypeEntryName(target_am_, target_entry.target_id);
+    }
+    if (target_name) {
+      print(target_entry.target_id, "target id: %s", target_name->c_str());
+    } else {
+      print(target_entry.target_id, "target id");
+    }
+
+    print("...", sizeof(Res_value::size) + sizeof(Res_value::res0), "padding");
+
+    print(target_entry.value.data_type, "type: %s",
+          utils::DataTypeToString(target_entry.value.data_type).data());
+
+    Result<std::string> overlay_name(Error(""));
+    if (overlay_package_loaded &&
+        (target_entry.value.data_value == Res_value::TYPE_REFERENCE ||
+         target_entry.value.data_value == Res_value::TYPE_DYNAMIC_REFERENCE)) {
+      overlay_name = utils::ResToTypeEntryName(overlay_am_, target_entry.value.data_value);
+    }
+
+    if (overlay_name) {
+      print(target_entry.value.data_value, "data: %s", overlay_name->c_str());
+    } else {
+      print(target_entry.value.data_value, "data");
     }
   }
 
@@ -121,19 +143,19 @@
     }
   }
 
-  const size_t string_pool_length = data.GetHeader()->GetStringPoolLength();
-  if (string_pool_length > 0) {
-    print_raw(string_pool_length, "%zu raw string pool bytes", string_pool_length);
-  }
+  uint32_t string_pool_size = data.GetStringPoolData().size();
+  print(string_pool_size, "string pool size");
+  print("...", string_pool_size + CalculatePadding(string_pool_size), "string pool");
 }
 
 void RawPrintVisitor::visit(const IdmapData::Header& header) {
   print(header.GetTargetPackageId(), "target package id");
   print(header.GetOverlayPackageId(), "overlay package id");
+  print("...", sizeof(Idmap_data_header::p0), "padding");
   print(header.GetTargetEntryCount(), "target entry count");
+  print(header.GetTargetInlineEntryCount(), "target inline entry count");
   print(header.GetOverlayEntryCount(), "overlay entry count");
   print(header.GetStringPoolIndexOffset(), "string pool index offset");
-  print(header.GetStringPoolLength(), "string pool byte length");
 }
 
 // NOLINTNEXTLINE(cert-dcl50-cpp)
@@ -190,17 +212,4 @@
   offset_ += encoded_size;
 }
 
-// NOLINTNEXTLINE(cert-dcl50-cpp)
-void RawPrintVisitor::print_raw(uint32_t length, const char* fmt, ...) {
-  va_list ap;
-  va_start(ap, fmt);
-  std::string comment;
-  base::StringAppendV(&comment, fmt, ap);
-  va_end(ap);
-
-  stream_ << base::StringPrintf("%08zx: ", offset_) << "........  " << comment << std::endl;
-
-  offset_ += length;
-}
-
 }  // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/ResourceMapping.cpp b/cmds/idmap2/libidmap2/ResourceMapping.cpp
index fd8b4eb..31f1c16 100644
--- a/cmds/idmap2/libidmap2/ResourceMapping.cpp
+++ b/cmds/idmap2/libidmap2/ResourceMapping.cpp
@@ -71,9 +71,9 @@
   if (!target_package.DefinesOverlayable()) {
     return (sDefaultPolicies & fulfilled_policies) != 0
                ? Result<Unit>({})
-               : Error(
-                     "overlay must be preinstalled or signed with the same signature as the "
-                     "target");
+               : Error("overlay must be preinstalled, signed with the same signature as the target,"
+                       " or signed with the same signature as the package referenced through"
+                       " <overlay-config-signature>.");
   }
 
   const OverlayableInfo* overlayable_info = target_package.GetOverlayableInfo(target_resource);
@@ -205,19 +205,14 @@
       overlay_resource->data += string_pool_offset;
     }
 
-    // Only rewrite resources defined within the overlay package to their corresponding target
-    // resource ids at runtime.
-    bool rewrite_overlay_reference =
-        IsReference(overlay_resource->dataType)
-            ? overlay_package_id == EXTRACT_PACKAGE(overlay_resource->data)
-            : false;
-
-    if (rewrite_overlay_reference) {
-      overlay_resource->dataType = Res_value::TYPE_DYNAMIC_REFERENCE;
+    if (IsReference(overlay_resource->dataType)) {
+      // Only rewrite resources defined within the overlay package to their corresponding target
+      // resource ids at runtime.
+      bool rewrite_reference = overlay_package_id == EXTRACT_PACKAGE(overlay_resource->data);
+      resource_mapping.AddMapping(target_id, overlay_resource->data, rewrite_reference);
+    } else {
+      resource_mapping.AddMapping(target_id, overlay_resource->dataType, overlay_resource->data);
     }
-
-    resource_mapping.AddMapping(target_id, overlay_resource->dataType, overlay_resource->data,
-                                rewrite_overlay_reference);
   }
 
   return resource_mapping;
@@ -246,9 +241,8 @@
 
     // Retrieve the compile-time resource id of the target resource.
     target_resource = REWRITE_PACKAGE(target_resource, target_package_id);
-
-    resource_mapping.AddMapping(target_resource, Res_value::TYPE_REFERENCE, overlay_resid,
-                                /* rewrite_overlay_reference */ false);
+    resource_mapping.AddMapping(target_resource, overlay_resid,
+                                false /* rewrite_overlay_reference */);
   }
 
   return resource_mapping;
@@ -396,9 +390,7 @@
   return map;
 }
 
-Result<Unit> ResourceMapping::AddMapping(ResourceId target_resource,
-                                         TargetValue::DataType data_type,
-                                         TargetValue::DataValue data_value,
+Result<Unit> ResourceMapping::AddMapping(ResourceId target_resource, ResourceId overlay_resource,
                                          bool rewrite_overlay_reference) {
   if (target_map_.find(target_resource) != target_map_.end()) {
     return Error(R"(target resource id "0x%08x" mapped to multiple values)", target_resource);
@@ -407,13 +399,26 @@
   // TODO(141485591): Ensure that the overlay type is compatible with the target type. If the
   // runtime types are not compatible, it could cause runtime crashes when the resource is resolved.
 
-  target_map_.insert(std::make_pair(target_resource, TargetValue{data_type, data_value}));
+  target_map_.insert(std::make_pair(target_resource, overlay_resource));
 
-  if (rewrite_overlay_reference && IsReference(data_type)) {
-    overlay_map_.insert(std::make_pair(data_value, target_resource));
+  if (rewrite_overlay_reference) {
+    overlay_map_.insert(std::make_pair(overlay_resource, target_resource));
+  }
+  return Unit{};
+}
+
+Result<Unit> ResourceMapping::AddMapping(ResourceId target_resource,
+                                         TargetValue::DataType data_type,
+                                         TargetValue::DataValue data_value) {
+  if (target_map_.find(target_resource) != target_map_.end()) {
+    return Error(R"(target resource id "0x%08x" mapped to multiple values)", target_resource);
   }
 
-  return Result<Unit>({});
+  // TODO(141485591): Ensure that the overlay type is compatible with the target type. If the
+  // runtime types are not compatible, it could cause runtime crashes when the resource is resolved.
+
+  target_map_.insert(std::make_pair(target_resource, TargetValue{data_type, data_value}));
+  return Unit{};
 }
 
 void ResourceMapping::RemoveMapping(ResourceId target_resource) {
@@ -422,14 +427,15 @@
     return;
   }
 
-  const TargetValue value = target_iter->second;
+  const auto value = target_iter->second;
   target_map_.erase(target_iter);
 
-  if (!IsReference(value.data_type)) {
+  const ResourceId* overlay_resource = std::get_if<ResourceId>(&value);
+  if (overlay_resource == nullptr) {
     return;
   }
 
-  auto overlay_iter = overlay_map_.equal_range(value.data_value);
+  auto overlay_iter = overlay_map_.equal_range(*overlay_resource);
   for (auto i = overlay_iter.first; i != overlay_iter.second; ++i) {
     if (i->second == target_resource) {
       overlay_map_.erase(i);
diff --git a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
index 5fea7bc..c3a3e0b 100644
--- a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
+++ b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
@@ -72,13 +72,20 @@
   const auto& target_entries2 = data2->GetTargetEntries();
   ASSERT_EQ(target_entries1.size(), target_entries2.size());
   ASSERT_EQ(target_entries1[0].target_id, target_entries2[0].target_id);
-  ASSERT_EQ(target_entries1[0].data_value, target_entries2[0].data_value);
+  ASSERT_EQ(target_entries1[0].overlay_id, target_entries2[0].overlay_id);
 
   ASSERT_EQ(target_entries1[1].target_id, target_entries2[1].target_id);
-  ASSERT_EQ(target_entries1[1].data_value, target_entries2[1].data_value);
+  ASSERT_EQ(target_entries1[1].overlay_id, target_entries2[1].overlay_id);
 
   ASSERT_EQ(target_entries1[2].target_id, target_entries2[2].target_id);
-  ASSERT_EQ(target_entries1[2].data_value, target_entries2[2].data_value);
+  ASSERT_EQ(target_entries1[2].overlay_id, target_entries2[2].overlay_id);
+
+  const auto& target_inline_entries1 = data1->GetTargetInlineEntries();
+  const auto& target_inline_entries2 = data2->GetTargetInlineEntries();
+  ASSERT_EQ(target_inline_entries1.size(), target_inline_entries2.size());
+  ASSERT_EQ(target_inline_entries1[0].target_id, target_inline_entries2[0].target_id);
+  ASSERT_EQ(target_inline_entries1[0].value.data_type, target_inline_entries2[0].value.data_type);
+  ASSERT_EQ(target_inline_entries1[0].value.data_value, target_inline_entries2[0].value.data_value);
 
   const auto& overlay_entries1 = data1->GetOverlayEntries();
   const auto& overlay_entries2 = data2->GetOverlayEntries();
diff --git a/cmds/idmap2/tests/Idmap2BinaryTests.cpp b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
index 61751b3..e7e9e4c 100644
--- a/cmds/idmap2/tests/Idmap2BinaryTests.cpp
+++ b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
@@ -128,13 +128,13 @@
   // clang-format on
   ASSERT_THAT(result, NotNull());
   ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
-  ASSERT_NE(result->stdout.find(R::target::integer::literal::int1 + " -> 0x7f010000 integer/int1"),
+  ASSERT_NE(result->stdout.find(R::target::integer::literal::int1 + " -> 0x7f010000"),
             std::string::npos);
-  ASSERT_NE(result->stdout.find(R::target::string::literal::str1 + " -> 0x7f020000 string/str1"),
+  ASSERT_NE(result->stdout.find(R::target::string::literal::str1 + " -> 0x7f020000"),
             std::string::npos);
-  ASSERT_NE(result->stdout.find(R::target::string::literal::str3 + " -> 0x7f020001 string/str3"),
+  ASSERT_NE(result->stdout.find(R::target::string::literal::str3 + " -> 0x7f020001"),
             std::string::npos);
-  ASSERT_NE(result->stdout.find(R::target::string::literal::str4 + " -> 0x7f020002 string/str4"),
+  ASSERT_NE(result->stdout.find(R::target::string::literal::str4 + " -> 0x7f020002"),
             std::string::npos);
 
   // clang-format off
diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp
index 6fab5e0..9b42a27 100644
--- a/cmds/idmap2/tests/IdmapTests.cpp
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -42,14 +42,18 @@
 
 namespace android::idmap2 {
 
-#define ASSERT_TARGET_ENTRY(entry, target_resid, type, value) \
-  ASSERT_EQ(entry.target_id, target_resid);                   \
-  ASSERT_EQ(entry.data_type, type);                           \
-  ASSERT_EQ(entry.data_value, value)
+#define ASSERT_TARGET_ENTRY(entry, target_resid, overlay_resid) \
+  ASSERT_EQ((entry).target_id, (target_resid));                 \
+  ASSERT_EQ((entry).overlay_id, (overlay_resid))
+
+#define ASSERT_TARGET_INLINE_ENTRY(entry, target_resid, expected_type, expected_value) \
+  ASSERT_EQ((entry).target_id, target_resid);                                          \
+  ASSERT_EQ((entry).value.data_type, (expected_type));                                 \
+  ASSERT_EQ((entry).value.data_value, (expected_value))
 
 #define ASSERT_OVERLAY_ENTRY(entry, overlay_resid, target_resid) \
-  ASSERT_EQ(entry.overlay_id, overlay_resid);                    \
-  ASSERT_EQ(entry.target_id, target_resid)
+  ASSERT_EQ((entry).overlay_id, (overlay_resid));                \
+  ASSERT_EQ((entry).target_id, (target_resid))
 
 TEST(IdmapTests, TestCanonicalIdmapPathFor) {
   ASSERT_EQ(Idmap::CanonicalIdmapPathFor("/foo", "/vendor/overlay/bar.apk"),
@@ -62,7 +66,7 @@
   std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(stream);
   ASSERT_THAT(header, NotNull());
   ASSERT_EQ(header->GetMagic(), 0x504d4449U);
-  ASSERT_EQ(header->GetVersion(), 0x04U);
+  ASSERT_EQ(header->GetVersion(), 0x05U);
   ASSERT_EQ(header->GetTargetCrc(), 0x1234U);
   ASSERT_EQ(header->GetOverlayCrc(), 0x5678U);
   ASSERT_EQ(header->GetFulfilledPolicies(), 0x11);
@@ -75,7 +79,7 @@
 TEST(IdmapTests, FailToCreateIdmapHeaderFromBinaryStreamIfPathTooLong) {
   std::string raw(reinterpret_cast<const char*>(idmap_raw_data), idmap_raw_data_len);
   // overwrite the target path string, including the terminating null, with '.'
-  for (size_t i = 0x15; i < 0x115; i++) {
+  for (size_t i = 0x18; i < 0x118; i++) {
     raw[i] = '.';
   }
   std::istringstream stream(raw);
@@ -84,7 +88,7 @@
 }
 
 TEST(IdmapTests, CreateIdmapDataHeaderFromBinaryStream) {
-  const size_t offset = 0x221;
+  const size_t offset = 0x224;
   std::string raw(reinterpret_cast<const char*>(idmap_raw_data + offset),
                   idmap_raw_data_len - offset);
   std::istringstream stream(raw);
@@ -96,7 +100,7 @@
 }
 
 TEST(IdmapTests, CreateIdmapDataFromBinaryStream) {
-  const size_t offset = 0x221;
+  const size_t offset = 0x224;
   std::string raw(reinterpret_cast<const char*>(idmap_raw_data + offset),
                   idmap_raw_data_len - offset);
   std::istringstream stream(raw);
@@ -106,12 +110,14 @@
 
   const auto& target_entries = data->GetTargetEntries();
   ASSERT_EQ(target_entries.size(), 3U);
-  ASSERT_TARGET_ENTRY(target_entries[0], 0x7f020000, 0x01 /* Res_value::TYPE_REFERENCE */,
-                      0x7f020000);
-  ASSERT_TARGET_ENTRY(target_entries[1], 0x7f030000, 0x01 /* Res_value::TYPE_REFERENCE */,
-                      0x7f030000);
-  ASSERT_TARGET_ENTRY(target_entries[2], 0x7f030002, 0x01 /* Res_value::TYPE_REFERENCE */,
-                      0x7f030001);
+  ASSERT_TARGET_ENTRY(target_entries[0], 0x7f020000, 0x7f020000);
+  ASSERT_TARGET_ENTRY(target_entries[1], 0x7f030000, 0x7f030000);
+  ASSERT_TARGET_ENTRY(target_entries[2], 0x7f030002, 0x7f030001);
+
+  const auto& target_inline_entries = data->GetTargetInlineEntries();
+  ASSERT_EQ(target_inline_entries.size(), 1U);
+  ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], 0x7f040000, Res_value::TYPE_INT_HEX,
+                             0x12345678);
 
   const auto& overlay_entries = data->GetOverlayEntries();
   ASSERT_EQ(target_entries.size(), 3U);
@@ -130,7 +136,7 @@
 
   ASSERT_THAT(idmap->GetHeader(), NotNull());
   ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
-  ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x04U);
+  ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x05U);
   ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x1234U);
   ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x5678U);
   ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), 0x11);
@@ -146,9 +152,14 @@
 
   const auto& target_entries = data->GetTargetEntries();
   ASSERT_EQ(target_entries.size(), 3U);
-  ASSERT_TARGET_ENTRY(target_entries[0], 0x7f020000, Res_value::TYPE_REFERENCE, 0x7f020000);
-  ASSERT_TARGET_ENTRY(target_entries[1], 0x7f030000, Res_value::TYPE_REFERENCE, 0x7f030000);
-  ASSERT_TARGET_ENTRY(target_entries[2], 0x7f030002, Res_value::TYPE_REFERENCE, 0x7f030001);
+  ASSERT_TARGET_ENTRY(target_entries[0], 0x7f020000, 0x7f020000);
+  ASSERT_TARGET_ENTRY(target_entries[1], 0x7f030000, 0x7f030000);
+  ASSERT_TARGET_ENTRY(target_entries[2], 0x7f030002, 0x7f030001);
+
+  const auto& target_inline_entries = data->GetTargetInlineEntries();
+  ASSERT_EQ(target_inline_entries.size(), 1U);
+  ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], 0x7f040000, Res_value::TYPE_INT_HEX,
+                             0x12345678);
 
   const auto& overlay_entries = data->GetOverlayEntries();
   ASSERT_EQ(target_entries.size(), 3U);
@@ -184,7 +195,7 @@
 
   ASSERT_THAT(idmap->GetHeader(), NotNull());
   ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
-  ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x04U);
+  ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x05U);
   ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), android::idmap2::TestConstants::TARGET_CRC);
   ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), android::idmap2::TestConstants::OVERLAY_CRC);
   ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), PolicyFlags::PUBLIC);
@@ -244,14 +255,13 @@
 
   const auto& target_entries = data->GetTargetEntries();
   ASSERT_EQ(target_entries.size(), 4U);
-  ASSERT_TARGET_ENTRY(target_entries[0], R::target::integer::int1,
-                      Res_value::TYPE_DYNAMIC_REFERENCE, R::overlay::integer::int1);
-  ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str1, Res_value::TYPE_DYNAMIC_REFERENCE,
-                      R::overlay::string::str1);
-  ASSERT_TARGET_ENTRY(target_entries[2], R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE,
-                      R::overlay::string::str3);
-  ASSERT_TARGET_ENTRY(target_entries[3], R::target::string::str4, Res_value::TYPE_DYNAMIC_REFERENCE,
-                      R::overlay::string::str4);
+  ASSERT_TARGET_ENTRY(target_entries[0], R::target::integer::int1, R::overlay::integer::int1);
+  ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str1, R::overlay::string::str1);
+  ASSERT_TARGET_ENTRY(target_entries[2], R::target::string::str3, R::overlay::string::str3);
+  ASSERT_TARGET_ENTRY(target_entries[3], R::target::string::str4, R::overlay::string::str4);
+
+  const auto& target_inline_entries = data->GetTargetInlineEntries();
+  ASSERT_EQ(target_inline_entries.size(), 0U);
 
   const auto& overlay_entries = data->GetOverlayEntries();
   ASSERT_EQ(target_entries.size(), 4U);
@@ -286,13 +296,13 @@
   const auto& target_entries = data->GetTargetEntries();
   ASSERT_EQ(target_entries.size(), 4U);
   ASSERT_TARGET_ENTRY(target_entries[0], R::target::integer::int1,
-                      Res_value::TYPE_DYNAMIC_REFERENCE, R::overlay_shared::integer::int1);
-  ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str1, Res_value::TYPE_DYNAMIC_REFERENCE,
-                      R::overlay_shared::string::str1);
-  ASSERT_TARGET_ENTRY(target_entries[2], R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE,
-                      R::overlay_shared::string::str3);
-  ASSERT_TARGET_ENTRY(target_entries[3], R::target::string::str4, Res_value::TYPE_DYNAMIC_REFERENCE,
-                      R::overlay_shared::string::str4);
+                      R::overlay_shared::integer::int1);
+  ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str1, R::overlay_shared::string::str1);
+  ASSERT_TARGET_ENTRY(target_entries[2], R::target::string::str3, R::overlay_shared::string::str3);
+  ASSERT_TARGET_ENTRY(target_entries[3], R::target::string::str4, R::overlay_shared::string::str4);
+
+  const auto& target_inline_entries = data->GetTargetInlineEntries();
+  ASSERT_EQ(target_inline_entries.size(), 0U);
 
   const auto& overlay_entries = data->GetOverlayEntries();
   ASSERT_EQ(target_entries.size(), 4U);
@@ -320,10 +330,12 @@
 
   const auto& target_entries = data->GetTargetEntries();
   ASSERT_EQ(target_entries.size(), 2U);
-  ASSERT_TARGET_ENTRY(target_entries[0], R::target::string::str1, Res_value::TYPE_REFERENCE,
+  ASSERT_TARGET_ENTRY(target_entries[0], R::target::string::str1,
                       0x0104000a);  // -> android:string/ok
-  ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE,
-                      R::overlay::string::str3);
+  ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str3, R::overlay::string::str3);
+
+  const auto& target_inline_entries = data->GetTargetInlineEntries();
+  ASSERT_EQ(target_inline_entries.size(), 0U);
 
   const auto& overlay_entries = data->GetOverlayEntries();
   ASSERT_EQ(overlay_entries.size(), 1U);
@@ -342,13 +354,17 @@
   ASSERT_TRUE(idmap_data) << idmap_data.GetErrorMessage();
   auto& data = *idmap_data;
 
-  constexpr size_t overlay_string_pool_size = 8U;
   const auto& target_entries = data->GetTargetEntries();
-  ASSERT_EQ(target_entries.size(), 2U);
-  ASSERT_TARGET_ENTRY(target_entries[0], R::target::integer::int1, Res_value::TYPE_INT_DEC,
-                      73U);  // -> 73
-  ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str1, Res_value::TYPE_STRING,
-                      overlay_string_pool_size + 0U);  // -> "Hello World"
+  ASSERT_EQ(target_entries.size(), 0U);
+
+  constexpr size_t overlay_string_pool_size = 8U;
+  const auto& target_inline_entries = data->GetTargetInlineEntries();
+  ASSERT_EQ(target_inline_entries.size(), 2U);
+  ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], R::target::integer::int1,
+                             Res_value::TYPE_INT_DEC, 73U);  // -> 73
+  ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[1], R::target::string::str1,
+                             Res_value::TYPE_STRING,
+                             overlay_string_pool_size + 0U);  // -> "Hello World"
 
   const auto& overlay_entries = data->GetOverlayEntries();
   ASSERT_EQ(overlay_entries.size(), 0U);
@@ -479,9 +495,9 @@
   ASSERT_FALSE(bad_enforce_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(),
                                               PolicyFlags::PUBLIC, /* enforce_overlayable */ true));
 
-  // target path: bytes (0x15, 0x114)
+  // target path: bytes (0x18, 0x117)
   std::string bad_target_path_string(stream.str());
-  bad_target_path_string[0x15] = '\0';
+  bad_target_path_string[0x18] = '\0';
   std::stringstream bad_target_path_stream(bad_target_path_string);
   std::unique_ptr<const IdmapHeader> bad_target_path_header =
       IdmapHeader::FromBinaryStream(bad_target_path_stream);
@@ -490,9 +506,9 @@
   ASSERT_FALSE(bad_magic_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(),
                                             PolicyFlags::PUBLIC, /* enforce_overlayable */ true));
 
-  // overlay path: bytes (0x115, 0x214)
+  // overlay path: bytes (0x118, 0x217)
   std::string bad_overlay_path_string(stream.str());
-  bad_overlay_path_string[0x115] = '\0';
+  bad_overlay_path_string[0x118] = '\0';
   std::stringstream bad_overlay_path_stream(bad_overlay_path_string);
   std::unique_ptr<const IdmapHeader> bad_overlay_path_header =
       IdmapHeader::FromBinaryStream(bad_overlay_path_stream);
diff --git a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
index 9a10079..d30fbfc 100644
--- a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
@@ -56,7 +56,8 @@
 
   ASSERT_NE(stream.str().find("target apk path  : "), std::string::npos);
   ASSERT_NE(stream.str().find("overlay apk path : "), std::string::npos);
-  ASSERT_NE(stream.str().find(R::target::integer::literal::int1 + " -> 0x7f010000 integer/int1\n"),
+  ASSERT_NE(stream.str().find(R::target::integer::literal::int1 +
+                              " -> 0x7f010000 (integer/int1 -> integer/int1)\n"),
             std::string::npos);
 }
 
@@ -75,7 +76,7 @@
 
   ASSERT_NE(stream.str().find("target apk path  : "), std::string::npos);
   ASSERT_NE(stream.str().find("overlay apk path : "), std::string::npos);
-  ASSERT_NE(stream.str().find("0x7f020000 -> 0x7f020000\n"), std::string::npos);
+  ASSERT_NE(stream.str().find("0x7f020000 -> 0x7f020000 (\?\?\? -> \?\?\?)\n"), std::string::npos);
 }
 
 }  // namespace android::idmap2
diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
index b268d5a..95bd9473 100644
--- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
@@ -65,7 +65,7 @@
   (*idmap)->accept(&visitor);
 
   ASSERT_CONTAINS_REGEX(ADDRESS "504d4449  magic\n", stream.str());
-  ASSERT_CONTAINS_REGEX(ADDRESS "00000004  version\n", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "00000005  version\n", stream.str());
   ASSERT_CONTAINS_REGEX(
       StringPrintf(ADDRESS "%s  target crc\n", android::idmap2::TestConstants::TARGET_CRC_STRING),
       stream.str());
@@ -73,19 +73,19 @@
       StringPrintf(ADDRESS "%s  overlay crc\n", android::idmap2::TestConstants::OVERLAY_CRC_STRING),
       stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "00000001  fulfilled policies: public\n", stream.str());
-  ASSERT_CONTAINS_REGEX(ADDRESS "      01  enforce overlayable\n", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "00000001  enforce overlayable\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "      7f  target package id\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "      7f  overlay package id\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "00000004  target entry count\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "00000004  overlay entry count\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "00000004  overlay entry count\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "00000008  string pool index offset\n", stream.str());
-  ASSERT_CONTAINS_REGEX(ADDRESS "000000b4  string pool byte length\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "7f010000  target id: integer/int1\n", stream.str());
-  ASSERT_CONTAINS_REGEX(ADDRESS "      07  type: reference \\(dynamic\\)\n", stream.str());
-  ASSERT_CONTAINS_REGEX(ADDRESS "7f010000  value: integer/int1\n", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "7f010000  overlay id: integer/int1\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "7f010000  overlay id: integer/int1\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "7f010000  target id: integer/int1\n", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "000000b4  string pool size\n", stream.str());
+  ASSERT_CONTAINS_REGEX("000002bc: ........  string pool: ...\n", stream.str());
 }
 
 TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) {
@@ -102,22 +102,26 @@
   (*idmap)->accept(&visitor);
 
   ASSERT_CONTAINS_REGEX(ADDRESS "504d4449  magic\n", stream.str());
-  ASSERT_CONTAINS_REGEX(ADDRESS "00000004  version\n", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "00000005  version\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "00001234  target crc\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "00005678  overlay crc\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "00000011  fulfilled policies: public|signature\n", stream.str());
-  ASSERT_CONTAINS_REGEX(ADDRESS "      01  enforce overlayable\n", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "00000001  enforce overlayable\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "      7f  target package id\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "      7f  overlay package id\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "00000003  target entry count\n", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "00000001  target inline entry count\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "00000003  overlay entry count\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "00000000  string pool index offset\n", stream.str());
-  ASSERT_CONTAINS_REGEX(ADDRESS "00000000  string pool byte length\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "7f020000  target id\n", stream.str());
-  ASSERT_CONTAINS_REGEX(ADDRESS "      01  type: reference\n", stream.str());
-  ASSERT_CONTAINS_REGEX(ADDRESS "7f020000  value\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "7f020000  overlay id\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "7f020000  target id\n", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "      11  type: integer\n", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "12345678  data\n", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "7f020000  overlay id\n", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "7f030002  target id\n", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "00000004  string pool size\n", stream.str());
+  ASSERT_CONTAINS_REGEX("00000278: ........  string pool: ...\n", stream.str());
 }
 
 }  // namespace android::idmap2
diff --git a/cmds/idmap2/tests/ResourceMappingTests.cpp b/cmds/idmap2/tests/ResourceMappingTests.cpp
index 3ec6ac2..185e929 100644
--- a/cmds/idmap2/tests/ResourceMappingTests.cpp
+++ b/cmds/idmap2/tests/ResourceMappingTests.cpp
@@ -77,30 +77,61 @@
                                 fulfilled_policies, enforce_overlayable);
 }
 
-Result<Unit> MappingExists(const ResourceMapping& mapping, const ResourceId& target_resource,
-                           const uint8_t type, const uint32_t value, bool rewrite) {
+Result<Unit> MappingExists(const ResourceMapping& mapping, ResourceId target_resource,
+                           ResourceId overlay_resource, bool rewrite) {
   auto target_map = mapping.GetTargetToOverlayMap();
   auto entry_map = target_map.find(target_resource);
   if (entry_map == target_map.end()) {
     return Error("Failed to find mapping for target resource");
   }
 
-  if (entry_map->second.data_type != type) {
-    return Error(R"(Expected type: "0x%02x" Actual type: "0x%02x")", type,
-                 entry_map->second.data_type);
+  auto actual_overlay_resource = std::get_if<ResourceId>(&entry_map->second);
+  if (actual_overlay_resource == nullptr) {
+    return Error("Target resource is not mapped to an overlay resource id");
   }
 
-  if (entry_map->second.data_value != value) {
-    return Error(R"(Expected value: "0x%08x" Actual value: "0x%08x")", type,
-                 entry_map->second.data_value);
+  if (*actual_overlay_resource != overlay_resource) {
+    return Error(R"(Expected id: "0x%02x" Actual id: "0x%02x")", overlay_resource,
+                 *actual_overlay_resource);
   }
 
   auto overlay_map = mapping.GetOverlayToTargetMap();
-  auto overlay_iter = overlay_map.find(entry_map->second.data_value);
+  auto overlay_iter = overlay_map.find(overlay_resource);
   if ((overlay_iter != overlay_map.end()) != rewrite) {
     return Error(R"(Expected rewriting: "%s")", rewrite ? "true" : "false");
   }
 
+  if (rewrite && overlay_iter->second != target_resource) {
+    return Error(R"(Expected rewrite id: "0x%02x" Actual id: "0x%02x")", target_resource,
+                 overlay_iter->second);
+  }
+
+  return Result<Unit>({});
+}
+
+Result<Unit> MappingExists(const ResourceMapping& mapping, const ResourceId& target_resource,
+                           const uint8_t type, const uint32_t value) {
+  auto target_map = mapping.GetTargetToOverlayMap();
+  auto entry_map = target_map.find(target_resource);
+  if (entry_map == target_map.end()) {
+    return Error("Failed to find mapping for target resource");
+  }
+
+  auto actual_overlay_value = std::get_if<TargetValue>(&entry_map->second);
+  if (actual_overlay_value == nullptr) {
+    return Error("Target resource is not mapped to an inline value");
+  }
+
+  if (actual_overlay_value->data_type != type) {
+    return Error(R"(Expected type: "0x%02x" Actual type: "0x%02x")", type,
+                 actual_overlay_value->data_type);
+  }
+
+  if (actual_overlay_value->data_value != value) {
+    return Error(R"(Expected value: "0x%08x" Actual value: "0x%08x")", type,
+                 actual_overlay_value->data_value);
+  }
+
   return Result<Unit>({});
 }
 
@@ -116,14 +147,14 @@
   ASSERT_TRUE(resources) << resources.GetErrorMessage();
   auto& res = *resources;
   ASSERT_EQ(res.GetTargetToOverlayMap().size(), 4U);
-  ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_REFERENCE,
-                              R::overlay::integer::int1, false /* rewrite */));
-  ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_REFERENCE,
-                              R::overlay::string::str1, false /* rewrite */));
-  ASSERT_RESULT(MappingExists(res, R::target::string::str3, Res_value::TYPE_REFERENCE,
-                              R::overlay::string::str3, false /* rewrite */));
-  ASSERT_RESULT(MappingExists(res, R::target::string::str4, Res_value::TYPE_REFERENCE,
-                              R::overlay::string::str4, false /* rewrite */));
+  ASSERT_RESULT(
+      MappingExists(res, R::target::integer::int1, R::overlay::integer::int1, false /* rewrite */));
+  ASSERT_RESULT(
+      MappingExists(res, R::target::string::str1, R::overlay::string::str1, false /* rewrite */));
+  ASSERT_RESULT(
+      MappingExists(res, R::target::string::str3, R::overlay::string::str3, false /* rewrite */));
+  ASSERT_RESULT(
+      MappingExists(res, R::target::string::str4, R::overlay::string::str4, false /* rewrite */));
 }
 
 TEST(ResourceMappingTests, ResourcesFromApkAssetsNonMatchingNames) {
@@ -138,12 +169,12 @@
   ASSERT_TRUE(resources) << resources.GetErrorMessage();
   auto& res = *resources;
   ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
-  ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_DYNAMIC_REFERENCE,
-                              R::overlay::string::str4, true /* rewrite */));
-  ASSERT_RESULT(MappingExists(res, R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE,
-                              R::overlay::string::str1, true /* rewrite */));
-  ASSERT_RESULT(MappingExists(res, R::target::string::str4, Res_value::TYPE_DYNAMIC_REFERENCE,
-                              R::overlay::string::str3, true /* rewrite */));
+  ASSERT_RESULT(
+      MappingExists(res, R::target::string::str1, R::overlay::string::str4, true /* rewrite */));
+  ASSERT_RESULT(
+      MappingExists(res, R::target::string::str3, R::overlay::string::str1, true /* rewrite */));
+  ASSERT_RESULT(
+      MappingExists(res, R::target::string::str4, R::overlay::string::str3, true /* rewrite */));
 }
 
 TEST(ResourceMappingTests, DoNotRewriteNonOverlayResourceId) {
@@ -159,10 +190,9 @@
   auto& res = *resources;
   ASSERT_EQ(res.GetTargetToOverlayMap().size(), 2U);
   ASSERT_EQ(res.GetOverlayToTargetMap().size(), 1U);
-  ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_REFERENCE, 0x0104000a,
+  ASSERT_RESULT(MappingExists(res, R::target::string::str1, 0x0104000a,
                               false /* rewrite */));  // -> android:string/ok
-  ASSERT_RESULT(MappingExists(res, R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE,
-                              0x7f020001, true /* rewrite */));
+  ASSERT_RESULT(MappingExists(res, R::target::string::str3, 0x7f020001, true /* rewrite */));
 }
 
 TEST(ResourceMappingTests, InlineResources) {
@@ -180,10 +210,8 @@
   ASSERT_EQ(res.GetTargetToOverlayMap().size(), 2U);
   ASSERT_EQ(res.GetOverlayToTargetMap().size(), 0U);
   ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_STRING,
-                              overlay_string_pool_size + 0U,
-                              false /* rewrite */));  // -> "Hello World"
-  ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_INT_DEC, 73U,
-                              false /* rewrite */));  // -> 73
+                              overlay_string_pool_size + 0U));  // -> "Hello World"
+  ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_INT_DEC, 73U));
 }
 
 TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublic) {
@@ -195,13 +223,13 @@
   ASSERT_TRUE(resources) << resources.GetErrorMessage();
   auto& res = *resources;
   ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
-  ASSERT_RESULT(MappingExists(res, R::target::string::policy_public, Res_value::TYPE_REFERENCE,
+  ASSERT_RESULT(MappingExists(res, R::target::string::policy_public,
                               R::system_overlay::string::policy_public, false /* rewrite */));
-  ASSERT_RESULT(MappingExists(res, R::target::string::policy_system, Res_value::TYPE_REFERENCE,
+  ASSERT_RESULT(MappingExists(res, R::target::string::policy_system,
                               R::system_overlay::string::policy_system, false /* rewrite */));
-  ASSERT_RESULT(
-      MappingExists(res, R::target::string::policy_system_vendor, Res_value::TYPE_REFERENCE,
-                    R::system_overlay::string::policy_system_vendor, false /* rewrite */));
+  ASSERT_RESULT(MappingExists(res, R::target::string::policy_system_vendor,
+                              R::system_overlay::string::policy_system_vendor,
+                              false /* rewrite */));
 }
 
 // Resources that are not declared as overlayable and resources that a protected by policies the
@@ -215,15 +243,15 @@
   ASSERT_TRUE(resources) << resources.GetErrorMessage();
   auto& res = *resources;
   ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
-  ASSERT_RESULT(MappingExists(res, R::target::string::policy_public, Res_value::TYPE_REFERENCE,
+  ASSERT_RESULT(MappingExists(res, R::target::string::policy_public,
                               R::system_overlay_invalid::string::policy_public,
                               false /* rewrite */));
-  ASSERT_RESULT(MappingExists(res, R::target::string::policy_system, Res_value::TYPE_REFERENCE,
+  ASSERT_RESULT(MappingExists(res, R::target::string::policy_system,
                               R::system_overlay_invalid::string::policy_system,
                               false /* rewrite */));
-  ASSERT_RESULT(
-      MappingExists(res, R::target::string::policy_system_vendor, Res_value::TYPE_REFERENCE,
-                    R::system_overlay_invalid::string::policy_system_vendor, false /* rewrite */));
+  ASSERT_RESULT(MappingExists(res, R::target::string::policy_system_vendor,
+                              R::system_overlay_invalid::string::policy_system_vendor,
+                              false /* rewrite */));
 }
 
 // Resources that are not declared as overlayable and resources that a protected by policies the
@@ -238,37 +266,36 @@
   ASSERT_TRUE(resources) << resources.GetErrorMessage();
   auto& res = *resources;
   ASSERT_EQ(res.GetTargetToOverlayMap().size(), 11U);
-  ASSERT_RESULT(MappingExists(res, R::target::string::not_overlayable, Res_value::TYPE_REFERENCE,
+  ASSERT_RESULT(MappingExists(res, R::target::string::not_overlayable,
                               R::system_overlay_invalid::string::not_overlayable,
                               false /* rewrite */));
-  ASSERT_RESULT(MappingExists(res, R::target::string::other, Res_value::TYPE_REFERENCE,
+  ASSERT_RESULT(MappingExists(res, R::target::string::other,
                               R::system_overlay_invalid::string::other, false /* rewrite */));
-  ASSERT_RESULT(MappingExists(res, R::target::string::policy_actor, Res_value::TYPE_REFERENCE,
+  ASSERT_RESULT(MappingExists(res, R::target::string::policy_actor,
                               R::system_overlay_invalid::string::policy_actor,
                               false /* rewrite */));
-  ASSERT_RESULT(MappingExists(res, R::target::string::policy_odm, Res_value::TYPE_REFERENCE,
+  ASSERT_RESULT(MappingExists(res, R::target::string::policy_odm,
                               R::system_overlay_invalid::string::policy_odm, false /* rewrite */));
-  ASSERT_RESULT(MappingExists(res, R::target::string::policy_oem, Res_value::TYPE_REFERENCE,
+  ASSERT_RESULT(MappingExists(res, R::target::string::policy_oem,
                               R::system_overlay_invalid::string::policy_oem, false /* rewrite */));
-  ASSERT_RESULT(MappingExists(res, R::target::string::policy_product, Res_value::TYPE_REFERENCE,
+  ASSERT_RESULT(MappingExists(res, R::target::string::policy_product,
                               R::system_overlay_invalid::string::policy_product,
                               false /* rewrite */));
-  ASSERT_RESULT(MappingExists(res, R::target::string::policy_public, Res_value::TYPE_REFERENCE,
+  ASSERT_RESULT(MappingExists(res, R::target::string::policy_public,
                               R::system_overlay_invalid::string::policy_public,
                               false /* rewrite */));
   ASSERT_RESULT(MappingExists(res, R::target::string::policy_config_signature,
-                              Res_value::TYPE_REFERENCE,
                               R::system_overlay_invalid::string::policy_config_signature,
                               false /* rewrite */));
-  ASSERT_RESULT(MappingExists(res, R::target::string::policy_signature, Res_value::TYPE_REFERENCE,
+  ASSERT_RESULT(MappingExists(res, R::target::string::policy_signature,
                               R::system_overlay_invalid::string::policy_signature,
                               false /* rewrite */));
-  ASSERT_RESULT(MappingExists(res, R::target::string::policy_system, Res_value::TYPE_REFERENCE,
+  ASSERT_RESULT(MappingExists(res, R::target::string::policy_system,
                               R::system_overlay_invalid::string::policy_system,
                               false /* rewrite */));
-  ASSERT_RESULT(
-      MappingExists(res, R::target::string::policy_system_vendor, Res_value::TYPE_REFERENCE,
-                    R::system_overlay_invalid::string::policy_system_vendor, false /* rewrite */));
+  ASSERT_RESULT(MappingExists(res, R::target::string::policy_system_vendor,
+                              R::system_overlay_invalid::string::policy_system_vendor,
+                              false /* rewrite */));
 }
 
 // Overlays that do not target an <overlayable> tag can overlay resources defined within any
@@ -281,14 +308,14 @@
   ASSERT_TRUE(resources) << resources.GetErrorMessage();
   auto& res = *resources;
   ASSERT_EQ(res.GetTargetToOverlayMap().size(), 4U);
-  ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_REFERENCE,
-                              R::overlay::integer::int1, false /* rewrite */));
-  ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_REFERENCE,
-                              R::overlay::string::str1, false /* rewrite */));
-  ASSERT_RESULT(MappingExists(res, R::target::string::str3, Res_value::TYPE_REFERENCE,
-                              R::overlay::string::str3, false /* rewrite */));
-  ASSERT_RESULT(MappingExists(res, R::target::string::str4, Res_value::TYPE_REFERENCE,
-                              R::overlay::string::str4, false /* rewrite */));
+  ASSERT_RESULT(
+      MappingExists(res, R::target::integer::int1, R::overlay::integer::int1, false /* rewrite */));
+  ASSERT_RESULT(
+      MappingExists(res, R::target::string::str1, R::overlay::string::str1, false /* rewrite */));
+  ASSERT_RESULT(
+      MappingExists(res, R::target::string::str3, R::overlay::string::str3, false /* rewrite */));
+  ASSERT_RESULT(
+      MappingExists(res, R::target::string::str4, R::overlay::string::str4, false /* rewrite */));
 }
 
 // Overlays that are neither pre-installed nor signed with the same signature as the target cannot
@@ -302,9 +329,9 @@
   ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 0U);
 }
 
-// Overlays that are pre-installed or are signed with the same signature as the target  or are signed
-// with the same signature as the reference package can overlay packages that have not defined
-// overlayable resources.
+// Overlays that are pre-installed or are signed with the same signature as the target  or are
+// signed with the same signature as the reference package can overlay packages that have not
+// defined overlayable resources.
 TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPolicies) {
   auto CheckEntries = [&](const PolicyBitmask& fulfilled_policies) -> void {
     auto resources = TestGetResourceMapping("/target/target-no-overlayable.apk",
@@ -315,39 +342,38 @@
     ASSERT_TRUE(resources) << resources.GetErrorMessage();
     auto& res = *resources;
     ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 11U);
-    ASSERT_RESULT(MappingExists(res, R::target::string::not_overlayable, Res_value::TYPE_REFERENCE,
+    ASSERT_RESULT(MappingExists(res, R::target::string::not_overlayable,
                                 R::system_overlay_invalid::string::not_overlayable,
                                 false /* rewrite */));
-    ASSERT_RESULT(MappingExists(res, R::target::string::other, Res_value::TYPE_REFERENCE,
+    ASSERT_RESULT(MappingExists(res, R::target::string::other,
                                 R::system_overlay_invalid::string::other, false /* rewrite */));
-    ASSERT_RESULT(MappingExists(res, R::target::string::policy_actor, Res_value::TYPE_REFERENCE,
+    ASSERT_RESULT(MappingExists(res, R::target::string::policy_actor,
                                 R::system_overlay_invalid::string::policy_actor,
                                 false /* rewrite */));
-    ASSERT_RESULT(MappingExists(res, R::target::string::policy_odm, Res_value::TYPE_REFERENCE,
+    ASSERT_RESULT(MappingExists(res, R::target::string::policy_odm,
                                 R::system_overlay_invalid::string::policy_odm,
                                 false /* rewrite */));
-    ASSERT_RESULT(MappingExists(res, R::target::string::policy_oem, Res_value::TYPE_REFERENCE,
+    ASSERT_RESULT(MappingExists(res, R::target::string::policy_oem,
                                 R::system_overlay_invalid::string::policy_oem,
                                 false /* rewrite */));
-    ASSERT_RESULT(MappingExists(res, R::target::string::policy_product, Res_value::TYPE_REFERENCE,
+    ASSERT_RESULT(MappingExists(res, R::target::string::policy_product,
                                 R::system_overlay_invalid::string::policy_product,
                                 false /* rewrite */));
-    ASSERT_RESULT(MappingExists(res, R::target::string::policy_public, Res_value::TYPE_REFERENCE,
+    ASSERT_RESULT(MappingExists(res, R::target::string::policy_public,
                                 R::system_overlay_invalid::string::policy_public,
                                 false /* rewrite */));
     ASSERT_RESULT(MappingExists(res, R::target::string::policy_config_signature,
-                                Res_value::TYPE_REFERENCE,
                                 R::system_overlay_invalid::string::policy_config_signature,
                                 false /* rewrite */));
-    ASSERT_RESULT(MappingExists(res, R::target::string::policy_signature, Res_value::TYPE_REFERENCE,
+    ASSERT_RESULT(MappingExists(res, R::target::string::policy_signature,
                                 R::system_overlay_invalid::string::policy_signature,
                                 false /* rewrite */));
-    ASSERT_RESULT(MappingExists(res, R::target::string::policy_system, Res_value::TYPE_REFERENCE,
+    ASSERT_RESULT(MappingExists(res, R::target::string::policy_system,
                                 R::system_overlay_invalid::string::policy_system,
                                 false /* rewrite */));
-    ASSERT_RESULT(MappingExists(
-        res, R::target::string::policy_system_vendor, Res_value::TYPE_REFERENCE,
-        R::system_overlay_invalid::string::policy_system_vendor, false /* rewrite */));
+    ASSERT_RESULT(MappingExists(res, R::target::string::policy_system_vendor,
+                                R::system_overlay_invalid::string::policy_system_vendor,
+                                false /* rewrite */));
   };
 
   CheckEntries(PolicyFlags::SIGNATURE);
diff --git a/cmds/idmap2/tests/TestConstants.h b/cmds/idmap2/tests/TestConstants.h
index 9641f6b5..69575b8 100644
--- a/cmds/idmap2/tests/TestConstants.h
+++ b/cmds/idmap2/tests/TestConstants.h
@@ -19,7 +19,7 @@
 
 namespace android::idmap2::TestConstants {
 
-constexpr const auto TARGET_CRC =  0x7c2d4719;
+constexpr const auto TARGET_CRC = 0x7c2d4719;
 constexpr const auto TARGET_CRC_STRING = "7c2d4719";
 
 constexpr const auto OVERLAY_CRC = 0x5afff726;
diff --git a/cmds/idmap2/tests/TestHelpers.h b/cmds/idmap2/tests/TestHelpers.h
index b599dcb..d0a8e3d 100644
--- a/cmds/idmap2/tests/TestHelpers.h
+++ b/cmds/idmap2/tests/TestHelpers.h
@@ -30,7 +30,7 @@
     0x49, 0x44, 0x4d, 0x50,
 
     // 0x4: version
-    0x04, 0x00, 0x00, 0x00,
+    0x05, 0x00, 0x00, 0x00,
 
     // 0x8: target crc
     0x34, 0x12, 0x00, 0x00,
@@ -42,9 +42,9 @@
     0x11, 0x00, 0x00, 0x00,
 
     // 0x14: enforce overlayable
-    0x01,
+    0x01, 0x00, 0x00, 0x00,
 
-    // 0x15: target path "targetX.apk"
+    // 0x18: target path "targetX.apk"
     0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x58, 0x2e, 0x61, 0x70, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -62,7 +62,7 @@
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 
-    // 0x115: overlay path "overlayX.apk"
+    // 0x118: overlay path "overlayX.apk"
     0x6f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x58, 0x2e, 0x61, 0x70, 0x6b, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -80,71 +80,89 @@
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 
-    // 0x215: debug string
-    // string length, including terminating null
-    0x08, 0x00, 0x00, 0x00,
+    // 0x218: debug string
+    // string length,
+    0x05, 0x00, 0x00, 0x00,
 
-    // string contents "debug\0\0\0" (padded to word alignment)
+    // 0x21c string contents "debug\0\0\0" (padded to word alignment)
     0x64, 0x65, 0x62, 0x75, 0x67, 0x00, 0x00, 0x00,
 
     // DATA HEADER
-    // 0x221: target_package_id
+    // 0x224: target_package_id
     0x7f,
 
-    // 0x222: overlay_package_id
+    // 0x225: overlay_package_id
     0x7f,
 
-    // 0x223: target_entry_count
+    // 0x226: padding
+    0x00, 0x00,
+
+    // 0x228: target_entry_count
     0x03, 0x00, 0x00, 0x00,
 
-    // 0x227: overlay_entry_count
+    // 0x22c: target_inline_entry_count
+    0x01, 0x00, 0x00, 0x00,
+
+    // 0x230: overlay_entry_count
     0x03, 0x00, 0x00, 0x00,
 
-    // 0x22b: string_pool_offset
-    0x00, 0x00, 0x00, 0x00,
-
-    // 0x22f: string_pool_byte_length
+    // 0x234: string_pool_offset
     0x00, 0x00, 0x00, 0x00,
 
     // TARGET ENTRIES
-    // 0x233: 0x7f020000
+    // 0x238: target id (0x7f020000)
     0x00, 0x00, 0x02, 0x7f,
 
-    // 0x237: TYPE_REFERENCE
-    0x01,
-
-    // 0x238: 0x7f020000
+    // 0x23c: overlay_id (0x7f020000)
     0x00, 0x00, 0x02, 0x7f,
 
-    // 0x23c: 0x7f030000
+    // 0x240: target id (0x7f030000)
     0x00, 0x00, 0x03, 0x7f,
 
-    // 0x240: TYPE_REFERENCE
-    0x01,
-
-    // 0x241: 0x7f030000
+    // 0x244: overlay_id (0x7f030000)
     0x00, 0x00, 0x03, 0x7f,
 
-    // 0x245: 0x7f030002
+    // 0x248: target id (0x7f030002)
     0x02, 0x00, 0x03, 0x7f,
 
-    // 0x249: TYPE_REFERENCE
-    0x01,
-
-    // 0x24a: 0x7f030001
+    // 0x24c: overlay_id (0x7f030001)
     0x01, 0x00, 0x03, 0x7f,
 
+    // INLINE TARGET ENTRIES
+
+    // 0x250: target_id
+    0x00, 0x00, 0x04, 0x7f,
+
+    // 0x254: Res_value::size (value ignored by idmap)
+    0x08, 0x00,
+
+    // 0x256: Res_value::res0 (value ignored by idmap)
+    0x00,
+
+    // 0x257: Res_value::dataType (TYPE_INT_HEX)
+    0x11,
+
+    // 0x258: Res_value::data
+    0x78, 0x56, 0x34, 0x12,
+
     // OVERLAY ENTRIES
-    // 0x24e: 0x7f020000 -> 0x7f020000
+    // 0x25c: 0x7f020000 -> 0x7f020000
     0x00, 0x00, 0x02, 0x7f, 0x00, 0x00, 0x02, 0x7f,
 
-    // 0x256: 0x7f030000 -> 0x7f030000
+    // 0x264: 0x7f030000 -> 0x7f030000
     0x00, 0x00, 0x03, 0x7f, 0x00, 0x00, 0x03, 0x7f,
 
-    // 0x25e: 0x7f030001 -> 0x7f030002
-    0x01, 0x00, 0x03, 0x7f, 0x02, 0x00, 0x03, 0x7f};
+    // 0x26c: 0x7f030001 -> 0x7f030002
+    0x01, 0x00, 0x03, 0x7f, 0x02, 0x00, 0x03, 0x7f,
 
-const unsigned int idmap_raw_data_len = 0x266;
+    // 0x274: string pool
+    // string length,
+    0x04, 0x00, 0x00, 0x00,
+
+    // 0x278 string contents "test" (padded to word alignment)
+    0x74, 0x65, 0x73, 0x74};
+
+const unsigned int idmap_raw_data_len = 0x27c;
 
 std::string GetTestDataPath();
 
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 88db1d8..012450d 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -430,6 +430,30 @@
     },
 }
 
+java_library {
+    name: "statsdprotonano",
+    sdk_version: "9",
+    proto: {
+        type: "nano",
+        output_params: ["store_unknown_fields=true"],
+        include_dirs: ["external/protobuf/src"],
+    },
+    srcs: [
+        "src/atoms.proto",
+        "src/shell/shell_config.proto",
+        "src/shell/shell_data.proto",
+        "src/stats_log.proto",
+        "src/statsd_config.proto",
+    ],
+    static_libs: [
+        "platformprotosnano",
+    ],
+    // Protos have lots of MissingOverride and similar.
+    errorprone: {
+        javacflags: ["-XepDisableAllChecks"],
+    },
+}
+
 // Filegroup for statsd config proto definition.
 filegroup {
     name: "statsd-config-proto-def",
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index bda9e24..3d67e65 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -266,7 +266,7 @@
                 147 [(module) = "framework", (module) = "statsd"];
         BiometricSystemHealthIssueDetected biometric_system_health_issue_detected =
                 148 [(module) = "framework"];
-        BubbleUIChanged bubble_ui_changed = 149 [(module) = "sysui"];
+        BubbleUIChanged bubble_ui_changed = 149 [(module) = "framework"];
         ScheduledJobConstraintChanged scheduled_job_constraint_changed =
                 150 [(module) = "framework"];
         BluetoothActiveDeviceChanged bluetooth_active_device_changed =
@@ -493,13 +493,18 @@
         WifiConnectionStateChanged wifi_connection_state_changed = 308 [(module) = "wifi"];
         HdmiCecActiveSourceChanged hdmi_cec_active_source_changed = 309 [(module) = "framework"];
         HdmiCecMessageReported hdmi_cec_message_reported = 310 [(module) = "framework"];
+        AirplaneMode airplane_mode = 311 [(module) = "telephony"];
+        ModemRestart modem_restart = 312 [(module) = "telephony"];
+        CarrierIdMismatchEvent carrier_id_mismatch_event = 313 [(module) = "telephony"];
+        CarrierIdMatchingTable carrier_id_table_update = 314 [(module) = "telephony"];
+        DataStallRecoveryReported data_stall_recovery_reported = 315 [(module) = "telephony"];
 
         // StatsdStats tracks platform atoms with ids upto 500.
         // Update StatsdStats::kMaxPushedAtomId when atom ids here approach that value.
     }
 
     // Pulled events will start at field 10000.
-    // Next: 10087
+    // Next: 10092
     oneof pulled {
         WifiBytesTransfer wifi_bytes_transfer = 10000 [(module) = "framework"];
         WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001 [(module) = "framework"];
@@ -599,6 +604,11 @@
         GeneralExternalStorageAccessStats general_external_storage_access_stats =
             10085 [(module) = "mediaprovider"];
         IncomingSms incoming_sms = 10086 [(module) = "telephony"];
+        OutgoingSms outgoing_sms = 10087 [(module) = "telephony"];
+        CarrierIdMatchingTable carrier_id_table_version = 10088 [(module) = "telephony"];
+        DataCallSession data_call_session = 10089 [(module) = "telephony"];
+        CellularServiceState cellular_service_state = 10090 [(module) = "telephony"];
+        CellularDataServiceSwitch cellular_data_service_switch = 10091 [(module) = "telephony"];
     }
 
     // DO NOT USE field numbers above 100,000 in AOSP.
@@ -3192,8 +3202,9 @@
     optional int32 end_y = 7;  // Y coordinate for ACTION_MOVE event.
     optional int32 left_boundary = 8;  // left edge width + left inset
     optional int32 right_boundary = 9;  // screen width - (right edge width + right inset)
-    optional float ml_model_score = 10;  // The score between 0 and 1 which is the prediction output
-        // for the Back Gesture model.
+    // The score between 0 and 1 which is the prediction output for the Back Gesture model.
+    optional float ml_model_score = 10;
+    optional string package_name = 11;  // The name of the top 100 most used package by all users.
 
     enum WindowHorizontalLocation {
         DEFAULT_LOCATION = 0;
@@ -3830,6 +3841,12 @@
     // App startup time (until call to Activity#reportFullyDrawn()).
     optional int64 app_startup_time_millis = 6;
 
+    // The compiler filter used when when the package was optimized.
+    optional int32 package_optimization_compilation_filter = 7;
+
+    // The reason why the package was optimized.
+    optional int32 package_optimization_compilation_reason = 8;
+
     enum SourceType {
         UNAVAILABLE = 0;
         LAUNCHER = 1;
@@ -3837,11 +3854,11 @@
         LOCKSCREEN = 3;
     }
     // The type of the startup source.
-    optional SourceType source_type = 7;
+    optional SourceType source_type = 9;
 
     // The time from the startup source to the beginning of handling the startup event.
     // -1 means not available.
-    optional int32 source_event_delay_millis = 8;
+    optional int32 source_event_delay_millis = 10;
 }
 
 /**
@@ -4464,6 +4481,7 @@
         // which requires reboot and not eligible for any reboot promotion strategy
         // (e.g. soft restart, notification restart).
         NO_REBOOT_PROMOTION_STRATEGY_ELIGIBLE = 30;
+        REBOOT_TRIGGER_FAILURE = 31;
     }
     optional State state = 6;
     // Possible experiment ids for monitoring this push.
@@ -5264,12 +5282,23 @@
  * Event to track Jank for various system interactions.
  *
  * Logged from:
- *  frameworks/base/core/java/android/os/aot/FrameTracker.java
+ *  frameworks/base/core/java/com/android/internal/jank/FrameTracker.java
  */
 message UIInteractionFrameInfoReported {
     enum InteractionType {
         UNKNOWN = 0;
         NOTIFICATION_SHADE_SWIPE = 1;
+        SHADE_EXPAND_COLLAPSE_LOCK = 2;
+        SHADE_SCROLL_FLING = 3;
+        SHADE_ROW_EXPAND = 4;
+        SHADE_ROW_SWIPE = 5;
+        SHADE_QS_EXPAND_COLLAPSE = 6;
+        SHADE_QS_SCROLL_SWIPE = 7;
+        LAUNCHER_APP_LAUNCH_FROM_RECENTS = 8;
+        LAUNCHER_APP_LAUNCH_FROM_ICON = 9;
+        LAUNCHER_APP_CLOSE_TO_HOME = 10;
+        LAUNCHER_APP_CLOSE_TO_PIP = 11;
+        LAUNCHER_QUICK_SWITCH = 12;
     }
 
     optional InteractionType interaction_type = 1;
@@ -10371,7 +10400,7 @@
     // Number of other calls going on during call termination, for the same SIM slot.
     optional int32 concurrent_call_count_at_end = 14;
 
-    // Index of the SIM is used, 0 for single-SIM devices.
+    // Index of the SIM used, 0 for single-SIM devices.
     optional int32 sim_slot_index = 15;
 
     // Whether the device was in multi-SIM mode (with multiple active SIM profiles).
@@ -10423,7 +10452,7 @@
     // Radio access technology.
     optional android.telephony.NetworkTypeEnum rat = 2;
 
-    // Total duration that voice calls spent on this carrier and RAT.
+    // Total duration that voice calls spent on this carrier and RAT, rounded to 5 minute.
     optional int64 total_duration_seconds = 3;
 
     // Total number of calls using this carrier and RAT.
@@ -10432,6 +10461,82 @@
 }
 
 /**
+ * Pulls amount of time spend in each cellular service state.
+ *
+ * Each pull creates multiple atoms, one for each SIM slot/carrier/RAT(including ENDC), the order of
+ * which is irrelevant to time. If multi SIM settings changes during the period, durations will be
+ * counted separately before and after the change. Airplane mode does not count towards durations.
+ *
+ * Pulled from:
+ *   frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
+ */
+message CellularServiceState {
+    // Radio access technology (RAT) for voice.
+    // NETWORK_TYPE_UNKNOWN when the device is out of service.
+    // NETWORK_TYPE_IWLAN when the device is using VoWiFi.
+    optional android.telephony.NetworkTypeEnum voice_rat = 1;
+
+    // Radio access technology (RAT) for data.
+    // NETWORK_TYPE_UNKNOWN when the device is out of service.
+    // Only cellular RATs are valid and show where the device is camped.
+    optional android.telephony.NetworkTypeEnum data_rat = 2;
+
+    // Whether the device was in roaming (domestic or international) for voice.
+    optional android.telephony.RoamingTypeEnum voice_roaming_type = 3;
+
+    // Whether the device was in roaming (domestic or international) for data.
+    optional android.telephony.RoamingTypeEnum data_roaming_type = 4;
+
+    // Whether the device is on LTE and has access to NR NSA, i.e. cell supports 5G (ENDC) and UE
+    // registration (attach/TAU) indicates ENDC is not restricted.
+    optional bool is_endc = 5;
+
+    // Index of the SIM used, 0 for single-SIM devices.
+    optional int32 sim_slot_index = 6;
+
+    // Whether the device was in multi-SIM mode (with multiple active SIM profiles).
+    optional bool is_multi_sim = 7;
+
+    // Carrier ID of the SIM card.
+    // See https://source.android.com/devices/tech/config/carrierid.
+    optional int32 carrier_id = 8;
+
+    // Total time spent in this service state, rounded to 5 minutes.
+    optional int32 total_time_seconds = 9;
+}
+
+/**
+ * Pulls the number of times cellular data service state switches.
+ *
+ * Each pull creates multiple atoms, one for each RAT combination, the order of which is irrelevant
+ * to time. Switches for different SIM slots, carrier IDs, or multi-SIM settings are counted
+ * separately.
+ *
+ * Pulled from:
+ *   frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
+ */
+message CellularDataServiceSwitch {
+    // Cellular RAT of the DATA domain from where the switch occurred.
+    optional android.telephony.NetworkTypeEnum rat_from = 1;
+
+    // Cellular RAT of the DATA domain to where the switch occurred.
+    optional android.telephony.NetworkTypeEnum rat_to = 2;
+
+    // Index of the SIM used, 0 for single-SIM devices.
+    optional int32 sim_slot_index = 3;
+
+    // Whether the device was in multi-SIM mode (with multiple active SIM profiles).
+    optional bool is_multi_sim = 4;
+
+    // Carrier ID of the SIM card.
+    // See https://source.android.com/devices/tech/config/carrierid.
+    optional int32 carrier_id = 5;
+
+    // Number of switches from rat_from to rat_to.
+    optional int32 switch_count = 6;
+}
+
+/**
  * Pulls the number of active SIM slots and SIMs/eSIM profiles.
  *
  * Pulled from:
@@ -10500,7 +10605,7 @@
     // Whether the SMS was received while roaming.
     optional bool is_roaming = 9;
 
-    // Index of the SIM is used, 0 for single-SIM devices.
+    // Index of the SIM used, 0 for single-SIM devices.
     optional int32 sim_slot_index = 10;
 
     // Whether the device was in multi-SIM mode (with multiple active SIM profiles).
@@ -10518,12 +10623,238 @@
 }
 
 /**
+ * Pulls information for a single outgoing SMS.
+ *
+ * Each pull creates multiple atoms, one for each SMS. The sequence is randomized when pulled.
+ *
+ * Pulled from:
+ *   frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
+ */
+message OutgoingSms {
+    // Format of the SMS (3GPP or 3GPP2).
+    optional android.telephony.SmsFormatEnum sms_format = 1;
+
+    // Technology of the SMS (CS or IMS).
+    optional android.telephony.SmsTechEnum sms_tech = 2;
+
+    // Radio access technology (RAT) used for the SMS. It can be IWLAN in case of IMS.
+    optional android.telephony.NetworkTypeEnum rat = 3;
+
+    // Result of the SMS sending.
+    optional android.telephony.SmsSendResultEnum send_result = 4;
+
+    // Error code
+    // For IMS technology, see @SmsManager.Result in
+    // http://cs/android/frameworks/base/telephony/java/android/telephony/SmsManager.java
+    // For CS technology:
+    //  - GSM format: see GsmSmsErrorCode (3GPP 27.005 clause 3.2.5)
+    //  - CDMA format: see CdmaSmsErrorCode (3GPP2 N.S0005 (IS-41-C) Table 171)
+    optional int32 error_code = 5;
+
+    // Whether the SMS was sent while roaming.
+    optional bool is_roaming = 6;
+
+    // Whether the default SMS application generated the SMS (regardless of which application).
+    optional bool is_from_default_app = 7;
+
+    // Index of the SIM used, 0 for single-SIM devices.
+    optional int32 sim_slot_index = 8;
+
+    // Whether the device was in multi-SIM mode (with multiple active SIM profiles).
+    optional bool is_multi_sim = 9;
+
+    // Whether the message was sent with an eSIM profile.
+    optional bool is_esim = 10;
+
+    // Carrier ID of the SIM card used for the SMS.
+    // See https://source.android.com/devices/tech/config/carrierid.
+    optional int32 carrier_id = 11;
+
+    // Random message ID.
+    optional int64 message_id = 12;
+
+    // Retry count: 0 for the first attempt and then increasing for each attempt.
+    optional int32 retry_id = 13;
+}
+
+/**
+ * Push information about usage of airplane mode.
+ *
+ * Logged from:
+ *   frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/AirplaneModeStats.java
+ */
+message AirplaneMode {
+    // Status of airplane mode
+    optional bool is_enabled = 1;
+
+    // When is_enabled is false, indicates if this was a very short airplane mode toggle
+    // (i.e. airplane mode was disabled after less than 10 seconds from enablement).
+    optional bool short_toggle = 2;
+
+    // Carrier ID of the SIM card.
+    // See https://source.android.com/devices/tech/config/carrierid.
+    optional int32 carrier_id = 3;
+}
+
+/**
+ * Push information about modem restarts.
+ *
+ * Logged from:
+ *   frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/ModemRestartStats.java
+ */
+message ModemRestart {
+    // Software version of the modem, as provided by android.os.Build.getRadioVersion().
+    optional string baseband_version = 1;
+
+    // Reason of the modem restart, as provided in the modemReset indication of IRadio HAL.
+    optional string reason = 2;
+
+    // Carrier ID of the first SIM card.
+    // See https://source.android.com/devices/tech/config/carrierid.
+    optional int32 carrier_id = 3;
+}
+
+/**
+ * Push the SIM card details when the carrier ID match is not complete.
+ *
+ * The atom is pushed when a SIM card is initialized and the MCC/MNC is not present in the
+ * carrier ID table, or the SIM card contains a GID1 value that is not present in the carrier ID
+ * table. This atom is pushed only once for each type of SIM card.
+ *
+ * Logged from:
+ *   frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/CarrierIdMatchStats.java
+ */
+message CarrierIdMismatchEvent {
+    // Matched carrier ID. The value -1 is used if no match is found.
+    optional int32 carrier_id = 1;
+
+    // MCC/MNC of the SIM card.
+    optional string mcc_mnc = 2;
+
+    // Group identifier (level 1) of the SIM card.
+    optional string gid1 = 3;
+
+    // SPN value of the SIM card.
+    optional string spn = 4;
+}
+
+/**
+ * Pulls/pushes the version of the carrier ID matching table.
+ *
+ * The atom is pushed when a new version is detected.
+ *
+ * Logged from:
+ *   frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/CarrierIdMatchStats.java
+ */
+message CarrierIdMatchingTable {
+    // Version of the CarrierId matching table.
+    optional int32 table_version = 1;
+}
+
+/**
+ * Pulls information for a single data call session
+ *
+ * Each pull creates multiple atoms, one for each data call session.
+ * The sequence is randomized when pulled.
+ *
+ * Pulled from:
+ *   frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
+ */
+message DataCallSession {
+    // A random number to be used as dimension to capture multiple atoms
+    optional int32 dimension = 1;
+
+    // Whether the device was in multi-SIM mode (with multiple active SIM profiles).
+    optional bool is_multi_sim = 2;
+
+    // Whether the call was made with an eSIM profile.
+    optional bool is_esim = 3;
+
+    // Data profile of this call (for what purpose this call was made)
+    optional android.telephony.DataProfileEnum profile = 4;
+
+    // APN type bitmask of the APN used:
+    // @ApnType in frameworks/base/telephony/java/android/telephony/Annotation.java.
+    optional int32 apn_type_bitmask = 5;
+
+    // Carrier ID of the SIM
+    // See https://source.android.com/devices/tech/config/carrierid.
+    optional int32 carrier_id = 6;
+
+    // Whether the subscription is roaming
+    optional bool is_roaming = 7;
+
+    // Data RAT when the call ended, can be IWLAN for IMS/MMS, otherwise should be WWAN PS RAT.
+    // In the case that the connection hasn't ended yet, this field holds the current RAT.
+    // In the case the call ended due to Out Of Service (OOS),
+    // this field should be the last known RAT.
+    optional android.telephony.NetworkTypeEnum rat_at_end = 8;
+
+    // Was the data call ended due to OOS
+    optional bool oos_at_end = 9;
+
+    // Number of RAT switches during the data call
+    optional int64 rat_switch_count = 10;
+
+    // Whether the call is on an opportunistic subscription
+    optional bool is_opportunistic = 11;
+
+    // Packet data protocol used
+    optional android.telephony.ApnProtocolEnum ip_type = 12;
+
+    // Whether the data call terminated before being established
+    optional bool setup_failed = 13;
+
+    // Reason why the data call terminated, as in RIL_DataCallFailCause from ril.h
+    optional int32 failure_cause = 14;
+
+    // Suggested retry back-off timer value from RIL
+    optional int32 suggested_retry_millis = 15;
+
+    // Why the data call was deactivated
+    // Set by telephony for MO deactivations (unrelated to failure_cause)
+    optional android.telephony.DataDeactivateReasonEnum deactivate_reason = 16;
+
+    // Duration of the data call, rounded into the closest 5 minutes.
+    optional int64 duration_minutes = 17;
+
+    // Whether the data call is still connected when the atom is collected.
+    optional bool ongoing = 18;
+}
+
+/**
+ * Logs data stall recovery event
+ *
+ * Logged from:
+ *   frameworks/opt/telephony/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
+ */
+message DataStallRecoveryReported {
+    // Carrier ID of the SIM
+    // See https://source.android.com/devices/tech/config/carrierid.
+    optional int32 carrier_id = 1;
+
+    // Data RAT when the stall happened
+    optional android.telephony.NetworkTypeEnum rat = 2;
+
+    // Signal strength when stall happened
+    optional android.telephony.SignalStrengthEnum signal_strength = 3;
+
+    // Action taken to recover
+    optional android.telephony.DataStallRecoveryActionEnum action = 4;
+
+    // Whether the subscription is opportunistic
+    optional bool is_opportunistic = 5;
+
+    // Whether the device is in multi-SIM mode
+    optional bool is_multi_sim = 6;
+}
+
+/**
  * Logs gnss stats from location service provider
  *
  * Pulled from:
  *  frameworks/base/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
  */
-
 message GnssStats {
     // Number of location reports since boot
     optional int64 location_reports = 1;
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 3dbb6ed..a8ef54a 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -24,6 +24,7 @@
 #include <stdlib.h>
 
 #include "guardrail/StatsdStats.h"
+#include "metrics/parsing_utils/metrics_manager_util.h"
 #include "stats_log_util.h"
 #include "stats_util.h"
 
@@ -122,6 +123,47 @@
     VLOG("~CountMetricProducer() called");
 }
 
+bool CountMetricProducer::onConfigUpdatedLocked(
+        const StatsdConfig& config, const int configIndex, const int metricIndex,
+        const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+        const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+        const sp<EventMatcherWizard>& matcherWizard,
+        const vector<sp<ConditionTracker>>& allConditionTrackers,
+        const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard,
+        const unordered_map<int64_t, int>& metricToActivationMap,
+        unordered_map<int, vector<int>>& trackerToMetricMap,
+        unordered_map<int, vector<int>>& conditionToMetricMap,
+        unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+        unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+        vector<int>& metricsWithActivation) {
+    if (!MetricProducer::onConfigUpdatedLocked(
+                config, configIndex, metricIndex, allAtomMatchingTrackers,
+                oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+                allConditionTrackers, conditionTrackerMap, wizard, metricToActivationMap,
+                trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap,
+                deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
+        return false;
+    }
+
+    const CountMetric& metric = config.count_metric(configIndex);
+    int trackerIndex;
+    // Update appropriate indices, specifically mConditionIndex and MetricsManager maps.
+    if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, false,
+                                              allAtomMatchingTrackers, newAtomMatchingTrackerMap,
+                                              trackerToMetricMap, trackerIndex)) {
+        return false;
+    }
+
+    if (metric.has_condition() &&
+        !handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+                                    metric.links(), allConditionTrackers, mConditionTrackerIndex,
+                                    conditionToMetricMap)) {
+        return false;
+    }
+    return true;
+}
+
 void CountMetricProducer::onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
                                          const HashableDimensionKey& primaryKey,
                                          const FieldValue& oldState, const FieldValue& newState) {
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index 6b2f2ca..0769ac4 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -97,6 +97,22 @@
     void flushCurrentBucketLocked(const int64_t& eventTimeNs,
                                   const int64_t& nextBucketStartTimeNs) override;
 
+    bool onConfigUpdatedLocked(
+            const StatsdConfig& config, const int configIndex, const int metricIndex,
+            const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+            const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+            const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+            const sp<EventMatcherWizard>& matcherWizard,
+            const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+            const std::unordered_map<int64_t, int>& conditionTrackerMap,
+            const sp<ConditionWizard>& wizard,
+            const std::unordered_map<int64_t, int>& metricToActivationMap,
+            std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+            std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+            std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+            std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+            std::vector<int>& metricsWithActivation) override;
+
     std::unordered_map<MetricDimensionKey, std::vector<CountBucket>> mPastBuckets;
 
     // The current bucket (may be a partial bucket).
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 3acafaa..b2c0b32 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -17,14 +17,17 @@
 #define DEBUG false
 
 #include "Log.h"
+
 #include "DurationMetricProducer.h"
-#include "guardrail/StatsdStats.h"
-#include "stats_util.h"
-#include "stats_log_util.h"
 
 #include <limits.h>
 #include <stdlib.h>
 
+#include "guardrail/StatsdStats.h"
+#include "metrics/parsing_utils/metrics_manager_util.h"
+#include "stats_log_util.h"
+#include "stats_util.h"
+
 using android::util::FIELD_COUNT_REPEATED;
 using android::util::FIELD_TYPE_BOOL;
 using android::util::FIELD_TYPE_FLOAT;
@@ -64,8 +67,8 @@
 
 DurationMetricProducer::DurationMetricProducer(
         const ConfigKey& key, const DurationMetric& metric, const int conditionIndex,
-        const vector<ConditionState>& initialConditionCache, const size_t startIndex,
-        const size_t stopIndex, const size_t stopAllIndex, const bool nesting,
+        const vector<ConditionState>& initialConditionCache, const int startIndex,
+        const int stopIndex, const int stopAllIndex, const bool nesting,
         const sp<ConditionWizard>& wizard, const uint64_t protoHash,
         const FieldMatcher& internalDimensions, const int64_t timeBaseNs, const int64_t startTimeNs,
         const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
@@ -143,6 +146,84 @@
     VLOG("~DurationMetric() called");
 }
 
+bool DurationMetricProducer::onConfigUpdatedLocked(
+        const StatsdConfig& config, const int configIndex, const int metricIndex,
+        const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+        const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+        const sp<EventMatcherWizard>& matcherWizard,
+        const vector<sp<ConditionTracker>>& allConditionTrackers,
+        const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard,
+        const unordered_map<int64_t, int>& metricToActivationMap,
+        unordered_map<int, vector<int>>& trackerToMetricMap,
+        unordered_map<int, vector<int>>& conditionToMetricMap,
+        unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+        unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+        vector<int>& metricsWithActivation) {
+    if (!MetricProducer::onConfigUpdatedLocked(
+                config, configIndex, metricIndex, allAtomMatchingTrackers,
+                oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+                allConditionTrackers, conditionTrackerMap, wizard, metricToActivationMap,
+                trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap,
+                deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
+        return false;
+    }
+
+    const DurationMetric& metric = config.duration_metric(configIndex);
+    const auto& what_it = conditionTrackerMap.find(metric.what());
+    if (what_it == conditionTrackerMap.end()) {
+        ALOGE("DurationMetric's \"what\" is not present in the config");
+        return false;
+    }
+
+    const Predicate& durationWhat = config.predicate(what_it->second);
+    if (durationWhat.contents_case() != Predicate::ContentsCase::kSimplePredicate) {
+        ALOGE("DurationMetric's \"what\" must be a simple condition");
+        return false;
+    }
+
+    const SimplePredicate& simplePredicate = durationWhat.simple_predicate();
+
+    // Update indices: mStartIndex, mStopIndex, mStopAllIndex, mConditionIndex and MetricsManager
+    // maps.
+    if (!handleMetricWithAtomMatchingTrackers(simplePredicate.start(), metricIndex,
+                                              metric.has_dimensions_in_what(),
+                                              allAtomMatchingTrackers, newAtomMatchingTrackerMap,
+                                              trackerToMetricMap, mStartIndex)) {
+        ALOGE("Duration metrics must specify a valid start event matcher");
+        return false;
+    }
+
+    if (simplePredicate.has_stop() &&
+        !handleMetricWithAtomMatchingTrackers(simplePredicate.stop(), metricIndex,
+                                              metric.has_dimensions_in_what(),
+                                              allAtomMatchingTrackers, newAtomMatchingTrackerMap,
+                                              trackerToMetricMap, mStopIndex)) {
+        return false;
+    }
+
+    if (simplePredicate.has_stop_all() &&
+        !handleMetricWithAtomMatchingTrackers(simplePredicate.stop_all(), metricIndex,
+                                              metric.has_dimensions_in_what(),
+                                              allAtomMatchingTrackers, newAtomMatchingTrackerMap,
+                                              trackerToMetricMap, mStopAllIndex)) {
+        return false;
+    }
+
+    if (metric.has_condition() &&
+        !handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+                                    metric.links(), allConditionTrackers, mConditionTrackerIndex,
+                                    conditionToMetricMap)) {
+        return false;
+    }
+
+    for (const auto& it : mCurrentSlicedDurationTrackerMap) {
+        it.second->onConfigUpdated(wizard, mConditionTrackerIndex);
+    }
+
+    return true;
+}
+
 sp<AnomalyTracker> DurationMetricProducer::addAnomalyTracker(
         const Alert &alert, const sp<AlarmMonitor>& anomalyAlarmMonitor) {
     std::lock_guard<std::mutex> lock(mMutex);
@@ -550,7 +631,7 @@
     }
 
     // Handles Stopall events.
-    if (matcherIndex == mStopAllIndex) {
+    if ((int)matcherIndex == mStopAllIndex) {
         for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
             whatIt.second->noteStopAll(event.GetElapsedTimestampNs());
         }
@@ -598,7 +679,7 @@
     }
 
     // Handles Stop events.
-    if (matcherIndex == mStopIndex) {
+    if ((int)matcherIndex == mStopIndex) {
         if (mUseWhatDimensionAsInternalDimension) {
             auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat);
             if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 3a94d9c..01198a9 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -40,8 +40,8 @@
 public:
     DurationMetricProducer(
             const ConfigKey& key, const DurationMetric& durationMetric, const int conditionIndex,
-            const vector<ConditionState>& initialConditionCache, const size_t startIndex,
-            const size_t stopIndex, const size_t stopAllIndex, const bool nesting,
+            const vector<ConditionState>& initialConditionCache, const int startIndex,
+            const int stopIndex, const int stopAllIndex, const bool nesting,
             const sp<ConditionWizard>& wizard, const uint64_t protoHash,
             const FieldMatcher& internalDimensions, const int64_t timeBaseNs,
             const int64_t startTimeNs,
@@ -112,16 +112,32 @@
     void flushCurrentBucketLocked(const int64_t& eventTimeNs,
                                   const int64_t& nextBucketStartTimeNs) override;
 
+    bool onConfigUpdatedLocked(
+            const StatsdConfig& config, const int configIndex, const int metricIndex,
+            const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+            const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+            const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+            const sp<EventMatcherWizard>& matcherWizard,
+            const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+            const std::unordered_map<int64_t, int>& conditionTrackerMap,
+            const sp<ConditionWizard>& wizard,
+            const std::unordered_map<int64_t, int>& metricToActivationMap,
+            std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+            std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+            std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+            std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+            std::vector<int>& metricsWithActivation) override;
+
     const DurationMetric_AggregationType mAggregationType;
 
     // Index of the SimpleAtomMatcher which defines the start.
-    const size_t mStartIndex;
+    int mStartIndex;
 
     // Index of the SimpleAtomMatcher which defines the stop.
-    const size_t mStopIndex;
+    int mStopIndex;
 
     // Index of the SimpleAtomMatcher which defines the stop all for all dimensions.
-    const size_t mStopAllIndex;
+    int mStopAllIndex;
 
     // nest counting -- for the same key, stops must match the number of starts to make real stop
     const bool mNested;
@@ -167,6 +183,8 @@
                 TestSumDurationWithSplitInFollowingBucket);
     FRIEND_TEST(DurationMetricProducerTest_PartialBucket, TestMaxDuration);
     FRIEND_TEST(DurationMetricProducerTest_PartialBucket, TestMaxDurationWithSplitInNextBucket);
+
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateDurationMetrics);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 9dda248..2a37b58 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -17,9 +17,11 @@
 #define DEBUG false  // STOPSHIP if true
 #include "Log.h"
 
-#include "../guardrail/StatsdStats.h"
 #include "GaugeMetricProducer.h"
-#include "../stats_log_util.h"
+
+#include "guardrail/StatsdStats.h"
+#include "metrics/parsing_utils/metrics_manager_util.h"
+#include "stats_log_util.h"
 
 using android::util::FIELD_COUNT_REPEATED;
 using android::util::FIELD_TYPE_BOOL;
@@ -154,6 +156,58 @@
     }
 }
 
+bool GaugeMetricProducer::onConfigUpdatedLocked(
+        const StatsdConfig& config, const int configIndex, const int metricIndex,
+        const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+        const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+        const sp<EventMatcherWizard>& matcherWizard,
+        const vector<sp<ConditionTracker>>& allConditionTrackers,
+        const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard,
+        const unordered_map<int64_t, int>& metricToActivationMap,
+        unordered_map<int, vector<int>>& trackerToMetricMap,
+        unordered_map<int, vector<int>>& conditionToMetricMap,
+        unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+        unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+        vector<int>& metricsWithActivation) {
+    if (!MetricProducer::onConfigUpdatedLocked(
+                config, configIndex, metricIndex, allAtomMatchingTrackers,
+                oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+                allConditionTrackers, conditionTrackerMap, wizard, metricToActivationMap,
+                trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap,
+                deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
+        return false;
+    }
+
+    const GaugeMetric& metric = config.gauge_metric(configIndex);
+    // Update appropriate indices: mWhatMatcherIndex, mConditionIndex and MetricsManager maps.
+    if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, /*enforceOneAtom=*/false,
+                                              allAtomMatchingTrackers, newAtomMatchingTrackerMap,
+                                              trackerToMetricMap, mWhatMatcherIndex)) {
+        return false;
+    }
+
+    // Need to update maps since the index changed, but mTriggerAtomId will not change.
+    int triggerTrackerIndex;
+    if (metric.has_trigger_event() &&
+        !handleMetricWithAtomMatchingTrackers(metric.trigger_event(), metricIndex,
+                                              /*enforceOneAtom=*/true, allAtomMatchingTrackers,
+                                              newAtomMatchingTrackerMap, trackerToMetricMap,
+                                              triggerTrackerIndex)) {
+        return false;
+    }
+
+    if (metric.has_condition() &&
+        !handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+                                    metric.links(), allConditionTrackers, mConditionTrackerIndex,
+                                    conditionToMetricMap)) {
+        return false;
+    }
+    sp<EventMatcherWizard> tmpEventWizard = mEventMatcherWizard;
+    mEventMatcherWizard = matcherWizard;
+    return true;
+}
+
 void GaugeMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const {
     if (mCurrentSlicedBucket == nullptr ||
         mCurrentSlicedBucket->size() == 0) {
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index e933d4b..9bdaac9 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -53,8 +53,8 @@
 // This gauge metric producer first register the puller to automatically pull the gauge at the
 // beginning of each bucket. If the condition is met, insert it to the bucket info. Otherwise
 // proactively pull the gauge when the condition is changed to be true. Therefore, the gauge metric
-// producer always reports the guage at the earliest time of the bucket when the condition is met.
-class GaugeMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
+// producer always reports the gauge at the earliest time of the bucket when the condition is met.
+class GaugeMetricProducer : public MetricProducer, public virtual PullDataReceiver {
 public:
     GaugeMetricProducer(
             const ConfigKey& key, const GaugeMetric& gaugeMetric, const int conditionIndex,
@@ -142,7 +142,23 @@
 
     void pullAndMatchEventsLocked(const int64_t timestampNs);
 
-    const int mWhatMatcherIndex;
+    bool onConfigUpdatedLocked(
+            const StatsdConfig& config, const int configIndex, const int metricIndex,
+            const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+            const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+            const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+            const sp<EventMatcherWizard>& matcherWizard,
+            const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+            const std::unordered_map<int64_t, int>& conditionTrackerMap,
+            const sp<ConditionWizard>& wizard,
+            const std::unordered_map<int64_t, int>& metricToActivationMap,
+            std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+            std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+            std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+            std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+            std::vector<int>& metricsWithActivation) override;
+
+    int mWhatMatcherIndex;
 
     sp<EventMatcherWizard> mEventMatcherWizard;
 
@@ -209,6 +225,8 @@
 
     FRIEND_TEST(GaugeMetricProducerTest_PartialBucket, TestPushedEvents);
     FRIEND_TEST(GaugeMetricProducerTest_PartialBucket, TestPulled);
+
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateGaugeMetrics);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 320b285..92c1a6e 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -566,7 +566,11 @@
     FRIEND_TEST(MetricsManagerTest, TestInitialConditions);
 
     FRIEND_TEST(ConfigUpdateTest, TestUpdateMetricActivations);
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateCountMetrics);
     FRIEND_TEST(ConfigUpdateTest, TestUpdateEventMetrics);
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateGaugeMetrics);
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateDurationMetrics);
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateMetricsMultipleTypes);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
index 8d59d13..657b2e4 100644
--- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -89,6 +89,12 @@
 
     virtual ~DurationTracker(){};
 
+    void onConfigUpdated(const sp<ConditionWizard>& wizard, const int conditionTrackerIndex) {
+        sp<ConditionWizard> tmpWizard = mWizard;
+        mWizard = wizard;
+        mConditionTrackerIndex = conditionTrackerIndex;
+    };
+
     virtual void noteStart(const HashableDimensionKey& key, bool condition, const int64_t eventTime,
                            const ConditionKey& conditionKey) = 0;
     virtual void noteStop(const HashableDimensionKey& key, const int64_t eventTime,
@@ -191,7 +197,7 @@
 
     sp<ConditionWizard> mWizard;
 
-    const int mConditionTrackerIndex;
+    int mConditionTrackerIndex;
 
     const int64_t mBucketSizeNs;
 
@@ -217,6 +223,8 @@
     FRIEND_TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp);
     FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm);
     FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm);
+
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateDurationMetrics);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
index 12d3b94..b6e5d88 100644
--- a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
+++ b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
@@ -516,6 +516,21 @@
             return false;
         }
     }
+    for (int i = 0; i < config.duration_metric_size(); i++, metricIndex++) {
+        const DurationMetric& metric = config.duration_metric(i);
+        set<int64_t> conditionDependencies({metric.what()});
+        if (metric.has_condition()) {
+            conditionDependencies.insert(metric.condition());
+        }
+        if (!determineMetricUpdateStatus(
+                    config, metric, metric.id(), METRIC_TYPE_DURATION, /*matcherDependencies=*/{},
+                    conditionDependencies, metric.slice_by_state(), metric.links(),
+                    oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+                    replacedMatchers, replacedConditions, replacedStates,
+                    metricsToUpdate[metricIndex])) {
+            return false;
+        }
+    }
     for (int i = 0; i < config.event_metric_size(); i++, metricIndex++) {
         const EventMetric& metric = config.event_metric(i);
         set<int64_t> conditionDependencies;
@@ -531,10 +546,82 @@
             return false;
         }
     }
-    // TODO: determine update status for count, gauge, value, duration metrics.
+    for (int i = 0; i < config.value_metric_size(); i++, metricIndex++) {
+        const ValueMetric& metric = config.value_metric(i);
+        set<int64_t> conditionDependencies;
+        if (metric.has_condition()) {
+            conditionDependencies.insert(metric.condition());
+        }
+        if (!determineMetricUpdateStatus(
+                    config, metric, metric.id(), METRIC_TYPE_VALUE, {metric.what()},
+                    conditionDependencies, metric.slice_by_state(), metric.links(),
+                    oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+                    replacedMatchers, replacedConditions, replacedStates,
+                    metricsToUpdate[metricIndex])) {
+            return false;
+        }
+    }
+    for (int i = 0; i < config.gauge_metric_size(); i++, metricIndex++) {
+        const GaugeMetric& metric = config.gauge_metric(i);
+        set<int64_t> conditionDependencies;
+        if (metric.has_condition()) {
+            conditionDependencies.insert(metric.condition());
+        }
+        set<int64_t> matcherDependencies({metric.what()});
+        if (metric.has_trigger_event()) {
+            matcherDependencies.insert(metric.trigger_event());
+        }
+        if (!determineMetricUpdateStatus(
+                    config, metric, metric.id(), METRIC_TYPE_GAUGE, matcherDependencies,
+                    conditionDependencies, ::google::protobuf::RepeatedField<int64_t>(),
+                    metric.links(), oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+                    replacedMatchers, replacedConditions, replacedStates,
+                    metricsToUpdate[metricIndex])) {
+            return false;
+        }
+    }
+    // TODO: determine update status for value metrics.
     return true;
 }
 
+// Called when a metric is preserved during a config update. Finds the metric in oldMetricProducers
+// and calls onConfigUpdated to update all indices.
+optional<sp<MetricProducer>> updateMetric(
+        const StatsdConfig& config, const int configIndex, const int metricIndex,
+        const int64_t metricId, const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+        const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+        const sp<EventMatcherWizard>& matcherWizard,
+        const vector<sp<ConditionTracker>>& allConditionTrackers,
+        const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard,
+        const unordered_map<int64_t, int>& oldMetricProducerMap,
+        const vector<sp<MetricProducer>>& oldMetricProducers,
+        const unordered_map<int64_t, int>& metricToActivationMap,
+        unordered_map<int, vector<int>>& trackerToMetricMap,
+        unordered_map<int, vector<int>>& conditionToMetricMap,
+        unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+        unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+        vector<int>& metricsWithActivation) {
+    const auto& oldMetricProducerIt = oldMetricProducerMap.find(metricId);
+    if (oldMetricProducerIt == oldMetricProducerMap.end()) {
+        ALOGE("Could not find Metric %lld in the previous config, but expected it "
+              "to be there",
+              (long long)metricId);
+        return nullopt;
+    }
+    const int oldIndex = oldMetricProducerIt->second;
+    sp<MetricProducer> producer = oldMetricProducers[oldIndex];
+    if (!producer->onConfigUpdated(config, configIndex, metricIndex, allAtomMatchingTrackers,
+                                   oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap,
+                                   matcherWizard, allConditionTrackers, conditionTrackerMap, wizard,
+                                   metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
+                                   activationAtomTrackerToMetricMap,
+                                   deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
+        return nullopt;
+    }
+    return {producer};
+}
+
 bool updateMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
                    const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
                    const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
@@ -587,41 +674,30 @@
 
     // Now, perform the update. Must iterate the metric types in the same order
     int metricIndex = 0;
-    for (int i = 0; i < config.event_metric_size(); i++, metricIndex++) {
-        newMetricProducerMap[config.event_metric(i).id()] = metricIndex;
-        const EventMetric& metric = config.event_metric(i);
+    for (int i = 0; i < config.count_metric_size(); i++, metricIndex++) {
+        const CountMetric& metric = config.count_metric(i);
+        newMetricProducerMap[metric.id()] = metricIndex;
+        optional<sp<MetricProducer>> producer;
         switch (metricsToUpdate[metricIndex]) {
             case UPDATE_PRESERVE: {
-                const auto& oldMetricProducerIt = oldMetricProducerMap.find(metric.id());
-                if (oldMetricProducerIt == oldMetricProducerMap.end()) {
-                    ALOGE("Could not find Metric %lld in the previous config, but expected it "
-                          "to be there",
-                          (long long)metric.id());
-                    return false;
-                }
-                const int oldIndex = oldMetricProducerIt->second;
-                sp<MetricProducer> producer = oldMetricProducers[oldIndex];
-                producer->onConfigUpdated(
-                        config, i, metricIndex, allAtomMatchingTrackers, oldAtomMatchingTrackerMap,
-                        newAtomMatchingTrackerMap, matcherWizard, allConditionTrackers,
-                        conditionTrackerMap, wizard, metricToActivationMap, trackerToMetricMap,
+                producer = updateMetric(
+                        config, i, metricIndex, metric.id(), allAtomMatchingTrackers,
+                        oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+                        allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap,
+                        oldMetricProducers, metricToActivationMap, trackerToMetricMap,
                         conditionToMetricMap, activationAtomTrackerToMetricMap,
                         deactivationAtomTrackerToMetricMap, metricsWithActivation);
-                newMetricProducers.push_back(producer);
                 break;
             }
             case UPDATE_REPLACE:
             case UPDATE_NEW: {
-                sp<MetricProducer> producer = createEventMetricProducerAndUpdateMetadata(
-                        key, config, timeBaseNs, metric, metricIndex, allAtomMatchingTrackers,
-                        newAtomMatchingTrackerMap, allConditionTrackers, conditionTrackerMap,
-                        initialConditionCache, wizard, metricToActivationMap, trackerToMetricMap,
+                producer = createCountMetricProducerAndUpdateMetadata(
+                        key, config, timeBaseNs, currentTimeNs, metric, metricIndex,
+                        allAtomMatchingTrackers, newAtomMatchingTrackerMap, allConditionTrackers,
+                        conditionTrackerMap, initialConditionCache, wizard, stateAtomIdMap,
+                        allStateGroupMaps, metricToActivationMap, trackerToMetricMap,
                         conditionToMetricMap, activationAtomTrackerToMetricMap,
                         deactivationAtomTrackerToMetricMap, metricsWithActivation);
-                if (producer == nullptr) {
-                    return false;
-                }
-                newMetricProducers.push_back(producer);
                 break;
             }
             default: {
@@ -630,8 +706,122 @@
                 return false;
             }
         }
+        if (!producer) {
+            return false;
+        }
+        newMetricProducers.push_back(producer.value());
     }
-    // TODO: perform update for count, gauge, value, duration metric.
+    for (int i = 0; i < config.duration_metric_size(); i++, metricIndex++) {
+        const DurationMetric& metric = config.duration_metric(i);
+        newMetricProducerMap[metric.id()] = metricIndex;
+        optional<sp<MetricProducer>> producer;
+        switch (metricsToUpdate[metricIndex]) {
+            case UPDATE_PRESERVE: {
+                producer = updateMetric(
+                        config, i, metricIndex, metric.id(), allAtomMatchingTrackers,
+                        oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+                        allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap,
+                        oldMetricProducers, metricToActivationMap, trackerToMetricMap,
+                        conditionToMetricMap, activationAtomTrackerToMetricMap,
+                        deactivationAtomTrackerToMetricMap, metricsWithActivation);
+                break;
+            }
+            case UPDATE_REPLACE:
+            case UPDATE_NEW: {
+                producer = createDurationMetricProducerAndUpdateMetadata(
+                        key, config, timeBaseNs, currentTimeNs, metric, metricIndex,
+                        allAtomMatchingTrackers, newAtomMatchingTrackerMap, allConditionTrackers,
+                        conditionTrackerMap, initialConditionCache, wizard, stateAtomIdMap,
+                        allStateGroupMaps, metricToActivationMap, trackerToMetricMap,
+                        conditionToMetricMap, activationAtomTrackerToMetricMap,
+                        deactivationAtomTrackerToMetricMap, metricsWithActivation);
+                break;
+            }
+            default: {
+                ALOGE("Metric \"%lld\" update state is unknown. This should never happen",
+                      (long long)metric.id());
+                return false;
+            }
+        }
+        if (!producer) {
+            return false;
+        }
+        newMetricProducers.push_back(producer.value());
+    }
+    for (int i = 0; i < config.event_metric_size(); i++, metricIndex++) {
+        newMetricProducerMap[config.event_metric(i).id()] = metricIndex;
+        const EventMetric& metric = config.event_metric(i);
+        optional<sp<MetricProducer>> producer;
+        switch (metricsToUpdate[metricIndex]) {
+            case UPDATE_PRESERVE: {
+                producer = updateMetric(
+                        config, i, metricIndex, metric.id(), allAtomMatchingTrackers,
+                        oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+                        allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap,
+                        oldMetricProducers, metricToActivationMap, trackerToMetricMap,
+                        conditionToMetricMap, activationAtomTrackerToMetricMap,
+                        deactivationAtomTrackerToMetricMap, metricsWithActivation);
+                break;
+            }
+            case UPDATE_REPLACE:
+            case UPDATE_NEW: {
+                producer = createEventMetricProducerAndUpdateMetadata(
+                        key, config, timeBaseNs, metric, metricIndex, allAtomMatchingTrackers,
+                        newAtomMatchingTrackerMap, allConditionTrackers, conditionTrackerMap,
+                        initialConditionCache, wizard, metricToActivationMap, trackerToMetricMap,
+                        conditionToMetricMap, activationAtomTrackerToMetricMap,
+                        deactivationAtomTrackerToMetricMap, metricsWithActivation);
+                break;
+            }
+            default: {
+                ALOGE("Metric \"%lld\" update state is unknown. This should never happen",
+                      (long long)metric.id());
+                return false;
+            }
+        }
+        if (!producer) {
+            return false;
+        }
+        newMetricProducers.push_back(producer.value());
+    }
+    for (int i = 0; i < config.gauge_metric_size(); i++, metricIndex++) {
+        const GaugeMetric& metric = config.gauge_metric(i);
+        newMetricProducerMap[metric.id()] = metricIndex;
+        optional<sp<MetricProducer>> producer;
+        switch (metricsToUpdate[metricIndex]) {
+            case UPDATE_PRESERVE: {
+                producer = updateMetric(
+                        config, i, metricIndex, metric.id(), allAtomMatchingTrackers,
+                        oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+                        allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap,
+                        oldMetricProducers, metricToActivationMap, trackerToMetricMap,
+                        conditionToMetricMap, activationAtomTrackerToMetricMap,
+                        deactivationAtomTrackerToMetricMap, metricsWithActivation);
+                break;
+            }
+            case UPDATE_REPLACE:
+            case UPDATE_NEW: {
+                producer = createGaugeMetricProducerAndUpdateMetadata(
+                        key, config, timeBaseNs, currentTimeNs, pullerManager, metric, metricIndex,
+                        allAtomMatchingTrackers, newAtomMatchingTrackerMap, allConditionTrackers,
+                        conditionTrackerMap, initialConditionCache, wizard, matcherWizard,
+                        metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
+                        activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+                        metricsWithActivation);
+                break;
+            }
+            default: {
+                ALOGE("Metric \"%lld\" update state is unknown. This should never happen",
+                      (long long)metric.id());
+                return false;
+            }
+        }
+        if (!producer) {
+            return false;
+        }
+        newMetricProducers.push_back(producer.value());
+    }
+    // TODO: perform update for value metric.
 
     const set<int> atomsAllowedFromAnyUid(config.whitelisted_atom_ids().begin(),
                                           config.whitelisted_atom_ids().end());
diff --git a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
index 51df7df..b7dc2c7 100644
--- a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
@@ -348,7 +348,201 @@
     return true;
 }
 
-sp<MetricProducer> createEventMetricProducerAndUpdateMetadata(
+optional<sp<MetricProducer>> createCountMetricProducerAndUpdateMetadata(
+        const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+        const int64_t currentTimeNs, const CountMetric& metric, const int metricIndex,
+        const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        const unordered_map<int64_t, int>& atomMatchingTrackerMap,
+        vector<sp<ConditionTracker>>& allConditionTrackers,
+        const unordered_map<int64_t, int>& conditionTrackerMap,
+        const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+        const unordered_map<int64_t, int>& stateAtomIdMap,
+        const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
+        const unordered_map<int64_t, int>& metricToActivationMap,
+        unordered_map<int, vector<int>>& trackerToMetricMap,
+        unordered_map<int, vector<int>>& conditionToMetricMap,
+        unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+        unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+        vector<int>& metricsWithActivation) {
+    if (!metric.has_id() || !metric.has_what()) {
+        ALOGE("cannot find metric id or \"what\" in CountMetric \"%lld\"", (long long)metric.id());
+        return nullopt;
+    }
+    int trackerIndex;
+    if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex,
+                                              metric.has_dimensions_in_what(),
+                                              allAtomMatchingTrackers, atomMatchingTrackerMap,
+                                              trackerToMetricMap, trackerIndex)) {
+        return nullopt;
+    }
+
+    int conditionIndex = -1;
+    if (metric.has_condition()) {
+        if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+                                        metric.links(), allConditionTrackers, conditionIndex,
+                                        conditionToMetricMap)) {
+            return nullopt;
+        }
+    } else {
+        if (metric.links_size() > 0) {
+            ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
+            return nullopt;
+        }
+    }
+
+    std::vector<int> slicedStateAtoms;
+    unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
+    if (metric.slice_by_state_size() > 0) {
+        if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
+                                    allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
+            return nullopt;
+        }
+    } else {
+        if (metric.state_link_size() > 0) {
+            ALOGW("CountMetric has a MetricStateLink but doesn't have a slice_by_state");
+            return nullopt;
+        }
+    }
+
+    unordered_map<int, shared_ptr<Activation>> eventActivationMap;
+    unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+    if (!handleMetricActivation(config, metric.id(), metricIndex, metricToActivationMap,
+                                atomMatchingTrackerMap, activationAtomTrackerToMetricMap,
+                                deactivationAtomTrackerToMetricMap, metricsWithActivation,
+                                eventActivationMap, eventDeactivationMap)) {
+        return nullopt;
+    }
+
+    uint64_t metricHash;
+    if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
+        return nullopt;
+    }
+
+    return {new CountMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
+                                    metricHash, timeBaseNs, currentTimeNs, eventActivationMap,
+                                    eventDeactivationMap, slicedStateAtoms, stateGroupMap)};
+}
+
+optional<sp<MetricProducer>> createDurationMetricProducerAndUpdateMetadata(
+        const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+        const int64_t currentTimeNs, const DurationMetric& metric, const int metricIndex,
+        const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        const unordered_map<int64_t, int>& atomMatchingTrackerMap,
+        vector<sp<ConditionTracker>>& allConditionTrackers,
+        const unordered_map<int64_t, int>& conditionTrackerMap,
+        const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+        const unordered_map<int64_t, int>& stateAtomIdMap,
+        const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
+        const unordered_map<int64_t, int>& metricToActivationMap,
+        unordered_map<int, vector<int>>& trackerToMetricMap,
+        unordered_map<int, vector<int>>& conditionToMetricMap,
+        unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+        unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+        vector<int>& metricsWithActivation) {
+    if (!metric.has_id() || !metric.has_what()) {
+        ALOGE("cannot find metric id or \"what\" in DurationMetric \"%lld\"",
+              (long long)metric.id());
+        return nullopt;
+    }
+    const auto& what_it = conditionTrackerMap.find(metric.what());
+    if (what_it == conditionTrackerMap.end()) {
+        ALOGE("DurationMetric's \"what\" is not present in the condition trackers");
+        return nullopt;
+    }
+
+    const Predicate& durationWhat = config.predicate(what_it->second);
+    if (durationWhat.contents_case() != Predicate::ContentsCase::kSimplePredicate) {
+        ALOGE("DurationMetric's \"what\" must be a simple condition");
+        return nullopt;
+    }
+
+    const SimplePredicate& simplePredicate = durationWhat.simple_predicate();
+    bool nesting = simplePredicate.count_nesting();
+
+    int startIndex = -1, stopIndex = -1, stopAllIndex = -1;
+    if (!simplePredicate.has_start() ||
+        !handleMetricWithAtomMatchingTrackers(
+                simplePredicate.start(), metricIndex, metric.has_dimensions_in_what(),
+                allAtomMatchingTrackers, atomMatchingTrackerMap, trackerToMetricMap, startIndex)) {
+        ALOGE("Duration metrics must specify a valid start event matcher");
+        return nullopt;
+    }
+
+    if (simplePredicate.has_stop() &&
+        !handleMetricWithAtomMatchingTrackers(
+                simplePredicate.stop(), metricIndex, metric.has_dimensions_in_what(),
+                allAtomMatchingTrackers, atomMatchingTrackerMap, trackerToMetricMap, stopIndex)) {
+        return nullopt;
+    }
+
+    if (simplePredicate.has_stop_all() &&
+        !handleMetricWithAtomMatchingTrackers(simplePredicate.stop_all(), metricIndex,
+                                              metric.has_dimensions_in_what(),
+                                              allAtomMatchingTrackers, atomMatchingTrackerMap,
+                                              trackerToMetricMap, stopAllIndex)) {
+        return nullopt;
+    }
+
+    FieldMatcher internalDimensions = simplePredicate.dimensions();
+
+    int conditionIndex = -1;
+    if (metric.has_condition()) {
+        if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+                                        metric.links(), allConditionTrackers, conditionIndex,
+                                        conditionToMetricMap)) {
+            return nullopt;
+        }
+    } else if (metric.links_size() > 0) {
+        ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
+        return nullopt;
+    }
+
+    std::vector<int> slicedStateAtoms;
+    unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
+    if (metric.slice_by_state_size() > 0) {
+        if (metric.aggregation_type() == DurationMetric::MAX_SPARSE) {
+            ALOGE("DurationMetric with aggregation type MAX_SPARSE cannot be sliced by state");
+            return nullopt;
+        }
+        if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
+                                    allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
+            return nullopt;
+        }
+    } else if (metric.state_link_size() > 0) {
+        ALOGW("DurationMetric has a MetricStateLink but doesn't have a sliced state");
+        return nullopt;
+    }
+
+    // Check that all metric state links are a subset of dimensions_in_what fields.
+    std::vector<Matcher> dimensionsInWhat;
+    translateFieldMatcher(metric.dimensions_in_what(), &dimensionsInWhat);
+    for (const auto& stateLink : metric.state_link()) {
+        if (!handleMetricWithStateLink(stateLink.fields_in_what(), dimensionsInWhat)) {
+            return nullopt;
+        }
+    }
+
+    unordered_map<int, shared_ptr<Activation>> eventActivationMap;
+    unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+    if (!handleMetricActivation(config, metric.id(), metricIndex, metricToActivationMap,
+                                atomMatchingTrackerMap, activationAtomTrackerToMetricMap,
+                                deactivationAtomTrackerToMetricMap, metricsWithActivation,
+                                eventActivationMap, eventDeactivationMap)) {
+        return nullopt;
+    }
+
+    uint64_t metricHash;
+    if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
+        return nullopt;
+    }
+
+    return {new DurationMetricProducer(
+            key, metric, conditionIndex, initialConditionCache, startIndex, stopIndex, stopAllIndex,
+            nesting, wizard, metricHash, internalDimensions, timeBaseNs, currentTimeNs,
+            eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap)};
+}
+
+optional<sp<MetricProducer>> createEventMetricProducerAndUpdateMetadata(
         const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
         const EventMetric& metric, const int metricIndex,
         const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
@@ -363,14 +557,14 @@
         unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
         vector<int>& metricsWithActivation) {
     if (!metric.has_id() || !metric.has_what()) {
-        ALOGW("cannot find the metric name or what in config");
-        return nullptr;
+        ALOGE("cannot find the metric name or what in config");
+        return nullopt;
     }
     int trackerIndex;
     if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, false,
                                               allAtomMatchingTrackers, atomMatchingTrackerMap,
                                               trackerToMetricMap, trackerIndex)) {
-        return nullptr;
+        return nullopt;
     }
 
     int conditionIndex = -1;
@@ -378,12 +572,12 @@
         if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
                                         metric.links(), allConditionTrackers, conditionIndex,
                                         conditionToMetricMap)) {
-            return nullptr;
+            return nullopt;
         }
     } else {
         if (metric.links_size() > 0) {
             ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
-            return nullptr;
+            return nullopt;
         }
     }
 
@@ -397,12 +591,125 @@
 
     uint64_t metricHash;
     if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
-        return nullptr;
+        return nullopt;
     }
 
-    return new EventMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
-                                   metricHash, timeBaseNs, eventActivationMap,
-                                   eventDeactivationMap);
+    return {new EventMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
+                                    metricHash, timeBaseNs, eventActivationMap,
+                                    eventDeactivationMap)};
+}
+
+optional<sp<MetricProducer>> createGaugeMetricProducerAndUpdateMetadata(
+        const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+        const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
+        const GaugeMetric& metric, const int metricIndex,
+        const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        const unordered_map<int64_t, int>& atomMatchingTrackerMap,
+        vector<sp<ConditionTracker>>& allConditionTrackers,
+        const unordered_map<int64_t, int>& conditionTrackerMap,
+        const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+        const sp<EventMatcherWizard>& matcherWizard,
+        const unordered_map<int64_t, int>& metricToActivationMap,
+        unordered_map<int, vector<int>>& trackerToMetricMap,
+        unordered_map<int, vector<int>>& conditionToMetricMap,
+        unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+        unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+        vector<int>& metricsWithActivation) {
+    if (!metric.has_id() || !metric.has_what()) {
+        ALOGE("cannot find metric id or \"what\" in GaugeMetric \"%lld\"", (long long)metric.id());
+        return nullopt;
+    }
+
+    if ((!metric.gauge_fields_filter().has_include_all() ||
+         (metric.gauge_fields_filter().include_all() == false)) &&
+        !hasLeafNode(metric.gauge_fields_filter().fields())) {
+        ALOGW("Incorrect field filter setting in GaugeMetric %lld", (long long)metric.id());
+        return nullopt;
+    }
+    if ((metric.gauge_fields_filter().has_include_all() &&
+         metric.gauge_fields_filter().include_all() == true) &&
+        hasLeafNode(metric.gauge_fields_filter().fields())) {
+        ALOGW("Incorrect field filter setting in GaugeMetric %lld", (long long)metric.id());
+        return nullopt;
+    }
+
+    int trackerIndex;
+    if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex,
+                                              metric.has_dimensions_in_what(),
+                                              allAtomMatchingTrackers, atomMatchingTrackerMap,
+                                              trackerToMetricMap, trackerIndex)) {
+        return nullopt;
+    }
+
+    sp<AtomMatchingTracker> atomMatcher = allAtomMatchingTrackers.at(trackerIndex);
+    // For GaugeMetric atom, it should be simple matcher with one tagId.
+    if (atomMatcher->getAtomIds().size() != 1) {
+        return nullopt;
+    }
+    int atomTagId = *(atomMatcher->getAtomIds().begin());
+    int pullTagId = pullerManager->PullerForMatcherExists(atomTagId) ? atomTagId : -1;
+
+    int triggerTrackerIndex;
+    int triggerAtomId = -1;
+    if (metric.has_trigger_event()) {
+        if (pullTagId == -1) {
+            ALOGW("Pull atom not specified for trigger");
+            return nullopt;
+        }
+        // trigger_event should be used with FIRST_N_SAMPLES
+        if (metric.sampling_type() != GaugeMetric::FIRST_N_SAMPLES) {
+            ALOGW("Gauge Metric with trigger event must have sampling type FIRST_N_SAMPLES");
+            return nullopt;
+        }
+        if (!handleMetricWithAtomMatchingTrackers(metric.trigger_event(), metricIndex,
+                                                  /*enforceOneAtom=*/true, allAtomMatchingTrackers,
+                                                  atomMatchingTrackerMap, trackerToMetricMap,
+                                                  triggerTrackerIndex)) {
+            return nullopt;
+        }
+        sp<AtomMatchingTracker> triggerAtomMatcher =
+                allAtomMatchingTrackers.at(triggerTrackerIndex);
+        triggerAtomId = *(triggerAtomMatcher->getAtomIds().begin());
+    }
+
+    if (!metric.has_trigger_event() && pullTagId != -1 &&
+        metric.sampling_type() == GaugeMetric::FIRST_N_SAMPLES) {
+        ALOGW("FIRST_N_SAMPLES is only for pushed event or pull_on_trigger");
+        return nullopt;
+    }
+
+    int conditionIndex = -1;
+    if (metric.has_condition()) {
+        if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+                                        metric.links(), allConditionTrackers, conditionIndex,
+                                        conditionToMetricMap)) {
+            return nullopt;
+        }
+    } else {
+        if (metric.links_size() > 0) {
+            ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
+            return nullopt;
+        }
+    }
+
+    unordered_map<int, shared_ptr<Activation>> eventActivationMap;
+    unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+    if (!handleMetricActivation(config, metric.id(), metricIndex, metricToActivationMap,
+                                atomMatchingTrackerMap, activationAtomTrackerToMetricMap,
+                                deactivationAtomTrackerToMetricMap, metricsWithActivation,
+                                eventActivationMap, eventDeactivationMap)) {
+        return nullopt;
+    }
+
+    uint64_t metricHash;
+    if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
+        return nullopt;
+    }
+
+    return {new GaugeMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
+                                    metricHash, trackerIndex, matcherWizard, pullTagId,
+                                    triggerAtomId, atomTagId, timeBaseNs, currentTimeNs,
+                                    pullerManager, eventActivationMap, eventDeactivationMap)};
 }
 
 bool initAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap,
@@ -550,68 +857,20 @@
     // Build MetricProducers for each metric defined in config.
     // build CountMetricProducer
     for (int i = 0; i < config.count_metric_size(); i++) {
-        const CountMetric& metric = config.count_metric(i);
-        if (!metric.has_what()) {
-            ALOGW("cannot find \"what\" in CountMetric \"%lld\"", (long long)metric.id());
-            return false;
-        }
-
         int metricIndex = allMetricProducers.size();
+        const CountMetric& metric = config.count_metric(i);
         metricMap.insert({metric.id(), metricIndex});
-        int trackerIndex;
-        if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex,
-                                                  metric.has_dimensions_in_what(),
-                                                  allAtomMatchingTrackers, atomMatchingTrackerMap,
-                                                  trackerToMetricMap, trackerIndex)) {
-            return false;
-        }
-
-        int conditionIndex = -1;
-        if (metric.has_condition()) {
-            if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
-                                            metric.links(), allConditionTrackers, conditionIndex,
-                                            conditionToMetricMap)) {
-                return false;
-            }
-        } else {
-            if (metric.links_size() > 0) {
-                ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
-                return false;
-            }
-        }
-
-        std::vector<int> slicedStateAtoms;
-        unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
-        if (metric.slice_by_state_size() > 0) {
-            if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
-                                        allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
-                return false;
-            }
-        } else {
-            if (metric.state_link_size() > 0) {
-                ALOGW("CountMetric has a MetricStateLink but doesn't have a slice_by_state");
-                return false;
-            }
-        }
-
-        unordered_map<int, shared_ptr<Activation>> eventActivationMap;
-        unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
-        bool success = handleMetricActivation(
-                config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap,
+        optional<sp<MetricProducer>> producer = createCountMetricProducerAndUpdateMetadata(
+                key, config, timeBaseTimeNs, currentTimeNs, metric, metricIndex,
+                allAtomMatchingTrackers, atomMatchingTrackerMap, allConditionTrackers,
+                conditionTrackerMap, initialConditionCache, wizard, stateAtomIdMap,
+                allStateGroupMaps, metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
                 activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
-                metricsWithActivation, eventActivationMap, eventDeactivationMap);
-        if (!success) return false;
-
-        uint64_t metricHash;
-        if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
+                metricsWithActivation);
+        if (!producer) {
             return false;
         }
-
-        sp<MetricProducer> countProducer = new CountMetricProducer(
-                key, metric, conditionIndex, initialConditionCache, wizard, metricHash,
-                timeBaseTimeNs, currentTimeNs, eventActivationMap, eventDeactivationMap,
-                slicedStateAtoms, stateGroupMap);
-        allMetricProducers.push_back(countProducer);
+        allMetricProducers.push_back(producer.value());
     }
 
     // build DurationMetricProducer
@@ -620,114 +879,17 @@
         const DurationMetric& metric = config.duration_metric(i);
         metricMap.insert({metric.id(), metricIndex});
 
-        auto what_it = conditionTrackerMap.find(metric.what());
-        if (what_it == conditionTrackerMap.end()) {
-            ALOGE("DurationMetric's \"what\" is invalid");
-            return false;
-        }
-
-        const Predicate& durationWhat = config.predicate(what_it->second);
-
-        if (durationWhat.contents_case() != Predicate::ContentsCase::kSimplePredicate) {
-            ALOGE("DurationMetric's \"what\" must be a simple condition");
-            return false;
-        }
-
-        const auto& simplePredicate = durationWhat.simple_predicate();
-
-        bool nesting = simplePredicate.count_nesting();
-
-        int trackerIndices[3] = {-1, -1, -1};
-        if (!simplePredicate.has_start() ||
-            !handleMetricWithAtomMatchingTrackers(simplePredicate.start(), metricIndex,
-                                                  metric.has_dimensions_in_what(),
-                                                  allAtomMatchingTrackers, atomMatchingTrackerMap,
-                                                  trackerToMetricMap, trackerIndices[0])) {
-            ALOGE("Duration metrics must specify a valid the start event matcher");
-            return false;
-        }
-
-        if (simplePredicate.has_stop() &&
-            !handleMetricWithAtomMatchingTrackers(simplePredicate.stop(), metricIndex,
-                                                  metric.has_dimensions_in_what(),
-                                                  allAtomMatchingTrackers, atomMatchingTrackerMap,
-                                                  trackerToMetricMap, trackerIndices[1])) {
-            return false;
-        }
-
-        if (simplePredicate.has_stop_all() &&
-            !handleMetricWithAtomMatchingTrackers(simplePredicate.stop_all(), metricIndex,
-                                                  metric.has_dimensions_in_what(),
-                                                  allAtomMatchingTrackers, atomMatchingTrackerMap,
-                                                  trackerToMetricMap, trackerIndices[2])) {
-            return false;
-        }
-
-        FieldMatcher internalDimensions = simplePredicate.dimensions();
-
-        int conditionIndex = -1;
-
-        if (metric.has_condition()) {
-            bool good = handleMetricWithConditions(
-                    metric.condition(), metricIndex, conditionTrackerMap, metric.links(),
-                    allConditionTrackers, conditionIndex, conditionToMetricMap);
-            if (!good) {
-                return false;
-            }
-        } else {
-            if (metric.links_size() > 0) {
-                ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
-                return false;
-            }
-        }
-
-        std::vector<int> slicedStateAtoms;
-        unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
-        if (metric.slice_by_state_size() > 0) {
-            if (metric.aggregation_type() == DurationMetric::MAX_SPARSE) {
-                ALOGE("DurationMetric with aggregation type MAX_SPARSE cannot be sliced by state");
-                return false;
-            }
-            if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
-                                        allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
-                return false;
-            }
-        } else {
-            if (metric.state_link_size() > 0) {
-                ALOGW("DurationMetric has a MetricStateLink but doesn't have a sliced state");
-                return false;
-            }
-        }
-
-        // Check that all metric state links are a subset of dimensions_in_what fields.
-        std::vector<Matcher> dimensionsInWhat;
-        translateFieldMatcher(metric.dimensions_in_what(), &dimensionsInWhat);
-        for (const auto& stateLink : metric.state_link()) {
-            if (!handleMetricWithStateLink(stateLink.fields_in_what(), dimensionsInWhat)) {
-                return false;
-            }
-        }
-
-        unordered_map<int, shared_ptr<Activation>> eventActivationMap;
-        unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
-        bool success = handleMetricActivation(
-                config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap,
+        optional<sp<MetricProducer>> producer = createDurationMetricProducerAndUpdateMetadata(
+                key, config, timeBaseTimeNs, currentTimeNs, metric, metricIndex,
+                allAtomMatchingTrackers, atomMatchingTrackerMap, allConditionTrackers,
+                conditionTrackerMap, initialConditionCache, wizard, stateAtomIdMap,
+                allStateGroupMaps, metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
                 activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
-                metricsWithActivation, eventActivationMap, eventDeactivationMap);
-        if (!success) return false;
-
-        uint64_t metricHash;
-        if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
+                metricsWithActivation);
+        if (!producer) {
             return false;
         }
-
-        sp<MetricProducer> durationMetric = new DurationMetricProducer(
-                key, metric, conditionIndex, initialConditionCache, trackerIndices[0],
-                trackerIndices[1], trackerIndices[2], nesting, wizard, metricHash,
-                internalDimensions, timeBaseTimeNs, currentTimeNs, eventActivationMap,
-                eventDeactivationMap, slicedStateAtoms, stateGroupMap);
-
-        allMetricProducers.push_back(durationMetric);
+        allMetricProducers.push_back(producer.value());
     }
 
     // build EventMetricProducer
@@ -735,16 +897,16 @@
         int metricIndex = allMetricProducers.size();
         const EventMetric& metric = config.event_metric(i);
         metricMap.insert({metric.id(), metricIndex});
-        sp<MetricProducer> producer = createEventMetricProducerAndUpdateMetadata(
+        optional<sp<MetricProducer>> producer = createEventMetricProducerAndUpdateMetadata(
                 key, config, timeBaseTimeNs, metric, metricIndex, allAtomMatchingTrackers,
                 atomMatchingTrackerMap, allConditionTrackers, conditionTrackerMap,
                 initialConditionCache, wizard, metricToActivationMap, trackerToMetricMap,
                 conditionToMetricMap, activationAtomTrackerToMetricMap,
                 deactivationAtomTrackerToMetricMap, metricsWithActivation);
-        if (producer == nullptr) {
+        if (!producer) {
             return false;
         }
-        allMetricProducers.push_back(producer);
+        allMetricProducers.push_back(producer.value());
     }
 
     // build ValueMetricProducer
@@ -844,104 +1006,20 @@
 
     // Gauge metrics.
     for (int i = 0; i < config.gauge_metric_size(); i++) {
-        const GaugeMetric& metric = config.gauge_metric(i);
-        if (!metric.has_what()) {
-            ALOGW("cannot find \"what\" in GaugeMetric \"%lld\"", (long long)metric.id());
-            return false;
-        }
-
-        if ((!metric.gauge_fields_filter().has_include_all() ||
-             (metric.gauge_fields_filter().include_all() == false)) &&
-            !hasLeafNode(metric.gauge_fields_filter().fields())) {
-            ALOGW("Incorrect field filter setting in GaugeMetric %lld", (long long)metric.id());
-            return false;
-        }
-        if ((metric.gauge_fields_filter().has_include_all() &&
-             metric.gauge_fields_filter().include_all() == true) &&
-            hasLeafNode(metric.gauge_fields_filter().fields())) {
-            ALOGW("Incorrect field filter setting in GaugeMetric %lld", (long long)metric.id());
-            return false;
-        }
-
         int metricIndex = allMetricProducers.size();
+        const GaugeMetric& metric = config.gauge_metric(i);
         metricMap.insert({metric.id(), metricIndex});
-        int trackerIndex;
-        if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex,
-                                                  metric.has_dimensions_in_what(),
-                                                  allAtomMatchingTrackers, atomMatchingTrackerMap,
-                                                  trackerToMetricMap, trackerIndex)) {
-            return false;
-        }
-
-        sp<AtomMatchingTracker> atomMatcher = allAtomMatchingTrackers.at(trackerIndex);
-        // For GaugeMetric atom, it should be simple matcher with one tagId.
-        if (atomMatcher->getAtomIds().size() != 1) {
-            return false;
-        }
-        int atomTagId = *(atomMatcher->getAtomIds().begin());
-        int pullTagId = pullerManager->PullerForMatcherExists(atomTagId) ? atomTagId : -1;
-
-        int triggerTrackerIndex;
-        int triggerAtomId = -1;
-        if (metric.has_trigger_event()) {
-            if (pullTagId == -1) {
-                ALOGW("Pull atom not specified for trigger");
-                return false;
-            }
-            // event_trigger should be used with FIRST_N_SAMPLES
-            if (metric.sampling_type() != GaugeMetric::FIRST_N_SAMPLES) {
-                return false;
-            }
-            if (!handleMetricWithAtomMatchingTrackers(
-                        metric.trigger_event(), metricIndex, /*enforceOneAtom=*/true,
-                        allAtomMatchingTrackers, atomMatchingTrackerMap, trackerToMetricMap,
-                        triggerTrackerIndex)) {
-                return false;
-            }
-            sp<AtomMatchingTracker> triggerAtomMatcher =
-                    allAtomMatchingTrackers.at(triggerTrackerIndex);
-            triggerAtomId = *(triggerAtomMatcher->getAtomIds().begin());
-        }
-
-        if (!metric.has_trigger_event() && pullTagId != -1 &&
-            metric.sampling_type() == GaugeMetric::FIRST_N_SAMPLES) {
-            ALOGW("FIRST_N_SAMPLES is only for pushed event or pull_on_trigger");
-            return false;
-        }
-
-        int conditionIndex = -1;
-        if (metric.has_condition()) {
-            bool good = handleMetricWithConditions(
-                    metric.condition(), metricIndex, conditionTrackerMap, metric.links(),
-                    allConditionTrackers, conditionIndex, conditionToMetricMap);
-            if (!good) {
-                return false;
-            }
-        } else {
-            if (metric.links_size() > 0) {
-                ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
-                return false;
-            }
-        }
-
-        unordered_map<int, shared_ptr<Activation>> eventActivationMap;
-        unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
-        bool success = handleMetricActivation(
-                config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap,
+        optional<sp<MetricProducer>> producer = createGaugeMetricProducerAndUpdateMetadata(
+                key, config, timeBaseTimeNs, currentTimeNs, pullerManager, metric, metricIndex,
+                allAtomMatchingTrackers, atomMatchingTrackerMap, allConditionTrackers,
+                conditionTrackerMap, initialConditionCache, wizard, matcherWizard,
+                metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
                 activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
-                metricsWithActivation, eventActivationMap, eventDeactivationMap);
-        if (!success) return false;
-
-        uint64_t metricHash;
-        if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
+                metricsWithActivation);
+        if (!producer) {
             return false;
         }
-
-        sp<MetricProducer> gaugeProducer = new GaugeMetricProducer(
-                key, metric, conditionIndex, initialConditionCache, wizard, metricHash,
-                trackerIndex, matcherWizard, pullTagId, triggerAtomId, atomTagId, timeBaseTimeNs,
-                currentTimeNs, pullerManager, eventActivationMap, eventDeactivationMap);
-        allMetricProducers.push_back(gaugeProducer);
+        allMetricProducers.push_back(producer.value());
     }
     for (int i = 0; i < config.no_report_metric_size(); ++i) {
         const auto no_report_metric = config.no_report_metric(i);
diff --git a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h
index fdfe5cf..6d1e6dd 100644
--- a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h
@@ -93,7 +93,47 @@
         std::unordered_map<int, shared_ptr<Activation>>& newEventActivationMap,
         std::unordered_map<int, std::vector<shared_ptr<Activation>>>& newEventDeactivationMap);
 
-sp<MetricProducer> createEventMetricProducerAndUpdateMetadata(
+// Creates a CountMetricProducer and updates the vectors/maps used by MetricsManager with
+// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error.
+optional<sp<MetricProducer>> createCountMetricProducerAndUpdateMetadata(
+        const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+        const int64_t currentTimeNs, const CountMetric& metric, const int metricIndex,
+        const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+        std::vector<sp<ConditionTracker>>& allConditionTrackers,
+        const std::unordered_map<int64_t, int>& conditionTrackerMap,
+        const std::vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+        const std::unordered_map<int64_t, int>& stateAtomIdMap,
+        const std::unordered_map<int64_t, std::unordered_map<int, int64_t>>& allStateGroupMaps,
+        const std::unordered_map<int64_t, int>& metricToActivationMap,
+        std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+        std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+        std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+        std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+        std::vector<int>& metricsWithActivation);
+
+// Creates a DurationMetricProducer and updates the vectors/maps used by MetricsManager with
+// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error.
+optional<sp<MetricProducer>> createDurationMetricProducerAndUpdateMetadata(
+        const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+        const int64_t currentTimeNs, const DurationMetric& metric, const int metricIndex,
+        const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+        std::vector<sp<ConditionTracker>>& allConditionTrackers,
+        const std::unordered_map<int64_t, int>& conditionTrackerMap,
+        const std::vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+        const std::unordered_map<int64_t, int>& stateAtomIdMap,
+        const std::unordered_map<int64_t, std::unordered_map<int, int64_t>>& allStateGroupMaps,
+        const std::unordered_map<int64_t, int>& metricToActivationMap,
+        std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+        std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+        std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+        std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+        std::vector<int>& metricsWithActivation);
+
+// Creates an EventMetricProducer and updates the vectors/maps used by MetricsManager with
+// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error.
+optional<sp<MetricProducer>> createEventMetricProducerAndUpdateMetadata(
         const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
         const EventMetric& metric, const int metricIndex,
         const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
@@ -108,6 +148,25 @@
         std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
         std::vector<int>& metricsWithActivation);
 
+// Creates a GaugeMetricProducer and updates the vectors/maps used by MetricsManager with
+// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error.
+optional<sp<MetricProducer>> createGaugeMetricProducerAndUpdateMetadata(
+        const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+        const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
+        const GaugeMetric& metric, const int metricIndex,
+        const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+        std::vector<sp<ConditionTracker>>& allConditionTrackers,
+        const std::unordered_map<int64_t, int>& conditionTrackerMap,
+        const std::vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+        const sp<EventMatcherWizard>& matcherWizard,
+        const std::unordered_map<int64_t, int>& metricToActivationMap,
+        std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+        std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+        std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+        std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+        std::vector<int>& metricsWithActivation);
+
 // Helper functions for MetricsManager to initialize from StatsdConfig.
 // *Note*: only initStatsdConfig() should be called from outside.
 // All other functions are intermediate
diff --git a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
index 3b346c1..0066030 100644
--- a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
+++ b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
@@ -27,6 +27,8 @@
 #include "src/condition/CombinationConditionTracker.h"
 #include "src/condition/SimpleConditionTracker.h"
 #include "src/matchers/CombinationAtomMatchingTracker.h"
+#include "src/metrics/DurationMetricProducer.h"
+#include "src/metrics/GaugeMetricProducer.h"
 #include "src/metrics/parsing_utils/metrics_manager_util.h"
 #include "tests/statsd_test_util.h"
 
@@ -97,6 +99,7 @@
         metricsWithActivation.clear();
         oldStateHashes.clear();
         noReportMetricIds.clear();
+        StateManager::getInstance().clear();
     }
 };
 
@@ -121,6 +124,69 @@
     return metric;
 }
 
+CountMetric createCountMetric(string name, int64_t what, optional<int64_t> condition,
+                              vector<int64_t> states) {
+    CountMetric metric;
+    metric.set_id(StringToId(name));
+    metric.set_what(what);
+    metric.set_bucket(TEN_MINUTES);
+    if (condition) {
+        metric.set_condition(condition.value());
+    }
+    for (const int64_t state : states) {
+        metric.add_slice_by_state(state);
+    }
+    return metric;
+}
+
+GaugeMetric createGaugeMetric(string name, int64_t what, GaugeMetric::SamplingType samplingType,
+                              optional<int64_t> condition, optional<int64_t> triggerEvent) {
+    GaugeMetric metric;
+    metric.set_id(StringToId(name));
+    metric.set_what(what);
+    metric.set_bucket(TEN_MINUTES);
+    metric.set_sampling_type(samplingType);
+    if (condition) {
+        metric.set_condition(condition.value());
+    }
+    if (triggerEvent) {
+        metric.set_trigger_event(triggerEvent.value());
+    }
+    metric.mutable_gauge_fields_filter()->set_include_all(true);
+    return metric;
+}
+
+DurationMetric createDurationMetric(string name, int64_t what, optional<int64_t> condition,
+                                    vector<int64_t> states) {
+    DurationMetric metric;
+    metric.set_id(StringToId(name));
+    metric.set_what(what);
+    metric.set_bucket(TEN_MINUTES);
+    if (condition) {
+        metric.set_condition(condition.value());
+    }
+    for (const int64_t state : states) {
+        metric.add_slice_by_state(state);
+    }
+    return metric;
+}
+
+ValueMetric createValueMetric(string name, const AtomMatcher& what, optional<int64_t> condition,
+                              vector<int64_t> states) {
+    ValueMetric metric;
+    metric.set_id(StringToId(name));
+    metric.set_what(what.id());
+    metric.set_bucket(TEN_MINUTES);
+    metric.mutable_value_field()->set_field(what.simple_atom_matcher().atom_id());
+    metric.mutable_value_field()->add_child()->set_field(2);
+    if (condition) {
+        metric.set_condition(condition.value());
+    }
+    for (const int64_t state : states) {
+        metric.add_slice_by_state(state);
+    }
+    return metric;
+}
 }  // anonymous namespace
 
 TEST_F(ConfigUpdateTest, TestSimpleMatcherPreserve) {
@@ -1250,6 +1316,353 @@
     EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
 }
 
+TEST_F(ConfigUpdateTest, TestGaugeMetricPreserve) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+    AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+    *config.add_atom_matcher() = whatMatcher;
+
+    Predicate predicate = CreateScreenIsOnPredicate();
+    *config.add_predicate() = predicate;
+
+    *config.add_gauge_metric() = createGaugeMetric(
+            "GAUGE1", whatMatcher.id(), GaugeMetric::RANDOM_ONE_SAMPLE, predicate.id(), nullopt);
+
+    EXPECT_TRUE(initConfig(config));
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+                                                 metricToActivationMap,
+                                                 /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+                                                 /*replacedStates=*/{}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_PRESERVE);
+}
+
+TEST_F(ConfigUpdateTest, TestGaugeMetricDefinitionChange) {
+    StatsdConfig config;
+    AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+    *config.add_atom_matcher() = whatMatcher;
+
+    *config.add_gauge_metric() = createGaugeMetric(
+            "GAUGE1", whatMatcher.id(), GaugeMetric::RANDOM_ONE_SAMPLE, nullopt, nullopt);
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Change split bucket on app upgrade, which should change the proto, causing replacement.
+    config.mutable_gauge_metric(0)->set_split_bucket_for_app_upgrade(false);
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+                                                 metricToActivationMap,
+                                                 /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+                                                 /*replacedStates=*/{}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestGaugeMetricWhatChanged) {
+    StatsdConfig config;
+    AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+    *config.add_atom_matcher() = whatMatcher;
+
+    *config.add_gauge_metric() = createGaugeMetric(
+            "GAUGE1", whatMatcher.id(), GaugeMetric::RANDOM_ONE_SAMPLE, nullopt, nullopt);
+
+    EXPECT_TRUE(initConfig(config));
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(
+            config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+            /*replacedMatchers*/ {whatMatcher.id()}, /*replacedConditions=*/{},
+            /*replacedStates=*/{}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestGaugeMetricConditionChanged) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+    AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+    *config.add_atom_matcher() = whatMatcher;
+
+    Predicate predicate = CreateScreenIsOnPredicate();
+    *config.add_predicate() = predicate;
+
+    *config.add_gauge_metric() = createGaugeMetric(
+            "GAUGE1", whatMatcher.id(), GaugeMetric::RANDOM_ONE_SAMPLE, predicate.id(), nullopt);
+
+    EXPECT_TRUE(initConfig(config));
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(
+            config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+            /*replacedMatchers*/ {}, /*replacedConditions=*/{predicate.id()},
+            /*replacedStates=*/{}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestGaugeMetricTriggerEventChanged) {
+    StatsdConfig config;
+    AtomMatcher triggerEvent = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = triggerEvent;
+    AtomMatcher whatMatcher = CreateTemperatureAtomMatcher();
+    *config.add_atom_matcher() = whatMatcher;
+
+    *config.add_gauge_metric() = createGaugeMetric(
+            "GAUGE1", whatMatcher.id(), GaugeMetric::FIRST_N_SAMPLES, nullopt, triggerEvent.id());
+
+    EXPECT_TRUE(initConfig(config));
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(
+            config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+            /*replacedMatchers*/ {triggerEvent.id()}, /*replacedConditions=*/{},
+            /*replacedStates=*/{}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestDurationMetricPreserve) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+
+    Predicate what = CreateScreenIsOnPredicate();
+    *config.add_predicate() = what;
+    Predicate condition = CreateScreenIsOffPredicate();
+    *config.add_predicate() = condition;
+
+    State sliceState = CreateScreenState();
+    *config.add_state() = sliceState;
+
+    *config.add_duration_metric() =
+            createDurationMetric("DURATION1", what.id(), condition.id(), {sliceState.id()});
+    EXPECT_TRUE(initConfig(config));
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+                                                 metricToActivationMap,
+                                                 /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+                                                 /*replacedStates=*/{}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_PRESERVE);
+}
+
+TEST_F(ConfigUpdateTest, TestDurationMetricDefinitionChange) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+
+    Predicate what = CreateScreenIsOnPredicate();
+    *config.add_predicate() = what;
+
+    *config.add_duration_metric() = createDurationMetric("DURATION1", what.id(), nullopt, {});
+    EXPECT_TRUE(initConfig(config));
+
+    config.mutable_duration_metric(0)->set_aggregation_type(DurationMetric::MAX_SPARSE);
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+                                                 metricToActivationMap, /*replacedMatchers*/ {},
+                                                 /*replacedConditions=*/{}, /*replacedStates=*/{},
+                                                 metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestDurationMetricWhatChanged) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+
+    Predicate what = CreateScreenIsOnPredicate();
+    *config.add_predicate() = what;
+
+    *config.add_duration_metric() = createDurationMetric("DURATION1", what.id(), nullopt, {});
+    EXPECT_TRUE(initConfig(config));
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(
+            config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+            /*replacedMatchers*/ {}, /*replacedConditions=*/{what.id()},
+            /*replacedStates=*/{}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestDurationMetricConditionChanged) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+
+    Predicate what = CreateScreenIsOnPredicate();
+    *config.add_predicate() = what;
+    Predicate condition = CreateScreenIsOffPredicate();
+    *config.add_predicate() = condition;
+
+    *config.add_duration_metric() = createDurationMetric("DURATION", what.id(), condition.id(), {});
+    EXPECT_TRUE(initConfig(config));
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(
+            config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+            /*replacedMatchers*/ {}, /*replacedConditions=*/{condition.id()},
+            /*replacedStates=*/{}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestDurationMetricStateChange) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+
+    Predicate what = CreateScreenIsOnPredicate();
+    *config.add_predicate() = what;
+
+    State sliceState = CreateScreenState();
+    *config.add_state() = sliceState;
+
+    *config.add_duration_metric() =
+            createDurationMetric("DURATION1", what.id(), nullopt, {sliceState.id()});
+    EXPECT_TRUE(initConfig(config));
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(
+            config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+            /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+            /*replacedStates=*/{sliceState.id()}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestValueMetricPreserve) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+    AtomMatcher whatMatcher = CreateTemperatureAtomMatcher();
+    *config.add_atom_matcher() = whatMatcher;
+
+    Predicate predicate = CreateScreenIsOnPredicate();
+    *config.add_predicate() = predicate;
+    State sliceState = CreateScreenState();
+    *config.add_state() = sliceState;
+
+    *config.add_value_metric() =
+            createValueMetric("VALUE1", whatMatcher, predicate.id(), {sliceState.id()});
+    EXPECT_TRUE(initConfig(config));
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+                                                 metricToActivationMap,
+                                                 /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+                                                 /*replacedStates=*/{}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_PRESERVE);
+}
+
+TEST_F(ConfigUpdateTest, TestValueMetricDefinitionChange) {
+    StatsdConfig config;
+    AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+    *config.add_atom_matcher() = whatMatcher;
+
+    *config.add_value_metric() = createValueMetric("VALUE1", whatMatcher, nullopt, {});
+    EXPECT_TRUE(initConfig(config));
+
+    // Change skip zero diff output, which should change the proto, causing replacement.
+    config.mutable_value_metric(0)->set_skip_zero_diff_output(true);
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+                                                 metricToActivationMap,
+                                                 /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+                                                 /*replacedStates=*/{}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestValueMetricWhatChanged) {
+    StatsdConfig config;
+    AtomMatcher whatMatcher = CreateTemperatureAtomMatcher();
+    *config.add_atom_matcher() = whatMatcher;
+
+    *config.add_value_metric() = createValueMetric("VALUE1", whatMatcher, nullopt, {});
+    EXPECT_TRUE(initConfig(config));
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(
+            config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+            /*replacedMatchers*/ {whatMatcher.id()}, /*replacedConditions=*/{},
+            /*replacedStates=*/{}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestValueMetricConditionChanged) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+    AtomMatcher whatMatcher = CreateTemperatureAtomMatcher();
+    *config.add_atom_matcher() = whatMatcher;
+
+    Predicate predicate = CreateScreenIsOnPredicate();
+    *config.add_predicate() = predicate;
+
+    *config.add_value_metric() = createValueMetric("VALUE1", whatMatcher, predicate.id(), {});
+    EXPECT_TRUE(initConfig(config));
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(
+            config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+            /*replacedMatchers*/ {}, /*replacedConditions=*/{predicate.id()},
+            /*replacedStates=*/{}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestValueMetricStateChanged) {
+    StatsdConfig config;
+    AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+    *config.add_atom_matcher() = whatMatcher;
+
+    State sliceState = CreateScreenState();
+    *config.add_state() = sliceState;
+
+    *config.add_value_metric() =
+            createValueMetric("VALUE1", whatMatcher, nullopt, {sliceState.id()});
+    EXPECT_TRUE(initConfig(config));
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(
+            config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+            /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+            /*replacedStates=*/{sliceState.id()}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
 TEST_F(ConfigUpdateTest, TestUpdateEventMetrics) {
     StatsdConfig config;
 
@@ -1419,7 +1832,7 @@
     EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(event1Id)],
               newMetricProducers[newMetricProducerMap.at(event1Id)]);
 
-    // Make sure replaced conditions are different and included in replacedConditions.
+    // Make sure replaced metrics are different.
     EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(event2Id)],
               newMetricProducers[newMetricProducerMap.at(event2Id)]);
     EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(event3Id)],
@@ -1478,6 +1891,800 @@
     EXPECT_EQ(oldConditionWizard->getStrongCount(), 1);
 }
 
+TEST_F(ConfigUpdateTest, TestUpdateCountMetrics) {
+    StatsdConfig config;
+
+    // Add atom matchers/predicates/states. These are mostly needed for initStatsdConfig.
+    AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher();
+    int64_t matcher1Id = matcher1.id();
+    *config.add_atom_matcher() = matcher1;
+
+    AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher();
+    int64_t matcher2Id = matcher2.id();
+    *config.add_atom_matcher() = matcher2;
+
+    AtomMatcher matcher3 = CreateStartScheduledJobAtomMatcher();
+    int64_t matcher3Id = matcher3.id();
+    *config.add_atom_matcher() = matcher3;
+
+    AtomMatcher matcher4 = CreateFinishScheduledJobAtomMatcher();
+    int64_t matcher4Id = matcher4.id();
+    *config.add_atom_matcher() = matcher4;
+
+    AtomMatcher matcher5 = CreateBatterySaverModeStartAtomMatcher();
+    int64_t matcher5Id = matcher5.id();
+    *config.add_atom_matcher() = matcher5;
+
+    Predicate predicate1 = CreateScreenIsOnPredicate();
+    int64_t predicate1Id = predicate1.id();
+    *config.add_predicate() = predicate1;
+
+    State state1 = CreateScreenStateWithOnOffMap(0x123, 0x321);
+    int64_t state1Id = state1.id();
+    *config.add_state() = state1;
+
+    State state2 = CreateScreenState();
+    int64_t state2Id = state2.id();
+    *config.add_state() = state2;
+
+    // Add a few count metrics.
+    // Will be preserved.
+    CountMetric count1 = createCountMetric("COUNT1", matcher1Id, predicate1Id, {state1Id});
+    int64_t count1Id = count1.id();
+    *config.add_count_metric() = count1;
+
+    // Will be replaced.
+    CountMetric count2 = createCountMetric("COUNT2", matcher2Id, nullopt, {});
+    int64_t count2Id = count2.id();
+    *config.add_count_metric() = count2;
+
+    // Will be replaced.
+    CountMetric count3 = createCountMetric("COUNT3", matcher3Id, nullopt, {});
+    int64_t count3Id = count3.id();
+    *config.add_count_metric() = count3;
+
+    // Will be replaced.
+    CountMetric count4 = createCountMetric("COUNT4", matcher4Id, nullopt, {state2Id});
+    int64_t count4Id = count4.id();
+    *config.add_count_metric() = count4;
+
+    // Will be deleted.
+    CountMetric count5 = createCountMetric("COUNT5", matcher5Id, nullopt, {});
+    int64_t count5Id = count5.id();
+    *config.add_count_metric() = count5;
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Change bucket size of count2, causing it to be replaced.
+    count2.set_bucket(ONE_HOUR);
+
+    // Mark matcher 3 as replaced. Causes count3 to be replaced.
+    set<int64_t> replacedMatchers;
+    replacedMatchers.insert(matcher3Id);
+
+    // Mark state 2 as replaced and change the state to be about a different atom.
+    // Causes count4 to be replaced.
+    set<int64_t> replacedStates;
+    replacedStates.insert(state2Id);
+    state2.set_atom_id(util::BATTERY_SAVER_MODE_STATE_CHANGED);
+
+    // Fake that predicate 1 is true for count metric 1.
+    ASSERT_EQ(oldMetricProducers[0]->getMetricId(), count1Id);
+    oldMetricProducers[0]->onConditionChanged(true, /*timestamp=*/0);
+    EXPECT_EQ(oldMetricProducers[0]->mCondition, ConditionState::kTrue);
+
+    EXPECT_EQ(StateManager::getInstance().getStateTrackersCount(), 1);
+    // Tell the StateManager that the screen is on.
+    unique_ptr<LogEvent> event =
+            CreateScreenStateChangedEvent(0, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+    StateManager::getInstance().onLogEvent(*event);
+
+    // New count metric. Should have an initial condition of true since it depends on predicate1.
+    CountMetric count6 = createCountMetric("EVENT6", matcher2Id, predicate1Id, {state1Id});
+    int64_t count6Id = count6.id();
+
+    // Map the matchers and predicates in reverse order to force the indices to change.
+    std::unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+    const int matcher5Index = 0;
+    newAtomMatchingTrackerMap[matcher5Id] = 0;
+    const int matcher4Index = 1;
+    newAtomMatchingTrackerMap[matcher4Id] = 1;
+    const int matcher3Index = 2;
+    newAtomMatchingTrackerMap[matcher3Id] = 2;
+    const int matcher2Index = 3;
+    newAtomMatchingTrackerMap[matcher2Id] = 3;
+    const int matcher1Index = 4;
+    newAtomMatchingTrackerMap[matcher1Id] = 4;
+    // Use the existing matchers. A bit hacky, but saves code and we don't rely on them.
+    vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(5);
+    std::reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(),
+                      newAtomMatchingTrackers.begin());
+
+    std::unordered_map<int64_t, int> newConditionTrackerMap;
+    const int predicate1Index = 0;
+    newConditionTrackerMap[predicate1Id] = 0;
+    // Use the existing conditionTrackers. A bit hacky, but saves code and we don't rely on them.
+    vector<sp<ConditionTracker>> newConditionTrackers(1);
+    std::reverse_copy(oldConditionTrackers.begin(), oldConditionTrackers.end(),
+                      newConditionTrackers.begin());
+    // Fake that predicate1 is true for all new metrics.
+    vector<ConditionState> conditionCache = {ConditionState::kTrue};
+
+    StatsdConfig newConfig;
+    *newConfig.add_count_metric() = count6;
+    const int count6Index = 0;
+    *newConfig.add_count_metric() = count3;
+    const int count3Index = 1;
+    *newConfig.add_count_metric() = count1;
+    const int count1Index = 2;
+    *newConfig.add_count_metric() = count4;
+    const int count4Index = 3;
+    *newConfig.add_count_metric() = count2;
+    const int count2Index = 4;
+
+    *newConfig.add_state() = state1;
+    *newConfig.add_state() = state2;
+
+    unordered_map<int64_t, int> stateAtomIdMap;
+    unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps;
+    map<int64_t, uint64_t> stateProtoHashes;
+    EXPECT_TRUE(initStates(newConfig, stateAtomIdMap, allStateGroupMaps, stateProtoHashes));
+    EXPECT_EQ(stateAtomIdMap[state2Id], util::BATTERY_SAVER_MODE_STATE_CHANGED);
+
+    // Output data structures to validate.
+    unordered_map<int64_t, int> newMetricProducerMap;
+    vector<sp<MetricProducer>> newMetricProducers;
+    unordered_map<int, vector<int>> conditionToMetricMap;
+    unordered_map<int, vector<int>> trackerToMetricMap;
+    set<int64_t> noReportMetricIds;
+    unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
+    unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
+    vector<int> metricsWithActivation;
+    EXPECT_TRUE(updateMetrics(
+            key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
+            oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers,
+            newAtomMatchingTrackers, newConditionTrackerMap, /*replacedConditions=*/{},
+            newConditionTrackers, conditionCache, stateAtomIdMap, allStateGroupMaps, replacedStates,
+            oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, newMetricProducers,
+            conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
+            activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+            metricsWithActivation));
+
+    unordered_map<int64_t, int> expectedMetricProducerMap = {
+            {count1Id, count1Index}, {count2Id, count2Index}, {count3Id, count3Index},
+            {count4Id, count4Index}, {count6Id, count6Index},
+    };
+    EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap));
+
+    // Make sure preserved metrics are the same.
+    ASSERT_EQ(newMetricProducers.size(), 5);
+    EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(count1Id)],
+              newMetricProducers[newMetricProducerMap.at(count1Id)]);
+
+    // Make sure replaced metrics are different.
+    EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(count2Id)],
+              newMetricProducers[newMetricProducerMap.at(count2Id)]);
+    EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(count3Id)],
+              newMetricProducers[newMetricProducerMap.at(count3Id)]);
+    EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(count4Id)],
+              newMetricProducers[newMetricProducerMap.at(count4Id)]);
+
+    // Verify the conditionToMetricMap.
+    ASSERT_EQ(conditionToMetricMap.size(), 1);
+    const vector<int>& condition1Metrics = conditionToMetricMap[predicate1Index];
+    EXPECT_THAT(condition1Metrics, UnorderedElementsAre(count1Index, count6Index));
+
+    // Verify the trackerToMetricMap.
+    ASSERT_EQ(trackerToMetricMap.size(), 4);
+    const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index];
+    EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(count1Index));
+    const vector<int>& matcher2Metrics = trackerToMetricMap[matcher2Index];
+    EXPECT_THAT(matcher2Metrics, UnorderedElementsAre(count2Index, count6Index));
+    const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index];
+    EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(count3Index));
+    const vector<int>& matcher4Metrics = trackerToMetricMap[matcher4Index];
+    EXPECT_THAT(matcher4Metrics, UnorderedElementsAre(count4Index));
+
+    // Verify event activation/deactivation maps.
+    ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 0);
+    ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 0);
+    ASSERT_EQ(metricsWithActivation.size(), 0);
+
+    // Verify tracker indices/ids/conditions/states are correct.
+    EXPECT_EQ(newMetricProducers[count1Index]->getMetricId(), count1Id);
+    EXPECT_EQ(newMetricProducers[count1Index]->mConditionTrackerIndex, predicate1Index);
+    EXPECT_EQ(newMetricProducers[count1Index]->mCondition, ConditionState::kTrue);
+    EXPECT_THAT(newMetricProducers[count1Index]->getSlicedStateAtoms(),
+                UnorderedElementsAre(util::SCREEN_STATE_CHANGED));
+    EXPECT_EQ(newMetricProducers[count2Index]->getMetricId(), count2Id);
+    EXPECT_EQ(newMetricProducers[count2Index]->mConditionTrackerIndex, -1);
+    EXPECT_EQ(newMetricProducers[count2Index]->mCondition, ConditionState::kTrue);
+    EXPECT_TRUE(newMetricProducers[count2Index]->getSlicedStateAtoms().empty());
+    EXPECT_EQ(newMetricProducers[count3Index]->getMetricId(), count3Id);
+    EXPECT_EQ(newMetricProducers[count3Index]->mConditionTrackerIndex, -1);
+    EXPECT_EQ(newMetricProducers[count3Index]->mCondition, ConditionState::kTrue);
+    EXPECT_TRUE(newMetricProducers[count3Index]->getSlicedStateAtoms().empty());
+    EXPECT_EQ(newMetricProducers[count4Index]->getMetricId(), count4Id);
+    EXPECT_EQ(newMetricProducers[count4Index]->mConditionTrackerIndex, -1);
+    EXPECT_EQ(newMetricProducers[count4Index]->mCondition, ConditionState::kTrue);
+    EXPECT_THAT(newMetricProducers[count4Index]->getSlicedStateAtoms(),
+                UnorderedElementsAre(util::BATTERY_SAVER_MODE_STATE_CHANGED));
+    EXPECT_EQ(newMetricProducers[count6Index]->getMetricId(), count6Id);
+    EXPECT_EQ(newMetricProducers[count6Index]->mConditionTrackerIndex, predicate1Index);
+    EXPECT_EQ(newMetricProducers[count6Index]->mCondition, ConditionState::kTrue);
+    EXPECT_THAT(newMetricProducers[count6Index]->getSlicedStateAtoms(),
+                UnorderedElementsAre(util::SCREEN_STATE_CHANGED));
+
+    oldMetricProducers.clear();
+    // Ensure that the screen state StateTracker did not get deleted and replaced.
+    EXPECT_EQ(StateManager::getInstance().getStateTrackersCount(), 2);
+    FieldValue screenState;
+    StateManager::getInstance().getStateValue(util::SCREEN_STATE_CHANGED, DEFAULT_DIMENSION_KEY,
+                                              &screenState);
+    EXPECT_EQ(screenState.mValue.int_value, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+}
+
+TEST_F(ConfigUpdateTest, TestUpdateGaugeMetrics) {
+    StatsdConfig config;
+
+    // Add atom matchers/predicates/states. These are mostly needed for initStatsdConfig.
+    AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher();
+    int64_t matcher1Id = matcher1.id();
+    *config.add_atom_matcher() = matcher1;
+
+    AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher();
+    int64_t matcher2Id = matcher2.id();
+    *config.add_atom_matcher() = matcher2;
+
+    AtomMatcher matcher3 = CreateStartScheduledJobAtomMatcher();
+    int64_t matcher3Id = matcher3.id();
+    *config.add_atom_matcher() = matcher3;
+
+    AtomMatcher matcher4 = CreateTemperatureAtomMatcher();
+    int64_t matcher4Id = matcher4.id();
+    *config.add_atom_matcher() = matcher4;
+
+    AtomMatcher matcher5 = CreateSimpleAtomMatcher("SubsystemSleep", util::SUBSYSTEM_SLEEP_STATE);
+    int64_t matcher5Id = matcher5.id();
+    *config.add_atom_matcher() = matcher5;
+
+    Predicate predicate1 = CreateScreenIsOnPredicate();
+    int64_t predicate1Id = predicate1.id();
+    *config.add_predicate() = predicate1;
+
+    // Add a few gauge metrics.
+    // Will be preserved.
+    GaugeMetric gauge1 = createGaugeMetric("GAUGE1", matcher4Id, GaugeMetric::FIRST_N_SAMPLES,
+                                           predicate1Id, matcher1Id);
+    int64_t gauge1Id = gauge1.id();
+    *config.add_gauge_metric() = gauge1;
+
+    // Will be replaced.
+    GaugeMetric gauge2 =
+            createGaugeMetric("GAUGE2", matcher1Id, GaugeMetric::FIRST_N_SAMPLES, nullopt, nullopt);
+    int64_t gauge2Id = gauge2.id();
+    *config.add_gauge_metric() = gauge2;
+
+    // Will be replaced.
+    GaugeMetric gauge3 = createGaugeMetric("GAUGE3", matcher5Id, GaugeMetric::FIRST_N_SAMPLES,
+                                           nullopt, matcher3Id);
+    int64_t gauge3Id = gauge3.id();
+    *config.add_gauge_metric() = gauge3;
+
+    // Will be replaced.
+    GaugeMetric gauge4 = createGaugeMetric("GAUGE4", matcher3Id, GaugeMetric::RANDOM_ONE_SAMPLE,
+                                           predicate1Id, nullopt);
+    int64_t gauge4Id = gauge4.id();
+    *config.add_gauge_metric() = gauge4;
+
+    // Will be deleted.
+    GaugeMetric gauge5 =
+            createGaugeMetric("GAUGE5", matcher2Id, GaugeMetric::RANDOM_ONE_SAMPLE, nullopt, {});
+    int64_t gauge5Id = gauge5.id();
+    *config.add_gauge_metric() = gauge5;
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Used later to ensure the condition wizard is replaced. Get it before doing the update.
+    sp<EventMatcherWizard> oldMatcherWizard =
+            static_cast<GaugeMetricProducer*>(oldMetricProducers[0].get())->mEventMatcherWizard;
+    EXPECT_EQ(oldMatcherWizard->getStrongCount(), 6);
+
+    // Change gauge2, causing it to be replaced.
+    gauge2.set_max_num_gauge_atoms_per_bucket(50);
+
+    // Mark matcher 3 as replaced. Causes gauge3 and gauge4 to be replaced.
+    set<int64_t> replacedMatchers = {matcher3Id};
+
+    // New gauge metric.
+    GaugeMetric gauge6 = createGaugeMetric("GAUGE6", matcher5Id, GaugeMetric::FIRST_N_SAMPLES,
+                                           predicate1Id, matcher3Id);
+    int64_t gauge6Id = gauge6.id();
+
+    // Map the matchers and predicates in reverse order to force the indices to change.
+    std::unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+    const int matcher5Index = 0;
+    newAtomMatchingTrackerMap[matcher5Id] = 0;
+    const int matcher4Index = 1;
+    newAtomMatchingTrackerMap[matcher4Id] = 1;
+    const int matcher3Index = 2;
+    newAtomMatchingTrackerMap[matcher3Id] = 2;
+    const int matcher2Index = 3;
+    newAtomMatchingTrackerMap[matcher2Id] = 3;
+    const int matcher1Index = 4;
+    newAtomMatchingTrackerMap[matcher1Id] = 4;
+    // Use the existing matchers. A bit hacky, but saves code and we don't rely on them.
+    vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(5);
+    std::reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(),
+                      newAtomMatchingTrackers.begin());
+
+    std::unordered_map<int64_t, int> newConditionTrackerMap;
+    const int predicate1Index = 0;
+    newConditionTrackerMap[predicate1Id] = 0;
+    // Use the existing conditionTrackers. A bit hacky, but saves code and we don't rely on them.
+    vector<sp<ConditionTracker>> newConditionTrackers(1);
+    std::reverse_copy(oldConditionTrackers.begin(), oldConditionTrackers.end(),
+                      newConditionTrackers.begin());
+    // Say that predicate1 is unknown since the initial condition never changed.
+    vector<ConditionState> conditionCache = {ConditionState::kUnknown};
+
+    StatsdConfig newConfig;
+    *newConfig.add_gauge_metric() = gauge6;
+    const int gauge6Index = 0;
+    *newConfig.add_gauge_metric() = gauge3;
+    const int gauge3Index = 1;
+    *newConfig.add_gauge_metric() = gauge1;
+    const int gauge1Index = 2;
+    *newConfig.add_gauge_metric() = gauge4;
+    const int gauge4Index = 3;
+    *newConfig.add_gauge_metric() = gauge2;
+    const int gauge2Index = 4;
+
+    // Output data structures to validate.
+    unordered_map<int64_t, int> newMetricProducerMap;
+    vector<sp<MetricProducer>> newMetricProducers;
+    unordered_map<int, vector<int>> conditionToMetricMap;
+    unordered_map<int, vector<int>> trackerToMetricMap;
+    set<int64_t> noReportMetricIds;
+    unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
+    unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
+    vector<int> metricsWithActivation;
+    EXPECT_TRUE(updateMetrics(
+            key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
+            oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers,
+            newAtomMatchingTrackers, newConditionTrackerMap, /*replacedConditions=*/{},
+            newConditionTrackers, conditionCache, /*stateAtomIdMap=*/{}, /*allStateGroupMaps=*/{},
+            /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap,
+            newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
+            activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+            metricsWithActivation));
+
+    unordered_map<int64_t, int> expectedMetricProducerMap = {
+            {gauge1Id, gauge1Index}, {gauge2Id, gauge2Index}, {gauge3Id, gauge3Index},
+            {gauge4Id, gauge4Index}, {gauge6Id, gauge6Index},
+    };
+    EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap));
+
+    // Make sure preserved metrics are the same.
+    ASSERT_EQ(newMetricProducers.size(), 5);
+    EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(gauge1Id)],
+              newMetricProducers[newMetricProducerMap.at(gauge1Id)]);
+
+    // Make sure replaced metrics are different.
+    EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(gauge2Id)],
+              newMetricProducers[newMetricProducerMap.at(gauge2Id)]);
+    EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(gauge3Id)],
+              newMetricProducers[newMetricProducerMap.at(gauge3Id)]);
+    EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(gauge4Id)],
+              newMetricProducers[newMetricProducerMap.at(gauge4Id)]);
+
+    // Verify the conditionToMetricMap.
+    ASSERT_EQ(conditionToMetricMap.size(), 1);
+    const vector<int>& condition1Metrics = conditionToMetricMap[predicate1Index];
+    EXPECT_THAT(condition1Metrics, UnorderedElementsAre(gauge1Index, gauge4Index, gauge6Index));
+
+    // Verify the trackerToMetricMap.
+    ASSERT_EQ(trackerToMetricMap.size(), 4);
+    const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index];
+    EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(gauge1Index, gauge2Index));
+    const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index];
+    EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(gauge3Index, gauge4Index, gauge6Index));
+    const vector<int>& matcher4Metrics = trackerToMetricMap[matcher4Index];
+    EXPECT_THAT(matcher4Metrics, UnorderedElementsAre(gauge1Index));
+    const vector<int>& matcher5Metrics = trackerToMetricMap[matcher5Index];
+    EXPECT_THAT(matcher5Metrics, UnorderedElementsAre(gauge3Index, gauge6Index));
+
+    // Verify event activation/deactivation maps.
+    ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 0);
+    ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 0);
+    ASSERT_EQ(metricsWithActivation.size(), 0);
+
+    // Verify tracker indices/ids/conditions/states are correct.
+    GaugeMetricProducer* gaugeProducer1 =
+            static_cast<GaugeMetricProducer*>(newMetricProducers[gauge1Index].get());
+    EXPECT_EQ(gaugeProducer1->getMetricId(), gauge1Id);
+    EXPECT_EQ(gaugeProducer1->mConditionTrackerIndex, predicate1Index);
+    EXPECT_EQ(gaugeProducer1->mCondition, ConditionState::kUnknown);
+    EXPECT_EQ(gaugeProducer1->mWhatMatcherIndex, matcher4Index);
+    GaugeMetricProducer* gaugeProducer2 =
+            static_cast<GaugeMetricProducer*>(newMetricProducers[gauge2Index].get());
+    EXPECT_EQ(gaugeProducer2->getMetricId(), gauge2Id);
+    EXPECT_EQ(gaugeProducer2->mConditionTrackerIndex, -1);
+    EXPECT_EQ(gaugeProducer2->mCondition, ConditionState::kTrue);
+    EXPECT_EQ(gaugeProducer2->mWhatMatcherIndex, matcher1Index);
+    GaugeMetricProducer* gaugeProducer3 =
+            static_cast<GaugeMetricProducer*>(newMetricProducers[gauge3Index].get());
+    EXPECT_EQ(gaugeProducer3->getMetricId(), gauge3Id);
+    EXPECT_EQ(gaugeProducer3->mConditionTrackerIndex, -1);
+    EXPECT_EQ(gaugeProducer3->mCondition, ConditionState::kTrue);
+    EXPECT_EQ(gaugeProducer3->mWhatMatcherIndex, matcher5Index);
+    GaugeMetricProducer* gaugeProducer4 =
+            static_cast<GaugeMetricProducer*>(newMetricProducers[gauge4Index].get());
+    EXPECT_EQ(gaugeProducer4->getMetricId(), gauge4Id);
+    EXPECT_EQ(gaugeProducer4->mConditionTrackerIndex, predicate1Index);
+    EXPECT_EQ(gaugeProducer4->mCondition, ConditionState::kUnknown);
+    EXPECT_EQ(gaugeProducer4->mWhatMatcherIndex, matcher3Index);
+    GaugeMetricProducer* gaugeProducer6 =
+            static_cast<GaugeMetricProducer*>(newMetricProducers[gauge6Index].get());
+    EXPECT_EQ(gaugeProducer6->getMetricId(), gauge6Id);
+    EXPECT_EQ(gaugeProducer6->mConditionTrackerIndex, predicate1Index);
+    EXPECT_EQ(gaugeProducer6->mCondition, ConditionState::kUnknown);
+    EXPECT_EQ(gaugeProducer6->mWhatMatcherIndex, matcher5Index);
+
+    sp<EventMatcherWizard> newMatcherWizard = gaugeProducer1->mEventMatcherWizard;
+    EXPECT_NE(newMatcherWizard, oldMatcherWizard);
+    EXPECT_EQ(newMatcherWizard->getStrongCount(), 6);
+    oldMetricProducers.clear();
+    // Only reference to the old wizard should be the one in the test.
+    EXPECT_EQ(oldMatcherWizard->getStrongCount(), 1);
+}
+
+TEST_F(ConfigUpdateTest, TestUpdateDurationMetrics) {
+    StatsdConfig config;
+    // Add atom matchers/predicates/states. These are mostly needed for initStatsdConfig.
+    AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher();
+    int64_t matcher1Id = matcher1.id();
+    *config.add_atom_matcher() = matcher1;
+
+    AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher();
+    int64_t matcher2Id = matcher2.id();
+    *config.add_atom_matcher() = matcher2;
+
+    AtomMatcher matcher3 = CreateAcquireWakelockAtomMatcher();
+    int64_t matcher3Id = matcher3.id();
+    *config.add_atom_matcher() = matcher3;
+
+    AtomMatcher matcher4 = CreateReleaseWakelockAtomMatcher();
+    int64_t matcher4Id = matcher4.id();
+    *config.add_atom_matcher() = matcher4;
+
+    AtomMatcher matcher5 = CreateMoveToForegroundAtomMatcher();
+    int64_t matcher5Id = matcher5.id();
+    *config.add_atom_matcher() = matcher5;
+
+    AtomMatcher matcher6 = CreateMoveToBackgroundAtomMatcher();
+    int64_t matcher6Id = matcher6.id();
+    *config.add_atom_matcher() = matcher6;
+
+    AtomMatcher matcher7 = CreateBatteryStateNoneMatcher();
+    int64_t matcher7Id = matcher7.id();
+    *config.add_atom_matcher() = matcher7;
+
+    AtomMatcher matcher8 = CreateBatteryStateUsbMatcher();
+    int64_t matcher8Id = matcher8.id();
+    *config.add_atom_matcher() = matcher8;
+
+    Predicate predicate1 = CreateScreenIsOnPredicate();
+    int64_t predicate1Id = predicate1.id();
+    *config.add_predicate() = predicate1;
+
+    Predicate predicate2 = CreateScreenIsOffPredicate();
+    int64_t predicate2Id = predicate2.id();
+    *config.add_predicate() = predicate2;
+
+    Predicate predicate3 = CreateDeviceUnpluggedPredicate();
+    int64_t predicate3Id = predicate3.id();
+    *config.add_predicate() = predicate3;
+
+    Predicate predicate4 = CreateIsInBackgroundPredicate();
+    *predicate4.mutable_simple_predicate()->mutable_dimensions() =
+            CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1});
+    int64_t predicate4Id = predicate4.id();
+    *config.add_predicate() = predicate4;
+
+    Predicate predicate5 = CreateHoldingWakelockPredicate();
+    *predicate5.mutable_simple_predicate()->mutable_dimensions() =
+            CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+    predicate5.mutable_simple_predicate()->set_stop_all(matcher7Id);
+    int64_t predicate5Id = predicate5.id();
+    *config.add_predicate() = predicate5;
+
+    State state1 = CreateScreenStateWithOnOffMap(0x123, 0x321);
+    int64_t state1Id = state1.id();
+    *config.add_state() = state1;
+
+    State state2 = CreateScreenState();
+    int64_t state2Id = state2.id();
+    *config.add_state() = state2;
+
+    // Add a few duration metrics.
+    // Will be preserved.
+    DurationMetric duration1 =
+            createDurationMetric("DURATION1", predicate5Id, predicate4Id, {state2Id});
+    *duration1.mutable_dimensions_in_what() =
+            CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+    MetricConditionLink* link = duration1.add_links();
+    link->set_condition(predicate4Id);
+    *link->mutable_fields_in_what() =
+            CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+    *link->mutable_fields_in_condition() =
+            CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1} /*uid field*/);
+    int64_t duration1Id = duration1.id();
+    *config.add_duration_metric() = duration1;
+
+    // Will be replaced.
+    DurationMetric duration2 = createDurationMetric("DURATION2", predicate1Id, nullopt, {});
+    int64_t duration2Id = duration2.id();
+    *config.add_duration_metric() = duration2;
+
+    // Will be replaced.
+    DurationMetric duration3 = createDurationMetric("DURATION3", predicate3Id, nullopt, {state1Id});
+    int64_t duration3Id = duration3.id();
+    *config.add_duration_metric() = duration3;
+
+    // Will be replaced.
+    DurationMetric duration4 = createDurationMetric("DURATION4", predicate3Id, predicate2Id, {});
+    int64_t duration4Id = duration4.id();
+    *config.add_duration_metric() = duration4;
+
+    // Will be deleted.
+    DurationMetric duration5 = createDurationMetric("DURATION5", predicate2Id, nullopt, {});
+    int64_t duration5Id = duration5.id();
+    *config.add_duration_metric() = duration5;
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Make some sliced conditions true.
+    int uid1 = 10;
+    int uid2 = 11;
+    vector<MatchingState> matchingStates(8, MatchingState::kNotMatched);
+    matchingStates[2] = kMatched;
+    vector<ConditionState> conditionCache(5, ConditionState::kNotEvaluated);
+    vector<bool> changedCache(5, false);
+    unique_ptr<LogEvent> event = CreateAcquireWakelockEvent(timeBaseNs + 3, {uid1}, {"tag"}, "wl1");
+    oldConditionTrackers[4]->evaluateCondition(*event.get(), matchingStates, oldConditionTrackers,
+                                               conditionCache, changedCache);
+    EXPECT_TRUE(oldConditionTrackers[4]->isSliced());
+    EXPECT_TRUE(changedCache[4]);
+    EXPECT_EQ(conditionCache[4], ConditionState::kTrue);
+    oldMetricProducers[0]->onMatchedLogEvent(2, *event.get());
+
+    fill(conditionCache.begin(), conditionCache.end(), ConditionState::kNotEvaluated);
+    fill(changedCache.begin(), changedCache.end(), false);
+    event = CreateAcquireWakelockEvent(timeBaseNs + 3, {uid2}, {"tag"}, "wl2");
+    oldConditionTrackers[4]->evaluateCondition(*event.get(), matchingStates, oldConditionTrackers,
+                                               conditionCache, changedCache);
+    EXPECT_TRUE(changedCache[4]);
+    EXPECT_EQ(conditionCache[4], ConditionState::kTrue);
+    oldMetricProducers[0]->onMatchedLogEvent(2, *event.get());
+
+    // Used later to ensure the condition wizard is replaced. Get it before doing the update.
+    // The duration trackers have a pointer to the wizard, and 2 trackers were created above.
+    sp<ConditionWizard> oldConditionWizard = oldMetricProducers[0]->mWizard;
+    EXPECT_EQ(oldConditionWizard->getStrongCount(), 8);
+
+    // Replace predicate1, predicate3, and state1. Causes duration2/3/4 to be replaced.
+    set<int64_t> replacedConditions({predicate1Id, predicate2Id});
+    set<int64_t> replacedStates({state1Id});
+
+    // New duration metric.
+    DurationMetric duration6 = createDurationMetric("DURATION6", predicate4Id, predicate5Id, {});
+    *duration6.mutable_dimensions_in_what() =
+            CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1} /*uid field*/);
+    link = duration6.add_links();
+    link->set_condition(predicate5Id);
+    *link->mutable_fields_in_what() =
+            CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1} /*uid field*/);
+    *link->mutable_fields_in_condition() =
+            CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+    int64_t duration6Id = duration6.id();
+
+    // Map the matchers and predicates in reverse order to force the indices to change.
+    const int matcher8Index = 0, matcher7Index = 1, matcher6Index = 2, matcher5Index = 3,
+              matcher4Index = 4, matcher3Index = 5, matcher2Index = 6, matcher1Index = 7;
+    std::unordered_map<int64_t, int> newAtomMatchingTrackerMap({{matcher8Id, matcher8Index},
+                                                                {matcher7Id, matcher7Index},
+                                                                {matcher6Id, matcher6Index},
+                                                                {matcher5Id, matcher5Index},
+                                                                {matcher4Id, matcher4Index},
+                                                                {matcher3Id, matcher3Index},
+                                                                {matcher2Id, matcher2Index},
+                                                                {matcher1Id, matcher1Index}});
+    // Use the existing matchers. A bit hacky, but saves code and we don't rely on them.
+    vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(8);
+    reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(),
+                 newAtomMatchingTrackers.begin());
+
+    const int predicate5Index = 0, predicate4Index = 1, predicate3Index = 2, predicate2Index = 3,
+              predicate1Index = 4;
+    std::unordered_map<int64_t, int> newConditionTrackerMap({
+            {predicate5Id, predicate5Index},
+            {predicate4Id, predicate4Index},
+            {predicate3Id, predicate3Index},
+            {predicate2Id, predicate2Index},
+            {predicate1Id, predicate1Index},
+    });
+    // Use the existing conditionTrackers and reinitialize them to get the initial condition cache.
+    vector<sp<ConditionTracker>> newConditionTrackers(5);
+    reverse_copy(oldConditionTrackers.begin(), oldConditionTrackers.end(),
+                 newConditionTrackers.begin());
+    vector<Predicate> conditionProtos(5);
+    reverse_copy(config.predicate().begin(), config.predicate().end(), conditionProtos.begin());
+    for (int i = 0; i < newConditionTrackers.size(); i++) {
+        EXPECT_TRUE(newConditionTrackers[i]->onConfigUpdated(
+                conditionProtos, i, newConditionTrackers, newAtomMatchingTrackerMap,
+                newConditionTrackerMap));
+    }
+    vector<bool> cycleTracker(5, false);
+    fill(conditionCache.begin(), conditionCache.end(), ConditionState::kNotEvaluated);
+    for (int i = 0; i < newConditionTrackers.size(); i++) {
+        EXPECT_TRUE(newConditionTrackers[i]->init(conditionProtos, newConditionTrackers,
+                                                  newConditionTrackerMap, cycleTracker,
+                                                  conditionCache));
+    }
+    // Predicate5 should be true since 2 uids have wakelocks
+    EXPECT_EQ(conditionCache, vector({kTrue, kUnknown, kUnknown, kUnknown, kUnknown}));
+
+    StatsdConfig newConfig;
+    *newConfig.add_duration_metric() = duration6;
+    const int duration6Index = 0;
+    *newConfig.add_duration_metric() = duration3;
+    const int duration3Index = 1;
+    *newConfig.add_duration_metric() = duration1;
+    const int duration1Index = 2;
+    *newConfig.add_duration_metric() = duration4;
+    const int duration4Index = 3;
+    *newConfig.add_duration_metric() = duration2;
+    const int duration2Index = 4;
+
+    for (const Predicate& predicate : conditionProtos) {
+        *newConfig.add_predicate() = predicate;
+    }
+    *newConfig.add_state() = state1;
+    *newConfig.add_state() = state2;
+    unordered_map<int64_t, int> stateAtomIdMap;
+    unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps;
+    map<int64_t, uint64_t> stateProtoHashes;
+    EXPECT_TRUE(initStates(newConfig, stateAtomIdMap, allStateGroupMaps, stateProtoHashes));
+
+    // Output data structures to validate.
+    unordered_map<int64_t, int> newMetricProducerMap;
+    vector<sp<MetricProducer>> newMetricProducers;
+    unordered_map<int, vector<int>> conditionToMetricMap;
+    unordered_map<int, vector<int>> trackerToMetricMap;
+    set<int64_t> noReportMetricIds;
+    unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
+    unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
+    vector<int> metricsWithActivation;
+    EXPECT_TRUE(updateMetrics(
+            key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
+            oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, /*replacedMatchers=*/{},
+            newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions,
+            newConditionTrackers, conditionCache, stateAtomIdMap, allStateGroupMaps, replacedStates,
+            oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, newMetricProducers,
+            conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
+            activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+            metricsWithActivation));
+
+    unordered_map<int64_t, int> expectedMetricProducerMap = {
+            {duration1Id, duration1Index}, {duration2Id, duration2Index},
+            {duration3Id, duration3Index}, {duration4Id, duration4Index},
+            {duration6Id, duration6Index},
+    };
+    EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap));
+
+    // Make sure preserved metrics are the same.
+    ASSERT_EQ(newMetricProducers.size(), 5);
+    EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(duration1Id)],
+              newMetricProducers[newMetricProducerMap.at(duration1Id)]);
+
+    // Make sure replaced metrics are different.
+    EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(duration2Id)],
+              newMetricProducers[newMetricProducerMap.at(duration2Id)]);
+    EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(duration3Id)],
+              newMetricProducers[newMetricProducerMap.at(duration3Id)]);
+    EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(duration4Id)],
+              newMetricProducers[newMetricProducerMap.at(duration4Id)]);
+
+    // Verify the conditionToMetricMap. Note that the "what" is not in this map.
+    ASSERT_EQ(conditionToMetricMap.size(), 3);
+    const vector<int>& condition2Metrics = conditionToMetricMap[predicate2Index];
+    EXPECT_THAT(condition2Metrics, UnorderedElementsAre(duration4Index));
+    const vector<int>& condition4Metrics = conditionToMetricMap[predicate4Index];
+    EXPECT_THAT(condition4Metrics, UnorderedElementsAre(duration1Index));
+    const vector<int>& condition5Metrics = conditionToMetricMap[predicate5Index];
+    EXPECT_THAT(condition5Metrics, UnorderedElementsAre(duration6Index));
+
+    // Verify the trackerToMetricMap. The start/stop/stopall indices from the "what" should be here.
+    ASSERT_EQ(trackerToMetricMap.size(), 8);
+    const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index];
+    EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(duration2Index));
+    const vector<int>& matcher2Metrics = trackerToMetricMap[matcher2Index];
+    EXPECT_THAT(matcher2Metrics, UnorderedElementsAre(duration2Index));
+    const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index];
+    EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(duration1Index));
+    const vector<int>& matcher4Metrics = trackerToMetricMap[matcher4Index];
+    EXPECT_THAT(matcher4Metrics, UnorderedElementsAre(duration1Index));
+    const vector<int>& matcher5Metrics = trackerToMetricMap[matcher5Index];
+    EXPECT_THAT(matcher5Metrics, UnorderedElementsAre(duration6Index));
+    const vector<int>& matcher6Metrics = trackerToMetricMap[matcher6Index];
+    EXPECT_THAT(matcher6Metrics, UnorderedElementsAre(duration6Index));
+    const vector<int>& matcher7Metrics = trackerToMetricMap[matcher7Index];
+    EXPECT_THAT(matcher7Metrics,
+                UnorderedElementsAre(duration1Index, duration3Index, duration4Index));
+    const vector<int>& matcher8Metrics = trackerToMetricMap[matcher8Index];
+    EXPECT_THAT(matcher8Metrics, UnorderedElementsAre(duration3Index, duration4Index));
+
+    // Verify event activation/deactivation maps.
+    ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 0);
+    ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 0);
+    ASSERT_EQ(metricsWithActivation.size(), 0);
+
+    // Verify tracker indices/ids/conditions are correct.
+    DurationMetricProducer* durationProducer1 =
+            static_cast<DurationMetricProducer*>(newMetricProducers[duration1Index].get());
+    EXPECT_EQ(durationProducer1->getMetricId(), duration1Id);
+    EXPECT_EQ(durationProducer1->mConditionTrackerIndex, predicate4Index);
+    EXPECT_EQ(durationProducer1->mCondition, ConditionState::kUnknown);
+    EXPECT_EQ(durationProducer1->mStartIndex, matcher3Index);
+    EXPECT_EQ(durationProducer1->mStopIndex, matcher4Index);
+    EXPECT_EQ(durationProducer1->mStopAllIndex, matcher7Index);
+    EXPECT_EQ(durationProducer1->mCurrentSlicedDurationTrackerMap.size(), 2);
+    for (const auto& durationTrackerIt : durationProducer1->mCurrentSlicedDurationTrackerMap) {
+        EXPECT_EQ(durationTrackerIt.second->mConditionTrackerIndex, predicate4Index);
+    }
+    DurationMetricProducer* durationProducer2 =
+            static_cast<DurationMetricProducer*>(newMetricProducers[duration2Index].get());
+    EXPECT_EQ(durationProducer2->getMetricId(), duration2Id);
+    EXPECT_EQ(durationProducer2->mConditionTrackerIndex, -1);
+    EXPECT_EQ(durationProducer2->mCondition, ConditionState::kTrue);
+    EXPECT_EQ(durationProducer2->mStartIndex, matcher1Index);
+    EXPECT_EQ(durationProducer2->mStopIndex, matcher2Index);
+    EXPECT_EQ(durationProducer2->mStopAllIndex, -1);
+    DurationMetricProducer* durationProducer3 =
+            static_cast<DurationMetricProducer*>(newMetricProducers[duration3Index].get());
+    EXPECT_EQ(durationProducer3->getMetricId(), duration3Id);
+    EXPECT_EQ(durationProducer3->mConditionTrackerIndex, -1);
+    EXPECT_EQ(durationProducer3->mCondition, ConditionState::kTrue);
+    EXPECT_EQ(durationProducer3->mStartIndex, matcher7Index);
+    EXPECT_EQ(durationProducer3->mStopIndex, matcher8Index);
+    EXPECT_EQ(durationProducer3->mStopAllIndex, -1);
+    DurationMetricProducer* durationProducer4 =
+            static_cast<DurationMetricProducer*>(newMetricProducers[duration4Index].get());
+    EXPECT_EQ(durationProducer4->getMetricId(), duration4Id);
+    EXPECT_EQ(durationProducer4->mConditionTrackerIndex, predicate2Index);
+    EXPECT_EQ(durationProducer4->mCondition, ConditionState::kUnknown);
+    EXPECT_EQ(durationProducer4->mStartIndex, matcher7Index);
+    EXPECT_EQ(durationProducer4->mStopIndex, matcher8Index);
+    EXPECT_EQ(durationProducer4->mStopAllIndex, -1);
+    DurationMetricProducer* durationProducer6 =
+            static_cast<DurationMetricProducer*>(newMetricProducers[duration6Index].get());
+    EXPECT_EQ(durationProducer6->getMetricId(), duration6Id);
+    EXPECT_EQ(durationProducer6->mConditionTrackerIndex, predicate5Index);
+    // TODO(b/167491517): should this be unknown since the condition is sliced?
+    EXPECT_EQ(durationProducer6->mCondition, ConditionState::kTrue);
+    EXPECT_EQ(durationProducer6->mStartIndex, matcher6Index);
+    EXPECT_EQ(durationProducer6->mStopIndex, matcher5Index);
+    EXPECT_EQ(durationProducer6->mStopAllIndex, -1);
+
+    sp<ConditionWizard> newConditionWizard = newMetricProducers[0]->mWizard;
+    EXPECT_NE(newConditionWizard, oldConditionWizard);
+    EXPECT_EQ(newConditionWizard->getStrongCount(), 8);
+    oldMetricProducers.clear();
+    // Only reference to the old wizard should be the one in the test.
+    EXPECT_EQ(oldConditionWizard->getStrongCount(), 1);
+}
+
 TEST_F(ConfigUpdateTest, TestUpdateMetricActivations) {
     StatsdConfig config;
     // Add atom matchers
@@ -1605,6 +2812,176 @@
     EXPECT_THAT(producer->mEventDeactivationMap[matcher2Index],
                 UnorderedElementsAre(matcher4Activation));
 }
+
+TEST_F(ConfigUpdateTest, TestUpdateMetricsMultipleTypes) {
+    StatsdConfig config;
+    // Add atom matchers/predicates/states. These are mostly needed for initStatsdConfig
+    AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher();
+    int64_t matcher1Id = matcher1.id();
+    *config.add_atom_matcher() = matcher1;
+
+    AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher();
+    int64_t matcher2Id = matcher2.id();
+    *config.add_atom_matcher() = matcher2;
+
+    AtomMatcher matcher3 = CreateTemperatureAtomMatcher();
+    int64_t matcher3Id = matcher3.id();
+    *config.add_atom_matcher() = matcher3;
+
+    Predicate predicate1 = CreateScreenIsOnPredicate();
+    int64_t predicate1Id = predicate1.id();
+    *config.add_predicate() = predicate1;
+
+    // Add a few count metrics.
+    // Will be preserved.
+    CountMetric countMetric = createCountMetric("COUNT1", matcher1Id, predicate1Id, {});
+    int64_t countMetricId = countMetric.id();
+    *config.add_count_metric() = countMetric;
+
+    // Will be replaced since matcher2 is replaced.
+    EventMetric eventMetric = createEventMetric("EVENT1", matcher2Id, nullopt);
+    int64_t eventMetricId = eventMetric.id();
+    *config.add_event_metric() = eventMetric;
+
+    // Will be replaced because the definition changes - a predicate is added.
+    GaugeMetric gaugeMetric = createGaugeMetric("GAUGE1", matcher3Id,
+                                                GaugeMetric::RANDOM_ONE_SAMPLE, nullopt, nullopt);
+    int64_t gaugeMetricId = gaugeMetric.id();
+    *config.add_gauge_metric() = gaugeMetric;
+
+    // Preserved.
+    DurationMetric durationMetric = createDurationMetric("DURATION1", predicate1Id, nullopt, {});
+    int64_t durationMetricId = durationMetric.id();
+    *config.add_duration_metric() = durationMetric;
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Used later to ensure the condition wizard is replaced. Get it before doing the update.
+    sp<ConditionWizard> oldConditionWizard = oldMetricProducers[0]->mWizard;
+    EXPECT_EQ(oldConditionWizard->getStrongCount(), 5);
+
+    // Mark matcher 2 as replaced. Causes eventMetric to be replaced.
+    set<int64_t> replacedMatchers;
+    replacedMatchers.insert(matcher2Id);
+
+    // Add predicate1 as a predicate on gaugeMetric, causing it to be replaced.
+    gaugeMetric.set_condition(predicate1Id);
+
+    // Map the matchers and predicates in reverse order to force the indices to change.
+    std::unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+    const int matcher3Index = 0;
+    newAtomMatchingTrackerMap[matcher3Id] = 0;
+    const int matcher2Index = 1;
+    newAtomMatchingTrackerMap[matcher2Id] = 1;
+    const int matcher1Index = 2;
+    newAtomMatchingTrackerMap[matcher1Id] = 2;
+    // Use the existing matchers. A bit hacky, but saves code and we don't rely on them.
+    vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(3);
+    std::reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(),
+                      newAtomMatchingTrackers.begin());
+
+    std::unordered_map<int64_t, int> newConditionTrackerMap;
+    const int predicate1Index = 0;
+    newConditionTrackerMap[predicate1Id] = 0;
+    // Use the existing conditionTrackers. A bit hacky, but saves code and we don't rely on them.
+    vector<sp<ConditionTracker>> newConditionTrackers(1);
+    std::reverse_copy(oldConditionTrackers.begin(), oldConditionTrackers.end(),
+                      newConditionTrackers.begin());
+    vector<ConditionState> conditionCache = {ConditionState::kUnknown};
+
+    StatsdConfig newConfig;
+    *newConfig.add_count_metric() = countMetric;
+    const int countMetricIndex = 0;
+    *newConfig.add_duration_metric() = durationMetric;
+    const int durationMetricIndex = 1;
+    *newConfig.add_event_metric() = eventMetric;
+    const int eventMetricIndex = 2;
+    *newConfig.add_gauge_metric() = gaugeMetric;
+    const int gaugeMetricIndex = 3;
+
+    // Add the predicate since duration metric needs it.
+    *newConfig.add_predicate() = predicate1;
+
+    // Output data structures to validate.
+    unordered_map<int64_t, int> newMetricProducerMap;
+    vector<sp<MetricProducer>> newMetricProducers;
+    unordered_map<int, vector<int>> conditionToMetricMap;
+    unordered_map<int, vector<int>> trackerToMetricMap;
+    set<int64_t> noReportMetricIds;
+    unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
+    unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
+    vector<int> metricsWithActivation;
+    EXPECT_TRUE(updateMetrics(
+            key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
+            oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers,
+            newAtomMatchingTrackers, newConditionTrackerMap, /*replacedConditions=*/{},
+            newConditionTrackers, conditionCache, /*stateAtomIdMap*/ {}, /*allStateGroupMaps=*/{},
+            /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap,
+            newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
+            activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+            metricsWithActivation));
+
+    unordered_map<int64_t, int> expectedMetricProducerMap = {
+            {countMetricId, countMetricIndex},
+            {durationMetricId, durationMetricIndex},
+            {eventMetricId, eventMetricIndex},
+            {gaugeMetricId, gaugeMetricIndex},
+    };
+    EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap));
+
+    // Make sure preserved metrics are the same.
+    ASSERT_EQ(newMetricProducers.size(), 4);
+    EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(countMetricId)],
+              newMetricProducers[newMetricProducerMap.at(countMetricId)]);
+    EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(durationMetricId)],
+              newMetricProducers[newMetricProducerMap.at(durationMetricId)]);
+
+    // Make sure replaced metrics are different.
+    EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(eventMetricId)],
+              newMetricProducers[newMetricProducerMap.at(eventMetricId)]);
+    EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(gaugeMetricId)],
+              newMetricProducers[newMetricProducerMap.at(gaugeMetricId)]);
+
+    // Verify the conditionToMetricMap.
+    ASSERT_EQ(conditionToMetricMap.size(), 1);
+    const vector<int>& condition1Metrics = conditionToMetricMap[predicate1Index];
+    EXPECT_THAT(condition1Metrics, UnorderedElementsAre(countMetricIndex, gaugeMetricIndex));
+
+    // Verify the trackerToMetricMap.
+    ASSERT_EQ(trackerToMetricMap.size(), 3);
+    const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index];
+    EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(countMetricIndex, durationMetricIndex));
+    const vector<int>& matcher2Metrics = trackerToMetricMap[matcher2Index];
+    EXPECT_THAT(matcher2Metrics, UnorderedElementsAre(eventMetricIndex, durationMetricIndex));
+    const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index];
+    EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(gaugeMetricIndex));
+
+    // Verify event activation/deactivation maps.
+    ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 0);
+    ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 0);
+    ASSERT_EQ(metricsWithActivation.size(), 0);
+
+    // Verify tracker indices/ids/conditions are correct.
+    EXPECT_EQ(newMetricProducers[countMetricIndex]->getMetricId(), countMetricId);
+    EXPECT_EQ(newMetricProducers[countMetricIndex]->mConditionTrackerIndex, predicate1Index);
+    EXPECT_EQ(newMetricProducers[countMetricIndex]->mCondition, ConditionState::kUnknown);
+    EXPECT_EQ(newMetricProducers[durationMetricIndex]->getMetricId(), durationMetricId);
+    EXPECT_EQ(newMetricProducers[durationMetricIndex]->mConditionTrackerIndex, -1);
+    EXPECT_EQ(newMetricProducers[durationMetricIndex]->mCondition, ConditionState::kTrue);
+    EXPECT_EQ(newMetricProducers[eventMetricIndex]->getMetricId(), eventMetricId);
+    EXPECT_EQ(newMetricProducers[eventMetricIndex]->mConditionTrackerIndex, -1);
+    EXPECT_EQ(newMetricProducers[eventMetricIndex]->mCondition, ConditionState::kTrue);
+    EXPECT_EQ(newMetricProducers[gaugeMetricIndex]->getMetricId(), gaugeMetricId);
+    EXPECT_EQ(newMetricProducers[gaugeMetricIndex]->mConditionTrackerIndex, predicate1Index);
+    EXPECT_EQ(newMetricProducers[gaugeMetricIndex]->mCondition, ConditionState::kUnknown);
+
+    sp<ConditionWizard> newConditionWizard = newMetricProducers[0]->mWizard;
+    EXPECT_NE(newConditionWizard, oldConditionWizard);
+    EXPECT_EQ(newConditionWizard->getStrongCount(), 5);
+    oldMetricProducers.clear();
+    // Only reference to the old wizard should be the one in the test.
+    EXPECT_EQ(oldConditionWizard->getStrongCount(), 1);
+}
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt
index f43bd2b..01272c7 100644
--- a/config/boot-image-profile.txt
+++ b/config/boot-image-profile.txt
@@ -47131,8 +47131,8 @@
 Landroid/view/IRotationWatcher$Stub$Proxy;
 Landroid/view/IRotationWatcher$Stub;
 Landroid/view/IRotationWatcher;
-Landroid/view/IScrollCaptureController$Stub;
-Landroid/view/IScrollCaptureController;
+Landroid/view/IScrollCaptureCallbacks$Stub;
+Landroid/view/IScrollCaptureCallbacks;
 Landroid/view/ISystemGestureExclusionListener$Stub$Proxy;
 Landroid/view/ISystemGestureExclusionListener$Stub;
 Landroid/view/ISystemGestureExclusionListener;
diff --git a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
index 25729ab..d4713cb 100644
--- a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
+++ b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
@@ -20,6 +20,7 @@
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD;
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD;
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_DOWN;
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_LEFT;
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_RIGHT;
@@ -28,11 +29,13 @@
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_DOWN;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_LEFT;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_RIGHT;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_UP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_TRIPLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_TRIPLE_TAP_AND_HOLD;
 import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_DOUBLE_TAP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD;
 import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SINGLE_TAP;
@@ -43,6 +46,7 @@
 import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_TRIPLE_TAP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_DOUBLE_TAP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_DOUBLE_TAP_AND_HOLD;
+import static android.accessibilityservice.AccessibilityService.GESTURE_PASSTHROUGH;
 import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_DOWN;
 import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_DOWN_AND_LEFT;
 import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_DOWN_AND_RIGHT;
@@ -59,15 +63,21 @@
 import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_UP_AND_DOWN;
 import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_UP_AND_LEFT;
 import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_UP_AND_RIGHT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_TOUCH_EXPLORATION;
+import static android.accessibilityservice.AccessibilityService.GESTURE_UNKNOWN;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.TestApi;
+import android.content.pm.ParceledListSlice;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.view.MotionEvent;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * This class describes the gesture event including gesture id and which display it happens
@@ -84,14 +94,19 @@
 
     /** @hide */
     @IntDef(prefix = { "GESTURE_" }, value = {
+          GESTURE_UNKNOWN,
+          GESTURE_TOUCH_EXPLORATION,
             GESTURE_2_FINGER_SINGLE_TAP,
+            GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD,
             GESTURE_2_FINGER_DOUBLE_TAP,
             GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD,
             GESTURE_2_FINGER_TRIPLE_TAP,
             GESTURE_3_FINGER_SINGLE_TAP,
+            GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD,
             GESTURE_3_FINGER_DOUBLE_TAP,
             GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD,
             GESTURE_3_FINGER_TRIPLE_TAP,
+            GESTURE_3_FINGER_TRIPLE_TAP_AND_HOLD,
             GESTURE_DOUBLE_TAP,
             GESTURE_DOUBLE_TAP_AND_HOLD,
             GESTURE_SWIPE_UP,
@@ -133,17 +148,27 @@
     @GestureId
     private final int mGestureId;
     private final int mDisplayId;
+    private List<MotionEvent> mMotionEvents = new ArrayList<>();
+
+    /** @hide */
+    public AccessibilityGestureEvent(
+            int gestureId, int displayId, @NonNull List<MotionEvent> motionEvents) {
+        mGestureId = gestureId;
+        mDisplayId = displayId;
+        mMotionEvents.addAll(motionEvents);
+    }
 
     /** @hide */
     @TestApi
     public AccessibilityGestureEvent(int gestureId, int displayId) {
-        mGestureId = gestureId;
-        mDisplayId = displayId;
+        this(gestureId, displayId, new ArrayList<MotionEvent>());
     }
 
     private AccessibilityGestureEvent(@NonNull Parcel parcel) {
         mGestureId = parcel.readInt();
         mDisplayId = parcel.readInt();
+        ParceledListSlice<MotionEvent> slice = parcel.readParcelable(getClass().getClassLoader());
+        mMotionEvents = slice.getList();
     }
 
     /**
@@ -166,6 +191,15 @@
         return mGestureId;
     }
 
+    /**
+     * Returns the motion events that lead to this gesture.
+     *
+     */
+    @NonNull
+    public List<MotionEvent> getMotionEvents() {
+        return mMotionEvents;
+    }
+
     @NonNull
     @Override
     public String toString() {
@@ -173,22 +207,42 @@
         stringBuilder.append("gestureId: ").append(eventTypeToString(mGestureId));
         stringBuilder.append(", ");
         stringBuilder.append("displayId: ").append(mDisplayId);
+        stringBuilder.append(", ");
+        stringBuilder.append("Motion Events: [");
+        for (int i = 0; i < mMotionEvents.size(); ++i) {
+            String action = MotionEvent.actionToString(mMotionEvents.get(i).getActionMasked());
+            stringBuilder.append(action);
+            if (i < (mMotionEvents.size() - 1)) {
+                stringBuilder.append(", ");
+            } else {
+                stringBuilder.append("]");
+            }
+        }
         stringBuilder.append(']');
         return stringBuilder.toString();
     }
 
     private static String eventTypeToString(int eventType) {
         switch (eventType) {
+            case GESTURE_UNKNOWN: return "GESTURE_UNKNOWN";
+            case GESTURE_PASSTHROUGH: return "GESTURE_PASSTHROUGH";
+            case GESTURE_TOUCH_EXPLORATION: return "GESTURE_TOUCH_EXPLORATION";
             case GESTURE_2_FINGER_SINGLE_TAP: return "GESTURE_2_FINGER_SINGLE_TAP";
+            case GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD:
+                return "GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD";
             case GESTURE_2_FINGER_DOUBLE_TAP: return "GESTURE_2_FINGER_DOUBLE_TAP";
             case GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD:
                 return "GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD";
             case GESTURE_2_FINGER_TRIPLE_TAP: return "GESTURE_2_FINGER_TRIPLE_TAP";
             case GESTURE_3_FINGER_SINGLE_TAP: return "GESTURE_3_FINGER_SINGLE_TAP";
+            case GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD:
+                return "GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD";
             case GESTURE_3_FINGER_DOUBLE_TAP: return "GESTURE_3_FINGER_DOUBLE_TAP";
             case GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD:
                 return "GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD";
             case GESTURE_3_FINGER_TRIPLE_TAP: return "GESTURE_3_FINGER_TRIPLE_TAP";
+            case GESTURE_3_FINGER_TRIPLE_TAP_AND_HOLD:
+                return "GESTURE_3_FINGER_TRIPLE_TAP_AND_HOLD";
             case GESTURE_4_FINGER_SINGLE_TAP: return "GESTURE_4_FINGER_SINGLE_TAP";
             case GESTURE_4_FINGER_DOUBLE_TAP: return "GESTURE_4_FINGER_DOUBLE_TAP";
             case GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD:
@@ -240,6 +294,7 @@
     public void writeToParcel(@NonNull Parcel parcel, int flags) {
         parcel.writeInt(mGestureId);
         parcel.writeInt(mDisplayId);
+        parcel.writeParcelable(new ParceledListSlice<MotionEvent>(mMotionEvents), 0);
     }
 
     /**
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index b5b0ce3..0ad9e44 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -235,6 +235,29 @@
 public abstract class AccessibilityService extends Service {
 
     /**
+     * The user has performed a touch-exploration gesture on the touch screen without ever
+     * triggering gesture detection. This gesture is only dispatched when {@link
+     * AccessibilityServiceInfo#FLAG_SEND_MOTION_EVENTS} is set.
+     *
+     * @hide
+     */
+    public static final int GESTURE_TOUCH_EXPLORATION = -2;
+
+    /**
+     * The user has performed a passthrough gesture on the touch screen without ever triggering
+     * gesture detection. This gesture is only dispatched when {@link
+     * AccessibilityServiceInfo#FLAG_SEND_MOTION_EVENTS} is set.
+     * @hide
+     */
+    public static final int GESTURE_PASSTHROUGH = -1;
+
+    /**
+     * The user has performed an unrecognized gesture on the touch screen. This gesture is only
+     * dispatched when {@link AccessibilityServiceInfo#FLAG_SEND_MOTION_EVENTS} is set.
+     */
+    public static final int GESTURE_UNKNOWN = 0;
+
+    /**
      * The user has performed a swipe up gesture on the touch screen.
      */
     public static final int GESTURE_SWIPE_UP = 1;
@@ -421,6 +444,15 @@
     /** The user has performed a three-finger double tap and hold gesture on the touch screen. */
     public static final int GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD = 41;
 
+    /** The user has performed a two-finger  single-tap and hold gesture on the touch screen. */
+    public static final int GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD = 43;
+
+    /** The user has performed a three-finger  single-tap and hold gesture on the touch screen. */
+    public static final int GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD = 44;
+
+    /** The user has performed a three-finger  triple-tap and hold gesture on the touch screen. */
+    public static final int GESTURE_3_FINGER_TRIPLE_TAP_AND_HOLD = 45;
+
     /** The user has performed a two-finger double tap and hold gesture on the touch screen. */
     public static final int GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD = 42;
 
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index d334de6..f953da4 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -376,6 +376,20 @@
      */
     public static final int FLAG_REQUEST_2_FINGER_PASSTHROUGH = 0x0002000;
 
+    /**
+     * This flag requests that when when {@link #FLAG_REQUEST_TOUCH_EXPLORATION_MODE} is enabled, a
+     * service will receive the motion events for each successfully-detected gesture. The service
+     * will also receive an AccessibilityGestureEvent of type GESTURE_INVALID for each cancelled
+     * gesture along with its motion events. A service will receive a gesture of type
+     * GESTURE_PASSTHROUGH and accompanying motion events for every passthrough gesture that does
+     * not start gesture detection. This information can be used to collect instances of improper
+     * gesture detection behavior and relay that information to framework developers. If {@link
+     * #FLAG_REQUEST_TOUCH_EXPLORATION_MODE} is disabled this flag has no effect.
+     *
+     * @see #FLAG_REQUEST_TOUCH_EXPLORATION_MODE
+     */
+    public static final int FLAG_SEND_MOTION_EVENTS = 0x0004000;
+
     /** {@hide} */
     public static final int FLAG_FORCE_DIRECT_BOOT_AWARE = 0x00010000;
 
@@ -1077,7 +1091,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (this == obj) {
             return true;
         }
@@ -1276,6 +1290,8 @@
                 return "FLAG_REQUEST_MULTI_FINGER_GESTURES";
             case FLAG_REQUEST_2_FINGER_PASSTHROUGH:
                 return "FLAG_REQUEST_2_FINGER_PASSTHROUGH";
+            case FLAG_SEND_MOTION_EVENTS:
+                return "FLAG_SEND_MOTION_EVENTS";
             case FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY:
                 return "FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY";
             case FLAG_REPORT_VIEW_IDS:
diff --git a/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
index 623734e..fe1cca5 100644
--- a/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
@@ -287,7 +287,7 @@
      * {@inheritDoc}
      */
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (this == obj) {
             return true;
         }
diff --git a/core/java/android/accounts/Account.java b/core/java/android/accounts/Account.java
index 9a18880..b960a7f 100644
--- a/core/java/android/accounts/Account.java
+++ b/core/java/android/accounts/Account.java
@@ -50,7 +50,7 @@
     @UnsupportedAppUsage
     private final @Nullable String accessId;
 
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (o == this) return true;
         if (!(o instanceof Account)) return false;
         final Account other = (Account)o;
diff --git a/core/java/android/accounts/AccountAndUser.java b/core/java/android/accounts/AccountAndUser.java
index fd67394..adbc4e9 100644
--- a/core/java/android/accounts/AccountAndUser.java
+++ b/core/java/android/accounts/AccountAndUser.java
@@ -16,6 +16,7 @@
 
 package android.accounts;
 
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 
 /**
@@ -35,7 +36,7 @@
         this.userId = userId;
     }
 
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) return true;
         if (!(o instanceof AccountAndUser)) return false;
         final AccountAndUser other = (AccountAndUser) o;
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 9a810a7..9bb02cd 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -19,6 +19,7 @@
 import android.annotation.BroadcastBehavior;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
@@ -357,7 +358,7 @@
         }
 
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (o == null) {
                 return false;
             }
@@ -419,7 +420,7 @@
         }
 
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (o == null) {
                 return false;
             }
diff --git a/core/java/android/accounts/AuthenticatorDescription.java b/core/java/android/accounts/AuthenticatorDescription.java
index b7bf11d..4be3538 100644
--- a/core/java/android/accounts/AuthenticatorDescription.java
+++ b/core/java/android/accounts/AuthenticatorDescription.java
@@ -16,6 +16,7 @@
 
 package android.accounts;
 
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Parcel;
@@ -111,7 +112,7 @@
     }
 
     /** Compares the type only, suitable for key comparisons. */
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (o == this) return true;
         if (!(o instanceof AuthenticatorDescription)) return false;
         final AuthenticatorDescription other = (AuthenticatorDescription) o;
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index e75d2f6..250f2f0 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -100,6 +100,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
+import java.util.concurrent.Executor;
 
 /**
  * <p>
@@ -1619,7 +1620,7 @@
         }
 
         @Override
-        public boolean equals(Object obj) {
+        public boolean equals(@Nullable Object obj) {
             if (!(obj instanceof TaskDescription)) {
                 return false;
             }
@@ -3607,7 +3608,7 @@
      * running its code, {@link RunningAppProcessInfo#IMPORTANCE_GONE} is returned.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS)
     public @RunningAppProcessInfo.Importance int getPackageImportance(String packageName) {
         try {
@@ -3627,7 +3628,7 @@
      * running its code, {@link RunningAppProcessInfo#IMPORTANCE_GONE} is returned.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS)
     public @RunningAppProcessInfo.Importance int getUidImportance(int uid) {
         try {
@@ -3644,7 +3645,7 @@
      * {@link #addOnUidImportanceListener}.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public interface OnUidImportanceListener {
         /**
          * The importance if a given uid has changed.  Will be one of the importance
@@ -3675,7 +3676,7 @@
      * {@link android.Manifest.permission#PACKAGE_USAGE_STATS}.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS)
     public void addOnUidImportanceListener(OnUidImportanceListener listener,
             @RunningAppProcessInfo.Importance int importanceCutpoint) {
@@ -3704,7 +3705,7 @@
      * @throws IllegalArgumentException If the listener is not registered.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS)
     public void removeOnUidImportanceListener(OnUidImportanceListener listener) {
         synchronized (this) {
@@ -3846,7 +3847,7 @@
      * @see #forceStopPackageAsUser(String, int)
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     @RequiresPermission(Manifest.permission.FORCE_STOP_PACKAGES)
     public void forceStopPackage(String packageName) {
         forceStopPackageAsUser(packageName, mContext.getUserId());
@@ -4185,7 +4186,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
     public boolean switchUser(@NonNull UserHandle user) {
         if (user == null) {
@@ -4618,7 +4618,7 @@
      *
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     @RequiresPermission(Manifest.permission.FORCE_STOP_PACKAGES)
     public void killProcessesWhenImperceptible(@NonNull int[] pids, @NonNull String reason) {
         try {
@@ -4752,31 +4752,43 @@
     }
 
     /**
-     * Register with {@link HomeVisibilityObserver} with ActivityManager.
-     * TODO: b/144351078 expose as SystemApi
+     * Register to be notified when the visibility of the home screen changes.
+     *
+     * @param executor The executor on which the listener should be called.
+     * @param listener The listener that is called when home visibility changes.
      * @hide
      */
-    public void registerHomeVisibilityObserver(@NonNull HomeVisibilityObserver observer) {
-        Preconditions.checkNotNull(observer);
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
+    public void addHomeVisibilityListener(@NonNull Executor executor,
+            @NonNull HomeVisibilityListener listener) {
+        Preconditions.checkNotNull(listener);
+        Preconditions.checkNotNull(executor);
         try {
-            observer.init(mContext, this);
-            getService().registerProcessObserver(observer.mObserver);
+            listener.init(mContext, executor, this);
+            getService().registerProcessObserver(listener.mObserver);
             // Notify upon first registration.
-            observer.onHomeVisibilityChanged(observer.mIsHomeActivityVisible);
+            executor.execute(() ->
+                    listener.onHomeVisibilityChanged(listener.mIsHomeActivityVisible));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Unregister with {@link HomeVisibilityObserver} with ActivityManager.
-     * TODO: b/144351078 expose as SystemApi
+     * Removes a listener that was previously added with {@link #addHomeVisibilityListener}.
+     *
+     * @param listener The listener that was previously added.
      * @hide
      */
-    public void unregisterHomeVisibilityObserver(@NonNull HomeVisibilityObserver observer) {
-        Preconditions.checkNotNull(observer);
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
+    public void removeHomeVisibilityListener(@NonNull HomeVisibilityListener listener) {
+        Preconditions.checkNotNull(listener);
         try {
-            getService().unregisterProcessObserver(observer.mObserver);
+            getService().unregisterProcessObserver(listener.mObserver);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 1f8cf8a..89cb8b8 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -103,6 +103,11 @@
             IBinder whitelistToken, long duration);
 
     /**
+     * Returns the flags set for a {@link PendingIntent}.
+     */
+    public abstract int getPendingIntentFlags(IIntentSender target);
+
+    /**
      * Allows a {@link PendingIntent} to start activities from background.
      */
     public abstract void setPendingIntentAllowBgActivityStarts(
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 85cb120..ef146b1 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -37,6 +37,7 @@
 import android.hardware.HardwareBuffer;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.IRemoteCallback;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -306,6 +307,12 @@
     private static final String KEY_REMOTE_ANIMATION_ADAPTER
             = "android:activity.remoteAnimationAdapter";
 
+    /**
+     * @see #setLaunchCookie
+     * @hide
+     */
+    private static final String KEY_LAUNCH_COOKIE = "android.activity.launchCookie";
+
     /** @hide */
     public static final int ANIM_UNDEFINED = -1;
     /** @hide */
@@ -381,6 +388,7 @@
     private Bundle mAppVerificationBundle;
     private IAppTransitionAnimationSpecsFuture mSpecsFuture;
     private RemoteAnimationAdapter mRemoteAnimationAdapter;
+    private IBinder mLaunchCookie;
 
     /**
      * Create an ActivityOptions specifying a custom animation to run when
@@ -1071,6 +1079,7 @@
                     KEY_SPECS_FUTURE));
         }
         mRemoteAnimationAdapter = opts.getParcelable(KEY_REMOTE_ANIMATION_ADAPTER);
+        mLaunchCookie = opts.getBinder(KEY_LAUNCH_COOKIE);
     }
 
     /**
@@ -1284,10 +1293,10 @@
     }
 
     /**
-     * Sets the id of the display where activity should be launched.
-     * An app can launch activities on public displays or private displays that are owned by the app
-     * or where an app already has activities. Otherwise, trying to launch on a private display
-     * or providing an invalid display id will result in an exception.
+     * Sets the id of the display where the activity should be launched.
+     * An app can launch activities on public displays or displays where the app already has
+     * activities. Otherwise, trying to launch on a private display or providing an invalid display
+     * id will result in an exception.
      * <p>
      * Setting launch display id will be ignored on devices that don't have
      * {@link android.content.pm.PackageManager#FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS}.
@@ -1496,6 +1505,25 @@
     }
 
     /**
+     * Sets a launch cookie that can be used to track the activity and task that are launch as a
+     * result of this option.
+     *
+     * @hide
+     */
+    public void setLaunchCookie(IBinder launchCookie) {
+        mLaunchCookie = launchCookie;
+    }
+
+    /**
+     * @return The launch tracking cookie if set or {@code null} otherwise.
+     *
+     * @hide
+     */
+    public IBinder getLaunchCookie() {
+        return mLaunchCookie;
+    }
+
+    /**
      * Update the current values in this ActivityOptions from those supplied
      * in <var>otherOptions</var>.  Any values
      * defined in <var>otherOptions</var> replace those in the base options.
@@ -1717,6 +1745,9 @@
         if (mRemoteAnimationAdapter != null) {
             b.putParcelable(KEY_REMOTE_ANIMATION_ADAPTER, mRemoteAnimationAdapter);
         }
+        if (mLaunchCookie != null) {
+            b.putBinder(KEY_LAUNCH_COOKIE, mLaunchCookie);
+        }
         return b;
     }
 
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 3e4d5ee..9edf81e 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -211,23 +211,23 @@
     }
 
     /**
-     * Removes stacks in the windowing modes from the system if they are of activity type
+     * Removes root tasks in the windowing modes from the system if they are of activity type
      * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
      */
     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
-    public void removeStacksInWindowingModes(int[] windowingModes) throws SecurityException {
+    public void removeRootTasksInWindowingModes(@NonNull int[] windowingModes) {
         try {
-            getService().removeStacksInWindowingModes(windowingModes);
+            getService().removeRootTasksInWindowingModes(windowingModes);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
-    /** Removes stack of the activity types from the system. */
+    /** Removes root tasks of the activity types from the system. */
     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
-    public void removeStacksWithActivityTypes(int[] activityTypes) throws SecurityException {
+    public void removeRootTasksWithActivityTypes(@NonNull int[] activityTypes) {
         try {
-            getService().removeStacksWithActivityTypes(activityTypes);
+            getService().removeRootTasksWithActivityTypes(activityTypes);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -310,15 +310,15 @@
     }
 
     /**
-     * Moves the top activity in the input stackId to the pinned stack.
-     * @param stackId Id of stack to move the top activity to pinned stack.
-     * @param bounds Bounds to use for pinned stack.
-     * @return True if the top activity of stack was successfully moved to the pinned stack.
+     * Moves the top activity in the input rootTaskId to the pinned root task.
+     * @param rootTaskId Id of root task to move the top activity to pinned root task.
+     * @param bounds Bounds to use for pinned root task.
+     * @return True if the top activity of root task was successfully moved to the pinned root task.
      */
     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
-    public boolean moveTopActivityToPinnedStack(int stackId, Rect bounds) {
+    public boolean moveTopActivityToPinnedRootTask(int rootTaskId, @NonNull Rect bounds) {
         try {
-            return getService().moveTopActivityToPinnedStack(stackId, bounds);
+            return getService().moveTopActivityToPinnedRootTask(rootTaskId, bounds);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -350,15 +350,15 @@
     }
 
     /**
-     * Move task to stack with given id.
+     * Move task to root task with given id.
      * @param taskId Id of the task to move.
-     * @param stackId Id of the stack for task moving.
+     * @param rootTaskId Id of the rootTask for task moving.
      * @param toTop Whether the given task should shown to top of stack.
      */
     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
-    public void moveTaskToStack(int taskId, int stackId, boolean toTop) {
+    public void moveTaskToRootTask(int taskId, int rootTaskId, boolean toTop) {
         try {
-            getService().moveTaskToStack(taskId, stackId, toTop);
+            getService().moveTaskToRootTask(taskId, rootTaskId, toTop);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -380,13 +380,13 @@
 
     /**
      * Resize docked stack & its task to given stack & task bounds.
-     * @param stackBounds Bounds to resize stack.
+     * @param rootTaskBounds Bounds to resize stack.
      * @param taskBounds Bounds to resize task.
      */
     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
-    public void resizeDockedStack(Rect stackBounds, Rect taskBounds) {
+    public void resizePrimarySplitScreen(@NonNull Rect rootTaskBounds, @NonNull Rect taskBounds) {
         try {
-            getService().resizeDockedStack(stackBounds, taskBounds, null, null, null);
+            getService().resizePrimarySplitScreen(rootTaskBounds, taskBounds, null, null, null);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -406,19 +406,6 @@
     }
 
     /**
-     * Makes the display with the given id a single task instance display. I.e the display can only
-     * contain one task.
-     */
-    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
-    public void setDisplayToSingleTaskInstance(int displayId) {
-        try {
-            getService().setDisplayToSingleTaskInstance(displayId);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * Requests that an activity should enter picture-in-picture mode if possible.
      * @hide
      */
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index bb194a5..87c729b 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -435,7 +435,7 @@
         }
 
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (o instanceof ProviderKey) {
                 final ProviderKey other = (ProviderKey) o;
                 return Objects.equals(authority, other.authority) && userId == other.userId;
@@ -513,7 +513,6 @@
         @UnsupportedAppUsage
         boolean stopped;
         boolean hideForNow;
-        Configuration newConfig;
         Configuration createdConfig;
         Configuration overrideConfig;
         // Used to save the last reported configuration from server side so that activity
@@ -2222,21 +2221,6 @@
         return sPermissionManager;
     }
 
-    private Configuration mMainThreadConfig = new Configuration();
-
-    Configuration applyConfigCompatMainThread(int displayDensity, Configuration config,
-            CompatibilityInfo compat) {
-        if (config == null) {
-            return null;
-        }
-        if (!compat.supportsScreen()) {
-            mMainThreadConfig.setTo(config);
-            config = mMainThreadConfig;
-            compat.applyToConfiguration(displayDensity, config);
-        }
-        return config;
-    }
-
     /**
      * Creates the top level resources for the given package. Will return an existing
      * Resources if one has already been created.
@@ -2791,7 +2775,7 @@
                         memInfo.getTotalPrivateDirty(),
                         memInfo.getTotalPrivateClean(),
                         memInfo.hasSwappedOutPss ? memInfo.getTotalSwappedOutPss() :
-                        memInfo.getTotalSwappedOut(), memInfo.getTotalPss(),
+                        memInfo.getTotalSwappedOut(), memInfo.getTotalRss(),
                         nativeMax+dalvikMax,
                         nativeAllocated+dalvikAllocated, nativeFree+dalvikFree);
             }
@@ -3144,12 +3128,25 @@
         }
     }
 
+    /**
+     * Returns {@code true} if the {@link android.app.ActivityManager.ProcessState} of the current
+     * process is cached.
+     */
+    @VisibleForTesting
+    public boolean isCachedProcessState() {
+        synchronized (mAppThread) {
+            return mLastProcessState >= ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
+        }
+    }
+
     @Override
     public void updateProcessState(int processState, boolean fromIpc) {
+        final boolean wasCached;
         synchronized (mAppThread) {
             if (mLastProcessState == processState) {
                 return;
             }
+            wasCached = isCachedProcessState();
             mLastProcessState = processState;
             // Defer the top state for VM to avoid aggressive JIT compilation affecting activity
             // launch time.
@@ -3166,6 +3163,24 @@
                         + (fromIpc ? " (from ipc" : ""));
             }
         }
+
+        // Handle the pending configuration if the process state is changed from cached to
+        // non-cached. Except the case where there is a launching activity because the
+        // LaunchActivityItem will handle it.
+        if (wasCached && !isCachedProcessState() && mNumLaunchingActivities.get() == 0) {
+            final Configuration pendingConfig;
+            synchronized (mResourcesManager) {
+                pendingConfig = mPendingConfiguration;
+            }
+            if (pendingConfig == null) {
+                return;
+            }
+            if (Looper.myLooper() == mH.getLooper()) {
+                handleConfigurationChanged(pendingConfig);
+            } else {
+                sendMessage(H.CONFIGURATION_CHANGED, pendingConfig);
+            }
+        }
     }
 
     /** Update VM state based on ActivityManager.PROCESS_STATE_* constants. */
@@ -3600,7 +3615,7 @@
             TAG, "Handling launch of " + r);
 
         // Initialize before creating the activity
-        if (!ThreadedRenderer.sRendererDisabled
+        if (ThreadedRenderer.sRendererEnabled
                 && (r.activityInfo.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
             HardwareRenderer.preload();
         }
@@ -4566,14 +4581,6 @@
         // The window is now visible if it has been added, we are not
         // simply finishing, and we are not starting another activity.
         if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {
-            if (r.newConfig != null) {
-                performConfigurationChangedForActivity(r, r.newConfig);
-                if (DEBUG_CONFIGURATION) {
-                    Slog.v(TAG, "Resuming activity " + r.activityInfo.name + " with newConfig "
-                            + r.activity.mCurrentConfig);
-                }
-                r.newConfig = null;
-            }
             if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward=" + isForward);
             ViewRootImpl impl = r.window.getDecorView().getViewRootImpl();
             WindowManager.LayoutParams l = impl != null
@@ -4879,13 +4886,6 @@
                         r.activity.makeVisible();
                     }
                 }
-                if (r.newConfig != null) {
-                    performConfigurationChangedForActivity(r, r.newConfig);
-                    if (DEBUG_CONFIGURATION) Slog.v(TAG, "Updating activity vis "
-                            + r.activityInfo.name + " with new config "
-                            + r.activity.mCurrentConfig);
-                    r.newConfig = null;
-                }
             } else {
                 if (r.activity.mVisibleFromServer) {
                     r.activity.mVisibleFromServer = false;
@@ -5457,8 +5457,7 @@
         }
     }
 
-    ArrayList<ComponentCallbacks2> collectComponentCallbacks(
-            boolean allActivities, Configuration newConfig) {
+    ArrayList<ComponentCallbacks2> collectComponentCallbacks(boolean includeActivities) {
         ArrayList<ComponentCallbacks2> callbacks
                 = new ArrayList<ComponentCallbacks2>();
 
@@ -5467,29 +5466,11 @@
             for (int i=0; i<NAPP; i++) {
                 callbacks.add(mAllApplications.get(i));
             }
-            final int NACT = mActivities.size();
-            for (int i=0; i<NACT; i++) {
-                ActivityClientRecord ar = mActivities.valueAt(i);
-                Activity a = ar.activity;
-                if (a != null) {
-                    Configuration thisConfig = applyConfigCompatMainThread(
-                            mCurDefaultDisplayDpi, newConfig,
-                            ar.packageInfo.getCompatibilityInfo());
-                    if (!ar.activity.mFinished && (allActivities || !ar.paused)) {
-                        // If the activity is currently resumed, its configuration
-                        // needs to change right now.
+            if (includeActivities) {
+                for (int i = mActivities.size() - 1; i >= 0; i--) {
+                    final Activity a = mActivities.valueAt(i).activity;
+                    if (a != null && !a.mFinished) {
                         callbacks.add(a);
-                    } else if (thisConfig != null) {
-                        // Otherwise, we will tell it about the change
-                        // the next time it is resumed or shown.  Note that
-                        // the activity manager may, before then, decide the
-                        // activity needs to be destroyed to handle its new
-                        // configuration.
-                        if (DEBUG_CONFIGURATION) {
-                            Slog.v(TAG, "Setting activity "
-                                    + ar.activityInfo.name + " newConfig=" + thisConfig);
-                        }
-                        ar.newConfig = thisConfig;
                     }
                 }
             }
@@ -5513,17 +5494,6 @@
     }
 
     /**
-     * Updates the configuration for an Activity in its current display.
-     *
-     * @see #performConfigurationChangedForActivity(ActivityClientRecord, Configuration, int,
-     *      boolean)
-     */
-    private void performConfigurationChangedForActivity(ActivityClientRecord r,
-            Configuration newBaseConfig) {
-        performConfigurationChangedForActivity(r, newBaseConfig, r.activity.getDisplayId());
-    }
-
-    /**
      * Updates the configuration for an Activity. The ActivityClientRecord's
      * {@link ActivityClientRecord#overrideConfig} is used to compute the final Configuration for
      * that Activity. {@link ActivityClientRecord#tmpConfig} is used as a temporary for delivering
@@ -5687,6 +5657,12 @@
 
     @Override
     public void handleConfigurationChanged(Configuration config) {
+        if (isCachedProcessState()) {
+            updatePendingConfiguration(config);
+            // If the process is in a cached state, delay the handling until the process is no
+            // longer cached.
+            return;
+        }
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "configChanged");
         mCurDefaultDisplayDpi = config.densityDpi;
         handleConfigurationChanged(config, null /* compat */);
@@ -5767,7 +5743,8 @@
             }
         }
 
-        ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(false, config);
+        final ArrayList<ComponentCallbacks2> callbacks =
+                collectComponentCallbacks(false /* includeActivities */);
 
         freeTextLayoutCachesIfNeeded(configDiff);
 
@@ -5775,13 +5752,7 @@
             final int N = callbacks.size();
             for (int i=0; i<N; i++) {
                 ComponentCallbacks2 cb = callbacks.get(i);
-                if (cb instanceof Activity) {
-                    // If callback is an Activity - call corresponding method to consider override
-                    // config and avoid onConfigurationChanged if it hasn't changed.
-                    Activity a = (Activity) cb;
-                    performConfigurationChangedForActivity(mActivities.get(a.getActivityToken()),
-                            config);
-                } else if (!equivalent) {
+                if (!equivalent) {
                     performConfigurationChanged(cb, config);
                 } else {
                     // TODO (b/135719017): Temporary log for debugging IME service.
@@ -6169,7 +6140,8 @@
     }
 
     final void handleLowMemory() {
-        ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(true, null);
+        final ArrayList<ComponentCallbacks2> callbacks =
+                collectComponentCallbacks(true /* includeActivities */);
 
         final int N = callbacks.size();
         for (int i=0; i<N; i++) {
@@ -6201,7 +6173,8 @@
             }
         }
 
-        ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(true, null);
+        final ArrayList<ComponentCallbacks2> callbacks =
+                collectComponentCallbacks(true /* includeActivities */);
 
         final int N = callbacks.size();
         for (int i = 0; i < N; i++) {
@@ -7380,14 +7353,7 @@
 
     @UnsupportedAppUsage
     public static ActivityThread systemMain() {
-        // The system process on low-memory devices do not get to use hardware
-        // accelerated drawing, since this can add too much overhead to the
-        // process.
-        if (!ActivityManager.isHighEndGfx()) {
-            ThreadedRenderer.disable(true);
-        } else {
-            ThreadedRenderer.enableForegroundTrimming();
-        }
+        ThreadedRenderer.initForSystemProcess();
         ActivityThread thread = new ActivityThread();
         thread.attach(true, 0);
         return thread;
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index db95eae..8c42a8a 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -85,8 +85,7 @@
     }
 
     public ActivityView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0 /* defStyle */, false /* singleTaskInstance */,
-                false /* usePublicVirtualDisplay */,
+        this(context, attrs, 0 /* defStyle */, false /* usePublicVirtualDisplay */,
                 false /* disableSurfaceViewBackgroundLayer */, false /* useTrustedDisplay */);
     }
 
@@ -97,22 +96,21 @@
      */
     public ActivityView(
             @NonNull Context context, @NonNull AttributeSet attrs, int defStyle,
-            boolean singleTaskInstance, boolean usePublicVirtualDisplay) {
-        this(context, attrs, defStyle, singleTaskInstance, usePublicVirtualDisplay,
-                false /* disableSurfaceViewBackgroundLayer */,
-                false /* useTrustedDisplay */);
+            boolean usePublicVirtualDisplay) {
+        this(context, attrs, defStyle, usePublicVirtualDisplay,
+                false /* disableSurfaceViewBackgroundLayer */, false /* useTrustedDisplay */);
     }
 
     private ActivityView(
             @NonNull Context context, @NonNull AttributeSet attrs, int defStyle,
-            boolean singleTaskInstance, boolean usePublicVirtualDisplay,
-            boolean disableSurfaceViewBackgroundLayer, boolean useTrustedDisplay) {
+            boolean usePublicVirtualDisplay, boolean disableSurfaceViewBackgroundLayer,
+            boolean useTrustedDisplay) {
         super(context, attrs, defStyle);
         if (useTaskOrganizer()) {
             mTaskEmbedder = new TaskOrganizerTaskEmbedder(context, this);
         } else {
-            mTaskEmbedder = new VirtualDisplayTaskEmbedder(context, this, singleTaskInstance,
-                    usePublicVirtualDisplay, useTrustedDisplay);
+            mTaskEmbedder = new VirtualDisplayTaskEmbedder(context, this, usePublicVirtualDisplay,
+                    useTrustedDisplay);
         }
         mSurfaceView = new SurfaceView(context, null, 0, 0, disableSurfaceViewBackgroundLayer);
         // Since ActivityView#getAlpha has been overridden, we should use parent class's alpha
@@ -641,7 +639,6 @@
         private final Context mContext;
         private AttributeSet mAttrs;
         private int mDefStyle;
-        private boolean mSingleInstance;
         private boolean mUsePublicVirtualDisplay;
         private boolean mDisableSurfaceViewBackgroundLayer;
         private boolean mUseTrustedDisplay;
@@ -650,7 +647,6 @@
             mContext = context;
             mAttrs = null;
             mDefStyle = 0;
-            mSingleInstance = false;
             mUsePublicVirtualDisplay = false;
             mDisableSurfaceViewBackgroundLayer = false;
             mUseTrustedDisplay = false;
@@ -676,13 +672,6 @@
             return this;
         }
 
-        /** Sets to {@code true} to make the {@link ActivityView} single instance. */
-        @NonNull
-        public Builder setSingleInstance(boolean singleInstance) {
-            mSingleInstance = singleInstance;
-            return this;
-        }
-
         /**
          * Sets to {@code true} to use public virtual display for the {@link ActivityView}.
          * <p>
@@ -722,7 +711,7 @@
         /** Creates an {@link ActivityView} */
         @NonNull
         public ActivityView build() {
-            return new ActivityView(mContext, mAttrs, mDefStyle, mSingleInstance,
+            return new ActivityView(mContext, mAttrs, mDefStyle,
                     mUsePublicVirtualDisplay, mDisableSurfaceViewBackgroundLayer,
                     mUseTrustedDisplay);
         }
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index ef4f099..f3b3789 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -461,7 +461,6 @@
      * state the more important the UID is for the user.
      * @hide
      */
-    @TestApi
     @SystemApi
     public static final int UID_STATE_PERSISTENT = 100;
 
@@ -470,7 +469,6 @@
      * state the more important the UID is for the user.
      * @hide
      */
-    @TestApi
     @SystemApi
     public static final int UID_STATE_TOP = 200;
 
@@ -482,7 +480,6 @@
      * @hide
      * @deprecated
      */
-    @TestApi
     @SystemApi
     @Deprecated
     public static final int UID_STATE_FOREGROUND_SERVICE_LOCATION = 300;
@@ -492,7 +489,6 @@
      * state the more important the UID is for the user.
      * @hide
      */
-    @TestApi
     @SystemApi
     public static final int UID_STATE_FOREGROUND_SERVICE = 400;
 
@@ -501,7 +497,6 @@
      * state the more important the UID is for the user.
      * @hide
      */
-    @TestApi
     @SystemApi
     public static final int UID_STATE_FOREGROUND = 500;
 
@@ -517,7 +512,6 @@
      * state the more important the UID is for the user.
      * @hide
      */
-    @TestApi
     @SystemApi
     public static final int UID_STATE_BACKGROUND = 600;
 
@@ -526,7 +520,6 @@
      * state the more important the UID is for the user.
      * @hide
      */
-    @TestApi
     @SystemApi
     public static final int UID_STATE_CACHED = 700;
 
@@ -604,7 +597,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     public static final int OP_FLAG_SELF = 0x1;
 
@@ -615,7 +607,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     public static final int OP_FLAG_TRUSTED_PROXY = 0x2;
 
@@ -626,7 +617,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     public static final int OP_FLAG_UNTRUSTED_PROXY = 0x4;
 
@@ -637,7 +627,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     public static final int OP_FLAG_TRUSTED_PROXIED = 0x8;
 
@@ -648,7 +637,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     public static final int OP_FLAG_UNTRUSTED_PROXIED = 0x10;
 
@@ -660,7 +648,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     public static final int OP_FLAGS_ALL =
             OP_FLAG_SELF
@@ -1185,7 +1172,7 @@
     public static final String OPSTR_GET_USAGE_STATS
             = "android:get_usage_stats";
     /** Activate a VPN connection without user intervention. @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_ACTIVATE_VPN
             = "android:activate_vpn";
     /** Allows an application to read the user's contacts data. */
@@ -1267,7 +1254,7 @@
     public static final String OPSTR_WRITE_SETTINGS
             = "android:write_settings";
     /** @hide Get device accounts. */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_GET_ACCOUNTS
             = "android:get_accounts";
     public static final String OPSTR_READ_PHONE_NUMBERS
@@ -1276,7 +1263,7 @@
     public static final String OPSTR_PICTURE_IN_PICTURE
             = "android:picture_in_picture";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_INSTANT_APP_START_FOREGROUND
             = "android:instant_app_start_foreground";
     /** Answer incoming phone calls */
@@ -1286,129 +1273,129 @@
      * Accept call handover
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_ACCEPT_HANDOVER
             = "android:accept_handover";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_GPS = "android:gps";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_VIBRATE = "android:vibrate";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_WIFI_SCAN = "android:wifi_scan";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_POST_NOTIFICATION = "android:post_notification";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_NEIGHBORING_CELLS = "android:neighboring_cells";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_WRITE_SMS = "android:write_sms";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_RECEIVE_EMERGENCY_BROADCAST =
             "android:receive_emergency_broadcast";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_READ_ICC_SMS = "android:read_icc_sms";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_WRITE_ICC_SMS = "android:write_icc_sms";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_ACCESS_NOTIFICATIONS = "android:access_notifications";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_PLAY_AUDIO = "android:play_audio";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_READ_CLIPBOARD = "android:read_clipboard";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_WRITE_CLIPBOARD = "android:write_clipboard";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_TAKE_MEDIA_BUTTONS = "android:take_media_buttons";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_TAKE_AUDIO_FOCUS = "android:take_audio_focus";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_AUDIO_MASTER_VOLUME = "android:audio_master_volume";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_AUDIO_VOICE_VOLUME = "android:audio_voice_volume";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_AUDIO_RING_VOLUME = "android:audio_ring_volume";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_AUDIO_MEDIA_VOLUME = "android:audio_media_volume";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_AUDIO_ALARM_VOLUME = "android:audio_alarm_volume";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_AUDIO_NOTIFICATION_VOLUME =
             "android:audio_notification_volume";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_AUDIO_BLUETOOTH_VOLUME = "android:audio_bluetooth_volume";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_WAKE_LOCK = "android:wake_lock";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_MUTE_MICROPHONE = "android:mute_microphone";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_TOAST_WINDOW = "android:toast_window";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_PROJECT_MEDIA = "android:project_media";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_WRITE_WALLPAPER = "android:write_wallpaper";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_ASSIST_STRUCTURE = "android:assist_structure";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_ASSIST_SCREENSHOT = "android:assist_screenshot";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_TURN_SCREEN_ON = "android:turn_screen_on";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_RUN_IN_BACKGROUND = "android:run_in_background";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_AUDIO_ACCESSIBILITY_VOLUME =
             "android:audio_accessibility_volume";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_REQUEST_INSTALL_PACKAGES = "android:request_install_packages";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_RUN_ANY_IN_BACKGROUND = "android:run_any_in_background";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_CHANGE_WIFI_STATE = "android:change_wifi_state";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_REQUEST_DELETE_PACKAGES = "android:request_delete_packages";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_BIND_ACCESSIBILITY_SERVICE =
             "android:bind_accessibility_service";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_MANAGE_IPSEC_TUNNELS = "android:manage_ipsec_tunnels";
     /** @hide */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_START_FOREGROUND = "android:start_foreground";
     /** @hide */
     public static final String OPSTR_BLUETOOTH_SCAN = "android:bluetooth_scan";
@@ -1424,25 +1411,25 @@
             "android:sms_financial_transactions";
 
     /** @hide Read media of audio type. */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_READ_MEDIA_AUDIO = "android:read_media_audio";
     /** @hide Write media of audio type. */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_WRITE_MEDIA_AUDIO = "android:write_media_audio";
     /** @hide Read media of video type. */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_READ_MEDIA_VIDEO = "android:read_media_video";
     /** @hide Write media of video type. */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_WRITE_MEDIA_VIDEO = "android:write_media_video";
     /** @hide Read media of image type. */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_READ_MEDIA_IMAGES = "android:read_media_images";
     /** @hide Write media of image type. */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_WRITE_MEDIA_IMAGES = "android:write_media_images";
     /** @hide Has a legacy (non-isolated) view of storage. */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String OPSTR_LEGACY_STORAGE = "android:legacy_storage";
     /** @hide Read location metadata from media */
     public static final String OPSTR_ACCESS_MEDIA_LOCATION = "android:access_media_location";
@@ -1456,7 +1443,6 @@
     public static final String OPSTR_QUERY_ALL_PACKAGES = "android:query_all_packages";
     /** @hide Access all external storage */
     @SystemApi
-    @TestApi
     public static final String OPSTR_MANAGE_EXTERNAL_STORAGE =
             "android:manage_external_storage";
 
@@ -2683,7 +2669,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     public static int opToDefaultMode(@NonNull String appOp) {
         return opToDefaultMode(strOpToOp(appOp));
@@ -2775,7 +2760,6 @@
      * Class holding all of the operation information associated with an app.
      * @hide
      */
-    @TestApi
     @SystemApi
     public static final class PackageOps implements Parcelable {
         private final String mPackageName;
@@ -2854,7 +2838,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     // @DataClass(genHiddenConstructor = true, genHiddenCopyConstructor = true)
     // genHiddenCopyConstructor does not work for @hide @SystemApi classes
@@ -3222,7 +3205,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     @Immutable
     // @DataClass(genHiddenConstructor = true) codegen verifier is broken
@@ -3796,7 +3778,6 @@
      *
      * @hide
      */
-    @TestApi
     @Immutable
     @SystemApi
     // @DataClass(genHiddenConstructor = true) codegen verifier is broken
@@ -4474,7 +4455,6 @@
      * @hide
      */
     @Immutable
-    @TestApi
     @SystemApi
     public static final class HistoricalOpsRequest {
         private final int mUid;
@@ -4505,7 +4485,6 @@
          *
          * @hide
          */
-        @TestApi
         @SystemApi
         public static final class Builder {
             private int mUid = Process.INVALID_UID;
@@ -4643,7 +4622,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     public static final class HistoricalOps implements Parcelable {
         private long mBeginTimeMillis;
@@ -5079,7 +5057,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     public static final class HistoricalUidOps implements Parcelable {
         private final int mUid;
@@ -5333,7 +5310,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     public static final class HistoricalPackageOps implements Parcelable {
         private final @NonNull String mPackageName;
@@ -5664,7 +5640,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     /* codegen verifier cannot deal with nested class parameters
     @DataClass(genHiddenConstructor = true,
@@ -5975,7 +5950,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     public static final class HistoricalOp implements Parcelable {
         private final int mOp;
@@ -6612,7 +6586,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS)
     public @NonNull List<AppOpsManager.PackageOps> getOpsForPackage(int uid,
@@ -6647,7 +6620,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS)
     public void getHistoricalOps(@NonNull HistoricalOpsRequest request,
@@ -6756,7 +6728,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
     public void setUidMode(@NonNull String appOp, int uid, @Mode int mode) {
         try {
@@ -6810,7 +6781,6 @@
      * be changed.
      * @hide
      */
-    @TestApi
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
     public void setMode(@NonNull String op, int uid, @Nullable String packageName,
@@ -7337,21 +7307,48 @@
     }
 
     /**
-     * Make note of an application performing an 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
-     * succeeds, the last execution time of the operation for this app will be updated to
-     * the current time.
+     * Make note of an application performing an operation and check if the application is allowed
+     * to perform it.
      *
      * <p>If this is a check that is not preceding the protected operation, use
      * {@link #unsafeCheckOp} instead.
      *
+     * <p>The identity of the package the app-op is noted for is specified by the
+     * {@code uid} and {@code packageName} parameters. If this is noted for a regular app both
+     * should be set and the package needs to be part of the uid. In the very rare case that an
+     * app-op is noted for an entity that does not have a package name, the package can be
+     * {@code null}. As it is possible that a single process contains more than one package the
+     * {@code packageName} should be {@link Context#getPackageName() read} from the context of the
+     * caller of the API (in the app process) that eventually triggers this check. If this op is
+     * not noted for a running process the {@code packageName} cannot be read from the context, but
+     * it should be clear which package the note is for.
+     *
+     * <p>If the  {@code uid} and {@code packageName} do not match this return
+     * {@link #MODE_IGNORED}.
+     *
+     * <p>Beside the access check this method also records the access. While the access check is
+     * based on {@code uid} and/or {@code packageName} the access recording is done based on the
+     * {@code packageName} and {@code attributionTag}. The {@code attributionTag} should be
+     * {@link Context#getAttributionTag() read} from the same context the package name is read from.
+     * In the case the check is not related to an API call, the  {@code attributionTag} should be
+     * {@code null}. Please note that e.g. registering a callback for later is still an API call and
+     * the code should store the attribution tag along the package name for being used in this
+     * method later.
+     *
+     * <p>The {@code message} parameter only needs to be set when this method is <ul>not</ul>
+     * called in a two-way binder call from the client. In this case the message is a free form text
+     * that is meant help the app developer determine what part of the app's code triggered the
+     * note. This message is passed back to the app in the
+     * {@link OnOpNotedCallback#onAsyncNoted(AsyncNotedAppOp)} callback. A good example of a useful
+     * message is including the {@link System#identityHashCode(Object)} of the listener that will
+     * receive data or the name of the manifest-receiver.
+     *
      * @param op The operation to note.  One of the OPSTR_* constants.
-     * @param uid The user id of the application attempting to perform the operation.
+     * @param uid The uid of the application attempting to perform the operation.
      * @param packageName The name of the application attempting to perform the operation.
-     * @param attributionTag The {@link Context#createAttributionContext attribution tag} or {@code
-     * null} for default attribution
-     * @param message A message describing the reason the op was noted
+     * @param attributionTag The {@link Context#createAttributionContext attribution tag} of the
+     *                       calling context or {@code null} for default attribution
+     * @param message A message describing why the op was noted
      *
      * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
      * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
@@ -7359,33 +7356,16 @@
      *
      * @throws SecurityException If the app has been configured to crash on this op.
      */
+    // For platform callers of this method, please read the package name parameter from
+    // Context#getOpPackageName.
+    // When noting a callback, the message can be computed using the #toReceiverId method.
     public int noteOp(@NonNull String op, int uid, @Nullable String packageName,
             @Nullable String attributionTag, @Nullable String message) {
         return noteOp(strOpToOp(op), uid, packageName, attributionTag, message);
     }
 
     /**
-     * Make note of an application performing an 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
-     * succeeds, the last execution time of the operation for this app will be updated to
-     * the current time.
-     *
-     * <p>If this is a check that is not preceding the protected operation, use
-     * {@link #unsafeCheckOp} instead.
-     *
-     * @param op The operation to note.  One of the OP_* constants.
-     * @param uid The user id of the application attempting to perform the operation.
-     * @param packageName The name of the application attempting to perform the operation.
-     * @param attributionTag The {@link Context#createAttributionContext attribution tag} or {@code
-     * null} for default attribution
-     * @param message A message describing the reason the op was noted
-     *
-     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
-     * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
-     * causing the app to crash).
-     *
-     * @throws SecurityException If the app has been configured to crash on this op.
+     * @see #noteOp(String, int, String, String, String
      *
      * @hide
      */
@@ -7423,16 +7403,7 @@
      * Like {@link #noteOp(String, int, String, String, String)} but instead of throwing a
      * {@link SecurityException} it returns {@link #MODE_ERRORED}.
      *
-     * @param op The operation to note.  One of the OPSTR_* constants.
-     * @param uid The user id of the application attempting to perform the operation.
-     * @param packageName The name of the application attempting to perform the operation.
-     * @param attributionTag The {@link Context#createAttributionContext attribution tag} or {@code
-     * null} for default attribution
-     * @param message A message describing the reason the op was noted
-     *
-     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
-     * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
-     * causing the app to crash).
+     * @see #noteOp(String, int, String, String, String)
      */
     public int noteOpNoThrow(@NonNull String op, int uid, @NonNull String packageName,
             @Nullable String attributionTag, @Nullable String message) {
@@ -7440,19 +7411,7 @@
     }
 
     /**
-     * Like {@link #noteOp(String, int, String, String, String)} but instead of throwing a
-     * {@link SecurityException} it returns {@link #MODE_ERRORED}.
-     *
-     * @param op The operation to note.  One of the OP_* constants.
-     * @param uid The user id of the application attempting to perform the operation.
-     * @param packageName The name of the application attempting to perform the operation.
-     * @param attributionTag The {@link Context#createAttributionContext attribution tag} or {@code
-     * null} for default attribution
-     * @param message A message describing the reason the op was noted
-     *
-     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
-     * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
-     * causing the app to crash).
+     * @see #noteOpNoThrow(String, int, String, String, String)
      *
      * @hide
      */
@@ -7509,23 +7468,7 @@
     }
 
     /**
-     * Make note of an application performing an operation on behalf of another application when
-     * handling an IPC. This function will verify that the calling uid and proxied package name
-     * match, and if not, return {@link #MODE_IGNORED}. If this call succeeds, the last execution
-     * time of the operation for the proxied app and your app will be updated to the current time.
-     *
-     * @param op The operation to note. One of the OP_* constants.
-     * @param proxiedPackageName The name of the application calling into the proxy application.
-     * @param proxiedUid The uid of the proxied application
-     * @param proxiedAttributionTag The proxied {@link Context#createAttributionContext
-     * attribution tag} or {@code null} for default attribution
-     * @param message A message describing the reason the op was noted
-     *
-     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or {@link #MODE_IGNORED}
-     * if it is not allowed and should be silently ignored (without causing the app to crash).
-     *
-     * @throws SecurityException If the proxy or proxied app has been configured to crash on this
-     * op.
+     * @see #noteProxyOp(String, String, int, String, String)
      *
      * @hide
      */
@@ -7587,15 +7530,7 @@
      * Like {@link #noteProxyOp(String, String, int, 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
-     * @param proxiedUid The uid the package belongs to
-     * @param proxiedAttributionTag The proxied {@link Context#createAttributionContext
-     * attribution tag} or {@code null} for default attribution
-     * @param message A message describing the reason the op was noted
+     * @see #noteOpNoThrow(String, int, String, String, String)
      */
     public int noteProxyOpNoThrow(@NonNull String op, @Nullable String proxiedPackageName,
             int proxiedUid, @Nullable String proxiedAttributionTag, @Nullable String message) {
@@ -7604,16 +7539,7 @@
     }
 
     /**
-     * Like {@link #noteProxyOp(int, String, int, String, String)} but instead
-     * of throwing a {@link SecurityException} it returns {@link #MODE_ERRORED}.
-     *
-     * @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
-     * @param proxiedAttributionTag The proxied {@link Context#createAttributionContext
-     * attribution tag} or {@code null} for default attribution
-     * @param message A message describing the reason the op was noted
+     * @see #noteProxyOpNoThrow(String, String, int, String, String)
      *
      * @hide
      */
@@ -7701,6 +7627,9 @@
     /**
      * Like {@link #checkOp} but instead of throwing a {@link SecurityException} it
      * returns {@link #MODE_ERRORED}.
+     *
+     * @see #checkOp(int, int, String)
+     *
      * @hide
      */
     @UnsupportedAppUsage
@@ -7832,6 +7761,10 @@
     /**
      * Report that an application has started executing a long-running operation.
      *
+     * <p>For more details how to determine the {@code callingPackageName},
+     * {@code callingAttributionTag}, and {@code message}, please check the description in
+     * {@link #noteOp(String, int, String, String, String)}
+     *
      * @param op The operation to start.  One of the OPSTR_* constants.
      * @param uid The user id of the application attempting to perform the operation.
      * @param packageName The name of the application attempting to perform the operation.
@@ -7852,22 +7785,7 @@
     }
 
     /**
-     * Report that an application has started executing a long-running operation.
-     *
-     * @param op The operation to start.  One of the OP_* constants.
-     * @param uid The user id of the application attempting to perform the operation.
-     * @param packageName The name of the application attempting to perform the operation.
-     * @param attributionTag The {@link Context#createAttributionContext attribution tag} or
-     * {@code null} for default attribution
-     * @param startIfModeDefault Whether to start if mode is {@link #MODE_DEFAULT}.
-     * @param message Description why op was started
-     *
-     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
-     * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
-     * causing the app to crash).
-     *
-     * @throws SecurityException If the app has been configured to crash on this op or
-     * the package is not in the passed in UID.
+     * @see #startOp(String, int, String, String, String)
      *
      * @hide
      */
@@ -7913,16 +7831,7 @@
      * Like {@link #startOp(String, int, String, String, String)} but instead of throwing a
      * {@link SecurityException} it returns {@link #MODE_ERRORED}.
      *
-     * @param op The operation to start.  One of the OP_* constants.
-     * @param uid The user id of the application attempting to perform the operation.
-     * @param packageName The name of the application attempting to perform the operation.
-     * @param attributionTag The {@link Context#createAttributionContext attribution tag} or
-     * {@code null} for default attribution
-     * @param message Description why op was started
-     *
-     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
-     * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
-     * causing the app to crash).
+     * @see #startOp(String, int, String, String, String)
      */
     public int startOpNoThrow(@NonNull String op, int uid, @NonNull String packageName,
             @NonNull String attributionTag, @Nullable String message) {
@@ -7930,20 +7839,7 @@
     }
 
     /**
-     * Like {@link #startOp(int, int, String, boolean, String, String)} but instead of throwing a
-     * {@link SecurityException} it returns {@link #MODE_ERRORED}.
-     *
-     * @param op The operation to start.  One of the OP_* constants.
-     * @param uid The user id of the application attempting to perform the operation.
-     * @param packageName The name of the application attempting to perform the operation.
-     * @param attributionTag The {@link Context#createAttributionContext attribution tag} or
-     * {@code null} for default attribution
-     * @param startIfModeDefault Whether to start if mode is {@link #MODE_DEFAULT}.
-     * @param message Description why op was started
-     *
-     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
-     * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
-     * causing the app to crash).
+     * @see #startOpNoThrow(String, int, String, String, String)
      *
      * @hide
      */
@@ -7978,6 +7874,81 @@
             throw e.rethrowFromSystemServer();
         }
     }
+    /**
+     * Report that an application has started executing a long-running operation on behalf of
+     * another application when handling an IPC. This function will verify that the calling uid and
+     * proxied package name match, and if not, return {@link #MODE_IGNORED}.
+     *
+     * @param op The op to note
+     * @param proxiedUid The uid to note the op for {@code null}
+     * @param proxiedPackageName The package name the uid belongs to
+     * @param proxiedAttributionTag The proxied {@link Context#createAttributionContext
+     * attribution tag} or {@code null} for default attribution
+     * @param message A message describing the reason the op was noted
+     *
+     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or {@link #MODE_IGNORED}
+     * if it is not allowed and should be silently ignored (without causing the app to crash).
+     *
+     * @throws SecurityException If the proxy or proxied app has been configured to crash on this
+     * op.
+     */
+    public int startProxyOp(@NonNull String op, int proxiedUid, @NonNull String proxiedPackageName,
+            @Nullable String proxiedAttributionTag, @Nullable String message) {
+        final int mode = startProxyOpNoThrow(op, proxiedUid, proxiedPackageName,
+                proxiedAttributionTag, message);
+        if (mode == MODE_ERRORED) {
+            throw new SecurityException("Proxy package " + mContext.getOpPackageName()
+                    + " from uid " + Process.myUid() + " or calling package " + proxiedPackageName
+                    + " from uid " + proxiedUid + " not allowed to perform "
+                    + sOpNames[strOpToOp(op)]);
+        }
+        return mode;
+    }
+
+    /**
+     *Like {@link #startProxyOp(String, int, String, String, String)} but instead
+     * of throwing a {@link SecurityException} it returns {@link #MODE_ERRORED}.
+     *
+     * @see #startProxyOp(String, int, String, String, String)
+     */
+    public int startProxyOpNoThrow(@NonNull String op, int proxiedUid,
+            @NonNull String proxiedPackageName, @Nullable String proxiedAttributionTag,
+            @Nullable String message) {
+        try {
+            int opInt = strOpToOp(op);
+
+            collectNoteOpCallsForValidation(opInt);
+            int collectionMode = getNotedOpCollectionMode(proxiedUid, proxiedPackageName, opInt);
+            boolean shouldCollectMessage = Process.myUid() == Process.SYSTEM_UID;
+            if (collectionMode == COLLECT_ASYNC) {
+                if (message == null) {
+                    // Set stack trace as default message
+                    message = getFormattedStackTrace();
+                    shouldCollectMessage = true;
+                }
+            }
+
+            int mode = mService.startProxyOperation(getClientId(), opInt, proxiedUid,
+                    proxiedPackageName, proxiedAttributionTag, Process.myUid(),
+                    mContext.getOpPackageName(), mContext.getAttributionTag(), false,
+                    collectionMode == COLLECT_ASYNC, message, shouldCollectMessage);
+
+            if (mode == MODE_ALLOWED) {
+                if (collectionMode == COLLECT_SELF) {
+                    collectNotedOpForSelf(opInt, proxiedAttributionTag);
+                } else if (collectionMode == COLLECT_SYNC
+                        // Only collect app-ops when the proxy is trusted
+                        && mContext.checkPermission(Manifest.permission.UPDATE_APP_OPS_STATS, -1,
+                        Process.myUid()) == PackageManager.PERMISSION_GRANTED) {
+                    collectNotedOpSync(opInt, proxiedAttributionTag);
+                }
+            }
+
+            return mode;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 
     /**
      * @deprecated Use {@link #finishOp(String, int, String, String)} instead
@@ -8017,10 +7988,7 @@
     }
 
     /**
-     * Report that an application is no longer performing an operation that had previously
-     * been started with {@link #startOp(int, int, String, boolean, String, String)}. There is no
-     * validation of input or result; the parameters supplied here must be the exact same ones
-     * previously passed in when starting the operation.
+     * @see #finishOp(String, int, String, String)
      *
      * @hide
      */
@@ -8034,6 +8002,28 @@
     }
 
     /**
+     *  Report that an application is no longer performing an operation that had previously
+     * been started with {@link #startProxyOp(String, int, String, String, String)}. There is no
+     * validation of input or result; the parameters supplied here must be the exact same ones
+     * previously passed in when starting the operation.
+     * @param op The operation which was started
+     * @param proxiedUid The uid the op was started on behalf of
+     * @param proxiedPackageName The package the op was started on behalf of
+     * @param proxiedAttributionTag The proxied {@link Context#createAttributionContext
+     * attribution tag} or {@code null} for default attribution
+     */
+    public void finishProxyOp(@NonNull String op, int proxiedUid,
+            @NonNull String proxiedPackageName, @Nullable String proxiedAttributionTag) {
+        try {
+            mService.finishProxyOperation(getClientId(), strOpToOp(op), proxiedUid,
+                    proxiedPackageName, proxiedAttributionTag, Process.myUid(),
+                    mContext.getOpPackageName(), mContext.getAttributionTag());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Checks whether the given op for a package is active, i.e. did someone call {@link #startOp}
      * without {@link #finishOp} yet.
      * <p>
@@ -8477,7 +8467,7 @@
             public void opNoted(AsyncNotedAppOp op) {
                 Objects.requireNonNull(op);
 
-                long token = Binder.clearCallingIdentity();
+                final long token = Binder.clearCallingIdentity();
                 try {
                     getAsyncNotedExecutor().execute(() -> onAsyncNoted(op));
                 } finally {
@@ -8753,7 +8743,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(Manifest.permission.GET_APP_OPS_STATS)
     public @Nullable RuntimeAppOpAccessMessage collectRuntimeAppOpAccessMessage() {
         try {
@@ -8768,7 +8757,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static String[] getOpStrs() {
         return Arrays.copyOf(sOpToString, sOpToString.length);
     }
diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java
index e7b3e14..d716a3c 100644
--- a/core/java/android/app/ApplicationExitInfo.java
+++ b/core/java/android/app/ApplicationExitInfo.java
@@ -1164,7 +1164,7 @@
     }
 
     @Override
-    public boolean equals(Object other) {
+    public boolean equals(@Nullable Object other) {
         if (other == null || !(other instanceof ApplicationExitInfo)) {
             return false;
         }
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 3d966c7..c6b52c1 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -16,13 +16,13 @@
 
 package android.app;
 
-import static android.content.pm.Checksum.PARTIAL_MERKLE_ROOT_1M_SHA256;
-import static android.content.pm.Checksum.PARTIAL_MERKLE_ROOT_1M_SHA512;
-import static android.content.pm.Checksum.WHOLE_MD5;
-import static android.content.pm.Checksum.WHOLE_MERKLE_ROOT_4K_SHA256;
-import static android.content.pm.Checksum.WHOLE_SHA1;
-import static android.content.pm.Checksum.WHOLE_SHA256;
-import static android.content.pm.Checksum.WHOLE_SHA512;
+import static android.content.pm.Checksum.TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256;
+import static android.content.pm.Checksum.TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512;
+import static android.content.pm.Checksum.TYPE_WHOLE_MD5;
+import static android.content.pm.Checksum.TYPE_WHOLE_MERKLE_ROOT_4K_SHA256;
+import static android.content.pm.Checksum.TYPE_WHOLE_SHA1;
+import static android.content.pm.Checksum.TYPE_WHOLE_SHA256;
+import static android.content.pm.Checksum.TYPE_WHOLE_SHA512;
 
 import android.annotation.DrawableRes;
 import android.annotation.NonNull;
@@ -117,7 +117,6 @@
 
 import libcore.util.EmptyArray;
 
-import java.io.IOException;
 import java.lang.ref.WeakReference;
 import java.security.cert.Certificate;
 import java.security.cert.CertificateEncodingException;
@@ -150,10 +149,11 @@
     private static final int sDefaultFlags = GET_SHARED_LIBRARY_FILES;
 
     /** Default set of checksums - includes all available checksums.
-     * @see PackageManager#getChecksums  */
+     * @see PackageManager#requestChecksums  */
     private static final int DEFAULT_CHECKSUMS =
-            WHOLE_MERKLE_ROOT_4K_SHA256 | WHOLE_MD5 | WHOLE_SHA1 | WHOLE_SHA256 | WHOLE_SHA512
-                    | PARTIAL_MERKLE_ROOT_1M_SHA256 | PARTIAL_MERKLE_ROOT_1M_SHA512;
+            TYPE_WHOLE_MERKLE_ROOT_4K_SHA256 | TYPE_WHOLE_MD5 | TYPE_WHOLE_SHA1 | TYPE_WHOLE_SHA256
+                    | TYPE_WHOLE_SHA512 | TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256
+                    | TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512;
 
     // Name of the resource which provides background permission button string
     public static final String APP_PERMISSION_BUTTON_ALLOW_ALWAYS =
@@ -667,7 +667,7 @@
                     name, version);
         }
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (o instanceof HasSystemFeatureQuery) {
                 HasSystemFeatureQuery r = (HasSystemFeatureQuery) o;
                 return Objects.equals(name, r.name) &&  version == r.version;
@@ -994,14 +994,24 @@
     }
 
     @Override
-    public void getChecksums(@NonNull String packageName, boolean includeSplits,
-            @Checksum.Kind int required, @Nullable List<Certificate> trustedInstallers,
+    public void requestChecksums(@NonNull String packageName, boolean includeSplits,
+            @Checksum.Type int required, @NonNull List<Certificate> trustedInstallers,
             @NonNull IntentSender statusReceiver)
-            throws CertificateEncodingException, IOException, NameNotFoundException {
+            throws CertificateEncodingException, NameNotFoundException {
         Objects.requireNonNull(packageName);
         Objects.requireNonNull(statusReceiver);
+        Objects.requireNonNull(trustedInstallers);
         try {
-            mPM.getChecksums(packageName, includeSplits, DEFAULT_CHECKSUMS, required,
+            if (trustedInstallers == TRUST_ALL) {
+                trustedInstallers = null;
+            } else if (trustedInstallers == TRUST_NONE) {
+                trustedInstallers = Collections.emptyList();
+            } else if (trustedInstallers.isEmpty()) {
+                throw new IllegalArgumentException(
+                        "trustedInstallers has to be one of TRUST_ALL/TRUST_NONE or a non-empty "
+                                + "list of certificates.");
+            }
+            mPM.requestChecksums(packageName, includeSplits, DEFAULT_CHECKSUMS, required,
                     encodeCertificates(trustedInstallers), statusReceiver, getUserId());
         } catch (ParcelableException e) {
             e.maybeRethrow(PackageManager.NameNotFoundException.class);
@@ -1040,7 +1050,7 @@
          * are handled first.
          */
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (o instanceof GetPackagesForUidResult) {
                 String [] r = ((GetPackagesForUidResult) o).mValue;
                 String [] l = mValue;
@@ -2073,7 +2083,7 @@
         }
 
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (this == o) return true;
             if (o == null || getClass() != o.getClass()) return false;
 
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
index 7180c01..c3272c1 100644
--- a/core/java/android/app/AutomaticZenRule.java
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -282,7 +282,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (!(o instanceof AutomaticZenRule)) return false;
         if (o == this) return true;
         final AutomaticZenRule other = (AutomaticZenRule) o;
diff --git a/core/java/android/app/DirectAction.java b/core/java/android/app/DirectAction.java
index 0268f7c..b0ed490 100644
--- a/core/java/android/app/DirectAction.java
+++ b/core/java/android/app/DirectAction.java
@@ -132,7 +132,7 @@
     }
 
     @Override
-    public boolean equals(Object other) {
+    public boolean equals(@Nullable Object other) {
         if (other == null) {
             return false;
         }
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index f7fb3c3..ce4109c 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -596,7 +596,7 @@
     /**
      * Subclasses can not override equals().
      */
-    @Override final public boolean equals(Object o) {
+    @Override final public boolean equals(@Nullable Object o) {
         return super.equals(o);
     }
 
diff --git a/core/java/android/app/HomeVisibilityListener.java b/core/java/android/app/HomeVisibilityListener.java
new file mode 100644
index 0000000..c6e5699
--- /dev/null
+++ b/core/java/android/app/HomeVisibilityListener.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.content.Context;
+import android.os.Binder;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * A listener that will be invoked when the visibility of the home screen changes.
+ * Register this callback via {@link ActivityManager#addHomeVisibilityListener}
+ * @hide
+ */
+// This is a single-method listener that needs a bunch of supporting code, so it can't be an
+// interface
+@SuppressLint("ListenerInterface")
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+@TestApi
+public abstract class HomeVisibilityListener {
+    private Context mContext;
+    private ActivityManager mActivityManager;
+    private Executor mExecutor;
+    /** @hide */
+    android.app.IProcessObserver.Stub mObserver;
+    /** @hide */
+    boolean mIsHomeActivityVisible;
+
+    /** @hide */
+    void init(Context context, Executor executor, ActivityManager activityManager) {
+        mContext = context;
+        mActivityManager = activityManager;
+        mIsHomeActivityVisible = isHomeActivityVisible();
+        mExecutor = executor;
+    }
+
+    /**
+     * Called when the visibility of the home screen changes.
+     *
+     * @param isHomeActivityVisible Whether the home screen activity is now visible.
+     */
+    public abstract void onHomeVisibilityChanged(boolean isHomeActivityVisible);
+
+    public HomeVisibilityListener() {
+        mObserver = new android.app.IProcessObserver.Stub() {
+            @Override
+            public void onForegroundActivitiesChanged(int pid, int uid, boolean fg) {
+                refreshHomeVisibility();
+            }
+
+            @Override
+            public void onForegroundServicesChanged(int pid, int uid, int fgServiceTypes) {
+            }
+
+            @Override
+            public void onProcessDied(int pid, int uid) {
+                refreshHomeVisibility();
+            }
+
+            private void refreshHomeVisibility() {
+                boolean isHomeActivityVisible = isHomeActivityVisible();
+                if (mIsHomeActivityVisible != isHomeActivityVisible) {
+                    mIsHomeActivityVisible = isHomeActivityVisible;
+                    Binder.withCleanCallingIdentity(() ->
+                            mExecutor.execute(() ->
+                                    onHomeVisibilityChanged(mIsHomeActivityVisible)));
+                }
+            }
+        };
+    }
+
+    private boolean isHomeActivityVisible() {
+        List<ActivityManager.RunningTaskInfo> tasks = mActivityManager.getRunningTasks(1);
+        if (tasks == null || tasks.isEmpty()) {
+            return false;
+        }
+
+        String top = tasks.get(0).topActivity.getPackageName();
+        if (top == null) {
+            return false;
+        }
+
+        // We can assume that the screen is idle if the home application is in the foreground.
+        String defaultHomePackage = mContext.getPackageManager()
+                .getHomeActivities(new ArrayList<>()).getPackageName();
+        if (Objects.equals(top, defaultHomePackage)) {
+            return true;
+        }
+
+        return false;
+    }
+}
diff --git a/core/java/android/app/HomeVisibilityObserver.java b/core/java/android/app/HomeVisibilityObserver.java
deleted file mode 100644
index 8422c6f..0000000
--- a/core/java/android/app/HomeVisibilityObserver.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-
-import java.util.List;
-
-/**
- * An observer / callback to create and register by
- * {@link ActivityManager#registerHomeVisibilityObserver} so that it's triggered when
- * visibility of home page changes.
- * TODO: b/144351078 expose as SystemApi
- * @hide
- */
-public abstract class HomeVisibilityObserver {
-    private Context mContext;
-    private ActivityManager mActivityManager;
-    /** @hide */
-    IProcessObserver.Stub mObserver;
-    /** @hide */
-    boolean mIsHomeActivityVisible;
-
-    /** @hide */
-    void init(Context context, ActivityManager activityManager) {
-        mContext = context;
-        mActivityManager = activityManager;
-        mIsHomeActivityVisible = isHomeActivityVisible();
-    }
-
-    /**
-     * The API that needs implemented and will be triggered when activity on home page changes.
-     */
-    public abstract void onHomeVisibilityChanged(boolean isHomeActivityVisible);
-
-    public HomeVisibilityObserver() {
-        mObserver = new IProcessObserver.Stub() {
-            @Override
-            public void onForegroundActivitiesChanged(int pid, int uid, boolean fg) {
-                boolean isHomeActivityVisible = isHomeActivityVisible();
-                if (mIsHomeActivityVisible != isHomeActivityVisible) {
-                    mIsHomeActivityVisible = isHomeActivityVisible;
-                    onHomeVisibilityChanged(mIsHomeActivityVisible);
-                }
-            }
-
-            @Override
-            public void onForegroundServicesChanged(int pid, int uid, int fgServiceTypes) {
-            }
-
-            @Override
-            public void onProcessDied(int pid, int uid) {
-            }
-        };
-    }
-
-    private boolean isHomeActivityVisible() {
-        List<ActivityManager.RunningTaskInfo> tasks = mActivityManager.getRunningTasks(1);
-        if (tasks == null || tasks.isEmpty()) {
-            return false;
-        }
-
-        String top = tasks.get(0).topActivity.getPackageName();
-        if (top == null) {
-            return false;
-        }
-
-        // We can assume that the screen is idle if the home application is in the foreground.
-        final Intent intent = new Intent(Intent.ACTION_MAIN, null);
-        intent.addCategory(Intent.CATEGORY_HOME);
-
-        ResolveInfo info = mContext.getPackageManager().resolveActivity(intent,
-                PackageManager.MATCH_DEFAULT_ONLY);
-        if (info != null && top.equals(info.activityInfo.packageName)) {
-            return true;
-        }
-
-        return false;
-    }
-}
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 1995128..357b26c 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -447,9 +447,8 @@
     void hang(in IBinder who, boolean allowRestart);
 
     List<ActivityTaskManager.RootTaskInfo> getAllRootTaskInfos();
-    @UnsupportedAppUsage
-    void moveTaskToStack(int taskId, int stackId, boolean toTop);
-    void setFocusedStack(int stackId);
+    void moveTaskToRootTask(int taskId, int rootTaskId, boolean toTop);
+    void setFocusedRootTask(int taskId);
     ActivityTaskManager.RootTaskInfo getFocusedRootTaskInfo();
     @UnsupportedAppUsage
     void restart();
@@ -507,15 +506,12 @@
     boolean stopBinderTrackingAndDump(in ParcelFileDescriptor fd);
     @UnsupportedAppUsage
     void suppressResizeConfigChanges(boolean suppress);
-    @UnsupportedAppUsage
-    boolean moveTopActivityToPinnedStack(int stackId, in Rect bounds);
+    boolean moveTopActivityToPinnedRootTask(int rootTaskId, in Rect bounds);
     boolean isAppStartModeDisabled(int uid, in String packageName);
     @UnsupportedAppUsage
     boolean unlockUser(int userid, in byte[] token, in byte[] secret,
             in IProgressListener listener);
     void killPackageDependents(in String packageName, int userId);
-    @UnsupportedAppUsage
-    void removeStack(int stackId);
     void makePackageIdle(String packageName, int userId);
     int getMemoryTrimLevel();
     boolean isVrModePackageEnabled(in ComponentName packageName);
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index ddf2dc50..8a03fcc 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -165,7 +165,8 @@
     int getTaskForActivity(in IBinder token, in boolean onlyRoot);
     /** Finish all activities that were started for result from the specified activity. */
     void finishSubActivity(in IBinder token, in String resultWho, int requestCode);
-    ParceledListSlice getRecentTasks(int maxNum, int flags, int userId);
+    ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags,
+            int userId);
     boolean willActivityBeVisible(in IBinder token);
     void setRequestedOrientation(in IBinder token, int requestedOrientation);
     int getRequestedOrientation(in IBinder token);
@@ -186,11 +187,11 @@
     void reportAssistContextExtras(in IBinder token, in Bundle extras,
             in AssistStructure structure, in AssistContent content, in Uri referrer);
 
-    void setFocusedStack(int stackId);
+    void setFocusedRootTask(int taskId);
     ActivityTaskManager.RootTaskInfo getFocusedRootTaskInfo();
     Rect getTaskBounds(int taskId);
 
-    void cancelRecentsAnimation(boolean restoreHomeStackPosition);
+    void cancelRecentsAnimation(boolean restoreHomeRootTaskPosition);
     void startLockTaskModeByToken(in IBinder token);
     void stopLockTaskModeByToken(in IBinder token);
     void updateLockTaskPackages(int userId, in String[] packages);
@@ -239,8 +240,7 @@
      * @return Return true on success. Otherwise false.
      */
     boolean resizeTask(int taskId, in Rect bounds, int resizeMode);
-    void moveStackToDisplay(int stackId, int displayId);
-    void removeStack(int stackId);
+    void moveRootTaskToDisplay(int taskId, int displayId);
 
     /**
      * Sets the windowing mode for a specific task. Only works on tasks of type
@@ -251,15 +251,15 @@
      * @return Whether the task was successfully put into the specified windowing mode.
      */
     boolean setTaskWindowingMode(int taskId, int windowingMode, boolean toTop);
-    void moveTaskToStack(int taskId, int stackId, boolean toTop);
+    void moveTaskToRootTask(int taskId, int rootTaskId, boolean toTop);
     boolean setTaskWindowingModeSplitScreenPrimary(int taskId, boolean toTop);
     /**
-     * Removes stacks in the input windowing modes from the system if they are of activity type
+     * Removes root tasks in the input windowing modes from the system if they are of activity type
      * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
      */
-    void removeStacksInWindowingModes(in int[] windowingModes);
-    /** Removes stack of the activity types from the system. */
-    void removeStacksWithActivityTypes(in int[] activityTypes);
+    void removeRootTasksInWindowingModes(in int[] windowingModes);
+    /** Removes root tasks of the activity types from the system. */
+    void removeRootTasksWithActivityTypes(in int[] activityTypes);
 
     List<ActivityTaskManager.RootTaskInfo> getAllRootTaskInfos();
     ActivityTaskManager.RootTaskInfo getRootTaskInfo(int windowingMode, int activityType);
@@ -299,7 +299,7 @@
             in int[] verticalSizeConfigurations, in int[] smallestWidthConfigurations);
 
     void suppressResizeConfigChanges(boolean suppress);
-    boolean moveTopActivityToPinnedStack(int stackId, in Rect bounds);
+    boolean moveTopActivityToPinnedRootTask(int rootTaskId, in Rect bounds);
     boolean enterPictureInPictureMode(in IBinder token, in PictureInPictureParams params);
     void setPictureInPictureParams(in IBinder token, in PictureInPictureParams params);
     void requestPictureInPictureMode(in IBinder token);
@@ -325,7 +325,7 @@
      *                                 stacks.
      * @throws RemoteException
      */
-    void resizeDockedStack(in Rect dockedBounds, in Rect tempDockedTaskBounds,
+    void resizePrimarySplitScreen(in Rect dockedBounds, in Rect tempDockedTaskBounds,
             in Rect tempDockedTaskInsetBounds,
             in Rect tempOtherTaskBounds, in Rect tempOtherTaskInsetBounds);
 
@@ -434,12 +434,6 @@
     void clearLaunchParamsForPackages(in List<String> packageNames);
 
     /**
-     * Makes the display with the given id a single task instance display. I.e the display can only
-     * contain one task.
-     */
-    void setDisplayToSingleTaskInstance(int displayId);
-
-    /**
      * Restarts the activity by killing its process if it is visible. If the activity is not
      * visible, the activity will not be restarted immediately and just keep the activity record in
      * the stack. It also resets the current override configuration so the activity will use the
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index aec9f3e..dc7782a 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -163,21 +163,6 @@
      */
     void onBackPressedOnTaskRoot(in ActivityManager.RunningTaskInfo taskInfo);
 
-    /*
-     * Called when contents are drawn for the first time on a display which can only contain one
-     * task.
-     *
-     * @param displayId the id of the display on which contents are drawn.
-     */
-    void onSingleTaskDisplayDrawn(int displayId);
-
-    /*
-     * Called when the last task is removed from a display which can only contain one task.
-     *
-     * @param displayId the id of the display from which the window is removed.
-     */
-    void onSingleTaskDisplayEmpty(int displayId);
-
     /**
      * Called when a task is reparented to a stack on a different display.
      *
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 0d682d6..5e50b96 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -82,7 +82,6 @@
 import android.util.SparseArray;
 import android.util.proto.ProtoOutputStream;
 import android.view.Gravity;
-import android.view.NotificationHeaderView;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.contentcapture.ContentCaptureContext;
@@ -2240,7 +2239,8 @@
                 .setTicker(tickerText)
                 .setContentTitle(contentTitle)
                 .setContentText(contentText)
-                .setContentIntent(PendingIntent.getActivity(context, 0, contentIntent, 0))
+                .setContentIntent(PendingIntent.getActivity(
+                        context, 0, contentIntent, PendingIntent.FLAG_MUTABLE))
                 .buildInto(this);
     }
 
@@ -4824,7 +4824,7 @@
         private void resetNotificationHeader(RemoteViews contentView) {
             // Small icon doesn't need to be reset, as it's always set. Resetting would prevent
             // re-using the drawable when the notification is updated.
-            contentView.setBoolean(R.id.notification_header, "setExpanded", false);
+            contentView.setBoolean(R.id.expand_button, "setExpanded", false);
             contentView.setTextViewText(R.id.app_name_text, null);
             contentView.setViewVisibility(R.id.chronometer, View.GONE);
             contentView.setViewVisibility(R.id.header_text, View.GONE);
@@ -5568,7 +5568,7 @@
          */
         public static void makeHeaderExpanded(RemoteViews result) {
             if (result != null) {
-                result.setBoolean(R.id.notification_header, "setExpanded", true);
+                result.setBoolean(R.id.expand_button, "setExpanded", true);
             }
         }
 
@@ -5861,24 +5861,16 @@
         }
 
         /**
-         * Apply any necessariy colors to the small icon
+         * Apply any necessary colors to the small icon
          */
         private void processSmallIconColor(Icon smallIcon, RemoteViews contentView,
                 StandardTemplateParams p) {
             boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
-            int color;
-            if (isColorized(p)) {
-                color = getPrimaryTextColor(p);
-            } else {
-                color = resolveContrastColor(p);
-            }
-            if (colorable) {
-                contentView.setDrawableTint(R.id.icon, false, color,
-                        PorterDuff.Mode.SRC_ATOP);
-
-            }
+            int color = isColorized(p) ? getPrimaryTextColor(p) : resolveContrastColor(p);
+            contentView.setInt(R.id.icon, "setBackgroundColor",
+                    resolveBackgroundColor(p));
             contentView.setInt(R.id.icon, "setOriginalIconColor",
-                    colorable ? color : NotificationHeaderView.NO_COLOR);
+                    colorable ? color : COLOR_INVALID);
         }
 
         /**
@@ -5891,8 +5883,8 @@
             if (largeIcon != null && isLegacy()
                     && getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
                 // resolve color will fall back to the default when legacy
-                contentView.setDrawableTint(R.id.icon, false, resolveContrastColor(p),
-                        PorterDuff.Mode.SRC_ATOP);
+                int color = resolveContrastColor(p);
+                contentView.setInt(R.id.icon, "setOriginalIconColor", color);
             }
         }
 
@@ -8456,9 +8448,7 @@
                 Action action, StandardTemplateParams p) {
             final boolean tombstone = (action.actionIntent == null);
             container.setViewVisibility(buttonId, View.VISIBLE);
-            if (buttonId != R.id.media_seamless) {
-                container.setImageViewIcon(buttonId, action.getIcon());
-            }
+            container.setImageViewIcon(buttonId, action.getIcon());
 
             // If the action buttons should not be tinted, then just use the default
             // notification color. Otherwise, just use the passed-in color.
@@ -8512,10 +8502,6 @@
                     view.setViewVisibility(MEDIA_BUTTON_IDS[i], View.GONE);
                 }
             }
-            bindMediaActionButton(view, R.id.media_seamless, new Action(
-                    R.drawable.ic_media_seamless, mBuilder.mContext.getString(
-                            com.android.internal.R.string.ext_media_seamless_action), null), p);
-            view.setViewVisibility(R.id.media_seamless, View.GONE);
             handleImage(view);
             // handle the content margin
             int endMargin = R.dimen.notification_content_margin_end;
@@ -8552,10 +8538,6 @@
                     big.setViewVisibility(MEDIA_BUTTON_IDS[i], View.GONE);
                 }
             }
-            bindMediaActionButton(big, R.id.media_seamless, new Action(R.drawable.ic_media_seamless,
-                    mBuilder.mContext.getString(
-                            com.android.internal.R.string.ext_media_seamless_action), null), p);
-            big.setViewVisibility(R.id.media_seamless, View.GONE);
             handleImage(big);
             return big;
         }
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index c827e60..a06ffbd 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -134,7 +134,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final int USER_LOCKED_SOUND = 0x00000020;
 
     /**
@@ -391,7 +390,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public void setBlockable(boolean blockable) {
         mBlockableSystem = blockable;
     }
@@ -1146,7 +1144,7 @@
     }
 
     private static String longArrayToString(long[] values) {
-        StringBuffer sb = new StringBuffer();
+        StringBuilder sb = new StringBuilder();
         if (values != null && values.length > 0) {
             for (int i = 0; i < values.length - 1; i++) {
                 sb.append(values[i]).append(DELIMITER);
@@ -1175,7 +1173,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
         NotificationChannel that = (NotificationChannel) o;
diff --git a/core/java/android/app/NotificationChannelGroup.java b/core/java/android/app/NotificationChannelGroup.java
index 403fb3e..ec7fa33 100644
--- a/core/java/android/app/NotificationChannelGroup.java
+++ b/core/java/android/app/NotificationChannelGroup.java
@@ -15,6 +15,7 @@
  */
 package android.app;
 
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -291,7 +292,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
         NotificationChannelGroup that = (NotificationChannelGroup) o;
diff --git a/core/java/android/app/NotificationHistory.java b/core/java/android/app/NotificationHistory.java
index 55fff8b..0c8188b 100644
--- a/core/java/android/app/NotificationHistory.java
+++ b/core/java/android/app/NotificationHistory.java
@@ -116,7 +116,7 @@
         }
 
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (this == o) return true;
             if (o == null || getClass() != o.getClass()) return false;
             HistoricalNotification that = (HistoricalNotification) o;
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index fe89366..f3bd04cd 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1430,7 +1430,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public boolean isNotificationAssistantAccessGranted(@NonNull ComponentName assistant) {
         INotificationManager service = getService();
         try {
@@ -1466,7 +1465,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public @NonNull @Adjustment.Keys List<String> getAllowedAssistantAdjustments() {
         INotificationManager service = getService();
         try {
@@ -1567,8 +1565,19 @@
         }
     }
 
-    /** @hide */
-    public void setNotificationListenerAccessGranted(ComponentName listener, boolean granted) {
+    /**
+     * Grants/revokes Notification Listener access to the given component for current user.
+     * To grant access for a particular user, obtain this service by using the {@link Context}
+     * provided by {@link Context#createPackageContextAsUser}
+     *
+     * @param listener Name of component to grant/revoke access
+     * @param granted Grant/revoke access
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS)
+    public void setNotificationListenerAccessGranted(
+            @NonNull ComponentName listener, boolean granted) {
         INotificationManager service = getService();
         try {
             service.setNotificationListenerAccessGranted(listener, granted);
@@ -1599,7 +1608,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public void setNotificationAssistantAccessGranted(@Nullable ComponentName assistant,
             boolean granted) {
         INotificationManager service = getService();
@@ -1610,6 +1618,20 @@
         }
     }
 
+    /**
+     * Gets the list of enabled notification listener components for current user.
+     * To query for a particular user, obtain this service by using the {@link Context}
+     * provided by {@link Context#createPackageContextAsUser}
+     *
+     * @return the list of {@link ComponentName}s of the notification listeners
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS)
+    public @NonNull List<ComponentName> getEnabledNotificationListeners() {
+        return getEnabledNotificationListeners(mContext.getUserId());
+    }
+
     /** @hide */
     public List<ComponentName> getEnabledNotificationListeners(int userId) {
         INotificationManager service = getService();
@@ -1622,7 +1644,6 @@
 
     /** @hide */
     @SystemApi
-    @TestApi
     public @Nullable ComponentName getAllowedNotificationAssistant() {
         INotificationManager service = getService();
         try {
@@ -2000,7 +2021,7 @@
         }
 
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (!(o instanceof Policy)) return false;
             if (o == this) return true;
             final Policy other = (Policy) o;
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index e8937a8..acc42dbc 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -363,6 +363,7 @@
      * parameters.  May return null only if {@link #FLAG_NO_CREATE} has been
      * supplied.
      */
+    @SuppressWarnings("AndroidFrameworkPendingIntentMutability")
     public static PendingIntent getActivity(Context context, int requestCode,
             Intent intent, @Flags int flags) {
         return getActivity(context, requestCode, intent, flags, null);
@@ -489,6 +490,7 @@
      * parameters.  May return null only if {@link #FLAG_NO_CREATE} has been
      * supplied.
      */
+    @SuppressWarnings("AndroidFrameworkPendingIntentMutability")
     public static PendingIntent getActivities(Context context, int requestCode,
             @NonNull Intent[] intents, @Flags int flags) {
         return getActivities(context, requestCode, intents, flags, null);
@@ -611,6 +613,7 @@
      * parameters.  May return null only if {@link #FLAG_NO_CREATE} has been
      * supplied.
      */
+    @SuppressWarnings("AndroidFrameworkPendingIntentMutability")
     public static PendingIntent getBroadcast(Context context, int requestCode,
             Intent intent, @Flags int flags) {
         return getBroadcastAsUser(context, requestCode, intent, flags, context.getUser());
@@ -1236,7 +1239,7 @@
      * operation.
      */
     @Override
-    public boolean equals(Object otherObj) {
+    public boolean equals(@Nullable Object otherObj) {
         if (otherObj instanceof PendingIntent) {
             return mTarget.asBinder().equals(((PendingIntent)otherObj)
                     .mTarget.asBinder());
diff --git a/core/java/android/app/Person.java b/core/java/android/app/Person.java
index 63ef248..97a794d 100644
--- a/core/java/android/app/Person.java
+++ b/core/java/android/app/Person.java
@@ -138,7 +138,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (obj instanceof Person) {
             final Person other = (Person) obj;
             return Objects.equals(mName, other.mName)
diff --git a/core/java/android/app/Presentation.java b/core/java/android/app/Presentation.java
index 7a18b81..ad90364 100644
--- a/core/java/android/app/Presentation.java
+++ b/core/java/android/app/Presentation.java
@@ -16,30 +16,28 @@
 
 package android.app;
 
-import static android.content.Context.DISPLAY_SERVICE;
-import static android.content.Context.WINDOW_SERVICE;
+import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
 import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION;
 import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
 
+import android.annotation.NonNull;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Resources;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManager.DisplayListener;
-import android.os.Binder;
+import android.os.Build;
 import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.util.DisplayMetrics;
-import android.util.Log;
+import android.os.Looper;
 import android.util.TypedValue;
 import android.view.ContextThemeWrapper;
 import android.view.Display;
 import android.view.Gravity;
 import android.view.Window;
 import android.view.WindowManager;
-import android.view.WindowManagerImpl;
+import android.view.WindowManager.LayoutParams.WindowType;
 
+import com.android.internal.util.Preconditions;
 /**
  * Base class for presentations.
  * <p>
@@ -153,11 +151,10 @@
 public class Presentation extends Dialog {
     private static final String TAG = "Presentation";
 
-    private static final int MSG_CANCEL = 1;
-
     private final Display mDisplay;
     private final DisplayManager mDisplayManager;
-    private final IBinder mToken = new Binder();
+    private final Handler mHandler = new Handler(Preconditions.checkNotNull(Looper.myLooper(),
+            "Presentation must be constructed on a looper thread."));
 
     /**
      * Creates a new presentation that is attached to the specified display
@@ -179,6 +176,11 @@
      * @param outerContext The context of the application that is showing the presentation.
      * The presentation will create its own context (see {@link #getContext()}) based
      * on this context and information about the associated display.
+     * From {@link android.os.Build.VERSION_CODES#S}, the presentation will create its own window
+     * context based on this context, information about the associated display. Customizing window
+     * type by {@link Window#setType(int) #getWindow#setType(int)} causes the mismatch of the window
+     * and the created window context, which leads to
+     * {@link android.view.WindowManager.InvalidDisplayException} when invoking {@link #show()}.
      * @param display The display to which the presentation should be attached.
      * @param theme A style resource describing the theme to use for the window.
      * See <a href="{@docRoot}guide/topics/resources/available-resources.html#stylesandthemes">
@@ -187,24 +189,53 @@
      * <var>outerContext</var>.  If 0, the default presentation theme will be used.
      */
     public Presentation(Context outerContext, Display display, int theme) {
-        super(createPresentationContext(outerContext, display, theme), theme, false);
+        this(outerContext, display, theme, INVALID_WINDOW_TYPE);
+    }
+
+    /**
+     * Creates a new presentation that is attached to the specified display
+     * using the optionally specified theme, and override the default window type for the
+     * presentation.
+     * @param outerContext The context of the application that is showing the presentation.
+     * The presentation will create its own context (see {@link #getContext()}) based
+     * on this context and information about the associated display.
+     * From {@link android.os.Build.VERSION_CODES#S}, the presentation will create its own window
+     * context based on this context, information about the associated display and the window type.
+     * If the window type is not specified, the presentation will choose the default type for the
+     * presentation.
+     * @param display The display to which the presentation should be attached.
+     * @param theme A style resource describing the theme to use for the window.
+     * See <a href="{@docRoot}guide/topics/resources/available-resources.html#stylesandthemes">
+     * Style and Theme Resources</a> for more information about defining and using
+     * styles.  This theme is applied on top of the current theme in
+     * <var>outerContext</var>.  If 0, the default presentation theme will be used.
+     * @param type Window type.
+     *
+     * @hide
+     */
+    public Presentation(@NonNull Context outerContext, @NonNull Display display, int theme,
+            @WindowType int type) {
+        super(createPresentationContext(outerContext, display, theme, type), theme, false);
 
         mDisplay = display;
-        mDisplayManager = (DisplayManager)getContext().getSystemService(DISPLAY_SERVICE);
-
-        final int windowType =
-                (display.getFlags() & Display.FLAG_PRIVATE) != 0 ? TYPE_PRIVATE_PRESENTATION
-                        : TYPE_PRESENTATION;
+        mDisplayManager = getContext().getSystemService(DisplayManager.class);
 
         final Window w = getWindow();
         final WindowManager.LayoutParams attr = w.getAttributes();
-        attr.token = mToken;
         w.setAttributes(attr);
         w.setGravity(Gravity.FILL);
-        w.setType(windowType);
+        w.setType(getWindowType(type, display));
         setCanceledOnTouchOutside(false);
     }
 
+    private static @WindowType int getWindowType(@WindowType int type, @NonNull Display display) {
+        if (type != INVALID_WINDOW_TYPE) {
+            return type;
+        }
+        return (display.getFlags() & Display.FLAG_PRIVATE) != 0 ? TYPE_PRIVATE_PRESENTATION
+                : TYPE_PRESENTATION;
+    }
+
     /**
      * Gets the {@link Display} that this presentation appears on.
      *
@@ -229,16 +260,6 @@
     protected void onStart() {
         super.onStart();
         mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
-
-        // Since we were not watching for display changes until just now, there is a
-        // chance that the display metrics have changed.  If so, we will need to
-        // dismiss the presentation immediately.  This case is expected
-        // to be rare but surprising, so we'll write a log message about it.
-        if (!isConfigurationStillValid()) {
-            Log.i(TAG, "Presentation is being dismissed because the "
-                    + "display metrics have changed since it was created.");
-            mHandler.sendEmptyMessage(MSG_CANCEL);
-        }
     }
 
     @Override
@@ -273,10 +294,6 @@
      * Called by the system when the properties of the {@link Display} to which
      * the presentation is attached have changed.
      *
-     * If the display metrics have changed (for example, if the display has been
-     * resized or rotated), then the system automatically calls
-     * {@link #cancel} to dismiss the presentation.
-     *
      * @see #getDisplay
      */
     public void onDisplayChanged() {
@@ -289,28 +306,16 @@
 
     private void handleDisplayChanged() {
         onDisplayChanged();
-
-        // We currently do not support configuration changes for presentations
-        // (although we could add that feature with a bit more work).
-        // If the display metrics have changed in any way then the current configuration
-        // is invalid and the application must recreate the presentation to get
-        // a new context.
-        if (!isConfigurationStillValid()) {
-            Log.i(TAG, "Presentation is being dismissed because the "
-                    + "display metrics have changed since it was created.");
-            cancel();
-        }
     }
 
-    private boolean isConfigurationStillValid() {
-        DisplayMetrics dm = new DisplayMetrics();
-        mDisplay.getMetrics(dm);
-        return dm.equalsPhysical(getResources().getDisplayMetrics());
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "{@code N/A}")
+    private static Context createPresentationContext(Context outerContext, Display display,
+            int theme) {
+        return createPresentationContext(outerContext, display, theme, INVALID_WINDOW_TYPE);
     }
 
-    @UnsupportedAppUsage
     private static Context createPresentationContext(
-            Context outerContext, Display display, int theme) {
+            Context outerContext, Display display, int theme, @WindowType int type) {
         if (outerContext == null) {
             throw new IllegalArgumentException("outerContext must not be null");
         }
@@ -318,31 +323,15 @@
             throw new IllegalArgumentException("display must not be null");
         }
 
-        Context displayContext = outerContext.createDisplayContext(display);
+        Context windowContext = outerContext.createDisplayContext(display)
+                .createWindowContext(getWindowType(type, display), null /* options */);
         if (theme == 0) {
             TypedValue outValue = new TypedValue();
-            displayContext.getTheme().resolveAttribute(
+            windowContext.getTheme().resolveAttribute(
                     com.android.internal.R.attr.presentationTheme, outValue, true);
             theme = outValue.resourceId;
         }
-
-        // Derive the display's window manager from the outer window manager.
-        // We do this because the outer window manager have some extra information
-        // such as the parent window, which is important if the presentation uses
-        // an application window type.
-        final WindowManagerImpl outerWindowManager =
-                (WindowManagerImpl)outerContext.getSystemService(WINDOW_SERVICE);
-        final WindowManagerImpl displayWindowManager =
-                outerWindowManager.createPresentationWindowManager(displayContext);
-        return new ContextThemeWrapper(displayContext, theme) {
-            @Override
-            public Object getSystemService(String name) {
-                if (WINDOW_SERVICE.equals(name)) {
-                    return displayWindowManager;
-                }
-                return super.getSystemService(name);
-            }
-        };
+        return new ContextThemeWrapper(windowContext, theme);
     }
 
     private final DisplayListener mDisplayListener = new DisplayListener() {
@@ -364,15 +353,4 @@
             }
         }
     };
-
-    private final Handler mHandler = new Handler() {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_CANCEL:
-                    cancel();
-                    break;
-            }
-        }
-    };
 }
diff --git a/core/java/android/app/ProfilerInfo.java b/core/java/android/app/ProfilerInfo.java
index d6eb4a8..854406c 100644
--- a/core/java/android/app/ProfilerInfo.java
+++ b/core/java/android/app/ProfilerInfo.java
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
@@ -172,7 +173,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) {
             return true;
         }
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index 3e3a956..04a12af 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -206,6 +206,10 @@
     private static final String TAG = "PropertyInvalidatedCache";
     private static final boolean DEBUG = false;
     private static final boolean VERIFY = false;
+    // If this is true, dumpsys will dump the cache entries along with cache statistics.
+    // Most of the time this causes dumpsys to fail because the output stream is too
+    // large.  Only set it to true in development images.
+    private static final boolean DETAILED = false;
 
     // Per-Cache performance counters. As some cache instances are declared static,
     @GuardedBy("mLock")
@@ -912,14 +916,13 @@
                     "    Current Size: %d, Max Size: %d, HW Mark: %d, Overflows: %d",
                     mCache.size(), mMaxEntries, mHighWaterMark, mMissOverflow));
             pw.println(String.format("    Enabled: %s", mDisabled ? "false" : "true"));
+            pw.println("");
 
             Set<Map.Entry<Query, Result>> cacheEntries = mCache.entrySet();
-            if (cacheEntries.size() == 0) {
-                pw.println("");
+            if (!DETAILED || cacheEntries.size() == 0) {
                 return;
             }
 
-            pw.println("");
             pw.println("    Contents:");
             for (Map.Entry<Query, Result> entry : cacheEntries) {
                 String key = Objects.toString(entry.getKey());
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index b267840..36d5b5e 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -127,7 +127,7 @@
         }
 
         @Override
-        public boolean equals(Object obj) {
+        public boolean equals(@Nullable Object obj) {
             if (!(obj instanceof ApkKey)) {
                 return false;
             }
diff --git a/core/java/android/app/ResultInfo.java b/core/java/android/app/ResultInfo.java
index 979d3db..535f69f 100644
--- a/core/java/android/app/ResultInfo.java
+++ b/core/java/android/app/ResultInfo.java
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Intent;
 import android.os.Build;
@@ -90,7 +91,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (obj == null || !(obj instanceof ResultInfo)) {
             return false;
         }
diff --git a/core/java/android/app/RuntimeAppOpAccessMessage.java b/core/java/android/app/RuntimeAppOpAccessMessage.java
index a19f815..db3ba4a 100644
--- a/core/java/android/app/RuntimeAppOpAccessMessage.java
+++ b/core/java/android/app/RuntimeAppOpAccessMessage.java
@@ -20,7 +20,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -32,7 +31,6 @@
  * @hide
  */
 @Immutable
-@TestApi
 @SystemApi
 /*@DataClass(genConstructor = false)
 @DataClass.Suppress("getOpCode")*/
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 99d2127..864db27 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -374,7 +374,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(android.Manifest.permission.STATUS_BAR)
     public void setDisabledForSetup(boolean disabled) {
         try {
@@ -423,7 +422,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(android.Manifest.permission.STATUS_BAR)
     @NonNull
     public DisableInfo getDisableInfo() {
@@ -456,7 +454,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final class DisableInfo {
 
         private boolean mStatusBarExpansion;
@@ -489,7 +486,6 @@
          * @hide
          */
         @SystemApi
-        @TestApi
         public boolean isStatusBarExpansionDisabled() {
             return mStatusBarExpansion;
         }
@@ -505,7 +501,6 @@
          * @hide
          */
         @SystemApi
-        @TestApi
         public boolean isNavigateToHomeDisabled() {
             return mNavigateHome;
         }
@@ -521,7 +516,6 @@
          * @hide
          */
         @SystemApi
-        @TestApi
         public boolean isNotificationPeekingDisabled() {
             return mNotificationPeeking;
         }
@@ -537,7 +531,6 @@
          * @hide
          */
         @SystemApi
-        @TestApi
         public boolean isRecentsDisabled() {
             return mRecents;
         }
@@ -553,7 +546,6 @@
          * @hide
          */
         @SystemApi
-        @TestApi
         public boolean isSearchDisabled() {
             return mSearch;
         }
@@ -611,7 +603,6 @@
          * @hide
          */
         @SystemApi
-        @TestApi
         public boolean areAllComponentsEnabled() {
             return !mStatusBarExpansion && !mNavigateHome && !mNotificationPeeking && !mRecents
                     && !mSearch && !mSystemIcons && !mClock && !mNotificationIcons;
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 9100d57..4b3bebe 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -81,6 +81,7 @@
 import android.hardware.biometrics.BiometricManager;
 import android.hardware.biometrics.IAuthService;
 import android.hardware.camera2.CameraManager;
+import android.hardware.devicestate.DeviceStateManager;
 import android.hardware.display.ColorDisplayManager;
 import android.hardware.display.DisplayManager;
 import android.hardware.face.FaceManager;
@@ -107,6 +108,8 @@
 import android.media.MediaTranscodeManager;
 import android.media.midi.IMidiManager;
 import android.media.midi.MidiManager;
+import android.media.musicrecognition.IMusicRecognitionManager;
+import android.media.musicrecognition.MusicRecognitionManager;
 import android.media.projection.MediaProjectionManager;
 import android.media.soundtrigger.SoundTriggerManager;
 import android.media.tv.ITvInputManager;
@@ -1119,6 +1122,17 @@
                 return new AutofillManager(ctx.getOuterContext(), service);
             }});
 
+        registerService(Context.MUSIC_RECOGNITION_SERVICE, MusicRecognitionManager.class,
+                new CachedServiceFetcher<MusicRecognitionManager>() {
+                    @Override
+                    public MusicRecognitionManager createService(ContextImpl ctx) {
+                        IBinder b = ServiceManager.getService(
+                                Context.MUSIC_RECOGNITION_SERVICE);
+                        return new MusicRecognitionManager(
+                                IMusicRecognitionManager.Stub.asInterface(b));
+                    }
+                });
+
         registerService(Context.CONTENT_CAPTURE_MANAGER_SERVICE, ContentCaptureManager.class,
                 new CachedServiceFetcher<ContentCaptureManager>() {
             @Override
@@ -1335,6 +1349,12 @@
                             throws ServiceNotFoundException {
                         return new DreamManager(ctx);
                     }});
+        registerService(Context.DEVICE_STATE_SERVICE, DeviceStateManager.class,
+                new CachedServiceFetcher<DeviceStateManager>() {
+                    @Override
+                    public DeviceStateManager createService(ContextImpl ctx) {
+                        return new DeviceStateManager();
+                    }});
 
         sInitializing = true;
         try {
@@ -1357,8 +1377,8 @@
 
     /** Throws {@link IllegalStateException} if not during a static initialization. */
     private static void ensureInitializing(String methodName) {
-        Preconditions.checkState(sInitializing, "Internal error: " + methodName
-                + " can only be called during class initialization.");
+        Preconditions.checkState(sInitializing, "Internal error: %s"
+                + " can only be called during class initialization.", methodName);
     }
     /**
      * Creates an array which is used to cache per-Context service instances.
@@ -1391,6 +1411,7 @@
                 case Context.CONTENT_CAPTURE_MANAGER_SERVICE:
                 case Context.APP_PREDICTION_SERVICE:
                 case Context.INCREMENTAL_SERVICE:
+                case Context.ETHERNET_SERVICE:
                     return null;
             }
             Slog.wtf(TAG, "Manager wrapper not available: " + name);
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 4718cf1..849f679 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -24,11 +24,14 @@
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
+import android.os.IBinder;
 import android.os.Parcel;
 import android.os.RemoteException;
 import android.util.Log;
 import android.window.WindowContainerToken;
 
+import java.util.ArrayList;
+
 /**
  * Stores information about a particular Task.
  */
@@ -177,6 +180,13 @@
      */
     public boolean isResizeable;
 
+    /**
+     * The launch cookies associated with activities in this task if any.
+     * @see ActivityOptions#setLaunchCookie(IBinder)
+     * @hide
+     */
+    public ArrayList<IBinder> launchCookies = new ArrayList<>();
+
     TaskInfo() {
         // Do nothing
     }
@@ -213,6 +223,11 @@
         return configuration;
     }
 
+    /** @hide */
+    public void addLaunchCookie(IBinder cookie) {
+        launchCookies.add(cookie);
+    }
+
     /**
      * Reads the TaskInfo from a parcel.
      */
@@ -222,9 +237,7 @@
         taskId = source.readInt();
         displayId = source.readInt();
         isRunning = source.readBoolean();
-        baseIntent = source.readInt() != 0
-                ? Intent.CREATOR.createFromParcel(source)
-                : null;
+        baseIntent = source.readTypedObject(Intent.CREATOR);
         baseActivity = ComponentName.readFromParcel(source);
         topActivity = ComponentName.readFromParcel(source);
         origActivity = ComponentName.readFromParcel(source);
@@ -233,21 +246,16 @@
         numActivities = source.readInt();
         lastActiveTime = source.readLong();
 
-        taskDescription = source.readInt() != 0
-                ? ActivityManager.TaskDescription.CREATOR.createFromParcel(source)
-                : null;
+        taskDescription = source.readTypedObject(ActivityManager.TaskDescription.CREATOR);
         supportsSplitScreenMultiWindow = source.readBoolean();
         resizeMode = source.readInt();
         configuration.readFromParcel(source);
         token = WindowContainerToken.CREATOR.createFromParcel(source);
         topActivityType = source.readInt();
-        pictureInPictureParams = source.readInt() != 0
-                ? PictureInPictureParams.CREATOR.createFromParcel(source)
-                : null;
-        topActivityInfo = source.readInt() != 0
-                ? ActivityInfo.CREATOR.createFromParcel(source)
-                : null;
+        pictureInPictureParams = source.readTypedObject(PictureInPictureParams.CREATOR);
+        topActivityInfo = source.readTypedObject(ActivityInfo.CREATOR);
         isResizeable = source.readBoolean();
+        source.readBinderList(launchCookies);
     }
 
     /**
@@ -259,13 +267,8 @@
         dest.writeInt(taskId);
         dest.writeInt(displayId);
         dest.writeBoolean(isRunning);
+        dest.writeTypedObject(baseIntent, 0);
 
-        if (baseIntent != null) {
-            dest.writeInt(1);
-            baseIntent.writeToParcel(dest, 0);
-        } else {
-            dest.writeInt(0);
-        }
         ComponentName.writeToParcel(baseActivity, dest);
         ComponentName.writeToParcel(topActivity, dest);
         ComponentName.writeToParcel(origActivity, dest);
@@ -274,30 +277,16 @@
         dest.writeInt(numActivities);
         dest.writeLong(lastActiveTime);
 
-        if (taskDescription != null) {
-            dest.writeInt(1);
-            taskDescription.writeToParcel(dest, flags);
-        } else {
-            dest.writeInt(0);
-        }
+        dest.writeTypedObject(taskDescription, flags);
         dest.writeBoolean(supportsSplitScreenMultiWindow);
         dest.writeInt(resizeMode);
         configuration.writeToParcel(dest, flags);
         token.writeToParcel(dest, flags);
         dest.writeInt(topActivityType);
-        if (pictureInPictureParams == null) {
-            dest.writeInt(0);
-        } else {
-            dest.writeInt(1);
-            pictureInPictureParams.writeToParcel(dest, flags);
-        }
-        if (topActivityInfo == null) {
-            dest.writeInt(0);
-        } else {
-            dest.writeInt(1);
-            topActivityInfo.writeToParcel(dest, flags);
-        }
+        dest.writeTypedObject(pictureInPictureParams, flags);
+        dest.writeTypedObject(topActivityInfo, flags);
         dest.writeBoolean(isResizeable);
+        dest.writeBinderList(launchCookies);
     }
 
     @Override
@@ -316,6 +305,7 @@
                 + " token=" + token
                 + " topActivityType=" + topActivityType
                 + " pictureInPictureParams=" + pictureInPictureParams
-                + " topActivityInfo=" + topActivityInfo;
+                + " topActivityInfo=" + topActivityInfo
+                + " launchCookies" + launchCookies;
     }
 }
diff --git a/core/java/android/app/TaskStackBuilder.java b/core/java/android/app/TaskStackBuilder.java
index b99b327..e238046 100644
--- a/core/java/android/app/TaskStackBuilder.java
+++ b/core/java/android/app/TaskStackBuilder.java
@@ -264,6 +264,7 @@
      *
      * @return The obtained PendingIntent
      */
+    @SuppressWarnings("AndroidFrameworkPendingIntentMutability")
     public PendingIntent getPendingIntent(int requestCode, @PendingIntent.Flags int flags,
             Bundle options) {
         if (mIntents.isEmpty()) {
@@ -278,6 +279,7 @@
     /**
      * @hide
      */
+    @SuppressWarnings("AndroidFrameworkPendingIntentMutability")
     public PendingIntent getPendingIntent(int requestCode, int flags, Bundle options,
             UserHandle user) {
         if (mIntents.isEmpty()) {
diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index 17e5e09..e77d7ac 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -173,14 +173,6 @@
     }
 
     @Override
-    public void onSingleTaskDisplayDrawn(int displayId) throws RemoteException {
-    }
-
-    @Override
-    public void onSingleTaskDisplayEmpty(int displayId) throws RemoteException {
-    }
-
-    @Override
     public void onTaskDisplayChanged(int taskId, int newDisplayId) throws RemoteException {
     }
 
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 4e868fe..1b0fd9e 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -1242,10 +1242,8 @@
      *
      * @param command The command to execute.
      * @return File descriptors (out, in) to the standard output/input streams.
-     *
-     * @hide
      */
-    @TestApi
+    @SuppressLint("ArrayReturn") // For consistency with other APIs
     public @NonNull ParcelFileDescriptor[] executeShellCommandRw(@NonNull String command) {
         return executeShellCommandInternal(command, false /* includeStderr */);
     }
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index 06d1b74..e2fc5db 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -314,7 +314,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(android.Manifest.permission.ENTER_CAR_MODE_PRIORITIZED)
     public void enableCarMode(@IntRange(from = 0) int priority, @EnableCarMode int flags) {
         if (mService != null) {
diff --git a/core/java/android/app/WallpaperColors.java b/core/java/android/app/WallpaperColors.java
index 7624f35..2d203f57 100644
--- a/core/java/android/app/WallpaperColors.java
+++ b/core/java/android/app/WallpaperColors.java
@@ -337,7 +337,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (o == null || getClass() != o.getClass()) {
             return false;
         }
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 0a80ccc..54f3209 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -1766,7 +1766,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT)
     public boolean setWallpaperComponent(ComponentName name) {
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 79f05a3..4ae1670 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -639,7 +639,7 @@
 
     /** @hide */
     @Override
-    public boolean equals(Object that) {
+    public boolean equals(@Nullable Object that) {
         if (that == null) return false;
         if (that == this) return true;
         if (!(that instanceof WindowConfiguration)) {
@@ -852,15 +852,6 @@
     }
 
     /**
-     * Returns true if this container may be scaled without resizing, and windows within may need
-     * to be configured as such.
-     * @hide
-     */
-    public boolean windowsAreScaleable() {
-        return mWindowingMode == WINDOWING_MODE_PINNED;
-    }
-
-    /**
      * Returns true if windows in this container should be given move animations by default.
      * @hide
      */
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 8a85b79..054e842 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1465,7 +1465,7 @@
      * @see #createAdminSupportIntent(String)
      * @hide
      */
-    @TestApi @SystemApi
+    @SystemApi
     public static final String EXTRA_RESTRICTION = "android.app.extra.RESTRICTION";
 
     /**
@@ -1830,6 +1830,15 @@
     public static final int STATE_USER_PROFILE_COMPLETE = 4;
 
     /**
+     * Management setup on a managed profile.
+     * <p>This is used as an intermediate state after {@link #STATE_USER_PROFILE_COMPLETE} once the
+     * work profile has been created.
+     * @hide
+     */
+    @SystemApi
+    public static final int STATE_USER_PROFILE_FINALIZED = 5;
+
+    /**
      * @hide
      */
     @IntDef(prefix = { "STATE_USER_" }, value = {
@@ -1837,7 +1846,8 @@
             STATE_USER_SETUP_INCOMPLETE,
             STATE_USER_SETUP_COMPLETE,
             STATE_USER_SETUP_FINALIZED,
-            STATE_USER_PROFILE_COMPLETE
+            STATE_USER_PROFILE_COMPLETE,
+            STATE_USER_PROFILE_FINALIZED
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface UserProvisioningState {}
@@ -2688,13 +2698,11 @@
      * </ul>
      */
     @SystemApi
-    @TestApi
     public static final String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED =
             "android.account.DEVICE_OR_PROFILE_OWNER_ALLOWED";
 
     /** @hide See {@link #ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED} */
     @SystemApi
-    @TestApi
     public static final String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED =
             "android.account.DEVICE_OR_PROFILE_OWNER_DISALLOWED";
 
@@ -5718,8 +5726,8 @@
      * System apps can always bypass VPN.
      * <p> Note that the system doesn't update the allowlist when packages are installed or
      * uninstalled, the admin app must call this method to keep the list up to date.
-     * <p> When {@code lockdownEnabled} is false {@code lockdownWhitelist} is ignored . When
-     * {@code lockdownEnabled} is {@code true} and {@code lockdownWhitelist} is {@code null} or
+     * <p> When {@code lockdownEnabled} is false {@code lockdownAllowlist} is ignored . When
+     * {@code lockdownEnabled} is {@code true} and {@code lockdownAllowlist} is {@code null} or
      * empty, only system apps can bypass VPN.
      * <p> Setting always-on VPN package to {@code null} or using
      * {@link #setAlwaysOnVpnPackage(ComponentName, String, boolean)} clears lockdown allowlist.
@@ -5728,24 +5736,24 @@
      *         to remove an existing always-on VPN configuration
      * @param lockdownEnabled {@code true} to disallow networking when the VPN is not connected or
      *         {@code false} otherwise. This has no effect when clearing.
-     * @param lockdownWhitelist Packages that will be able to access the network directly when VPN
+     * @param lockdownAllowlist Packages that will be able to access the network directly when VPN
      *         is in lockdown mode but not connected. Has no effect when clearing.
      * @throws SecurityException if {@code admin} is not a device or a profile
      *         owner.
      * @throws NameNotFoundException if {@code vpnPackage} or one of
-     *         {@code lockdownWhitelist} is not installed.
+     *         {@code lockdownAllowlist} is not installed.
      * @throws UnsupportedOperationException if {@code vpnPackage} exists but does
      *         not support being set as always-on, or if always-on VPN is not
      *         available.
      */
     public void setAlwaysOnVpnPackage(@NonNull ComponentName admin, @Nullable String vpnPackage,
-            boolean lockdownEnabled, @Nullable Set<String> lockdownWhitelist)
+            boolean lockdownEnabled, @Nullable Set<String> lockdownAllowlist)
             throws NameNotFoundException {
         throwIfParentInstance("setAlwaysOnVpnPackage");
         if (mService != null) {
             try {
                 mService.setAlwaysOnVpnPackage(admin, vpnPackage, lockdownEnabled,
-                        lockdownWhitelist == null ? null : new ArrayList<>(lockdownWhitelist));
+                        lockdownAllowlist == null ? null : new ArrayList<>(lockdownAllowlist));
             } catch (ServiceSpecificException e) {
                 switch (e.errorCode) {
                     case ERROR_VPN_PACKAGE_NOT_FOUND:
@@ -5820,9 +5828,9 @@
         throwIfParentInstance("getAlwaysOnVpnLockdownWhitelist");
         if (mService != null) {
             try {
-                final List<String> whitelist =
-                        mService.getAlwaysOnVpnLockdownWhitelist(admin);
-                return whitelist == null ? null : new HashSet<>(whitelist);
+                final List<String> allowlist =
+                        mService.getAlwaysOnVpnLockdownAllowlist(admin);
+                return allowlist == null ? null : new HashSet<>(allowlist);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -6671,7 +6679,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @SuppressLint("Doclava125")
     public boolean isDeviceManaged() {
         try {
@@ -10381,7 +10388,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @SuppressLint("Doclava125")
     public @Nullable CharSequence getDeviceOwnerOrganizationName() {
         try {
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index 19242ba..cb879fc 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -76,16 +76,24 @@
             OnCrossProfileWidgetProvidersChangeListener listener);
 
     /**
-     * Checks if an app with given uid is an active device admin of its user and has the policy
-     * specified.
+     * Checks if an app with given uid is an active device owner of its user.
      *
      * <p>This takes the DPMS lock.  DO NOT call from PM/UM/AM with their lock held.
      *
      * @param uid App uid.
-     * @param reqPolicy Required policy, for policies see {@link DevicePolicyManager}.
-     * @return true if the uid is an active admin with the given policy.
+     * @return true if the uid is an active device owner.
      */
-    public abstract boolean isActiveAdminWithPolicy(int uid, int reqPolicy);
+    public abstract boolean isActiveDeviceOwner(int uid);
+
+    /**
+     * Checks if an app with given uid is an active profile owner of its user.
+     *
+     * <p>This takes the DPMS lock.  DO NOT call from PM/UM/AM with their lock held.
+     *
+     * @param uid App uid.
+     * @return true if the uid is an active profile owner.
+     */
+    public abstract boolean isActiveProfileOwner(int uid);
 
     /**
      * Checks if an app with given uid is the active supervision admin.
diff --git a/core/java/android/app/admin/FreezePeriod.java b/core/java/android/app/admin/FreezePeriod.java
index 657f017..eb6efec 100644
--- a/core/java/android/app/admin/FreezePeriod.java
+++ b/core/java/android/app/admin/FreezePeriod.java
@@ -39,8 +39,8 @@
 public class FreezePeriod {
     private static final String TAG = "FreezePeriod";
 
-    private static final int DUMMY_YEAR = 2001;
-    static final int DAYS_IN_YEAR = 365; // 365 since DUMMY_YEAR is not a leap year
+    private static final int SENTINEL_YEAR = 2001;
+    static final int DAYS_IN_YEAR = 365; // 365 since SENTINEL_YEAR is not a leap year
 
     private final MonthDay mStart;
     private final MonthDay mEnd;
@@ -60,9 +60,9 @@
      */
     public FreezePeriod(MonthDay start, MonthDay end) {
         mStart = start;
-        mStartDay = mStart.atYear(DUMMY_YEAR).getDayOfYear();
+        mStartDay = mStart.atYear(SENTINEL_YEAR).getDayOfYear();
         mEnd = end;
-        mEndDay = mEnd.atYear(DUMMY_YEAR).getDayOfYear();
+        mEndDay = mEnd.atYear(SENTINEL_YEAR).getDayOfYear();
     }
 
     /**
@@ -166,9 +166,9 @@
                 endYearAdjustment = 1;
             }
         }
-        final LocalDate startDate = LocalDate.ofYearDay(DUMMY_YEAR, mStartDay).withYear(
+        final LocalDate startDate = LocalDate.ofYearDay(SENTINEL_YEAR, mStartDay).withYear(
                 now.getYear() + startYearAdjustment);
-        final LocalDate endDate = LocalDate.ofYearDay(DUMMY_YEAR, mEndDay).withYear(
+        final LocalDate endDate = LocalDate.ofYearDay(SENTINEL_YEAR, mEndDay).withYear(
                 now.getYear() + endYearAdjustment);
         return new Pair<>(startDate, endDate);
     }
@@ -176,13 +176,13 @@
     @Override
     public String toString() {
         DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMM dd");
-        return LocalDate.ofYearDay(DUMMY_YEAR, mStartDay).format(formatter) + " - "
-                + LocalDate.ofYearDay(DUMMY_YEAR, mEndDay).format(formatter);
+        return LocalDate.ofYearDay(SENTINEL_YEAR, mStartDay).format(formatter) + " - "
+                + LocalDate.ofYearDay(SENTINEL_YEAR, mEndDay).format(formatter);
     }
 
     /** @hide */
     private static MonthDay dayOfYearToMonthDay(int dayOfYear) {
-        LocalDate date = LocalDate.ofYearDay(DUMMY_YEAR, dayOfYear);
+        LocalDate date = LocalDate.ofYearDay(SENTINEL_YEAR, dayOfYear);
         return MonthDay.of(date.getMonth(), date.getDayOfMonth());
     }
 
@@ -191,7 +191,7 @@
      * @hide
      */
     private static int dayOfYearDisregardLeapYear(LocalDate date) {
-        return date.withYear(DUMMY_YEAR).getDayOfYear();
+        return date.withYear(SENTINEL_YEAR).getDayOfYear();
     }
 
     /**
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 1c7b617..60dce22 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -197,12 +197,12 @@
     void setCertInstallerPackage(in ComponentName who, String installerPackage);
     String getCertInstallerPackage(in ComponentName who);
 
-    boolean setAlwaysOnVpnPackage(in ComponentName who, String vpnPackage, boolean lockdown, in List<String> lockdownWhitelist);
+    boolean setAlwaysOnVpnPackage(in ComponentName who, String vpnPackage, boolean lockdown, in List<String> lockdownAllowlist);
     String getAlwaysOnVpnPackage(in ComponentName who);
     String getAlwaysOnVpnPackageForUser(int userHandle);
     boolean isAlwaysOnVpnLockdownEnabled(in ComponentName who);
     boolean isAlwaysOnVpnLockdownEnabledForUser(int userHandle);
-    List<String> getAlwaysOnVpnLockdownWhitelist(in ComponentName who);
+    List<String> getAlwaysOnVpnLockdownAllowlist(in ComponentName who);
 
     void addPersistentPreferredActivity(in ComponentName admin, in IntentFilter filter, in ComponentName activity);
     void clearPackagePersistentPreferredActivities(in ComponentName admin, String packageName);
diff --git a/core/java/android/app/admin/PasswordMetrics.java b/core/java/android/app/admin/PasswordMetrics.java
index 39e1f0d..20a60bb 100644
--- a/core/java/android/app/admin/PasswordMetrics.java
+++ b/core/java/android/app/admin/PasswordMetrics.java
@@ -44,6 +44,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.admin.DevicePolicyManager.PasswordComplexity;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -705,7 +706,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
         final PasswordMetrics that = (PasswordMetrics) o;
diff --git a/core/java/android/app/admin/SecurityLog.java b/core/java/android/app/admin/SecurityLog.java
index fb7f573..5a4ab48 100644
--- a/core/java/android/app/admin/SecurityLog.java
+++ b/core/java/android/app/admin/SecurityLog.java
@@ -17,6 +17,7 @@
 package android.app.admin;
 
 import android.annotation.IntDef;
+import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
@@ -745,7 +746,7 @@
          * @hide
          */
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (this == o) return true;
             if (o == null || getClass() != o.getClass()) return false;
             SecurityEvent other = (SecurityEvent) o;
diff --git a/core/java/android/app/admin/SystemUpdateInfo.java b/core/java/android/app/admin/SystemUpdateInfo.java
index 4019290..53b2386 100644
--- a/core/java/android/app/admin/SystemUpdateInfo.java
+++ b/core/java/android/app/admin/SystemUpdateInfo.java
@@ -179,7 +179,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
         SystemUpdateInfo that = (SystemUpdateInfo) o;
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index b5234f8..c15504c 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -3,7 +3,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.app.Activity;
 import android.content.ComponentName;
 import android.content.Context;
@@ -722,7 +721,6 @@
         // COntent Capture.
         /** @hide */
         @SystemApi
-        @TestApi
         public ViewNode() {
         }
 
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 056cfc7..7e232ac 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -1054,14 +1054,15 @@
                 long quotaBytes,
                 IBackupCallback callbackBinder,
                 int transportFlags) throws RemoteException {
-            // Ensure that we're running with the app's normal permission level
-            long ident = Binder.clearCallingIdentity();
-
             if (DEBUG) Log.v(TAG, "doBackup() invoked");
+
             BackupDataOutput output = new BackupDataOutput(
                     data.getFileDescriptor(), quotaBytes, transportFlags);
 
             long result = RESULT_ERROR;
+
+            // Ensure that we're running with the app's normal permission level
+            final long ident = Binder.clearCallingIdentity();
             try {
                 BackupAgent.this.onBackup(oldState, output, newState);
                 result = RESULT_SUCCESS;
@@ -1111,9 +1112,6 @@
         private void doRestoreInternal(ParcelFileDescriptor data, long appVersionCode,
                 ParcelFileDescriptor newState, int token, IBackupManager callbackBinder,
                 List<String> excludedKeys) throws RemoteException {
-            // Ensure that we're running with the app's normal permission level
-            long ident = Binder.clearCallingIdentity();
-
             if (DEBUG) Log.v(TAG, "doRestore() invoked");
 
             // Ensure that any side-effect SharedPreferences writes have landed *before*
@@ -1121,6 +1119,9 @@
             waitForSharedPrefs();
 
             BackupDataInput input = new BackupDataInput(data.getFileDescriptor());
+
+            // Ensure that we're running with the app's normal permission level
+            final long ident = Binder.clearCallingIdentity();
             try {
                 BackupAgent.this.onRestore(input, appVersionCode, newState,
                         excludedKeys != null ? new HashSet<>(excludedKeys)
@@ -1152,15 +1153,14 @@
         @Override
         public void doFullBackup(ParcelFileDescriptor data,
                 long quotaBytes, int token, IBackupManager callbackBinder, int transportFlags) {
-            // Ensure that we're running with the app's normal permission level
-            long ident = Binder.clearCallingIdentity();
-
             if (DEBUG) Log.v(TAG, "doFullBackup() invoked");
 
             // Ensure that any SharedPreferences writes have landed *before*
             // we potentially try to back up the underlying files directly.
             waitForSharedPrefs();
 
+            // Ensure that we're running with the app's normal permission level
+            final long ident = Binder.clearCallingIdentity();
             try {
                 BackupAgent.this.onFullBackup(new FullBackupDataOutput(
                         data, quotaBytes, transportFlags));
@@ -1199,12 +1199,13 @@
 
         public void doMeasureFullBackup(long quotaBytes, int token, IBackupManager callbackBinder,
                 int transportFlags) {
-            // Ensure that we're running with the app's normal permission level
-            final long ident = Binder.clearCallingIdentity();
             FullBackupDataOutput measureOutput =
                     new FullBackupDataOutput(quotaBytes, transportFlags);
 
             waitForSharedPrefs();
+
+            // Ensure that we're running with the app's normal permission level
+            final long ident = Binder.clearCallingIdentity();
             try {
                 BackupAgent.this.onFullBackup(measureOutput);
             } catch (IOException ex) {
@@ -1228,7 +1229,7 @@
         public void doRestoreFile(ParcelFileDescriptor data, long size,
                 int type, String domain, String path, long mode, long mtime,
                 int token, IBackupManager callbackBinder) throws RemoteException {
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
                 BackupAgent.this.onRestoreFile(data, size, type, domain, path, mode, mtime);
             } catch (IOException e) {
@@ -1255,7 +1256,7 @@
 
         @Override
         public void doRestoreFinished(int token, IBackupManager callbackBinder) {
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
                 BackupAgent.this.onRestoreFinished();
             } catch (Exception e) {
@@ -1284,9 +1285,10 @@
                 long backupDataBytes,
                 long quotaBytes,
                 IBackupCallback callbackBinder) {
-            long ident = Binder.clearCallingIdentity();
-
             long result = RESULT_ERROR;
+
+            // Ensure that we're running with the app's normal permission level
+            final long ident = Binder.clearCallingIdentity();
             try {
                 BackupAgent.this.onQuotaExceeded(backupDataBytes, quotaBytes);
                 result = RESULT_SUCCESS;
@@ -1356,7 +1358,7 @@
 
         /**  @hide */
         @Override
-        public boolean equals(Object object) {
+        public boolean equals(@Nullable Object object) {
             if (this == object) {
                 return true;
             }
diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java
index 9b67587..0531359 100644
--- a/core/java/android/app/backup/BackupManager.java
+++ b/core/java/android/app/backup/BackupManager.java
@@ -21,7 +21,6 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
@@ -919,7 +918,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(android.Manifest.permission.BACKUP)
     public Intent getConfigurationIntent(String transportName) {
         checkServiceBinder();
@@ -941,7 +939,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(android.Manifest.permission.BACKUP)
     public String getDestinationString(String transportName) {
         checkServiceBinder();
@@ -963,7 +960,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(android.Manifest.permission.BACKUP)
     public Intent getDataManagementIntent(String transportName) {
         checkServiceBinder();
@@ -989,7 +985,6 @@
      */
     @Deprecated
     @SystemApi
-    @TestApi
     @RequiresPermission(android.Manifest.permission.BACKUP)
     @Nullable
     public String getDataManagementLabel(@NonNull String transportName) {
@@ -1006,7 +1001,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(android.Manifest.permission.BACKUP)
     @Nullable
     public CharSequence getDataManagementIntentLabel(@NonNull String transportName) {
diff --git a/core/java/android/app/compat/ChangeIdStateQuery.java b/core/java/android/app/compat/ChangeIdStateQuery.java
index 3c245b1..91765f7 100644
--- a/core/java/android/app/compat/ChangeIdStateQuery.java
+++ b/core/java/android/app/compat/ChangeIdStateQuery.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 
 import com.android.internal.annotations.Immutable;
 
@@ -25,7 +26,6 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.Objects;
 
-
 /**
  * A key type for caching calls to {@link com.android.internal.compat.IPlatformCompat}
  *
@@ -68,7 +68,7 @@
     }
 
     @Override
-    public boolean equals(Object other) {
+    public boolean equals(@Nullable Object other) {
         if (this == other) {
             return true;
         }
diff --git a/core/java/android/app/prediction/AppPredictionContext.java b/core/java/android/app/prediction/AppPredictionContext.java
index d14238b..99fa869 100644
--- a/core/java/android/app/prediction/AppPredictionContext.java
+++ b/core/java/android/app/prediction/AppPredictionContext.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.content.Context;
 import android.os.Bundle;
 import android.os.Parcel;
@@ -32,7 +31,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class AppPredictionContext implements Parcelable {
 
     private final int mPredictedTargetCount;
@@ -129,7 +127,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final class Builder {
 
         @NonNull
@@ -147,7 +144,6 @@
          * @hide
          */
         @SystemApi
-        @TestApi
         public Builder(@NonNull Context context) {
             mPackageName = context.getPackageName();
         }
diff --git a/core/java/android/app/prediction/AppPredictionManager.java b/core/java/android/app/prediction/AppPredictionManager.java
index ca22721..5da7aa9 100644
--- a/core/java/android/app/prediction/AppPredictionManager.java
+++ b/core/java/android/app/prediction/AppPredictionManager.java
@@ -17,7 +17,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.content.Context;
 
 import java.util.Objects;
@@ -28,7 +27,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class AppPredictionManager {
 
     private final Context mContext;
diff --git a/core/java/android/app/prediction/AppPredictionSessionId.java b/core/java/android/app/prediction/AppPredictionSessionId.java
index 876bafd..6277a7d 100644
--- a/core/java/android/app/prediction/AppPredictionSessionId.java
+++ b/core/java/android/app/prediction/AppPredictionSessionId.java
@@ -18,7 +18,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -30,7 +29,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class AppPredictionSessionId implements Parcelable {
 
     private final String mId;
diff --git a/core/java/android/app/prediction/AppPredictor.java b/core/java/android/app/prediction/AppPredictor.java
index fa135b1..fd1b9e3 100644
--- a/core/java/android/app/prediction/AppPredictor.java
+++ b/core/java/android/app/prediction/AppPredictor.java
@@ -70,7 +70,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class AppPredictor {
 
     private static final String TAG = AppPredictor.class.getSimpleName();
diff --git a/core/java/android/app/prediction/AppTarget.java b/core/java/android/app/prediction/AppTarget.java
index 14e32b83..fef9e70 100644
--- a/core/java/android/app/prediction/AppTarget.java
+++ b/core/java/android/app/prediction/AppTarget.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.content.pm.ShortcutInfo;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -33,7 +32,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class AppTarget implements Parcelable {
 
     private final AppTargetId mId;
@@ -190,7 +188,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final class Builder {
 
         @NonNull
@@ -221,7 +218,6 @@
          * @hide
          */
         @SystemApi
-        @TestApi
         public Builder(@NonNull AppTargetId id, @NonNull String packageName,
                 @NonNull UserHandle user) {
             mId = Objects.requireNonNull(id);
@@ -235,7 +231,6 @@
          * @hide
          */
         @SystemApi
-        @TestApi
         public Builder(@NonNull AppTargetId id, @NonNull ShortcutInfo info) {
             mId = Objects.requireNonNull(id);
             mShortcutInfo = Objects.requireNonNull(info);
diff --git a/core/java/android/app/prediction/AppTargetEvent.java b/core/java/android/app/prediction/AppTargetEvent.java
index f519145..963e750 100644
--- a/core/java/android/app/prediction/AppTargetEvent.java
+++ b/core/java/android/app/prediction/AppTargetEvent.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -32,7 +31,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class AppTargetEvent implements Parcelable {
 
     /**
@@ -141,7 +139,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final class Builder {
         private AppTarget mTarget;
         private String mLocation;
diff --git a/core/java/android/app/prediction/AppTargetId.java b/core/java/android/app/prediction/AppTargetId.java
index 052fdc1..048e12c 100644
--- a/core/java/android/app/prediction/AppTargetId.java
+++ b/core/java/android/app/prediction/AppTargetId.java
@@ -18,7 +18,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -28,7 +27,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class AppTargetId implements Parcelable {
 
     @NonNull
@@ -40,7 +38,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public AppTargetId(@NonNull String id) {
         mId = id;
     }
diff --git a/core/java/android/app/role/OnRoleHoldersChangedListener.java b/core/java/android/app/role/OnRoleHoldersChangedListener.java
index d6f7679..5958deb 100644
--- a/core/java/android/app/role/OnRoleHoldersChangedListener.java
+++ b/core/java/android/app/role/OnRoleHoldersChangedListener.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.UserHandle;
 
 /**
@@ -27,7 +26,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public interface OnRoleHoldersChangedListener {
 
     /**
diff --git a/core/java/android/app/role/RoleControllerManager.java b/core/java/android/app/role/RoleControllerManager.java
index 96a4deb..8dde2c5 100644
--- a/core/java/android/app/role/RoleControllerManager.java
+++ b/core/java/android/app/role/RoleControllerManager.java
@@ -258,7 +258,7 @@
             Consumer<Boolean> destination) {
         operation.orTimeout(REQUEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
                 .whenComplete((res, err) -> executor.execute(() -> {
-                    long token = Binder.clearCallingIdentity();
+                    final long token = Binder.clearCallingIdentity();
                     try {
                         if (err != null) {
                             Log.e(LOG_TAG, "Error calling " + opName + "()", err);
@@ -276,7 +276,7 @@
             RemoteCallback destination) {
         operation.orTimeout(REQUEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
                 .whenComplete((res, err) -> {
-                    long token = Binder.clearCallingIdentity();
+                    final long token = Binder.clearCallingIdentity();
                     try {
                         if (err != null) {
                             Log.e(LOG_TAG, "Error calling " + opName + "()", err);
diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java
index 87e1df3..408ce0f 100644
--- a/core/java/android/app/role/RoleManager.java
+++ b/core/java/android/app/role/RoleManager.java
@@ -144,7 +144,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final int MANAGE_HOLDERS_FLAG_DONT_KILL_APP = 1;
 
     /**
@@ -255,7 +254,6 @@
     @NonNull
     @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
     @SystemApi
-    @TestApi
     public List<String> getRoleHolders(@NonNull String roleName) {
         return getRoleHoldersAsUser(roleName, Process.myUserHandle());
     }
@@ -281,7 +279,6 @@
     @NonNull
     @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
     @SystemApi
-    @TestApi
     public List<String> getRoleHoldersAsUser(@NonNull String roleName, @NonNull UserHandle user) {
         Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
         Objects.requireNonNull(user, "user cannot be null");
@@ -315,7 +312,6 @@
      */
     @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
     @SystemApi
-    @TestApi
     public void addRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName,
             @ManageHoldersFlags int flags, @NonNull UserHandle user,
             @CallbackExecutor @NonNull Executor executor, @NonNull Consumer<Boolean> callback) {
@@ -354,7 +350,6 @@
      */
     @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
     @SystemApi
-    @TestApi
     public void removeRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName,
             @ManageHoldersFlags int flags, @NonNull UserHandle user,
             @CallbackExecutor @NonNull Executor executor, @NonNull Consumer<Boolean> callback) {
@@ -392,7 +387,6 @@
      */
     @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
     @SystemApi
-    @TestApi
     public void clearRoleHoldersAsUser(@NonNull String roleName, @ManageHoldersFlags int flags,
             @NonNull UserHandle user, @CallbackExecutor @NonNull Executor executor,
             @NonNull Consumer<Boolean> callback) {
@@ -413,7 +407,7 @@
             @NonNull Consumer<Boolean> callback) {
         return new RemoteCallback(result -> executor.execute(() -> {
             boolean successful = result != null;
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 callback.accept(successful);
             } finally {
@@ -439,7 +433,6 @@
      */
     @RequiresPermission(Manifest.permission.OBSERVE_ROLE_HOLDERS)
     @SystemApi
-    @TestApi
     public void addOnRoleHoldersChangedListenerAsUser(@CallbackExecutor @NonNull Executor executor,
             @NonNull OnRoleHoldersChangedListener listener, @NonNull UserHandle user) {
         Objects.requireNonNull(executor, "executor cannot be null");
@@ -485,7 +478,6 @@
      */
     @RequiresPermission(Manifest.permission.OBSERVE_ROLE_HOLDERS)
     @SystemApi
-    @TestApi
     public void removeOnRoleHoldersChangedListenerAsUser(
             @NonNull OnRoleHoldersChangedListener listener, @NonNull UserHandle user) {
         Objects.requireNonNull(listener, "listener cannot be null");
@@ -527,7 +519,6 @@
      */
     @RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER)
     @SystemApi
-    @TestApi
     public void setRoleNamesFromController(@NonNull List<String> roleNames) {
         Objects.requireNonNull(roleNames, "roleNames cannot be null");
         try {
@@ -558,7 +549,6 @@
      */
     @RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER)
     @SystemApi
-    @TestApi
     public boolean addRoleHolderFromController(@NonNull String roleName,
             @NonNull String packageName) {
         Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
@@ -591,7 +581,6 @@
      */
     @RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER)
     @SystemApi
-    @TestApi
     public boolean removeRoleHolderFromController(@NonNull String roleName,
             @NonNull String packageName) {
         Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
@@ -614,7 +603,6 @@
     @NonNull
     @RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER)
     @SystemApi
-    @TestApi
     public List<String> getHeldRolesFromController(@NonNull String packageName) {
         Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
         try {
@@ -660,7 +648,7 @@
 
         @Override
         public void onRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId) {
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 mExecutor.execute(PooledLambda.obtainRunnable(
                         OnRoleHoldersChangedListener::onRoleHoldersChanged, mListener, roleName,
diff --git a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
index 8a4bee9..d5585db 100644
--- a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
+++ b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
@@ -20,6 +20,7 @@
 import static android.view.Display.INVALID_DISPLAY;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityThread.ActivityClientRecord;
 import android.app.ClientTransactionHandler;
 import android.content.res.Configuration;
@@ -107,7 +108,7 @@
     };
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) {
             return true;
         }
diff --git a/core/java/android/app/servertransaction/ActivityRelaunchItem.java b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
index 87ea3f8..b3e34b3 100644
--- a/core/java/android/app/servertransaction/ActivityRelaunchItem.java
+++ b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
@@ -19,6 +19,7 @@
 import static android.app.ActivityThread.DEBUG_ORDER;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityThread.ActivityClientRecord;
 import android.app.ClientTransactionHandler;
 import android.app.ResultInfo;
@@ -144,7 +145,7 @@
     };
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) {
             return true;
         }
diff --git a/core/java/android/app/servertransaction/ActivityResultItem.java b/core/java/android/app/servertransaction/ActivityResultItem.java
index 47096a8..8320f49 100644
--- a/core/java/android/app/servertransaction/ActivityResultItem.java
+++ b/core/java/android/app/servertransaction/ActivityResultItem.java
@@ -19,6 +19,7 @@
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityThread.ActivityClientRecord;
 import android.app.ClientTransactionHandler;
 import android.app.ResultInfo;
@@ -101,7 +102,7 @@
     };
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) {
             return true;
         }
diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java
index 3d04437..fbb37db 100644
--- a/core/java/android/app/servertransaction/ClientTransaction.java
+++ b/core/java/android/app/servertransaction/ClientTransaction.java
@@ -222,7 +222,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) {
             return true;
         }
@@ -241,6 +241,8 @@
         int result = 17;
         result = 31 * result + Objects.hashCode(mActivityCallbacks);
         result = 31 * result + Objects.hashCode(mLifecycleStateRequest);
+        result = 31 * result + Objects.hashCode(mClient);
+        result = 31 * result + Objects.hashCode(mActivityToken);
         return result;
     }
 
diff --git a/core/java/android/app/servertransaction/ConfigurationChangeItem.java b/core/java/android/app/servertransaction/ConfigurationChangeItem.java
index 0f244d0..2acff49 100644
--- a/core/java/android/app/servertransaction/ConfigurationChangeItem.java
+++ b/core/java/android/app/servertransaction/ConfigurationChangeItem.java
@@ -16,6 +16,7 @@
 
 package android.app.servertransaction;
 
+import android.annotation.Nullable;
 import android.app.ClientTransactionHandler;
 import android.content.res.Configuration;
 import android.os.IBinder;
@@ -90,7 +91,7 @@
     };
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) {
             return true;
         }
diff --git a/core/java/android/app/servertransaction/DestroyActivityItem.java b/core/java/android/app/servertransaction/DestroyActivityItem.java
index 1611369..a074286 100644
--- a/core/java/android/app/servertransaction/DestroyActivityItem.java
+++ b/core/java/android/app/servertransaction/DestroyActivityItem.java
@@ -19,6 +19,7 @@
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityThread.ActivityClientRecord;
 import android.app.ClientTransactionHandler;
 import android.os.IBinder;
@@ -106,7 +107,7 @@
     };
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) {
             return true;
         }
diff --git a/core/java/android/app/servertransaction/EnterPipRequestedItem.java b/core/java/android/app/servertransaction/EnterPipRequestedItem.java
index b7e81a5..7dcae65 100644
--- a/core/java/android/app/servertransaction/EnterPipRequestedItem.java
+++ b/core/java/android/app/servertransaction/EnterPipRequestedItem.java
@@ -16,6 +16,7 @@
 
 package android.app.servertransaction;
 
+import android.annotation.Nullable;
 import android.app.ActivityThread.ActivityClientRecord;
 import android.app.ClientTransactionHandler;
 import android.os.Parcel;
@@ -67,7 +68,7 @@
             };
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         return this == o;
     }
 
diff --git a/core/java/android/app/servertransaction/FixedRotationAdjustmentsItem.java b/core/java/android/app/servertransaction/FixedRotationAdjustmentsItem.java
index 6183d5f..4c06333 100644
--- a/core/java/android/app/servertransaction/FixedRotationAdjustmentsItem.java
+++ b/core/java/android/app/servertransaction/FixedRotationAdjustmentsItem.java
@@ -16,6 +16,7 @@
 
 package android.app.servertransaction;
 
+import android.annotation.Nullable;
 import android.app.ClientTransactionHandler;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -74,7 +75,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) {
             return true;
         }
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index 77457af..7f08bfb 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -19,6 +19,7 @@
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityThread.ActivityClientRecord;
 import android.app.ClientTransactionHandler;
 import android.app.ProfilerInfo;
@@ -176,7 +177,7 @@
     };
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) {
             return true;
         }
diff --git a/core/java/android/app/servertransaction/MoveToDisplayItem.java b/core/java/android/app/servertransaction/MoveToDisplayItem.java
index 32de53f..944367e 100644
--- a/core/java/android/app/servertransaction/MoveToDisplayItem.java
+++ b/core/java/android/app/servertransaction/MoveToDisplayItem.java
@@ -19,6 +19,7 @@
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityThread.ActivityClientRecord;
 import android.app.ClientTransactionHandler;
 import android.content.res.Configuration;
@@ -110,7 +111,7 @@
     };
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) {
             return true;
         }
diff --git a/core/java/android/app/servertransaction/NewIntentItem.java b/core/java/android/app/servertransaction/NewIntentItem.java
index b4e2a7b..ac57f2b 100644
--- a/core/java/android/app/servertransaction/NewIntentItem.java
+++ b/core/java/android/app/servertransaction/NewIntentItem.java
@@ -20,6 +20,7 @@
 import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityThread.ActivityClientRecord;
 import android.app.ClientTransactionHandler;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -107,7 +108,7 @@
     };
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) {
             return true;
         }
diff --git a/core/java/android/app/servertransaction/PauseActivityItem.java b/core/java/android/app/servertransaction/PauseActivityItem.java
index cb154e9..f7c645e 100644
--- a/core/java/android/app/servertransaction/PauseActivityItem.java
+++ b/core/java/android/app/servertransaction/PauseActivityItem.java
@@ -19,6 +19,7 @@
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityTaskManager;
 import android.app.ActivityThread.ActivityClientRecord;
 import android.app.ClientTransactionHandler;
@@ -143,7 +144,7 @@
     };
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) {
             return true;
         }
diff --git a/core/java/android/app/servertransaction/ResumeActivityItem.java b/core/java/android/app/servertransaction/ResumeActivityItem.java
index d2a156c..b4523f5 100644
--- a/core/java/android/app/servertransaction/ResumeActivityItem.java
+++ b/core/java/android/app/servertransaction/ResumeActivityItem.java
@@ -19,6 +19,7 @@
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
 import android.app.ActivityThread.ActivityClientRecord;
@@ -142,7 +143,7 @@
     };
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) {
             return true;
         }
diff --git a/core/java/android/app/servertransaction/StartActivityItem.java b/core/java/android/app/servertransaction/StartActivityItem.java
index ae0bd24..483f9de 100644
--- a/core/java/android/app/servertransaction/StartActivityItem.java
+++ b/core/java/android/app/servertransaction/StartActivityItem.java
@@ -19,6 +19,7 @@
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityThread.ActivityClientRecord;
 import android.app.ClientTransactionHandler;
 import android.os.Parcel;
@@ -92,7 +93,7 @@
             };
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) {
             return true;
         }
diff --git a/core/java/android/app/servertransaction/StopActivityItem.java b/core/java/android/app/servertransaction/StopActivityItem.java
index 7708104..7e9116d 100644
--- a/core/java/android/app/servertransaction/StopActivityItem.java
+++ b/core/java/android/app/servertransaction/StopActivityItem.java
@@ -19,6 +19,7 @@
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityThread.ActivityClientRecord;
 import android.app.ClientTransactionHandler;
 import android.os.IBinder;
@@ -107,7 +108,7 @@
     };
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) {
             return true;
         }
diff --git a/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java b/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java
index 345c1dd..2b0c1b9 100644
--- a/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java
+++ b/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java
@@ -18,6 +18,7 @@
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityTaskManager;
 import android.app.ActivityThread.ActivityClientRecord;
 import android.app.ClientTransactionHandler;
@@ -111,7 +112,7 @@
     };
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) {
             return true;
         }
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index cef6ab0..0589f4a 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -519,7 +519,7 @@
         intent.setData(sliceUri.buildUpon().appendQueryParameter("package", callingPackage)
                 .build());
 
-        return PendingIntent.getActivity(context, 0, intent, 0);
+        return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE);
     }
 
     /**
diff --git a/core/java/android/app/slice/SliceSpec.java b/core/java/android/app/slice/SliceSpec.java
index b1080e0..a332349 100644
--- a/core/java/android/app/slice/SliceSpec.java
+++ b/core/java/android/app/slice/SliceSpec.java
@@ -17,6 +17,7 @@
 package android.app.slice;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -97,7 +98,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (!(obj instanceof SliceSpec)) return false;
         SliceSpec other = (SliceSpec) obj;
         return mType.equals(other.mType) && mRevision == other.mRevision;
diff --git a/core/java/android/app/time/TimeZoneCapabilities.java b/core/java/android/app/time/TimeZoneCapabilities.java
index df89c28..7cc67b3 100644
--- a/core/java/android/app/time/TimeZoneCapabilities.java
+++ b/core/java/android/app/time/TimeZoneCapabilities.java
@@ -205,7 +205,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) {
             return true;
         }
diff --git a/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java b/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java
index b339e53..f9a0c74 100644
--- a/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java
+++ b/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java
@@ -17,6 +17,7 @@
 package android.app.time;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -93,7 +94,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) {
             return true;
         }
diff --git a/core/java/android/app/time/TimeZoneConfiguration.java b/core/java/android/app/time/TimeZoneConfiguration.java
index c0a0c21..7403c12 100644
--- a/core/java/android/app/time/TimeZoneConfiguration.java
+++ b/core/java/android/app/time/TimeZoneConfiguration.java
@@ -17,6 +17,7 @@
 package android.app.time;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.StringDef;
 import android.annotation.SystemApi;
 import android.os.Bundle;
@@ -155,7 +156,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) {
             return true;
         }
diff --git a/core/java/android/app/timedetector/ManualTimeSuggestion.java b/core/java/android/app/timedetector/ManualTimeSuggestion.java
index da51ce2..299e951 100644
--- a/core/java/android/app/timedetector/ManualTimeSuggestion.java
+++ b/core/java/android/app/timedetector/ManualTimeSuggestion.java
@@ -109,7 +109,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) {
             return true;
         }
diff --git a/core/java/android/app/timedetector/NetworkTimeSuggestion.java b/core/java/android/app/timedetector/NetworkTimeSuggestion.java
index 89fd6f31..a5259c2 100644
--- a/core/java/android/app/timedetector/NetworkTimeSuggestion.java
+++ b/core/java/android/app/timedetector/NetworkTimeSuggestion.java
@@ -109,7 +109,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) {
             return true;
         }
diff --git a/core/java/android/app/timedetector/TelephonyTimeSuggestion.java b/core/java/android/app/timedetector/TelephonyTimeSuggestion.java
index c0e8957..6c3a304 100644
--- a/core/java/android/app/timedetector/TelephonyTimeSuggestion.java
+++ b/core/java/android/app/timedetector/TelephonyTimeSuggestion.java
@@ -154,7 +154,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) {
             return true;
         }
diff --git a/core/java/android/app/timezone/DistroFormatVersion.java b/core/java/android/app/timezone/DistroFormatVersion.java
index 04e3142..13ecaf5 100644
--- a/core/java/android/app/timezone/DistroFormatVersion.java
+++ b/core/java/android/app/timezone/DistroFormatVersion.java
@@ -16,6 +16,7 @@
 
 package android.app.timezone;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -86,7 +87,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) {
             return true;
         }
diff --git a/core/java/android/app/timezone/DistroRulesVersion.java b/core/java/android/app/timezone/DistroRulesVersion.java
index 3fae161..54937b8 100644
--- a/core/java/android/app/timezone/DistroRulesVersion.java
+++ b/core/java/android/app/timezone/DistroRulesVersion.java
@@ -19,6 +19,7 @@
 import static android.app.timezone.Utils.validateRulesVersion;
 import static android.app.timezone.Utils.validateVersion;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -94,7 +95,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) {
             return true;
         }
diff --git a/core/java/android/app/timezone/RulesState.java b/core/java/android/app/timezone/RulesState.java
index d35967d..ee88ec54 100644
--- a/core/java/android/app/timezone/RulesState.java
+++ b/core/java/android/app/timezone/RulesState.java
@@ -223,7 +223,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) {
             return true;
         }
diff --git a/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java b/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java
index 002c663..01a60b1 100644
--- a/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java
+++ b/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java
@@ -105,7 +105,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) {
             return true;
         }
diff --git a/core/java/android/app/timezonedetector/TelephonyTimeZoneSuggestion.java b/core/java/android/app/timezonedetector/TelephonyTimeZoneSuggestion.java
index 430462b..eb6750f 100644
--- a/core/java/android/app/timezonedetector/TelephonyTimeZoneSuggestion.java
+++ b/core/java/android/app/timezonedetector/TelephonyTimeZoneSuggestion.java
@@ -263,7 +263,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) {
             return true;
         }
diff --git a/core/java/android/app/usage/CacheQuotaHint.java b/core/java/android/app/usage/CacheQuotaHint.java
index b5aed49f..0ccb058 100644
--- a/core/java/android/app/usage/CacheQuotaHint.java
+++ b/core/java/android/app/usage/CacheQuotaHint.java
@@ -44,14 +44,14 @@
      * Create a new request.
      * @param builder A builder for this object.
      */
-    public CacheQuotaHint(Builder builder) {
+    public CacheQuotaHint(@NonNull Builder builder) {
         this.mUuid = builder.mUuid;
         this.mUid = builder.mUid;
         this.mUsageStats = builder.mUsageStats;
         this.mQuota = builder.mQuota;
     }
 
-    public String getVolumeUuid() {
+    @Nullable public String getVolumeUuid() {
         return mUuid;
     }
 
@@ -63,12 +63,12 @@
         return mQuota;
     }
 
-    public UsageStats getUsageStats() {
+    @Nullable public UsageStats getUsageStats() {
         return mUsageStats;
     }
 
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeString(mUuid);
         dest.writeInt(mUid);
         dest.writeLong(mQuota);
@@ -106,7 +106,7 @@
         public Builder() {
         }
 
-        public Builder(CacheQuotaHint hint) {
+        public Builder(@NonNull CacheQuotaHint hint) {
             setVolumeUuid(hint.getVolumeUuid());
             setUid(hint.getUid());
             setUsageStats(hint.getUsageStats());
diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java
index f2a054d..dcecd90 100644
--- a/core/java/android/app/usage/UsageStats.java
+++ b/core/java/android/app/usage/UsageStats.java
@@ -30,6 +30,7 @@
 import static android.app.usage.UsageEvents.Event.USER_INTERACTION;
 
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Bundle;
@@ -155,6 +156,7 @@
     /**
      * {@hide}
      */
+    @TestApi
     public UsageStats() {
     }
 
diff --git a/core/java/android/bluetooth/BluetoothAudioConfig.java b/core/java/android/bluetooth/BluetoothAudioConfig.java
index 9591a70..4c8b8c1 100644
--- a/core/java/android/bluetooth/BluetoothAudioConfig.java
+++ b/core/java/android/bluetooth/BluetoothAudioConfig.java
@@ -16,6 +16,7 @@
 
 package android.bluetooth;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -39,7 +40,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (o instanceof BluetoothAudioConfig) {
             BluetoothAudioConfig bac = (BluetoothAudioConfig) o;
             return (bac.mSampleRate == mSampleRate && bac.mChannelConfig == mChannelConfig
diff --git a/core/java/android/bluetooth/BluetoothClass.java b/core/java/android/bluetooth/BluetoothClass.java
index 905b0cee..603a7ff 100755
--- a/core/java/android/bluetooth/BluetoothClass.java
+++ b/core/java/android/bluetooth/BluetoothClass.java
@@ -16,6 +16,7 @@
 
 package android.bluetooth;
 
+import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
@@ -72,7 +73,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (o instanceof BluetoothClass) {
             return mClass == ((BluetoothClass) o).mClass;
         }
diff --git a/core/java/android/bluetooth/BluetoothCodecConfig.java b/core/java/android/bluetooth/BluetoothCodecConfig.java
index a52fc89..1d0bf97 100644
--- a/core/java/android/bluetooth/BluetoothCodecConfig.java
+++ b/core/java/android/bluetooth/BluetoothCodecConfig.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -208,7 +209,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (o instanceof BluetoothCodecConfig) {
             BluetoothCodecConfig other = (BluetoothCodecConfig) o;
             return (other.mCodecType == mCodecType
diff --git a/core/java/android/bluetooth/BluetoothCodecStatus.java b/core/java/android/bluetooth/BluetoothCodecStatus.java
index 7b567b4..7764ebe 100644
--- a/core/java/android/bluetooth/BluetoothCodecStatus.java
+++ b/core/java/android/bluetooth/BluetoothCodecStatus.java
@@ -56,7 +56,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (o instanceof BluetoothCodecStatus) {
             BluetoothCodecStatus other = (BluetoothCodecStatus) o;
             return (Objects.equals(other.mCodecConfig, mCodecConfig)
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 6287453..1b0fe9d 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -974,7 +974,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (o instanceof BluetoothDevice) {
             return mAddress.equals(((BluetoothDevice) o).getAddress());
         }
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 6ce05f9..e6d6e7a 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -79,7 +79,7 @@
 
     /**
      * Intent used to broadcast the change in the Audio Connection state of the
-     * A2DP profile.
+     * HDP profile.
      *
      * <p>This intent will have 3 extras:
      * <ul>
diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java
index b5959c0..2baa738 100644
--- a/core/java/android/bluetooth/BluetoothHidDevice.java
+++ b/core/java/android/bluetooth/BluetoothHidDevice.java
@@ -342,44 +342,72 @@
 
         @Override
         public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) {
-            clearCallingIdentity();
-            mExecutor.execute(() -> mCallback.onAppStatusChanged(pluggedDevice, registered));
+            final long token = clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onAppStatusChanged(pluggedDevice, registered));
+            } finally {
+                restoreCallingIdentity(token);
+            }
         }
 
         @Override
         public void onConnectionStateChanged(BluetoothDevice device, int state) {
-            clearCallingIdentity();
-            mExecutor.execute(() -> mCallback.onConnectionStateChanged(device, state));
+            final long token = clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onConnectionStateChanged(device, state));
+            } finally {
+                restoreCallingIdentity(token);
+            }
         }
 
         @Override
         public void onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize) {
-            clearCallingIdentity();
-            mExecutor.execute(() -> mCallback.onGetReport(device, type, id, bufferSize));
+            final long token = clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onGetReport(device, type, id, bufferSize));
+            } finally {
+                restoreCallingIdentity(token);
+            }
         }
 
         @Override
         public void onSetReport(BluetoothDevice device, byte type, byte id, byte[] data) {
-            clearCallingIdentity();
-            mExecutor.execute(() -> mCallback.onSetReport(device, type, id, data));
+            final long token = clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onSetReport(device, type, id, data));
+            } finally {
+                restoreCallingIdentity(token);
+            }
         }
 
         @Override
         public void onSetProtocol(BluetoothDevice device, byte protocol) {
-            clearCallingIdentity();
-            mExecutor.execute(() -> mCallback.onSetProtocol(device, protocol));
+            final long token = clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onSetProtocol(device, protocol));
+            } finally {
+                restoreCallingIdentity(token);
+            }
         }
 
         @Override
         public void onInterruptData(BluetoothDevice device, byte reportId, byte[] data) {
-            clearCallingIdentity();
-            mExecutor.execute(() -> mCallback.onInterruptData(device, reportId, data));
+            final long token = clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onInterruptData(device, reportId, data));
+            } finally {
+                restoreCallingIdentity(token);
+            }
         }
 
         @Override
         public void onVirtualCableUnplug(BluetoothDevice device) {
-            clearCallingIdentity();
-            mExecutor.execute(() -> mCallback.onVirtualCableUnplug(device));
+            final long token = clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onVirtualCableUnplug(device));
+            } finally {
+                restoreCallingIdentity(token);
+            }
         }
     }
 
diff --git a/core/java/android/bluetooth/BluetoothMasInstance.java b/core/java/android/bluetooth/BluetoothMasInstance.java
index b64d049..eeaf085 100644
--- a/core/java/android/bluetooth/BluetoothMasInstance.java
+++ b/core/java/android/bluetooth/BluetoothMasInstance.java
@@ -16,6 +16,7 @@
 
 package android.bluetooth;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -34,7 +35,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (o instanceof BluetoothMasInstance) {
             return mId == ((BluetoothMasInstance) o).mId;
         }
diff --git a/core/java/android/bluetooth/le/AdvertiseData.java b/core/java/android/bluetooth/le/AdvertiseData.java
index 5fd8258..397326c 100644
--- a/core/java/android/bluetooth/le/AdvertiseData.java
+++ b/core/java/android/bluetooth/le/AdvertiseData.java
@@ -16,6 +16,7 @@
 
 package android.bluetooth.le;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.ParcelUuid;
@@ -43,17 +44,22 @@
     @Nullable
     private final List<ParcelUuid> mServiceUuids;
 
+    @Nullable
+    private final List<ParcelUuid> mServiceSolicitationUuids;
+
     private final SparseArray<byte[]> mManufacturerSpecificData;
     private final Map<ParcelUuid, byte[]> mServiceData;
     private final boolean mIncludeTxPowerLevel;
     private final boolean mIncludeDeviceName;
 
     private AdvertiseData(List<ParcelUuid> serviceUuids,
+            List<ParcelUuid> serviceSolicitationUuids,
             SparseArray<byte[]> manufacturerData,
             Map<ParcelUuid, byte[]> serviceData,
             boolean includeTxPowerLevel,
             boolean includeDeviceName) {
         mServiceUuids = serviceUuids;
+        mServiceSolicitationUuids = serviceSolicitationUuids;
         mManufacturerSpecificData = manufacturerData;
         mServiceData = serviceData;
         mIncludeTxPowerLevel = includeTxPowerLevel;
@@ -69,6 +75,14 @@
     }
 
     /**
+     * Returns a list of service solicitation UUIDs within the advertisement that we invite to connect.
+     */
+    @Nullable
+    public List<ParcelUuid> getServiceSolicitationUuids() {
+        return mServiceSolicitationUuids;
+    }
+
+    /**
      * Returns an array of manufacturer Id and the corresponding manufacturer specific data. The
      * manufacturer id is a non-negative number assigned by Bluetooth SIG.
      */
@@ -102,15 +116,15 @@
      */
     @Override
     public int hashCode() {
-        return Objects.hash(mServiceUuids, mManufacturerSpecificData, mServiceData,
-                mIncludeDeviceName, mIncludeTxPowerLevel);
+        return Objects.hash(mServiceUuids, mServiceSolicitationUuids, mManufacturerSpecificData,
+                mServiceData, mIncludeDeviceName, mIncludeTxPowerLevel);
     }
 
     /**
      * @hide
      */
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (this == obj) {
             return true;
         }
@@ -119,6 +133,7 @@
         }
         AdvertiseData other = (AdvertiseData) obj;
         return Objects.equals(mServiceUuids, other.mServiceUuids)
+                && Objects.equals(mServiceSolicitationUuids, other.mServiceSolicitationUuids)
                 && BluetoothLeUtils.equals(mManufacturerSpecificData,
                     other.mManufacturerSpecificData)
                 && BluetoothLeUtils.equals(mServiceData, other.mServiceData)
@@ -128,7 +143,8 @@
 
     @Override
     public String toString() {
-        return "AdvertiseData [mServiceUuids=" + mServiceUuids + ", mManufacturerSpecificData="
+        return "AdvertiseData [mServiceUuids=" + mServiceUuids + ", mServiceSolicitationUuids="
+                + mServiceSolicitationUuids + ", mManufacturerSpecificData="
                 + BluetoothLeUtils.toString(mManufacturerSpecificData) + ", mServiceData="
                 + BluetoothLeUtils.toString(mServiceData)
                 + ", mIncludeTxPowerLevel=" + mIncludeTxPowerLevel + ", mIncludeDeviceName="
@@ -143,6 +159,8 @@
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeTypedArray(mServiceUuids.toArray(new ParcelUuid[mServiceUuids.size()]), flags);
+        dest.writeTypedArray(mServiceSolicitationUuids.toArray(
+                new ParcelUuid[mServiceSolicitationUuids.size()]), flags);
 
         // mManufacturerSpecificData could not be null.
         dest.writeInt(mManufacturerSpecificData.size());
@@ -174,6 +192,11 @@
                         builder.addServiceUuid(uuid);
                     }
 
+                    ArrayList<ParcelUuid> solicitationUuids = in.createTypedArrayList(ParcelUuid.CREATOR);
+                    for (ParcelUuid uuid : solicitationUuids) {
+                        builder.addServiceSolicitationUuid(uuid);
+                    }
+
                     int manufacturerSize = in.readInt();
                     for (int i = 0; i < manufacturerSize; ++i) {
                         int manufacturerId = in.readInt();
@@ -198,6 +221,8 @@
     public static final class Builder {
         @Nullable
         private List<ParcelUuid> mServiceUuids = new ArrayList<ParcelUuid>();
+        @Nullable
+        private List<ParcelUuid> mServiceSolicitationUuids = new ArrayList<ParcelUuid>();
         private SparseArray<byte[]> mManufacturerSpecificData = new SparseArray<byte[]>();
         private Map<ParcelUuid, byte[]> mServiceData = new ArrayMap<ParcelUuid, byte[]>();
         private boolean mIncludeTxPowerLevel;
@@ -207,17 +232,31 @@
          * Add a service UUID to advertise data.
          *
          * @param serviceUuid A service UUID to be advertised.
-         * @throws IllegalArgumentException If the {@code serviceUuids} are null.
+         * @throws IllegalArgumentException If the {@code serviceUuid} is null.
          */
         public Builder addServiceUuid(ParcelUuid serviceUuid) {
             if (serviceUuid == null) {
-                throw new IllegalArgumentException("serivceUuids are null");
+                throw new IllegalArgumentException("serviceUuid is null");
             }
             mServiceUuids.add(serviceUuid);
             return this;
         }
 
         /**
+         * Add a service solicitation UUID to advertise data.
+         *
+         * @param serviceSolicitationUuid A service solicitation UUID to be advertised.
+         * @throws IllegalArgumentException If the {@code serviceSolicitationUuid} is null.
+         */
+        @NonNull
+        public Builder addServiceSolicitationUuid(@NonNull ParcelUuid serviceSolicitationUuid) {
+            if (serviceSolicitationUuid == null) {
+                throw new IllegalArgumentException("serviceSolicitationUuid is null");
+            }
+            mServiceSolicitationUuids.add(serviceSolicitationUuid);
+            return this;
+        }
+        /**
          * Add service data to advertise data.
          *
          * @param serviceDataUuid 16-bit UUID of the service the data is associated with
@@ -279,8 +318,9 @@
          * Build the {@link AdvertiseData}.
          */
         public AdvertiseData build() {
-            return new AdvertiseData(mServiceUuids, mManufacturerSpecificData, mServiceData,
-                    mIncludeTxPowerLevel, mIncludeDeviceName);
+            return new AdvertiseData(mServiceUuids, mServiceSolicitationUuids,
+                    mManufacturerSpecificData, mServiceData, mIncludeTxPowerLevel,
+                    mIncludeDeviceName);
         }
     }
 }
diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
index 13c5ff6..5f166f4 100644
--- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
+++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
@@ -507,6 +507,33 @@
                         + num128BitUuids * BluetoothUuid.UUID_BYTES_128_BIT;
             }
         }
+        if (data.getServiceSolicitationUuids() != null) {
+            int num16BitUuids = 0;
+            int num32BitUuids = 0;
+            int num128BitUuids = 0;
+            for (ParcelUuid uuid : data.getServiceSolicitationUuids()) {
+                if (BluetoothUuid.is16BitUuid(uuid)) {
+                    ++num16BitUuids;
+                } else if (BluetoothUuid.is32BitUuid(uuid)) {
+                    ++num32BitUuids;
+                } else {
+                    ++num128BitUuids;
+                }
+            }
+            // 16 bit service uuids are grouped into one field when doing advertising.
+            if (num16BitUuids != 0) {
+                size += OVERHEAD_BYTES_PER_FIELD + num16BitUuids * BluetoothUuid.UUID_BYTES_16_BIT;
+            }
+            // 32 bit service uuids are grouped into one field when doing advertising.
+            if (num32BitUuids != 0) {
+                size += OVERHEAD_BYTES_PER_FIELD + num32BitUuids * BluetoothUuid.UUID_BYTES_32_BIT;
+            }
+            // 128 bit service uuids are grouped into one field when doing advertising.
+            if (num128BitUuids != 0) {
+                size += OVERHEAD_BYTES_PER_FIELD
+                        + num128BitUuids * BluetoothUuid.UUID_BYTES_128_BIT;
+            }
+        }
         for (ParcelUuid uuid : data.getServiceData().keySet()) {
             int uuidLen = BluetoothUuid.uuidToBytes(uuid).length;
             size += OVERHEAD_BYTES_PER_FIELD + uuidLen
diff --git a/core/java/android/bluetooth/le/PeriodicAdvertisingReport.java b/core/java/android/bluetooth/le/PeriodicAdvertisingReport.java
index 7a8c2c6..54b953c 100644
--- a/core/java/android/bluetooth/le/PeriodicAdvertisingReport.java
+++ b/core/java/android/bluetooth/le/PeriodicAdvertisingReport.java
@@ -148,7 +148,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (this == obj) {
             return true;
         }
diff --git a/core/java/android/bluetooth/le/ScanFilter.java b/core/java/android/bluetooth/le/ScanFilter.java
index 7511fd0..51f63f7 100644
--- a/core/java/android/bluetooth/le/ScanFilter.java
+++ b/core/java/android/bluetooth/le/ScanFilter.java
@@ -479,7 +479,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (this == obj) {
             return true;
         }
diff --git a/core/java/android/bluetooth/le/ScanResult.java b/core/java/android/bluetooth/le/ScanResult.java
index 855d345..57dad1a 100644
--- a/core/java/android/bluetooth/le/ScanResult.java
+++ b/core/java/android/bluetooth/le/ScanResult.java
@@ -309,7 +309,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (this == obj) {
             return true;
         }
diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java
index 1f57c7d..c55f0ff 100644
--- a/core/java/android/companion/AssociationRequest.java
+++ b/core/java/android/companion/AssociationRequest.java
@@ -73,7 +73,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
         AssociationRequest that = (AssociationRequest) o;
diff --git a/core/java/android/companion/BluetoothDeviceFilter.java b/core/java/android/companion/BluetoothDeviceFilter.java
index cf9eeca..48dab3b 100644
--- a/core/java/android/companion/BluetoothDeviceFilter.java
+++ b/core/java/android/companion/BluetoothDeviceFilter.java
@@ -126,7 +126,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
         BluetoothDeviceFilter that = (BluetoothDeviceFilter) o;
diff --git a/core/java/android/companion/BluetoothLeDeviceFilter.java b/core/java/android/companion/BluetoothLeDeviceFilter.java
index 8c071fe..784e3a0 100644
--- a/core/java/android/companion/BluetoothLeDeviceFilter.java
+++ b/core/java/android/companion/BluetoothLeDeviceFilter.java
@@ -184,7 +184,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
         BluetoothLeDeviceFilter that = (BluetoothLeDeviceFilter) o;
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index da4980b..c3c270e 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -21,7 +21,6 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
-import android.annotation.TestApi;
 import android.app.Activity;
 import android.app.Application;
 import android.app.PendingIntent;
@@ -131,7 +130,7 @@
      * you use the {@link android.Manifest.permission#REQUEST_COMPANION_RUN_IN_BACKGROUND} and {@link
      * android.Manifest.permission#REQUEST_COMPANION_USE_DATA_IN_BACKGROUND} respectively. Note that these
      * special capabilities have a negative effect on the device's battery and user's data
-     * usage, therefore you should requested them when absolutely necessary.</p>
+     * usage, therefore you should request them when absolutely necessary.</p>
      *
      * <p>You can call {@link #getAssociations} to get the list of currently associated
      * devices, and {@link #disassociate} to remove an association. Consider doing so when the
@@ -285,7 +284,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES)
     public boolean isDeviceAssociatedForWifiConnection(
             @NonNull String packageName,
diff --git a/core/java/android/companion/ICompanionDeviceDiscoveryService.aidl b/core/java/android/companion/ICompanionDeviceDiscoveryService.aidl
index 5e3d46c..a630873 100644
--- a/core/java/android/companion/ICompanionDeviceDiscoveryService.aidl
+++ b/core/java/android/companion/ICompanionDeviceDiscoveryService.aidl
@@ -23,7 +23,7 @@
 
 
 /** @hide */
-interface ICompanionDeviceDiscoveryService {
+oneway interface ICompanionDeviceDiscoveryService {
     void startDiscovery(
         in AssociationRequest request,
         in String callingPackage,
diff --git a/core/java/android/content/ApexEnvironment.java b/core/java/android/content/ApexEnvironment.java
index 9f15a42..b4cc3c2 100644
--- a/core/java/android/content/ApexEnvironment.java
+++ b/core/java/android/content/ApexEnvironment.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Environment;
 import android.os.UserHandle;
 
@@ -31,7 +30,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public class ApexEnvironment {
 
     private static final String APEX_DATA = "apexdata";
diff --git a/core/java/android/content/ComponentName.java b/core/java/android/content/ComponentName.java
index 1967a01..b1cee0c 100644
--- a/core/java/android/content/ComponentName.java
+++ b/core/java/android/content/ComponentName.java
@@ -312,7 +312,7 @@
      * same name, and if the classes that implement each component also have the same name.
      */
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         try {
             if (obj != null) {
                 ComponentName other = (ComponentName)obj;
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 33be50d..6cb5b92 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -1043,6 +1043,7 @@
      *         calling identity by passing it to
      *         {@link #restoreCallingIdentity}.
      */
+    @SuppressWarnings("AndroidFrameworkBinderIdentity")
     public final @NonNull CallingIdentity clearCallingIdentity() {
         return new CallingIdentity(Binder.clearCallingIdentity(), setCallingPackage(null));
     }
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index d0f5ec4..5af7861 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -21,7 +21,6 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.AssetFileDescriptor;
 import android.database.CrossProcessCursorWrapper;
@@ -123,7 +122,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(android.Manifest.permission.REMOVE_TASKS)
     public void setDetectNotResponding(@DurationMillisLong long timeoutMillis) {
         synchronized (ContentProviderClient.class) {
diff --git a/core/java/android/content/ContentProviderOperation.java b/core/java/android/content/ContentProviderOperation.java
index 1fb426e..30775b1 100644
--- a/core/java/android/content/ContentProviderOperation.java
+++ b/core/java/android/content/ContentProviderOperation.java
@@ -533,21 +533,21 @@
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder("ContentProviderOperation(");
-        sb.append("type=" + typeToString(mType) + " ");
+        sb.append("type=").append(typeToString(mType)).append(' ');
         if (mUri != null) {
-            sb.append("uri=" + mUri + " ");
+            sb.append("uri=").append(mUri).append(' ');
         }
         if (mValues != null) {
-            sb.append("values=" + mValues + " ");
+            sb.append("values=").append(mValues).append(' ');
         }
         if (mSelection != null) {
-            sb.append("selection=" + mSelection + " ");
+            sb.append("selection=").append(mSelection).append(' ');
         }
         if (mSelectionArgs != null) {
-            sb.append("selectionArgs=" + mSelectionArgs + " ");
+            sb.append("selectionArgs=").append(mSelectionArgs).append(' ');
         }
         if (mExpectedCount != null) {
-            sb.append("expectedCount=" + mExpectedCount + " ");
+            sb.append("expectedCount=").append(mExpectedCount).append(' ');
         }
         if (mYieldAllowed) {
             sb.append("yieldAllowed ");
diff --git a/core/java/android/content/ContentProviderResult.java b/core/java/android/content/ContentProviderResult.java
index 4fb1ddb..fdfe7eb 100644
--- a/core/java/android/content/ContentProviderResult.java
+++ b/core/java/android/content/ContentProviderResult.java
@@ -143,16 +143,16 @@
     public String toString() {
         final StringBuilder sb = new StringBuilder("ContentProviderResult(");
         if (uri != null) {
-            sb.append("uri=" + uri + " ");
+            sb.append("uri=").append(uri).append(' ');
         }
         if (count != null) {
-            sb.append("count=" + count + " ");
+            sb.append("count=").append(count).append(' ');
         }
         if (extras != null) {
-            sb.append("extras=" + extras + " ");
+            sb.append("extras=").append(extras).append(' ');
         }
         if (exception != null) {
-            sb.append("exception=" + exception + " ");
+            sb.append("exception=").append(exception).append(' ');
         }
         sb.deleteCharAt(sb.length() - 1);
         sb.append(")");
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index c39f176..fd7074c 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -4098,7 +4098,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     // We can't accept an already-opened FD here, since these methods are
     // rewriting actual filesystem paths
     @SuppressLint("StreamFiles")
@@ -4118,7 +4117,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     // We can't accept an already-opened FD here, since these methods are
     // rewriting actual filesystem paths
     @SuppressLint("StreamFiles")
diff --git a/core/java/android/content/ContentValues.java b/core/java/android/content/ContentValues.java
index f9f4c5d..02a5ba1 100644
--- a/core/java/android/content/ContentValues.java
+++ b/core/java/android/content/ContentValues.java
@@ -93,7 +93,7 @@
     }
 
     @Override
-    public boolean equals(Object object) {
+    public boolean equals(@Nullable Object object) {
         if (!(object instanceof ContentValues)) {
             return false;
         }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 666ba32..42fe0e1 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -382,6 +382,7 @@
      * {@link android.Manifest.permission#START_ACTIVITIES_FROM_BACKGROUND}.
      * @hide
      */
+    @SystemApi
     public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 0x00100000;
 
     /**
@@ -1848,7 +1849,6 @@
      */
     @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
     @SystemApi
-    @TestApi
     public void startActivityAsUser(@RequiresPermission @NonNull Intent intent,
             @NonNull UserHandle user) {
         throw new RuntimeException("Not implemented. Must override in a subclass.");
@@ -3508,6 +3508,7 @@
             PERMISSION_SERVICE,
             LIGHTS_SERVICE,
             //@hide: PEOPLE_SERVICE,
+            //@hide: DEVICE_STATE_SERVICE,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ServiceName {}
@@ -3980,7 +3981,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @SuppressLint("ServiceName")
     public static final String STATUS_BAR_SERVICE = "statusbar";
 
@@ -4192,7 +4192,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final String ETHERNET_SERVICE = "ethernet";
 
     /**
@@ -4502,12 +4501,19 @@
     public static final String SOUND_TRIGGER_MIDDLEWARE_SERVICE = "soundtrigger_middleware";
 
     /**
+     * Used to access {@link MusicRecognitionManagerService}.
+     *
+     * @hide
+     * @see #getSystemService(String)
+     */
+    public static final String MUSIC_RECOGNITION_SERVICE = "music_recognition";
+
+    /**
      * Official published name of the (internal) permission service.
      *
      * @see #getSystemService(String)
      * @hide
      */
-    @TestApi
     @SystemApi
     public static final String PERMISSION_SERVICE = "permission";
 
@@ -4538,7 +4544,7 @@
      * @see #getSystemService(String)
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String ROLLBACK_SERVICE = "rollback";
 
     /**
@@ -5012,7 +5018,7 @@
      * @see android.os.BugreportManager
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String BUGREPORT_SERVICE = "bugreport";
 
     /**
@@ -5174,7 +5180,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final String APP_INTEGRITY_SERVICE = "app_integrity";
 
     /**
@@ -5238,6 +5243,14 @@
     public static final String PEOPLE_SERVICE = "people";
 
     /**
+     * Use with {@link #getSystemService(String)} to access device state service.
+     *
+     * @see #getSystemService(String)
+     * @hide
+     */
+    public static final String DEVICE_STATE_SERVICE = "device_state";
+
+    /**
      * Determine whether the given permission is allowed for a particular
      * process and user ID running in the system.
      *
@@ -5738,7 +5751,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @NonNull
     public Context createPackageContextAsUser(
             @NonNull String packageName, @CreatePackageOptions int flags, @NonNull UserHandle user)
@@ -5757,7 +5769,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @NonNull
     public Context createContextAsUser(@NonNull UserHandle user, @CreatePackageOptions int flags) {
         if (Build.IS_ENG) {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 9a9f165..9216a08 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -28,7 +28,6 @@
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.app.AppGlobals;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.pm.ActivityInfo;
@@ -631,6 +630,7 @@
  *     <li> {@link #EXTRA_TEXT}
  *     <li> {@link #EXTRA_TITLE}
  *     <li> {@link #EXTRA_UID}
+ *     <li> {@link #EXTRA_USER_INITIATED}
  * </ul>
  *
  * <h3>Flags</h3>
@@ -1747,7 +1747,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final String EXTRA_ORIGINATING_UID
             = "android.intent.extra.ORIGINATING_UID";
 
@@ -1978,7 +1977,6 @@
     @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS)
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     @SystemApi
-    @TestApi
     public static final String ACTION_MANAGE_DEFAULT_APP =
             "android.intent.action.MANAGE_DEFAULT_APP";
 
@@ -1993,7 +1991,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final String EXTRA_ROLE_NAME = "android.intent.extra.ROLE_NAME";
 
     /**
@@ -2464,6 +2461,8 @@
      * application -- data and code -- is being removed.
      * <li> {@link #EXTRA_REPLACING} is set to true if this will be followed
      * by an {@link #ACTION_PACKAGE_ADDED} broadcast for the same package.
+     * <li> {@link #EXTRA_USER_INITIATED} containing boolean field to signal that the application
+     * was removed with the user-initiated action.
      * </ul>
      *
      * <p class="note">This is a protected intent that can only be sent
@@ -2538,7 +2537,7 @@
      *
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_ROLLBACK_COMMITTED =
             "android.intent.action.ROLLBACK_COMMITTED";
@@ -2741,7 +2740,6 @@
      * </ul>
      *
      * <p class="note">This is a protected intent that can only be sent by the system.
-     * @hide
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_PACKAGE_STARTABLE = "android.intent.action.PACKAGE_STARTABLE";
@@ -2752,13 +2750,13 @@
      * <ul>
      * <li> {@link #EXTRA_UID} containing the integer uid assigned to the package. </li>
      * <li> {@link #EXTRA_PACKAGE_NAME} containing the package name. </li>
-     * <li> {@link #EXTRA_REASON} containing the integer indicating the reason for the state change,
+     * <li> {@link #EXTRA_UNSTARTABLE_REASON} containing the integer indicating the reason for
+     * the state change,
      * @see PackageManager.UnstartableReason
      * </li>
      * </ul>
      *
      * <p class="note">This is a protected intent that can only be sent by the system.
-     * @hide
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_PACKAGE_UNSTARTABLE =
@@ -2773,7 +2771,6 @@
      * </ul>
      *
      * <p class="note">This is a protected intent that can only be sent by the system.
-     * @hide
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_PACKAGE_FULLY_LOADED =
@@ -5557,6 +5554,12 @@
     public static final String EXTRA_DONT_KILL_APP = "android.intent.extra.DONT_KILL_APP";
 
     /**
+     * Used as a boolean extra field in {@link android.content.Intent#ACTION_PACKAGE_REMOVED}
+     * intents to signal that the application was removed with the user-initiated action.
+     */
+    public static final String EXTRA_USER_INITIATED = "android.intent.extra.USER_INITIATED";
+
+    /**
      * A String holding the phone number originally entered in
      * {@link android.content.Intent#ACTION_NEW_OUTGOING_CALL}, or the actual
      * number to call in a {@link android.content.Intent#ACTION_CALL}.
@@ -6004,6 +6007,13 @@
      */
     public static final String EXTRA_LOCUS_ID = "android.intent.extra.LOCUS_ID";
 
+    /**
+     * Intent extra: the reason that the package associated with this intent has become unstartable.
+     *
+     * <p>Type: String
+     */
+    public static final String EXTRA_UNSTARTABLE_REASON = "android.intent.extra.UNSTARTABLE_REASON";
+
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
     // Intent flags (see mFlags variable).
@@ -7435,6 +7445,7 @@
 
     /** @hide */
     @UnsupportedAppUsage
+    @SuppressWarnings("AndroidFrameworkEfficientCollections")
     public static Intent parseCommandArgs(ShellCommand cmd, CommandOptionHandler optionHandler)
             throws URISyntaxException {
         Intent intent = new Intent();
@@ -10362,7 +10373,7 @@
         }
 
         @Override
-        public boolean equals(Object obj) {
+        public boolean equals(@Nullable Object obj) {
             if (obj instanceof FilterComparison) {
                 Intent other = ((FilterComparison)obj).mIntent;
                 return mIntent.filterEquals(other);
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index 7fe29a9..5240ab4 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -1144,7 +1144,7 @@
         }
 
         @Override
-        public boolean equals(Object obj) {
+        public boolean equals(@Nullable Object obj) {
             if (obj instanceof AuthorityEntry) {
                 final AuthorityEntry other = (AuthorityEntry)obj;
                 return match(other);
diff --git a/core/java/android/content/IntentSender.java b/core/java/android/content/IntentSender.java
index f40dc29..858d1e4 100644
--- a/core/java/android/content/IntentSender.java
+++ b/core/java/android/content/IntentSender.java
@@ -16,6 +16,7 @@
 
 package android.content;
 
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Bundle;
@@ -27,7 +28,6 @@
 import android.os.UserHandle;
 import android.util.AndroidException;
 
-
 /**
  * A description of an Intent and target action to perform with it.
  * The returned object can be
@@ -284,7 +284,7 @@
      * same package.
      */
     @Override
-    public boolean equals(Object otherObj) {
+    public boolean equals(@Nullable Object otherObj) {
         if (otherObj instanceof IntentSender) {
             return mTarget.asBinder().equals(((IntentSender)otherObj)
                     .mTarget.asBinder());
diff --git a/core/java/android/content/LocusId.java b/core/java/android/content/LocusId.java
index 98e71f0..a04fbd1 100644
--- a/core/java/android/content/LocusId.java
+++ b/core/java/android/content/LocusId.java
@@ -16,6 +16,7 @@
 package android.content;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.view.contentcapture.ContentCaptureManager;
@@ -92,7 +93,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (this == obj) return true;
         if (obj == null) return false;
         if (getClass() != obj.getClass()) return false;
diff --git a/core/java/android/content/PeriodicSync.java b/core/java/android/content/PeriodicSync.java
index a075148..432e81b 100644
--- a/core/java/android/content/PeriodicSync.java
+++ b/core/java/android/content/PeriodicSync.java
@@ -16,10 +16,11 @@
 
 package android.content;
 
-import android.os.Parcelable;
+import android.accounts.Account;
+import android.annotation.Nullable;
 import android.os.Bundle;
 import android.os.Parcel;
-import android.accounts.Account;
+import android.os.Parcelable;
 
 import java.util.Objects;
 
@@ -117,7 +118,7 @@
     };
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (o == this) {
             return true;
         }
diff --git a/core/java/android/content/PermissionChecker.java b/core/java/android/content/PermissionChecker.java
index eec7c9c..159db92 100644
--- a/core/java/android/content/PermissionChecker.java
+++ b/core/java/android/content/PermissionChecker.java
@@ -116,6 +116,10 @@
      * will evaluate the permission access based on the current fg/bg state of the app and
      * leave a record that the data was accessed.
      *
+     * <p>For more details how to determine the {@code packageName}, {@code attributionTag}, and
+     * {@code message}, please check the description in
+     * {@link AppOpsManager#noteOp(String, int, String, String, String)}
+     *
      * @param context Context for accessing resources.
      * @param permission The permission to check.
      * @param pid The process id for which to check. Use {@link #PID_UNKNOWN} if the PID
@@ -262,11 +266,15 @@
      * will evaluate the permission access based on the current fg/bg state of the app and
      * leave a record that the data was accessed.
      *
+     * <p>For more details how to determine the {@code callingPackageName},
+     * {@code callingAttributionTag}, and {@code message}, please check the description in
+     * {@link AppOpsManager#noteOp(String, int, String, String, String)}
+     *
      * @param context Context for accessing resources.
      * @param permission The permission to check.
-     * @param packageName The package name making the IPC. If null the
+     * @param callingPackageName The package name making the IPC. If null the
      *     the first package for the calling UID will be used.
-     * @param attributionTag attribution tag
+     * @param callingAttributionTag attribution tag
      * @return The permission check result which is either {@link #PERMISSION_GRANTED}
      *     or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
      * @param message A message describing the reason the permission was checked
@@ -275,13 +283,13 @@
      */
     @PermissionResult
     public static int checkCallingPermissionForDataDelivery(@NonNull Context context,
-            @NonNull String permission, @Nullable String packageName,
-            @Nullable String attributionTag, @Nullable String message) {
+            @NonNull String permission, @Nullable String callingPackageName,
+            @Nullable String callingAttributionTag, @Nullable String message) {
         if (Binder.getCallingPid() == Process.myPid()) {
             return PERMISSION_HARD_DENIED;
         }
         return checkPermissionForDataDelivery(context, permission, Binder.getCallingPid(),
-                Binder.getCallingUid(), packageName, attributionTag, message);
+                Binder.getCallingUid(), callingPackageName, callingAttributionTag, message);
     }
 
     /**
@@ -339,6 +347,10 @@
      * will evaluate the permission access based on the current fg/bg state of the app and
      * leave a record that the data was accessed.
      *
+     * <p>For more details how to determine the {@code callingPackageName},
+     * {@code callingAttributionTag}, and {@code message}, please check the description in
+     * {@link AppOpsManager#noteOp(String, int, String, String, String)}
+     *
      * @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/RestrictionEntry.java b/core/java/android/content/RestrictionEntry.java
index 8f08fde..63fcb49 100644
--- a/core/java/android/content/RestrictionEntry.java
+++ b/core/java/android/content/RestrictionEntry.java
@@ -17,6 +17,7 @@
 package android.content;
 
 import android.annotation.ArrayRes;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -452,7 +453,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (o == this) return true;
         if (!(o instanceof RestrictionEntry)) return false;
         final RestrictionEntry other = (RestrictionEntry) o;
diff --git a/core/java/android/content/SyncAdapterType.java b/core/java/android/content/SyncAdapterType.java
index 7bcdbfd..ffcdf53 100644
--- a/core/java/android/content/SyncAdapterType.java
+++ b/core/java/android/content/SyncAdapterType.java
@@ -176,7 +176,7 @@
         return new SyncAdapterType(authority, accountType);
     }
 
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (o == this) return true;
         if (!(o instanceof SyncAdapterType)) return false;
         final SyncAdapterType other = (SyncAdapterType)o;
diff --git a/core/java/android/content/SyncResult.java b/core/java/android/content/SyncResult.java
index 8280f8e..7e68dcaf 100644
--- a/core/java/android/content/SyncResult.java
+++ b/core/java/android/content/SyncResult.java
@@ -292,7 +292,7 @@
      * @return debugging string.
      */
     public String toDebugString() {
-        StringBuffer sb = new StringBuffer();
+        StringBuilder sb = new StringBuilder();
 
         if (fullSyncRequested) {
             sb.append("f1");
diff --git a/core/java/android/content/integrity/AppIntegrityManager.java b/core/java/android/content/integrity/AppIntegrityManager.java
index 4db4c73..1196064 100644
--- a/core/java/android/content/integrity/AppIntegrityManager.java
+++ b/core/java/android/content/integrity/AppIntegrityManager.java
@@ -36,7 +36,6 @@
  *
  * @hide
  */
-@TestApi
 @SystemApi
 @SystemService(Context.APP_INTEGRITY_SERVICE)
 public class AppIntegrityManager {
diff --git a/core/java/android/content/integrity/AtomicFormula.java b/core/java/android/content/integrity/AtomicFormula.java
index f363a54..e359800 100644
--- a/core/java/android/content/integrity/AtomicFormula.java
+++ b/core/java/android/content/integrity/AtomicFormula.java
@@ -20,6 +20,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -128,7 +129,7 @@
     private final @Key int mKey;
 
     public AtomicFormula(@Key int key) {
-        checkArgument(isValidKey(key), String.format("Unknown key: %d", key));
+        checkArgument(isValidKey(key), "Unknown key: %d", key);
         mKey = key;
     }
 
@@ -148,8 +149,7 @@
             super(key);
             checkArgument(
                     key == VERSION_CODE,
-                    String.format(
-                            "Key %s cannot be used with LongAtomicFormula", keyToString(key)));
+                    "Key %s cannot be used with LongAtomicFormula", keyToString(key));
             mValue = null;
             mOperator = null;
         }
@@ -167,10 +167,9 @@
             super(key);
             checkArgument(
                     key == VERSION_CODE,
-                    String.format(
-                            "Key %s cannot be used with LongAtomicFormula", keyToString(key)));
+                    "Key %s cannot be used with LongAtomicFormula", keyToString(key));
             checkArgument(
-                    isValidOperator(operator), String.format("Unknown operator: %d", operator));
+                    isValidOperator(operator), "Unknown operator: %d", operator);
             mOperator = operator;
             mValue = value;
         }
@@ -240,7 +239,7 @@
         }
 
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (this == o) {
                 return true;
             }
@@ -316,8 +315,7 @@
                             || key == INSTALLER_CERTIFICATE
                             || key == INSTALLER_NAME
                             || key == STAMP_CERTIFICATE_HASH,
-                    String.format(
-                            "Key %s cannot be used with StringAtomicFormula", keyToString(key)));
+                    "Key %s cannot be used with StringAtomicFormula", keyToString(key));
             mValue = null;
             mIsHashedValue = null;
         }
@@ -338,8 +336,7 @@
                             || key == INSTALLER_CERTIFICATE
                             || key == INSTALLER_NAME
                             || key == STAMP_CERTIFICATE_HASH,
-                    String.format(
-                            "Key %s cannot be used with StringAtomicFormula", keyToString(key)));
+                    "Key %s cannot be used with StringAtomicFormula", keyToString(key));
             mValue = value;
             mIsHashedValue = isHashed;
         }
@@ -364,8 +361,7 @@
                             || key == INSTALLER_CERTIFICATE
                             || key == INSTALLER_NAME
                             || key == STAMP_CERTIFICATE_HASH,
-                    String.format(
-                            "Key %s cannot be used with StringAtomicFormula", keyToString(key)));
+                    "Key %s cannot be used with StringAtomicFormula", keyToString(key));
             mValue = hashValue(key, value);
             mIsHashedValue =
                     (key == APP_CERTIFICATE
@@ -426,7 +422,7 @@
         }
 
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (this == o) {
                 return true;
             }
@@ -594,7 +590,7 @@
         }
 
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (this == o) {
                 return true;
             }
diff --git a/core/java/android/content/integrity/CompoundFormula.java b/core/java/android/content/integrity/CompoundFormula.java
index 14b1197..1ffabd0 100644
--- a/core/java/android/content/integrity/CompoundFormula.java
+++ b/core/java/android/content/integrity/CompoundFormula.java
@@ -20,6 +20,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -83,7 +84,7 @@
      */
     public CompoundFormula(@Connector int connector, List<IntegrityFormula> formulas) {
         checkArgument(
-                isValidConnector(connector), String.format("Unknown connector: %d", connector));
+                isValidConnector(connector), "Unknown connector: %d", connector);
         validateFormulas(connector, formulas);
         this.mConnector = connector;
         this.mFormulas = Collections.unmodifiableList(formulas);
@@ -92,7 +93,7 @@
     CompoundFormula(Parcel in) {
         mConnector = in.readInt();
         int length = in.readInt();
-        checkArgument(length >= 0, "Must have non-negative length. Got " + length);
+        checkArgument(length >= 0, "Must have non-negative length. Got %d", length);
         mFormulas = new ArrayList<>(length);
         for (int i = 0; i < length; i++) {
             mFormulas.add(IntegrityFormula.readFromParcel(in));
@@ -158,7 +159,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) {
             return true;
         }
@@ -195,16 +196,14 @@
             case OR:
                 checkArgument(
                         formulas.size() >= 2,
-                        String.format(
-                                "Connector %s must have at least 2 formulas",
-                                connectorToString(connector)));
+                        "Connector %s must have at least 2 formulas",
+                        connectorToString(connector));
                 break;
             case NOT:
                 checkArgument(
                         formulas.size() == 1,
-                        String.format(
-                                "Connector %s must have 1 formula only",
-                                connectorToString(connector)));
+                        "Connector %s must have 1 formula only",
+                        connectorToString(connector));
                 break;
         }
     }
diff --git a/core/java/android/content/integrity/IntegrityFormula.java b/core/java/android/content/integrity/IntegrityFormula.java
index fc177721..d965ef5 100644
--- a/core/java/android/content/integrity/IntegrityFormula.java
+++ b/core/java/android/content/integrity/IntegrityFormula.java
@@ -19,7 +19,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.content.integrity.AtomicFormula.BooleanAtomicFormula;
 import android.content.integrity.AtomicFormula.LongAtomicFormula;
 import android.content.integrity.AtomicFormula.StringAtomicFormula;
@@ -38,7 +37,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 @VisibleForTesting
 public abstract class IntegrityFormula {
 
diff --git a/core/java/android/content/integrity/IntegrityUtils.java b/core/java/android/content/integrity/IntegrityUtils.java
index c3f7624..c184c69 100644
--- a/core/java/android/content/integrity/IntegrityUtils.java
+++ b/core/java/android/content/integrity/IntegrityUtils.java
@@ -36,7 +36,7 @@
     public static byte[] getBytesFromHexDigest(String hexDigest) {
         checkArgument(
                 hexDigest.length() % 2 == 0,
-                "Invalid hex encoding " + hexDigest + ": must have even length");
+                "Invalid hex encoding %s: must have even length", hexDigest);
 
         byte[] rawBytes = new byte[hexDigest.length() / 2];
         for (int i = 0; i < rawBytes.length; i++) {
diff --git a/core/java/android/content/integrity/Rule.java b/core/java/android/content/integrity/Rule.java
index d29e6df..34eb1e7 100644
--- a/core/java/android/content/integrity/Rule.java
+++ b/core/java/android/content/integrity/Rule.java
@@ -20,8 +20,8 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -38,7 +38,6 @@
  *
  * @hide
  */
-@TestApi
 @SystemApi
 @VisibleForTesting
 public final class Rule implements Parcelable {
@@ -65,7 +64,7 @@
     private final @Effect int mEffect;
 
     public Rule(@NonNull IntegrityFormula formula, @Effect int effect) {
-        checkArgument(isValidEffect(effect), String.format("Unknown effect: %d", effect));
+        checkArgument(isValidEffect(effect), "Unknown effect: %d", effect);
         this.mFormula = Objects.requireNonNull(formula);
         this.mEffect = effect;
     }
@@ -115,7 +114,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) {
             return true;
         }
diff --git a/core/java/android/content/integrity/RuleSet.java b/core/java/android/content/integrity/RuleSet.java
index e121ff8..b423b54 100644
--- a/core/java/android/content/integrity/RuleSet.java
+++ b/core/java/android/content/integrity/RuleSet.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -30,7 +29,6 @@
  *
  * @hide
  */
-@TestApi
 @SystemApi
 public class RuleSet {
     private final String mVersion;
diff --git a/core/java/android/content/om/OverlayableInfo.java b/core/java/android/content/om/OverlayableInfo.java
index 5923907..6bd42d9 100644
--- a/core/java/android/content/om/OverlayableInfo.java
+++ b/core/java/android/content/om/OverlayableInfo.java
@@ -82,7 +82,7 @@
 
     @Override
     @DataClass.Generated.Member
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         // You can override field equality logic by defining either of the methods like:
         // boolean fieldNameEquals(OverlayableInfo other) { ... }
         // boolean fieldNameEquals(FieldType otherValue) { ... }
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index ea4b762..b371141 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1419,6 +1419,13 @@
     public static final class WindowLayout {
         public WindowLayout(int width, float widthFraction, int height, float heightFraction,
                 int gravity, int minWidth, int minHeight) {
+            this(width, widthFraction, height, heightFraction, gravity, minWidth, minHeight,
+                    null /* windowLayoutAffinity */);
+        }
+
+        /** @hide */
+        public WindowLayout(int width, float widthFraction, int height, float heightFraction,
+                int gravity, int minWidth, int minHeight, String windowLayoutAffinity) {
             this.width = width;
             this.widthFraction = widthFraction;
             this.height = height;
@@ -1426,6 +1433,7 @@
             this.gravity = gravity;
             this.minWidth = minWidth;
             this.minHeight = minHeight;
+            this.windowLayoutAffinity = windowLayoutAffinity;
         }
 
         /** @hide */
@@ -1506,6 +1514,8 @@
         /**
          * Affinity of window layout parameters. Activities with the same UID and window layout
          * affinity will share the same window dimension record.
+         *
+         * @attr ref android.R.styleable#AndroidManifestLayout_windowLayoutAffinity
          * @hide
          */
         public String windowLayoutAffinity;
diff --git a/core/java/android/content/pm/ApkChecksum.java b/core/java/android/content/pm/ApkChecksum.java
index bf67841..eca48ec 100644
--- a/core/java/android/content/pm/ApkChecksum.java
+++ b/core/java/android/content/pm/ApkChecksum.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.IntentSender;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -31,12 +30,11 @@
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
-import java.util.List;
 
 /**
  * A typed checksum of an APK.
  *
- * @see PackageManager#getChecksums(String, boolean, int, List, IntentSender)
+ * @see PackageManager#requestChecksums
  */
 @DataClass(genHiddenConstructor = true)
 @DataClass.Suppress({"getChecksum"})
@@ -52,20 +50,20 @@
     /**
      * For Installer-provided checksums, package name of the Installer.
      */
-    private final @Nullable String mSourcePackageName;
+    private final @Nullable String mInstallerPackageName;
     /**
      * For Installer-provided checksums, certificate of the Installer.
      */
-    private final @Nullable byte[] mSourceCertificate;
+    private final @Nullable byte[] mInstallerCertificate;
 
     /**
      * Constructor, internal use only.
      *
      * @hide
      */
-    public ApkChecksum(@Nullable String splitName, @Checksum.Kind int kind,
+    public ApkChecksum(@Nullable String splitName, @Checksum.Type int type,
             @NonNull byte[] value) {
-        this(splitName, new Checksum(kind, value), (String) null, (byte[]) null);
+        this(splitName, new Checksum(type, value), (String) null, (byte[]) null);
     }
 
     /**
@@ -73,19 +71,19 @@
      *
      * @hide
      */
-    public ApkChecksum(@Nullable String splitName, @Checksum.Kind int kind, @NonNull byte[] value,
+    public ApkChecksum(@Nullable String splitName, @Checksum.Type int type, @NonNull byte[] value,
             @Nullable String sourcePackageName, @Nullable Certificate sourceCertificate)
             throws CertificateEncodingException {
-        this(splitName, new Checksum(kind, value), sourcePackageName,
+        this(splitName, new Checksum(type, value), sourcePackageName,
                 (sourceCertificate != null) ? sourceCertificate.getEncoded() : null);
     }
 
 
     /**
-     * Checksum kind.
+     * Checksum type.
      */
-    public @Checksum.Kind int getKind() {
-        return mChecksum.getKind();
+    public @Checksum.Type int getType() {
+        return mChecksum.getType();
     }
 
     /**
@@ -96,24 +94,24 @@
     }
 
     /**
-     * Returns raw bytes representing encoded certificate of the source of this checksum.
+     * Returns raw bytes representing encoded certificate of the Installer.
      * @hide
      */
-    public @Nullable byte[] getSourceCertificateBytes() {
-        return mSourceCertificate;
+    public @Nullable byte[] getInstallerCertificateBytes() {
+        return mInstallerCertificate;
     }
 
     /**
-     * Certificate of the source of this checksum.
+     * For Installer-provided checksums, certificate of the Installer.
      * @throws CertificateException in case when certificate can't be re-created from serialized
      * data.
      */
-    public @Nullable Certificate getSourceCertificate() throws CertificateException {
-        if (mSourceCertificate == null) {
+    public @Nullable Certificate getInstallerCertificate() throws CertificateException {
+        if (mInstallerCertificate == null) {
             return null;
         }
         final CertificateFactory cf = CertificateFactory.getInstance("X.509");
-        final InputStream is = new ByteArrayInputStream(mSourceCertificate);
+        final InputStream is = new ByteArrayInputStream(mInstallerCertificate);
         final X509Certificate cert = (X509Certificate) cf.generateCertificate(is);
         return cert;
     }
@@ -140,9 +138,9 @@
      *   Checksum for which split. Null indicates base.apk.
      * @param checksum
      *   Checksum.
-     * @param sourcePackageName
+     * @param installerPackageName
      *   For Installer-provided checksums, package name of the Installer.
-     * @param sourceCertificate
+     * @param installerCertificate
      *   For Installer-provided checksums, certificate of the Installer.
      * @hide
      */
@@ -150,14 +148,14 @@
     public ApkChecksum(
             @Nullable String splitName,
             @NonNull Checksum checksum,
-            @Nullable String sourcePackageName,
-            @Nullable byte[] sourceCertificate) {
+            @Nullable String installerPackageName,
+            @Nullable byte[] installerCertificate) {
         this.mSplitName = splitName;
         this.mChecksum = checksum;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mChecksum);
-        this.mSourcePackageName = sourcePackageName;
-        this.mSourceCertificate = sourceCertificate;
+        this.mInstallerPackageName = installerPackageName;
+        this.mInstallerCertificate = installerCertificate;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -174,8 +172,8 @@
      * For Installer-provided checksums, package name of the Installer.
      */
     @DataClass.Generated.Member
-    public @Nullable String getSourcePackageName() {
-        return mSourcePackageName;
+    public @Nullable String getInstallerPackageName() {
+        return mInstallerPackageName;
     }
 
     @Override
@@ -186,13 +184,13 @@
 
         byte flg = 0;
         if (mSplitName != null) flg |= 0x1;
-        if (mSourcePackageName != null) flg |= 0x4;
-        if (mSourceCertificate != null) flg |= 0x8;
+        if (mInstallerPackageName != null) flg |= 0x4;
+        if (mInstallerCertificate != null) flg |= 0x8;
         dest.writeByte(flg);
         if (mSplitName != null) dest.writeString(mSplitName);
         dest.writeTypedObject(mChecksum, flags);
-        if (mSourcePackageName != null) dest.writeString(mSourcePackageName);
-        if (mSourceCertificate != null) dest.writeByteArray(mSourceCertificate);
+        if (mInstallerPackageName != null) dest.writeString(mInstallerPackageName);
+        if (mInstallerCertificate != null) dest.writeByteArray(mInstallerCertificate);
     }
 
     @Override
@@ -209,15 +207,15 @@
         byte flg = in.readByte();
         String splitName = (flg & 0x1) == 0 ? null : in.readString();
         Checksum checksum = (Checksum) in.readTypedObject(Checksum.CREATOR);
-        String sourcePackageName = (flg & 0x4) == 0 ? null : in.readString();
-        byte[] sourceCertificate = (flg & 0x8) == 0 ? null : in.createByteArray();
+        String installerPackageName = (flg & 0x4) == 0 ? null : in.readString();
+        byte[] installerCertificate = (flg & 0x8) == 0 ? null : in.createByteArray();
 
         this.mSplitName = splitName;
         this.mChecksum = checksum;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mChecksum);
-        this.mSourcePackageName = sourcePackageName;
-        this.mSourceCertificate = sourceCertificate;
+        this.mInstallerPackageName = installerPackageName;
+        this.mInstallerCertificate = installerCertificate;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -237,10 +235,10 @@
     };
 
     @DataClass.Generated(
-            time = 1600407436287L,
+            time = 1601589269293L,
             codegenVersion = "1.0.15",
             sourceFile = "frameworks/base/core/java/android/content/pm/ApkChecksum.java",
-            inputSignatures = "private final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.NonNull android.content.pm.Checksum mChecksum\nprivate final @android.annotation.Nullable java.lang.String mSourcePackageName\nprivate final @android.annotation.Nullable byte[] mSourceCertificate\npublic @android.content.pm.Checksum.Kind int getKind()\npublic @android.annotation.NonNull byte[] getValue()\npublic @android.annotation.Nullable byte[] getSourceCertificateBytes()\npublic @android.annotation.Nullable java.security.cert.Certificate getSourceCertificate()\nclass ApkChecksum extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true)")
+            inputSignatures = "private final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.NonNull android.content.pm.Checksum mChecksum\nprivate final @android.annotation.Nullable java.lang.String mInstallerPackageName\nprivate final @android.annotation.Nullable byte[] mInstallerCertificate\npublic @android.content.pm.Checksum.Type int getType()\npublic @android.annotation.NonNull byte[] getValue()\npublic @android.annotation.Nullable byte[] getInstallerCertificateBytes()\npublic @android.annotation.Nullable java.security.cert.Certificate getInstallerCertificate()\nclass ApkChecksum extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index d5f2c12..8f4fc26 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1141,7 +1141,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public int targetSandboxVersion;
 
     /**
diff --git a/core/java/android/content/pm/Checksum.java b/core/java/android/content/pm/Checksum.java
index 10ca5ca..318d363 100644
--- a/core/java/android/content/pm/Checksum.java
+++ b/core/java/android/content/pm/Checksum.java
@@ -25,12 +25,12 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.List;
 
 /**
  * A typed checksum.
  *
- * @see PackageInstaller.Session#addChecksums(String, List)
+ * @see ApkChecksum
+ * @see PackageManager#requestChecksums
  */
 @DataClass(genConstDefs = false)
 public final class Checksum implements Parcelable {
@@ -43,88 +43,89 @@
      * Can be used by kernel to enforce authenticity and integrity of the APK.
      * <a href="https://git.kernel.org/pub/scm/fs/fscrypt/fscrypt.git/tree/Documentation/filesystems/fsverity.rst#">See fs-verity for details</a>
      *
-     * @see PackageManager#getChecksums
-     * @see PackageInstaller.Session#addChecksums
+     * @see PackageManager#requestChecksums
      */
-    public static final int WHOLE_MERKLE_ROOT_4K_SHA256 = 0x00000001;
+    public static final int TYPE_WHOLE_MERKLE_ROOT_4K_SHA256 = 0x00000001;
 
     /**
      * MD5 hash computed over all file bytes.
      *
-     * @see PackageManager#getChecksums
-     * @see PackageInstaller.Session#addChecksums
-     * @deprecated Use SHA2 family of hashes (SHA256/SHA512) instead.
-     *             MD5 is cryptographically broken and unsuitable for further use.
+     * @see PackageManager#requestChecksums
+     * @deprecated Not platform enforced. Cryptographically broken and unsuitable for further use.
+     *             Use platform enforced digests e.g. {@link #TYPE_WHOLE_MERKLE_ROOT_4K_SHA256}.
      *             Provided for completeness' sake and to support legacy usecases.
      */
     @Deprecated
-    public static final int WHOLE_MD5 = 0x00000002;
+    public static final int TYPE_WHOLE_MD5 = 0x00000002;
 
     /**
      * SHA1 hash computed over all file bytes.
      *
-     * @see PackageManager#getChecksums
-     * @see PackageInstaller.Session#addChecksums
-     * @deprecated Use SHA2 family of hashes (SHA256/SHA512) instead.
-     *             SHA1 is broken and should not be used.
+     * @see PackageManager#requestChecksums
+     * @deprecated Not platform enforced. Broken and should not be used.
+     *             Use platform enforced digests e.g. {@link #TYPE_WHOLE_MERKLE_ROOT_4K_SHA256}.
      *             Provided for completeness' sake and to support legacy usecases.
      */
     @Deprecated
-    public static final int WHOLE_SHA1 = 0x00000004;
+    public static final int TYPE_WHOLE_SHA1 = 0x00000004;
 
     /**
      * SHA256 hash computed over all file bytes.
+     * @deprecated Not platform enforced.
+     *             Use platform enforced digests e.g. {@link #TYPE_WHOLE_MERKLE_ROOT_4K_SHA256}.
+     *             Provided for completeness' sake and to support legacy usecases.
      *
-     * @see PackageManager#getChecksums
-     * @see PackageInstaller.Session#addChecksums
+     * @see PackageManager#requestChecksums
      */
-    public static final int WHOLE_SHA256 = 0x00000008;
+    @Deprecated
+    public static final int TYPE_WHOLE_SHA256 = 0x00000008;
 
     /**
      * SHA512 hash computed over all file bytes.
+     * @deprecated Not platform enforced.
+     *             Use platform enforced digests e.g. {@link #TYPE_WHOLE_MERKLE_ROOT_4K_SHA256}.
+     *             Provided for completeness' sake and to support legacy usecases.
      *
-     * @see PackageManager#getChecksums
-     * @see PackageInstaller.Session#addChecksums
+     * @see PackageManager#requestChecksums
      */
-    public static final int WHOLE_SHA512 = 0x00000010;
+    @Deprecated
+    public static final int TYPE_WHOLE_SHA512 = 0x00000010;
 
     /**
      * Root SHA256 hash of a 1M Merkle tree computed over protected content.
      * Excludes signing block.
      * <a href="https://source.android.com/security/apksigning/v2">See APK Signature Scheme V2</a>.
      *
-     * @see PackageManager#getChecksums
-     * @see PackageInstaller.Session#addChecksums
+     * @see PackageManager#requestChecksums
      */
-    public static final int PARTIAL_MERKLE_ROOT_1M_SHA256 = 0x00000020;
+    public static final int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256 = 0x00000020;
 
     /**
      * Root SHA512 hash of a 1M Merkle tree computed over protected content.
      * Excludes signing block.
      * <a href="https://source.android.com/security/apksigning/v2">See APK Signature Scheme V2</a>.
      *
-     * @see PackageManager#getChecksums
-     * @see PackageInstaller.Session#addChecksums
+     * @see PackageManager#requestChecksums
      */
-    public static final int PARTIAL_MERKLE_ROOT_1M_SHA512 = 0x00000040;
+    public static final int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512 = 0x00000040;
 
     /** @hide */
-    @IntDef(flag = true, prefix = {"WHOLE_", "PARTIAL_"}, value = {
-            WHOLE_MERKLE_ROOT_4K_SHA256,
-            WHOLE_MD5,
-            WHOLE_SHA1,
-            WHOLE_SHA256,
-            WHOLE_SHA512,
-            PARTIAL_MERKLE_ROOT_1M_SHA256,
-            PARTIAL_MERKLE_ROOT_1M_SHA512,
+    @IntDef(flag = true, prefix = {"TYPE_"}, value = {
+            TYPE_WHOLE_MERKLE_ROOT_4K_SHA256,
+            TYPE_WHOLE_MD5,
+            TYPE_WHOLE_SHA1,
+            TYPE_WHOLE_SHA256,
+            TYPE_WHOLE_SHA512,
+            TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256,
+            TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512,
     })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface Kind {}
+    public @interface Type {}
 
     /**
-     * Checksum kind.
+     * Checksum type.
      */
-    private final @Checksum.Kind int mKind;
+    private final @Checksum.Type int mType;
     /**
      * Checksum value.
      */
@@ -132,7 +133,6 @@
 
 
 
-
     // Code below generated by codegen v1.0.15.
     //
     // DO NOT MODIFY!
@@ -149,18 +149,18 @@
     /**
      * Creates a new Checksum.
      *
-     * @param kind
-     *   Checksum kind.
+     * @param type
+     *   Checksum type.
      * @param value
      *   Checksum value.
      */
     @DataClass.Generated.Member
     public Checksum(
-            @Checksum.Kind int kind,
+            @Checksum.Type int type,
             @NonNull byte[] value) {
-        this.mKind = kind;
+        this.mType = type;
         com.android.internal.util.AnnotationValidations.validate(
-                Checksum.Kind.class, null, mKind);
+                Checksum.Type.class, null, mType);
         this.mValue = value;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mValue);
@@ -169,11 +169,11 @@
     }
 
     /**
-     * Checksum kind.
+     * Checksum type.
      */
     @DataClass.Generated.Member
-    public @Checksum.Kind int getKind() {
-        return mKind;
+    public @Checksum.Type int getType() {
+        return mType;
     }
 
     /**
@@ -190,7 +190,7 @@
         // You can override field parcelling by defining methods like:
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
-        dest.writeInt(mKind);
+        dest.writeInt(mType);
         dest.writeByteArray(mValue);
     }
 
@@ -205,12 +205,12 @@
         // You can override field unparcelling by defining methods like:
         // static FieldType unparcelFieldName(Parcel in) { ... }
 
-        int kind = in.readInt();
+        int type = in.readInt();
         byte[] value = in.createByteArray();
 
-        this.mKind = kind;
+        this.mType = type;
         com.android.internal.util.AnnotationValidations.validate(
-                Checksum.Kind.class, null, mKind);
+                Checksum.Type.class, null, mType);
         this.mValue = value;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mValue);
@@ -233,10 +233,10 @@
     };
 
     @DataClass.Generated(
-            time = 1600717052366L,
+            time = 1601955017774L,
             codegenVersion = "1.0.15",
             sourceFile = "frameworks/base/core/java/android/content/pm/Checksum.java",
-            inputSignatures = "public static final  int WHOLE_MERKLE_ROOT_4K_SHA256\npublic static final @java.lang.Deprecated int WHOLE_MD5\npublic static final @java.lang.Deprecated int WHOLE_SHA1\npublic static final  int WHOLE_SHA256\npublic static final  int WHOLE_SHA512\npublic static final  int PARTIAL_MERKLE_ROOT_1M_SHA256\npublic static final  int PARTIAL_MERKLE_ROOT_1M_SHA512\nprivate final @android.content.pm.Checksum.Kind int mKind\nprivate final @android.annotation.NonNull byte[] mValue\nclass Checksum extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstDefs=false)")
+            inputSignatures = "public static final  int TYPE_WHOLE_MERKLE_ROOT_4K_SHA256\npublic static final @java.lang.Deprecated int TYPE_WHOLE_MD5\npublic static final @java.lang.Deprecated int TYPE_WHOLE_SHA1\npublic static final @java.lang.Deprecated int TYPE_WHOLE_SHA256\npublic static final @java.lang.Deprecated int TYPE_WHOLE_SHA512\npublic static final  int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256\npublic static final  int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512\nprivate final @android.content.pm.Checksum.Type int mType\nprivate final @android.annotation.NonNull byte[] mValue\nclass Checksum extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstDefs=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index ba894ae..30f3325 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -379,8 +379,7 @@
     /**
      * Logs process start information (including APK hash) to the security log.
      */
-    void logAppProcessStartIfNeeded(String processName, int uid, String seinfo, String apkFile,
-            int pid);
+    void logAppProcessStartIfNeeded(String packageName, String processName, int uid, String seinfo, String apkFile, int pid);
 
     /**
      * As per {@link android.content.pm.PackageManager#flushPackageRestrictionsAsUser}.
@@ -749,7 +748,7 @@
 
     void notifyPackagesReplacedReceived(in String[] packages);
 
-    void getChecksums(in String packageName, boolean includeSplits, int optional, int required, in List trustedInstallers, in IntentSender statusReceiver, int userId);
+    void requestChecksums(in String packageName, boolean includeSplits, int optional, int required, in List trustedInstallers, in IntentSender statusReceiver, int userId);
 
     //------------------------------------------------------------------------
     //
diff --git a/core/java/android/content/pm/IncrementalStatesInfo.aidl b/core/java/android/content/pm/IncrementalStatesInfo.aidl
index b5aad12..7064fbf 100644
--- a/core/java/android/content/pm/IncrementalStatesInfo.aidl
+++ b/core/java/android/content/pm/IncrementalStatesInfo.aidl
@@ -15,6 +15,6 @@
 ** limitations under the License.
 */
 
-package com.android.server.pm;
+package android.content.pm;
 
-parcelable IncrementalStatesInfo;
\ No newline at end of file
+parcelable IncrementalStatesInfo;
diff --git a/core/java/android/content/pm/KeySet.java b/core/java/android/content/pm/KeySet.java
index 5c1d35e..fd459e6 100644
--- a/core/java/android/content/pm/KeySet.java
+++ b/core/java/android/content/pm/KeySet.java
@@ -16,6 +16,7 @@
 
 package android.content.pm;
 
+import android.annotation.Nullable;
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -45,7 +46,7 @@
 
     /** @hide */
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (o instanceof KeySet) {
             KeySet ks = (KeySet) o;
             return token == ks.token;
diff --git a/core/java/android/content/pm/ModuleInfo.java b/core/java/android/content/pm/ModuleInfo.java
index a6db662..a7306a3 100644
--- a/core/java/android/content/pm/ModuleInfo.java
+++ b/core/java/android/content/pm/ModuleInfo.java
@@ -130,7 +130,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (!(obj instanceof ModuleInfo)) {
             return false;
         }
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index a9f1439..e2f8528 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1222,13 +1222,15 @@
          * Adds installer-provided checksums for the APK file in session.
          *
          * @param name      previously written as part of this session.
+         *                  {@link #openWrite}
          * @param checksums installer intends to make available via
-         *                  {@link PackageManager#getChecksums(String, boolean, int, List,
-         *                  IntentSender)}.
+         *                  {@link PackageManager#requestChecksums}.
          * @throws SecurityException if called after the session has been
          *                           committed or abandoned.
-         * @deprecated  use platform-enforced checksums e.g.
-         *              {@link Checksum#WHOLE_MERKLE_ROOT_4K_SHA256}
+         * @deprecated  do not use installer-provided checksums,
+         *              use platform-enforced checksums
+         *              e.g. {@link Checksum#TYPE_WHOLE_MERKLE_ROOT_4K_SHA256}
+         *              in {@link PackageManager#requestChecksums}.
          */
         @Deprecated
         public void addChecksums(@NonNull String name, @NonNull List<Checksum> checksums)
@@ -1720,7 +1722,6 @@
          *
          * @hide
          */
-        @TestApi
         @SystemApi
         @RequiresPermission(android.Manifest.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS)
         public void setGrantedRuntimePermissions(String[] permissions) {
@@ -1792,7 +1793,7 @@
          * @see SessionParams#setEnableRollback(boolean, int)
          * @hide
          */
-        @SystemApi @TestApi
+        @SystemApi
         public void setEnableRollback(boolean enable) {
             if (enable) {
                 installFlags |= PackageManager.INSTALL_ENABLE_ROLLBACK;
@@ -1816,7 +1817,7 @@
          * @param dataPolicy the rollback data policy for this session
          * @hide
          */
-        @SystemApi @TestApi
+        @SystemApi
         public void setEnableRollback(boolean enable,
                 @PackageManager.RollbackDataPolicy int dataPolicy) {
             if (enable) {
@@ -1839,7 +1840,7 @@
         }
 
         /** {@hide} */
-        @SystemApi @TestApi
+        @SystemApi
         public void setRequestDowngrade(boolean requestDowngrade) {
             if (requestDowngrade) {
                 installFlags |= PackageManager.INSTALL_REQUEST_DOWNGRADE;
@@ -1878,7 +1879,6 @@
 
         /** {@hide} */
         @SystemApi
-        @TestApi
         public void setInstallAsInstantApp(boolean isInstantApp) {
             if (isInstantApp) {
                 installFlags |= PackageManager.INSTALL_INSTANT_APP;
@@ -1963,7 +1963,7 @@
          *
          * {@hide}
          */
-        @SystemApi @TestApi
+        @SystemApi
         @RequiresPermission(Manifest.permission.INSTALL_PACKAGES)
         public void setStaged() {
             this.isStaged = true;
@@ -1974,7 +1974,7 @@
          *
          * {@hide}
          */
-        @SystemApi @TestApi
+        @SystemApi
         @RequiresPermission(Manifest.permission.INSTALL_PACKAGES)
         public void setInstallAsApex() {
             installFlags |= PackageManager.INSTALL_APEX;
@@ -2478,7 +2478,6 @@
          *
          * @hide
          */
-        @TestApi
         @SystemApi
         public @NonNull Set<String> getWhitelistedRestrictedPermissions() {
             if ((installFlags & PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS) != 0) {
@@ -2502,7 +2501,6 @@
          *
          * @hide
          */
-        @TestApi
         @SystemApi
         public int getAutoRevokePermissionsMode() {
             return autoRevokePermissionsMode;
@@ -2631,7 +2629,7 @@
          *
          * @hide
          */
-        @SystemApi @TestApi
+        @SystemApi
         @PackageManager.RollbackDataPolicy
         public int getRollbackDataPolicy() {
             return rollbackDataPolicy;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index c293e4ad..3fb9a9e 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -79,7 +79,6 @@
 import dalvik.system.VMRuntime;
 
 import java.io.File;
-import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.security.cert.Certificate;
@@ -121,7 +120,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public interface OnPermissionsChangedListener {
 
         /**
@@ -303,7 +301,10 @@
     /**
      * {@link PackageInfo} flag: return information about the
      * intent filters supported by the activity.
+     *
+     * @deprecated The platform does not support getting {@link IntentFilter}s for the package.
      */
+    @Deprecated
     public static final int GET_INTENT_FILTERS          = 0x00000020;
 
     /**
@@ -480,7 +481,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final int MATCH_FACTORY_ONLY = 0x00200000;
 
     /**
@@ -612,7 +612,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final int MODULE_APEX_NAME = 0x00000001;
 
     /** @hide */
@@ -1578,6 +1577,26 @@
      */
     public static final int INSTALL_PARSE_FAILED_SKIPPED = -125;
 
+    /**
+     * Installation failed return code: this is passed in the
+     * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the system failed to install the package
+     * because it is attempting to define a permission group that is already defined by some
+     * existing package.
+     *
+     * @hide
+     */
+    public static final int INSTALL_FAILED_DUPLICATE_PERMISSION_GROUP = -126;
+
+    /**
+     * Installation failed return code: this is passed in the
+     * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the system failed to install the package
+     * because it is attempting to define a permission in a group that does not exists or that is
+     * defined by an packages with an incompatible certificate.
+     *
+     * @hide
+     */
+    public static final int INSTALL_FAILED_BAD_PERMISSION_GROUP = -127;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "DELETE_" }, value = {
             DELETE_KEEP_DATA,
@@ -3309,7 +3328,7 @@
     /**
      * Extra field name for the ID of a package pending verification. Passed to
      * a package verifier and is used to call back to
-     * @see #getChecksums
+     * @see #requestChecksums
      */
     public static final String EXTRA_CHECKSUMS = "android.content.pm.extra.CHECKSUMS";
 
@@ -3320,7 +3339,6 @@
     * @hide
     */
     @SystemApi
-    @TestApi
     public static final int FLAG_PERMISSION_USER_SET = 1 << 0;
 
     /**
@@ -3331,7 +3349,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final int FLAG_PERMISSION_USER_FIXED =  1 << 1;
 
     /**
@@ -3342,7 +3359,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final int FLAG_PERMISSION_POLICY_FIXED =  1 << 2;
 
     /**
@@ -3359,7 +3375,6 @@
      */
     @Deprecated
     @SystemApi
-    @TestApi
     public static final int FLAG_PERMISSION_REVOKE_ON_UPGRADE =  1 << 3;
 
     /**
@@ -3369,7 +3384,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final int FLAG_PERMISSION_SYSTEM_FIXED =  1 << 4;
 
     /**
@@ -3381,7 +3395,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final int FLAG_PERMISSION_GRANTED_BY_DEFAULT =  1 << 5;
 
     /**
@@ -3391,7 +3404,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final int FLAG_PERMISSION_REVIEW_REQUIRED =  1 << 6;
 
     /**
@@ -3429,7 +3441,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     public static final int FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT =  1 << 11;
 
@@ -3441,7 +3452,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     public static final int FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT =  1 << 12;
 
@@ -3454,7 +3464,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     public static final int FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT =  1 << 13;
 
@@ -3467,7 +3476,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     public static final int FLAG_PERMISSION_APPLY_RESTRICTION =  1 << 14;
 
@@ -3477,7 +3485,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final int FLAG_PERMISSION_GRANTED_BY_ROLE =  1 << 15;
 
     /**
@@ -3489,7 +3496,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final int FLAG_PERMISSION_REVOKED_COMPAT =  FLAG_PERMISSION_REVOKE_ON_UPGRADE;
 
     /**
@@ -3499,7 +3505,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final int FLAG_PERMISSION_ONE_TIME = 1 << 16;
 
     /**
@@ -3517,7 +3522,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     public static final int FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT =  1 << 18;
 
@@ -3791,8 +3795,8 @@
      * @hide
      */
     @IntDef({UNSTARTABLE_REASON_UNKNOWN,
-            UNSTARTABLE_REASON_DATALOADER_TRANSPORT,
-            UNSTARTABLE_REASON_DATALOADER_STORAGE
+            UNSTARTABLE_REASON_CONNECTION_ERROR,
+            UNSTARTABLE_REASON_INSUFFICIENT_STORAGE
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface UnstartableReason {}
@@ -3801,23 +3805,20 @@
      * Unstartable state with no root cause specified. E.g., data loader seeing missing pages but
      * unclear about the cause. This corresponds to a generic alert window shown to the user when
      * the user attempts to launch the app.
-     * @hide
      */
     public static final int UNSTARTABLE_REASON_UNKNOWN = 0;
 
     /**
-     * Unstartable state after hint from dataloader of issues with the transport layer.
-     * This corresponds to an alert window shown to the user indicating network errors.
-     * @hide
+     * Unstartable state due to connection issues that interrupt package loading.
+     * This corresponds to an alert window shown to the user indicating connection errors.
      */
-    public static final int UNSTARTABLE_REASON_DATALOADER_TRANSPORT = 1;
+    public static final int UNSTARTABLE_REASON_CONNECTION_ERROR = 1;
 
     /**
      * Unstartable state after encountering storage limitations.
      * This corresponds to an alert window indicating limited storage.
-     * @hide
      */
-    public static final int UNSTARTABLE_REASON_DATALOADER_STORAGE = 2;
+    public static final int UNSTARTABLE_REASON_INSUFFICIENT_STORAGE = 2;
 
     /** {@hide} */
     public int getUserId() {
@@ -4087,7 +4088,7 @@
      *
      * @hide
      */
-    @TestApi @SystemApi
+    @SystemApi
     public abstract boolean arePermissionsIndividuallyControlled();
 
     /**
@@ -4338,7 +4339,6 @@
      * @hide
      */
     @NonNull
-    @TestApi
     @SystemApi
     @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
     public abstract List<PackageInfo> getInstalledPackagesAsUser(@PackageInfoFlags int flags,
@@ -4499,7 +4499,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS)
     public abstract void grantRuntimePermission(@NonNull String packageName,
@@ -4526,7 +4525,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
     public abstract void revokeRuntimePermission(@NonNull String packageName,
@@ -4554,7 +4552,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
     public void revokeRuntimePermission(@NonNull String packageName,
@@ -4573,7 +4570,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(anyOf = {
             android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
             android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,
@@ -4596,7 +4592,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(anyOf = {
             android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
             android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS
@@ -6196,9 +6191,30 @@
     public abstract Resources getResourcesForApplication(@NonNull String packageName)
             throws NameNotFoundException;
 
-    /** @hide */
+    /**
+     * Please don't use this function because it is no longer supported.
+     *
+     * @deprecated Instead of using this function, please use
+     *             {@link Context#createContextAsUser(UserHandle, int)} to create the specified user
+     *             context, {@link Context#getPackageManager()} to get PackageManager instance for
+     *             the specified user, and then
+     *             {@link PackageManager#getResourcesForApplication(String)} to get the same
+     *             Resources instance.
+     * @see {@link Context#createContextAsUser(android.os.UserHandle, int)}
+     * @see {@link Context#getPackageManager()}
+     * @see {@link android.content.pm.PackageManager#getResourcesForApplication(java.lang.String)}
+     * TODO(b/170852794): mark maxTargetSdk as {@code Build.VERSION_CODES.S}
+     * @hide
+     */
     @NonNull
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170928809,
+            publicAlternatives = "Use {@code Context#createContextAsUser(UserHandle, int)}"
+                    + " to create the relevant user context,"
+                    + " {@link android.content.Context#getPackageManager()} and"
+                    + " {@link android.content.pm.PackageManager#getResourcesForApplication("
+                    + "java.lang.String)}"
+                    + " instead.")
+    @Deprecated
     public abstract Resources getResourcesForApplicationAsUser(@NonNull String packageName,
             @UserIdInt int userId) throws NameNotFoundException;
 
@@ -6444,7 +6460,6 @@
      * @hide
      */
     @Nullable
-    @TestApi
     @SystemApi
     @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL)
     public abstract String getDefaultBrowserPackageNameAsUser(@UserIdInt int userId);
@@ -7098,7 +7113,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS)
     public abstract void addOnPermissionsChangeListener(
             @NonNull OnPermissionsChangedListener listener);
@@ -7111,7 +7125,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS)
     public abstract void removeOnPermissionsChangeListener(
             @NonNull OnPermissionsChangedListener listener);
@@ -7987,19 +8000,20 @@
 
     /**
      * Trust any Installer to provide checksums for the package.
-     * @see #getChecksums
+     * @see #requestChecksums
      */
-    public static final @Nullable List<Certificate> TRUST_ALL = null;
+    public static final @Nullable List<Certificate> TRUST_ALL = Collections.singletonList(null);
 
     /**
      * Don't trust any Installer to provide checksums for the package.
      * This effectively disables optimized Installer-enforced checksums.
-     * @see #getChecksums
+     * @see #requestChecksums
      */
-    public static final @NonNull List<Certificate> TRUST_NONE = Collections.emptyList();
+    public static final @NonNull List<Certificate> TRUST_NONE = Collections.singletonList(null);
 
     /**
-     * Returns the checksums for APKs within a package.
+     * Requesting the checksums for APKs within a package.
+     * The checksums will be returned asynchronously via statusReceiver.
      *
      * By default returns all readily available checksums:
      * - enforced by platform,
@@ -8011,21 +8025,24 @@
      *
      * @param packageName whose checksums to return.
      * @param includeSplits whether to include checksums for non-base splits.
-     * @param required explicitly request the checksum kinds. Will incur significant
+     * @param required explicitly request the checksum types. May incur significant
      *                 CPU/memory/disk usage.
-     * @param trustedInstallers for checksums enforced by Installer, which ones to be trusted.
-     *                          {@link #TRUST_ALL} will return checksums from any Installer,
-     *                          {@link #TRUST_NONE} disables optimized Installer-enforced checksums.
+     * @param trustedInstallers for checksums enforced by installer, which installers are to be
+     *                          trusted.
+     *                          {@link #TRUST_ALL} will return checksums from any installer,
+     *                          {@link #TRUST_NONE} disables optimized installer-enforced checksums,
+     *                          otherwise the list has to be non-empty list of certificates.
      * @param statusReceiver called once when the results are available as
-     *                       {@link #EXTRA_CHECKSUMS} of type ApkChecksum[].
+     *                       {@link #EXTRA_CHECKSUMS} of type {@link ApkChecksum}[].
      * @throws CertificateEncodingException if an encoding error occurs for trustedInstallers.
+     * @throws IllegalArgumentException if the list of trusted installer certificates is empty.
      * @throws NameNotFoundException if a package with the given name cannot be found on the system.
      */
-    public void getChecksums(@NonNull String packageName, boolean includeSplits,
-            @Checksum.Kind int required, @Nullable List<Certificate> trustedInstallers,
+    public void requestChecksums(@NonNull String packageName, boolean includeSplits,
+            @Checksum.Type int required, @NonNull List<Certificate> trustedInstallers,
             @NonNull IntentSender statusReceiver)
-            throws CertificateEncodingException, IOException, NameNotFoundException {
-        throw new UnsupportedOperationException("getChecksums not implemented in subclass");
+            throws CertificateEncodingException, NameNotFoundException {
+        throw new UnsupportedOperationException("requestChecksums not implemented in subclass");
     }
 
     /**
@@ -8126,7 +8143,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @Nullable
     public String getIncidentReportApproverPackageName() {
         throw new UnsupportedOperationException(
@@ -8268,7 +8284,7 @@
         }
 
         @Override
-        public boolean equals(Object rval) {
+        public boolean equals(@Nullable Object rval) {
             if (rval == null) {
                 return false;
             }
@@ -8371,7 +8387,7 @@
         }
 
         @Override
-        public boolean equals(Object rval) {
+        public boolean equals(@Nullable Object rval) {
             if (rval == null) {
                 return false;
             }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index e108451..177f691 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -6473,7 +6473,7 @@
         };
 
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (this == o) return true;
             if (!(o instanceof SigningDetails)) return false;
 
diff --git a/core/java/android/content/pm/PackageStats.java b/core/java/android/content/pm/PackageStats.java
index 7c12527..81cfc07 100644
--- a/core/java/android/content/pm/PackageStats.java
+++ b/core/java/android/content/pm/PackageStats.java
@@ -16,6 +16,7 @@
 
 package android.content.pm;
 
+import android.annotation.Nullable;
 import android.app.usage.StorageStatsManager;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
@@ -186,7 +187,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (!(obj instanceof PackageStats)) {
             return false;
         }
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index 3ed21b0..2fca980 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -369,7 +369,7 @@
     }
 
     @Override
-    final public boolean equals(Object obj) {
+    final public boolean equals(@Nullable Object obj) {
         if (!(obj instanceof PackageUserState)) {
             return false;
         }
@@ -527,7 +527,7 @@
         }
 
         @Override
-        public boolean equals(Object obj) {
+        public boolean equals(@Nullable Object obj) {
             if (this == obj) {
                 return true;
             }
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 5d4c843..0f9e8e5 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -160,7 +160,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final int PROTECTION_FLAG_OEM = 0x4000;
 
     /**
@@ -181,7 +180,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 0x10000;
 
     /**
@@ -192,7 +190,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final int PROTECTION_FLAG_WELLBEING = 0x20000;
 
     /**
@@ -202,7 +199,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final int PROTECTION_FLAG_DOCUMENTER = 0x40000;
 
     /**
@@ -212,7 +208,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final int PROTECTION_FLAG_CONFIGURATOR = 0x80000;
 
     /**
@@ -223,7 +218,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final int PROTECTION_FLAG_INCIDENT_REPORT_APPROVER = 0x100000;
 
     /**
@@ -234,7 +228,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final int PROTECTION_FLAG_APP_PREDICTOR = 0x200000;
 
     /**
@@ -245,7 +238,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final int PROTECTION_FLAG_COMPANION = 0x800000;
 
     /**
@@ -256,7 +248,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final int PROTECTION_FLAG_RETAIL_DEMO = 0x1000000;
 
     /** @hide */
@@ -340,7 +331,6 @@
      * value of {@link android.R.attr#permissionFlags}.
      * @hide
      */
-    @TestApi
     @SystemApi
     public static final int FLAG_REMOVED = 1<<1;
 
@@ -436,7 +426,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public final @Nullable String backgroundPermission;
 
     /**
@@ -464,82 +453,85 @@
     /** @hide */
     @UnsupportedAppUsage
     public static @NonNull String protectionToString(int level) {
-        String protLevel = "????";
+        final StringBuilder protLevel = new StringBuilder();
         switch (level & PROTECTION_MASK_BASE) {
             case PermissionInfo.PROTECTION_DANGEROUS:
-                protLevel = "dangerous";
+                protLevel.append("dangerous");
                 break;
             case PermissionInfo.PROTECTION_NORMAL:
-                protLevel = "normal";
+                protLevel.append("normal");
                 break;
             case PermissionInfo.PROTECTION_SIGNATURE:
-                protLevel = "signature";
+                protLevel.append("signature");
                 break;
             case PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM:
-                protLevel = "signatureOrSystem";
+                protLevel.append("signatureOrSystem");
+                break;
+            default:
+                protLevel.append("????");
                 break;
         }
         if ((level & PermissionInfo.PROTECTION_FLAG_PRIVILEGED) != 0) {
-            protLevel += "|privileged";
+            protLevel.append("|privileged");
         }
         if ((level & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0) {
-            protLevel += "|development";
+            protLevel.append("|development");
         }
         if ((level & PermissionInfo.PROTECTION_FLAG_APPOP) != 0) {
-            protLevel += "|appop";
+            protLevel.append("|appop");
         }
         if ((level & PermissionInfo.PROTECTION_FLAG_PRE23) != 0) {
-            protLevel += "|pre23";
+            protLevel.append("|pre23");
         }
         if ((level & PermissionInfo.PROTECTION_FLAG_INSTALLER) != 0) {
-            protLevel += "|installer";
+            protLevel.append("|installer");
         }
         if ((level & PermissionInfo.PROTECTION_FLAG_VERIFIER) != 0) {
-            protLevel += "|verifier";
+            protLevel.append("|verifier");
         }
         if ((level & PermissionInfo.PROTECTION_FLAG_PREINSTALLED) != 0) {
-            protLevel += "|preinstalled";
+            protLevel.append("|preinstalled");
         }
         if ((level & PermissionInfo.PROTECTION_FLAG_SETUP) != 0) {
-            protLevel += "|setup";
+            protLevel.append("|setup");
         }
         if ((level & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0) {
-            protLevel += "|instant";
+            protLevel.append("|instant");
         }
         if ((level & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0) {
-            protLevel += "|runtime";
+            protLevel.append("|runtime");
         }
         if ((level & PermissionInfo.PROTECTION_FLAG_OEM) != 0) {
-            protLevel += "|oem";
+            protLevel.append("|oem");
         }
         if ((level & PermissionInfo.PROTECTION_FLAG_VENDOR_PRIVILEGED) != 0) {
-            protLevel += "|vendorPrivileged";
+            protLevel.append("|vendorPrivileged");
         }
         if ((level & PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER) != 0) {
-            protLevel += "|textClassifier";
+            protLevel.append("|textClassifier");
         }
         if ((level & PermissionInfo.PROTECTION_FLAG_WELLBEING) != 0) {
-            protLevel += "|wellbeing";
+            protLevel.append("|wellbeing");
         }
         if ((level & PermissionInfo.PROTECTION_FLAG_DOCUMENTER) != 0) {
-            protLevel += "|documenter";
+            protLevel.append("|documenter");
         }
         if ((level & PROTECTION_FLAG_CONFIGURATOR) != 0) {
-            protLevel += "|configurator";
+            protLevel.append("|configurator");
         }
         if ((level & PermissionInfo.PROTECTION_FLAG_INCIDENT_REPORT_APPROVER) != 0) {
-            protLevel += "|incidentReportApprover";
+            protLevel.append("|incidentReportApprover");
         }
         if ((level & PermissionInfo.PROTECTION_FLAG_APP_PREDICTOR) != 0) {
-            protLevel += "|appPredictor";
+            protLevel.append("|appPredictor");
         }
         if ((level & PermissionInfo.PROTECTION_FLAG_COMPANION) != 0) {
-            protLevel += "|companion";
+            protLevel.append("|companion");
         }
         if ((level & PermissionInfo.PROTECTION_FLAG_RETAIL_DEMO) != 0) {
-            protLevel += "|retailDemo";
+            protLevel.append("|retailDemo");
         }
-        return protLevel;
+        return protLevel.toString();
     }
 
     /**
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 1b3c46f..7ed803f 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -630,7 +630,7 @@
      * This will set {@link #FLAG_STRINGS_RESOLVED}.
      *
      * @param res {@link Resources} for the publisher.  Must have been loaded with
-     * {@link PackageManager#getResourcesForApplicationAsUser}.
+     * {@link PackageManager#getResourcesForApplication(String)}.
      *
      * @hide
      */
@@ -752,7 +752,7 @@
      * aforementioned method would do internally, but not documented, so doing here explicitly.)
      *
      * @param res {@link Resources} for the publisher.  Must have been loaded with
-     * {@link PackageManager#getResourcesForApplicationAsUser}.
+     * {@link PackageManager#getResourcesForApplication(String)}.
      *
      * @hide
      */
@@ -782,7 +782,7 @@
      * in the resource name fields.
      *
      * @param res {@link Resources} for the publisher.  Must have been loaded with
-     * {@link PackageManager#getResourcesForApplicationAsUser}.
+     * {@link PackageManager#getResourcesForApplication(String)}.
      *
      * @hide
      */
diff --git a/core/java/android/content/pm/Signature.java b/core/java/android/content/pm/Signature.java
index b2875e9..fc5d945 100644
--- a/core/java/android/content/pm/Signature.java
+++ b/core/java/android/content/pm/Signature.java
@@ -16,6 +16,7 @@
 
 package android.content.pm;
 
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -248,7 +249,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         try {
             if (obj != null) {
                 Signature other = (Signature)obj;
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index 08b23b0..d81dff8 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -366,8 +366,9 @@
      * @return true if this user can be switched to.
      **/
     public boolean supportsSwitchTo() {
-        if (isEphemeral() && !isEnabled()) {
-            // Don't support switching to an ephemeral user with removal in progress.
+        if (partial || !isEnabled()) {
+            // Don't support switching to disabled or partial users, which includes users with
+            // removal in progress.
             return false;
         }
         if (preCreated) {
diff --git a/core/java/android/content/pm/VerifierDeviceIdentity.java b/core/java/android/content/pm/VerifierDeviceIdentity.java
index f29aaec..3ce0b65 100644
--- a/core/java/android/content/pm/VerifierDeviceIdentity.java
+++ b/core/java/android/content/pm/VerifierDeviceIdentity.java
@@ -16,6 +16,7 @@
 
 package android.content.pm;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -194,7 +195,7 @@
     }
 
     @Override
-    public boolean equals(Object other) {
+    public boolean equals(@Nullable Object other) {
         if (!(other instanceof VerifierDeviceIdentity)) {
             return false;
         }
diff --git a/core/java/android/content/pm/VersionedPackage.java b/core/java/android/content/pm/VersionedPackage.java
index 2987557..fa50fca 100644
--- a/core/java/android/content/pm/VersionedPackage.java
+++ b/core/java/android/content/pm/VersionedPackage.java
@@ -17,6 +17,7 @@
 
 import android.annotation.IntRange;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -96,7 +97,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         return o instanceof VersionedPackage
                 && ((VersionedPackage) o).mPackageName.equals(mPackageName)
                 && ((VersionedPackage) o).mVersionCode == mVersionCode;
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 9197020..b936c63 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -1080,14 +1080,57 @@
                 }
             }
 
-            final String requiredFeature = sa.getNonConfigurationString(
-                    R.styleable.AndroidManifestUsesPermission_requiredFeature, 0);
-
-            final String requiredNotfeature = sa.getNonConfigurationString(
-                    R.styleable.AndroidManifestUsesPermission_requiredNotFeature,
+            final ArraySet<String> requiredFeatures = new ArraySet<>();
+            String feature = sa.getNonConfigurationString(
+                    com.android.internal.R.styleable.AndroidManifestUsesPermission_requiredFeature,
                     0);
+            if (feature != null) {
+                requiredFeatures.add(feature);
+            }
 
-            XmlUtils.skipCurrentTag(parser);
+            final ArraySet<String> requiredNotFeatures = new ArraySet<>();
+            feature = sa.getNonConfigurationString(
+                    com.android.internal.R.styleable
+                            .AndroidManifestUsesPermission_requiredNotFeature,
+                    0);
+            if (feature != null) {
+                requiredNotFeatures.add(feature);
+            }
+
+            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;
+                }
+
+                final ParseResult<?> result;
+                switch (parser.getName()) {
+                    case "required-feature":
+                        result = parseRequiredFeature(input, res, parser);
+                        if (result.isSuccess()) {
+                            requiredFeatures.add((String) result.getResult());
+                        }
+                        break;
+
+                    case "required-not-feature":
+                        result = parseRequiredNotFeature(input, res, parser);
+                        if (result.isSuccess()) {
+                            requiredNotFeatures.add((String) result.getResult());
+                        }
+                        break;
+
+                    default:
+                        result = ParsingUtils.unknownTag("<uses-permission>", pkg, parser, input);
+                        break;
+                }
+
+                if (result.isError()) {
+                    return input.error(result);
+                }
+            }
 
             // Can only succeed from here on out
             ParseResult<ParsingPackage> success = input.success(pkg);
@@ -1100,17 +1143,22 @@
                 return success;
             }
 
-            // Only allow requesting this permission if the platform supports the given feature.
-            if (requiredFeature != null && mCallback != null && !mCallback.hasFeature(
-                    requiredFeature)) {
-                return success;
-            }
+            if (mCallback != null) {
+                // Only allow requesting this permission if the platform supports all of the
+                // "required-feature"s.
+                for (int i = requiredFeatures.size() - 1; i >= 0; i--) {
+                    if (!mCallback.hasFeature(requiredFeatures.valueAt(i))) {
+                        return success;
+                    }
+                }
 
-            // Only allow requesting this permission if the platform doesn't support the given
-            // feature.
-            if (requiredNotfeature != null && mCallback != null
-                    && mCallback.hasFeature(requiredNotfeature)) {
-                return success;
+                // Only allow requesting this permission if the platform does not supports any of
+                // the "required-not-feature"s.
+                for (int i = requiredNotFeatures.size() - 1; i >= 0; i--) {
+                    if (mCallback.hasFeature(requiredNotFeatures.valueAt(i))) {
+                        return success;
+                    }
+                }
             }
 
             if (!pkg.getRequestedPermissions().contains(name)) {
@@ -1127,6 +1175,36 @@
         }
     }
 
+    private ParseResult<String> parseRequiredFeature(ParseInput input, Resources res,
+            AttributeSet attrs) {
+        final TypedArray sa = res.obtainAttributes(attrs,
+                com.android.internal.R.styleable.AndroidManifestRequiredFeature);
+        try {
+            final String featureName = sa.getString(
+                    R.styleable.AndroidManifestRequiredFeature_name);
+            return TextUtils.isEmpty(featureName)
+                    ? input.error("Feature name is missing from <required-feature> tag.")
+                    : input.success(featureName);
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    private ParseResult<String> parseRequiredNotFeature(ParseInput input, Resources res,
+            AttributeSet attrs) {
+        final TypedArray sa = res.obtainAttributes(attrs,
+                com.android.internal.R.styleable.AndroidManifestRequiredNotFeature);
+        try {
+            final String featureName = sa.getString(
+                    R.styleable.AndroidManifestRequiredNotFeature_name);
+            return TextUtils.isEmpty(featureName)
+                    ? input.error("Feature name is missing from <required-not-feature> tag.")
+                    : input.success(featureName);
+        } finally {
+            sa.recycle();
+        }
+    }
+
     private static ParseResult<ParsingPackage> parseUsesConfiguration(ParseInput input,
             ParsingPackage pkg, Resources res, XmlResourceParser parser) {
         ConfigurationInfo cPref = new ConfigurationInfo();
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
index 8c0bfef..511ee5d 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
@@ -368,8 +368,8 @@
                 }
                 result = intentResult;
             } else if (!isReceiver && !isAlias && parser.getName().equals("layout")) {
-                ParseResult<ActivityInfo.WindowLayout> layoutResult = parseLayout(resources, parser,
-                        input);
+                ParseResult<ActivityInfo.WindowLayout> layoutResult =
+                        parseActivityWindowLayout(resources, parser, input);
                 if (layoutResult.isSuccess()) {
                     activity.windowLayout = layoutResult.getResult();
                 }
@@ -383,7 +383,8 @@
             }
         }
 
-        ParseResult<ActivityInfo.WindowLayout> layoutResult = resolveWindowLayout(activity, input);
+        ParseResult<ActivityInfo.WindowLayout> layoutResult =
+                resolveActivityWindowLayout(activity, input);
         if (layoutResult.isError()) {
             return input.error(layoutResult);
         }
@@ -468,7 +469,7 @@
     }
 
     @NonNull
-    private static ParseResult<ActivityInfo.WindowLayout> parseLayout(Resources res,
+    private static ParseResult<ActivityInfo.WindowLayout> parseActivityWindowLayout(Resources res,
             AttributeSet attrs, ParseInput input) {
         TypedArray sw = res.obtainAttributes(attrs, R.styleable.AndroidManifestLayout);
         try {
@@ -496,8 +497,13 @@
             int minWidth = sw.getDimensionPixelSize(R.styleable.AndroidManifestLayout_minWidth, -1);
             int minHeight = sw.getDimensionPixelSize(R.styleable.AndroidManifestLayout_minHeight,
                     -1);
-            return input.success(new ActivityInfo.WindowLayout(width, widthFraction, height,
-                    heightFraction, gravity, minWidth, minHeight));
+            String windowLayoutAffinity =
+                    sw.getNonConfigurationString(
+                            R.styleable.AndroidManifestLayout_windowLayoutAffinity, 0);
+            final ActivityInfo.WindowLayout windowLayout = new ActivityInfo.WindowLayout(width,
+                    widthFraction, height, heightFraction, gravity, minWidth, minHeight,
+                    windowLayoutAffinity);
+            return input.success(windowLayout);
         } finally {
             sw.recycle();
         }
@@ -509,7 +515,7 @@
      * <p>{@link ActivityInfo.WindowLayout#windowLayoutAffinity} has a fallback metadata used in
      * Android R and some variants of pre-R.
      */
-    private static ParseResult<ActivityInfo.WindowLayout> resolveWindowLayout(
+    private static ParseResult<ActivityInfo.WindowLayout> resolveActivityWindowLayout(
             ParsedActivity activity, ParseInput input) {
         // There isn't a metadata for us to fall back. Whatever is in layout is correct.
         if (activity.metaData == null || !activity.metaData.containsKey(
@@ -528,9 +534,10 @@
         if (layout == null) {
             layout = new ActivityInfo.WindowLayout(-1 /* width */, -1 /* widthFraction */,
                     -1 /* height */, -1 /* heightFraction */, Gravity.NO_GRAVITY,
-                    -1 /* minWidth */, -1 /* minHeight */);
+                    -1 /* minWidth */, -1 /* minHeight */, windowLayoutAffinity);
+        } else {
+            layout.windowLayoutAffinity = windowLayoutAffinity;
         }
-        layout.windowLayoutAffinity = windowLayoutAffinity;
         return input.success(layout);
     }
 }
diff --git a/core/java/android/content/pm/parsing/component/ParsedPermission.java b/core/java/android/content/pm/parsing/component/ParsedPermission.java
index ced3226..f99a0b1 100644
--- a/core/java/android/content/pm/parsing/component/ParsedPermission.java
+++ b/core/java/android/content/pm/parsing/component/ParsedPermission.java
@@ -54,22 +54,6 @@
         this.parsedPermissionGroup = other.parsedPermissionGroup;
     }
 
-    public ParsedPermission(ParsedPermission other, PermissionInfo pendingPermissionInfo,
-            String packageName, String name) {
-        this(other);
-
-        this.flags = pendingPermissionInfo.flags;
-        this.descriptionRes = pendingPermissionInfo.descriptionRes;
-
-        this.backgroundPermission = pendingPermissionInfo.backgroundPermission;
-        this.group = pendingPermissionInfo.group;
-        this.requestRes = pendingPermissionInfo.requestRes;
-        this.protectionLevel = pendingPermissionInfo.protectionLevel;
-
-        setName(name);
-        setPackageName(packageName);
-    }
-
     public ParsedPermission setGroup(String group) {
         this.group = TextUtils.safeIntern(group);
         return this;
diff --git a/core/java/android/content/pm/permission/SplitPermissionInfoParcelable.java b/core/java/android/content/pm/permission/SplitPermissionInfoParcelable.java
index 421ae49..e7a554a 100644
--- a/core/java/android/content/pm/permission/SplitPermissionInfoParcelable.java
+++ b/core/java/android/content/pm/permission/SplitPermissionInfoParcelable.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntRange;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -122,7 +123,7 @@
 
     @Override
     @DataClass.Generated.Member
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         // You can override field equality logic by defining either of the methods like:
         // boolean fieldNameEquals(SplitPermissionInfoParcelable other) { ... }
         // boolean fieldNameEquals(FieldType otherValue) { ... }
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
index c66c70a2..f8ed27a 100644
--- a/core/java/android/content/res/CompatibilityInfo.java
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -16,6 +16,7 @@
 
 package android.content.res;
 
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.pm.ApplicationInfo;
 import android.graphics.Canvas;
@@ -27,6 +28,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.DisplayMetrics;
+import android.view.InsetsState;
 import android.view.MotionEvent;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
@@ -329,15 +331,7 @@
         }
 
         /**
-         * Translate the screen rect to the application frame.
-         */
-        @UnsupportedAppUsage
-        public void translateRectInScreenToAppWinFrame(Rect rect) {
-            rect.scale(applicationInvertedScale);
-        }
-
-        /**
-         * Translate the region in window to screen. 
+         * Translate the region in window to screen.
          */
         @UnsupportedAppUsage
         public void translateRegionInWindowToScreen(Region transparentRegion) {
@@ -387,7 +381,14 @@
         public void translateWindowLayout(WindowManager.LayoutParams params) {
             params.scale(applicationScale);
         }
-        
+
+        /**
+         * Translate a length in application's window to screen.
+         */
+        public float translateLengthInAppWindowToScreen(float length) {
+            return length * applicationScale;
+        }
+
         /**
          * Translate a Rect in application's window to screen.
          */
@@ -395,7 +396,7 @@
         public void translateRectInAppWindowToScreen(Rect rect) {
             rect.scale(applicationScale);
         }
- 
+
         /**
          * Translate a Rect in screen coordinates into the app window's coordinates.
          */
@@ -405,6 +406,13 @@
         }
 
         /**
+         * Translate an InsetsState in screen coordinates into the app window's coordinates.
+         */
+        public void translateInsetsStateInScreenToAppWindow(InsetsState state) {
+            state.scale(applicationInvertedScale);
+        }
+
+        /**
          * Translate a Point in screen coordinates into the app window's coordinates.
          */
         public void translatePointInScreenToAppWindow(PointF point) {
@@ -549,7 +557,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) {
             return true;
         }
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 49f81f8..9f36344 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -2128,7 +2128,7 @@
         return this.compareTo(that) == 0;
     }
 
-    public boolean equals(Object that) {
+    public boolean equals(@Nullable Object that) {
         try {
             return equals((Configuration)that);
         } catch (ClassCastException e) {
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 0f1c876..c1f0a5f7 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1913,7 +1913,7 @@
         }
 
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (this == o) {
                 return true;
             }
diff --git a/core/java/android/content/res/ResourcesKey.java b/core/java/android/content/res/ResourcesKey.java
index fcb80aa..05769dd 100644
--- a/core/java/android/content/res/ResourcesKey.java
+++ b/core/java/android/content/res/ResourcesKey.java
@@ -137,7 +137,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (!(obj instanceof ResourcesKey)) {
             return false;
         }
diff --git a/core/java/android/content/rollback/PackageRollbackInfo.java b/core/java/android/content/rollback/PackageRollbackInfo.java
index 642a76b..0140280 100644
--- a/core/java/android/content/rollback/PackageRollbackInfo.java
+++ b/core/java/android/content/rollback/PackageRollbackInfo.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.content.pm.PackageManager;
 import android.content.pm.VersionedPackage;
 import android.os.Parcel;
@@ -32,7 +31,7 @@
  *
  * @hide
  */
-@SystemApi @TestApi
+@SystemApi
 public final class PackageRollbackInfo implements Parcelable {
 
     private final VersionedPackage mVersionRolledBackFrom;
diff --git a/core/java/android/content/rollback/RollbackInfo.java b/core/java/android/content/rollback/RollbackInfo.java
index c09cfd5..a363718 100644
--- a/core/java/android/content/rollback/RollbackInfo.java
+++ b/core/java/android/content/rollback/RollbackInfo.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.content.pm.VersionedPackage;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -31,7 +30,7 @@
  *
  * @hide
  */
-@SystemApi @TestApi
+@SystemApi
 public final class RollbackInfo implements Parcelable {
 
     /**
diff --git a/core/java/android/content/rollback/RollbackManager.java b/core/java/android/content/rollback/RollbackManager.java
index 7ebeb21..3636222 100644
--- a/core/java/android/content/rollback/RollbackManager.java
+++ b/core/java/android/content/rollback/RollbackManager.java
@@ -24,7 +24,6 @@
 import android.annotation.TestApi;
 import android.content.Context;
 import android.content.IntentSender;
-import android.content.pm.PackageInstaller;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.VersionedPackage;
 import android.os.RemoteException;
@@ -43,7 +42,7 @@
  * @see PackageInstaller.SessionParams#setEnableRollback(boolean)
  * @hide
  */
-@SystemApi @TestApi
+@SystemApi
 @SystemService(Context.ROLLBACK_SERVICE)
 public final class RollbackManager {
     private final String mCallerPackageName;
diff --git a/core/java/android/database/Cursor.java b/core/java/android/database/Cursor.java
index 2afb755..afa1c20 100644
--- a/core/java/android/database/Cursor.java
+++ b/core/java/android/database/Cursor.java
@@ -16,6 +16,8 @@
 
 package android.database;
 
+import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.ContentResolver;
@@ -23,6 +25,8 @@
 import android.os.Bundle;
 
 import java.io.Closeable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
 import java.util.List;
 
@@ -56,12 +60,23 @@
     /** Value returned by {@link #getType(int)} if the specified column type is blob */
     static final int FIELD_TYPE_BLOB = 4;
 
+    /** @hide */
+    @IntDef(prefix = { "FIELD_TYPE_" }, value = {
+            FIELD_TYPE_NULL,
+            FIELD_TYPE_INTEGER,
+            FIELD_TYPE_FLOAT,
+            FIELD_TYPE_STRING,
+            FIELD_TYPE_BLOB,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FieldType {}
+
     /**
      * Returns the numbers of rows in the cursor.
      *
      * @return the number of rows in the cursor.
      */
-    int getCount();
+    @IntRange(from = 0) int getCount();
 
     /**
      * Returns the current position of the cursor in the row set.
@@ -72,7 +87,7 @@
      *
      * @return the current cursor position.
      */
-    int getPosition();
+    @IntRange(from = -1) int getPosition();
 
     /**
      * Move the cursor by a relative amount, forward or backward, from the
@@ -101,7 +116,7 @@
      * @param position the zero-based position to move to.
      * @return whether the requested move fully succeeded.
      */
-    boolean moveToPosition(int position);
+    boolean moveToPosition(@IntRange(from = -1) int position);
 
     /**
      * Move the cursor to the first row.
@@ -181,7 +196,7 @@
      * the column name does not exist.
      * @see #getColumnIndexOrThrow(String)
      */
-    int getColumnIndex(String columnName);
+    @IntRange(from = -1) int getColumnIndex(String columnName);
 
     /**
      * Returns the zero-based index for the given column name, or throws
@@ -194,7 +209,8 @@
      * @see #getColumnIndex(String)
      * @throws IllegalArgumentException if the column does not exist
      */
-    int getColumnIndexOrThrow(String columnName) throws IllegalArgumentException;
+    @IntRange(from = 0) int getColumnIndexOrThrow(String columnName)
+            throws IllegalArgumentException;
 
     /**
      * Returns the column name at the given zero-based column index.
@@ -202,7 +218,7 @@
      * @param columnIndex the zero-based index of the target column.
      * @return the column name for the given column index.
      */
-    String getColumnName(int columnIndex);
+    String getColumnName(@IntRange(from = 0) int columnIndex);
 
     /**
      * Returns a string array holding the names of all of the columns in the
@@ -216,7 +232,7 @@
      * Return total number of columns
      * @return number of columns 
      */
-    int getColumnCount();
+    @IntRange(from = 0) int getColumnCount();
     
     /**
      * Returns the value of the requested column as a byte array.
@@ -228,7 +244,7 @@
      * @param columnIndex the zero-based index of the target column.
      * @return the value of that column as a byte array.
      */
-    byte[] getBlob(int columnIndex);
+    byte[] getBlob(@IntRange(from = 0) int columnIndex);
 
     /**
      * Returns the value of the requested column as a String.
@@ -240,7 +256,7 @@
      * @param columnIndex the zero-based index of the target column.
      * @return the value of that column as a String.
      */
-    String getString(int columnIndex);
+    String getString(@IntRange(from = 0) int columnIndex);
     
     /**
      * Retrieves the requested column text and stores it in the buffer provided.
@@ -250,7 +266,7 @@
      *        if the target column is null, return buffer
      * @param buffer the buffer to copy the text into. 
      */
-    void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer);
+    void copyStringToBuffer(@IntRange(from = 0) int columnIndex, CharArrayBuffer buffer);
     
     /**
      * Returns the value of the requested column as a short.
@@ -263,7 +279,7 @@
      * @param columnIndex the zero-based index of the target column.
      * @return the value of that column as a short.
      */
-    short getShort(int columnIndex);
+    short getShort(@IntRange(from = 0) int columnIndex);
 
     /**
      * Returns the value of the requested column as an int.
@@ -276,7 +292,7 @@
      * @param columnIndex the zero-based index of the target column.
      * @return the value of that column as an int.
      */
-    int getInt(int columnIndex);
+    int getInt(@IntRange(from = 0) int columnIndex);
 
     /**
      * Returns the value of the requested column as a long.
@@ -289,7 +305,7 @@
      * @param columnIndex the zero-based index of the target column.
      * @return the value of that column as a long.
      */
-    long getLong(int columnIndex);
+    long getLong(@IntRange(from = 0) int columnIndex);
 
     /**
      * Returns the value of the requested column as a float.
@@ -302,7 +318,7 @@
      * @param columnIndex the zero-based index of the target column.
      * @return the value of that column as a float.
      */
-    float getFloat(int columnIndex);
+    float getFloat(@IntRange(from = 0) int columnIndex);
 
     /**
      * Returns the value of the requested column as a double.
@@ -315,28 +331,18 @@
      * @param columnIndex the zero-based index of the target column.
      * @return the value of that column as a double.
      */
-    double getDouble(int columnIndex);
+    double getDouble(@IntRange(from = 0) int columnIndex);
 
     /**
      * Returns data type of the given column's value.
      * The preferred type of the column is returned but the data may be converted to other types
      * as documented in the get-type methods such as {@link #getInt(int)}, {@link #getFloat(int)}
      * etc.
-     *<p>
-     * Returned column types are
-     * <ul>
-     *   <li>{@link #FIELD_TYPE_NULL}</li>
-     *   <li>{@link #FIELD_TYPE_INTEGER}</li>
-     *   <li>{@link #FIELD_TYPE_FLOAT}</li>
-     *   <li>{@link #FIELD_TYPE_STRING}</li>
-     *   <li>{@link #FIELD_TYPE_BLOB}</li>
-     *</ul>
-     *</p>
      *
      * @param columnIndex the zero-based index of the target column.
      * @return column value type
      */
-    int getType(int columnIndex);
+    @FieldType int getType(@IntRange(from = 0) int columnIndex);
 
     /**
      * Returns <code>true</code> if the value in the indicated column is null.
@@ -344,7 +350,7 @@
      * @param columnIndex the zero-based index of the target column.
      * @return whether the column value is null.
      */
-    boolean isNull(int columnIndex);
+    boolean isNull(@IntRange(from = 0) int columnIndex);
 
     /**
      * Deactivates the Cursor, making all calls on it fail until {@link #requery} is called.
diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java
index 063a2d0..edb7b71 100644
--- a/core/java/android/database/CursorWindow.java
+++ b/core/java/android/database/CursorWindow.java
@@ -17,6 +17,7 @@
 package android.database;
 
 import android.annotation.BytesLong;
+import android.annotation.IntRange;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.Resources;
 import android.database.sqlite.SQLiteClosable;
@@ -230,7 +231,7 @@
      *
      * @return The zero-based start position.
      */
-    public int getStartPosition() {
+    public @IntRange(from = 0) int getStartPosition() {
         return mStartPos;
     }
 
@@ -243,7 +244,7 @@
      *
      * @param pos The new zero-based start position.
      */
-    public void setStartPosition(int pos) {
+    public void setStartPosition(@IntRange(from = 0) int pos) {
         mStartPos = pos;
     }
 
@@ -252,7 +253,7 @@
      *
      * @return The number of rows in this cursor window.
      */
-    public int getNumRows() {
+    public @IntRange(from = 0) int getNumRows() {
         acquireReference();
         try {
             return nativeGetNumRows(mWindowPtr);
@@ -272,7 +273,7 @@
      * @param columnNum The new number of columns.
      * @return True if successful.
      */
-    public boolean setNumColumns(int columnNum) {
+    public boolean setNumColumns(@IntRange(from = 0) int columnNum) {
         acquireReference();
         try {
             return nativeSetNumColumns(mWindowPtr, columnNum);
@@ -317,7 +318,7 @@
      * @deprecated Use {@link #getType(int, int)} instead.
      */
     @Deprecated
-    public boolean isNull(int row, int column) {
+    public boolean isNull(@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
         return getType(row, column) == Cursor.FIELD_TYPE_NULL;
     }
 
@@ -332,7 +333,7 @@
      * @deprecated Use {@link #getType(int, int)} instead.
      */
     @Deprecated
-    public boolean isBlob(int row, int column) {
+    public boolean isBlob(@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
         int type = getType(row, column);
         return type == Cursor.FIELD_TYPE_BLOB || type == Cursor.FIELD_TYPE_NULL;
     }
@@ -347,7 +348,7 @@
      * @deprecated Use {@link #getType(int, int)} instead.
      */
     @Deprecated
-    public boolean isLong(int row, int column) {
+    public boolean isLong(@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
         return getType(row, column) == Cursor.FIELD_TYPE_INTEGER;
     }
 
@@ -361,7 +362,7 @@
      * @deprecated Use {@link #getType(int, int)} instead.
      */
     @Deprecated
-    public boolean isFloat(int row, int column) {
+    public boolean isFloat(@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
         return getType(row, column) == Cursor.FIELD_TYPE_FLOAT;
     }
 
@@ -376,29 +377,20 @@
      * @deprecated Use {@link #getType(int, int)} instead.
      */
     @Deprecated
-    public boolean isString(int row, int column) {
+    public boolean isString(@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
         int type = getType(row, column);
         return type == Cursor.FIELD_TYPE_STRING || type == Cursor.FIELD_TYPE_NULL;
     }
 
     /**
      * Returns the type of the field at the specified row and column index.
-     * <p>
-     * The returned field types are:
-     * <ul>
-     * <li>{@link Cursor#FIELD_TYPE_NULL}</li>
-     * <li>{@link Cursor#FIELD_TYPE_INTEGER}</li>
-     * <li>{@link Cursor#FIELD_TYPE_FLOAT}</li>
-     * <li>{@link Cursor#FIELD_TYPE_STRING}</li>
-     * <li>{@link Cursor#FIELD_TYPE_BLOB}</li>
-     * </ul>
-     * </p>
      *
      * @param row The zero-based row index.
      * @param column The zero-based column index.
      * @return The field type.
      */
-    public int getType(int row, int column) {
+    public @Cursor.FieldType int getType(@IntRange(from = 0) int row,
+            @IntRange(from = 0) int column) {
         acquireReference();
         try {
             return nativeGetType(mWindowPtr, row - mStartPos, column);
@@ -428,7 +420,7 @@
      * @param column The zero-based column index.
      * @return The value of the field as a byte array.
      */
-    public byte[] getBlob(int row, int column) {
+    public byte[] getBlob(@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
         acquireReference();
         try {
             return nativeGetBlob(mWindowPtr, row - mStartPos, column);
@@ -463,7 +455,7 @@
      * @param column The zero-based column index.
      * @return The value of the field as a string.
      */
-    public String getString(int row, int column) {
+    public String getString(@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
         acquireReference();
         try {
             return nativeGetString(mWindowPtr, row - mStartPos, column);
@@ -502,7 +494,8 @@
      * @param buffer The {@link CharArrayBuffer} to hold the string.  It is automatically
      * resized if the requested string is larger than the buffer's current capacity.
       */
-    public void copyStringToBuffer(int row, int column, CharArrayBuffer buffer) {
+    public void copyStringToBuffer(@IntRange(from = 0) int row, @IntRange(from = 0) int column,
+            CharArrayBuffer buffer) {
         if (buffer == null) {
             throw new IllegalArgumentException("CharArrayBuffer should not be null");
         }
@@ -536,7 +529,7 @@
      * @param column The zero-based column index.
      * @return The value of the field as a <code>long</code>.
      */
-    public long getLong(int row, int column) {
+    public long getLong(@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
         acquireReference();
         try {
             return nativeGetLong(mWindowPtr, row - mStartPos, column);
@@ -568,7 +561,7 @@
      * @param column The zero-based column index.
      * @return The value of the field as a <code>double</code>.
      */
-    public double getDouble(int row, int column) {
+    public double getDouble(@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
         acquireReference();
         try {
             return nativeGetDouble(mWindowPtr, row - mStartPos, column);
@@ -589,7 +582,7 @@
      * @param column The zero-based column index.
      * @return The value of the field as a <code>short</code>.
      */
-    public short getShort(int row, int column) {
+    public short getShort(@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
         return (short) getLong(row, column);
     }
 
@@ -605,7 +598,7 @@
      * @param column The zero-based column index.
      * @return The value of the field as an <code>int</code>.
      */
-    public int getInt(int row, int column) {
+    public int getInt(@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
         return (int) getLong(row, column);
     }
 
@@ -621,7 +614,7 @@
      * @param column The zero-based column index.
      * @return The value of the field as an <code>float</code>.
      */
-    public float getFloat(int row, int column) {
+    public float getFloat(@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
         return (float) getDouble(row, column);
     }
 
@@ -633,7 +626,8 @@
      * @param column The zero-based column index.
      * @return True if successful.
      */
-    public boolean putBlob(byte[] value, int row, int column) {
+    public boolean putBlob(byte[] value,
+            @IntRange(from = 0) int row, @IntRange(from = 0) int column) {
         acquireReference();
         try {
             return nativePutBlob(mWindowPtr, value, row - mStartPos, column);
@@ -650,7 +644,8 @@
      * @param column The zero-based column index.
      * @return True if successful.
      */
-    public boolean putString(String value, int row, int column) {
+    public boolean putString(String value,
+            @IntRange(from = 0) int row, @IntRange(from = 0) int column) {
         acquireReference();
         try {
             return nativePutString(mWindowPtr, value, row - mStartPos, column);
@@ -667,7 +662,8 @@
      * @param column The zero-based column index.
      * @return True if successful.
      */
-    public boolean putLong(long value, int row, int column) {
+    public boolean putLong(long value,
+            @IntRange(from = 0) int row, @IntRange(from = 0) int column) {
         acquireReference();
         try {
             return nativePutLong(mWindowPtr, value, row - mStartPos, column);
@@ -685,7 +681,8 @@
      * @param column The zero-based column index.
      * @return True if successful.
      */
-    public boolean putDouble(double value, int row, int column) {
+    public boolean putDouble(double value,
+            @IntRange(from = 0) int row, @IntRange(from = 0) int column) {
         acquireReference();
         try {
             return nativePutDouble(mWindowPtr, value, row - mStartPos, column);
@@ -701,7 +698,7 @@
      * @param column The zero-based column index.
      * @return True if successful.
      */
-    public boolean putNull(int row, int column) {
+    public boolean putNull(@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
         acquireReference();
         try {
             return nativePutNull(mWindowPtr, row - mStartPos, column);
@@ -795,10 +792,10 @@
             if (pid == myPid) {
                 buff.append("this proc=");
             } else {
-                buff.append("pid " + pid + "=");
+                buff.append("pid ").append(pid).append('=');
             }
             int num = pidCounts.get(pid);
-            buff.append(num + ")");
+            buff.append(num).append(')');
             total += num;
         }
         // limit the returned string size to 1000
diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java
index b978ae5..6c8a850 100644
--- a/core/java/android/database/DatabaseUtils.java
+++ b/core/java/android/database/DatabaseUtils.java
@@ -672,7 +672,7 @@
      * @param sb the StringBuilder to print to
      */
     public static void dumpCursor(Cursor cursor, StringBuilder sb) {
-        sb.append(">>>>> Dumping cursor " + cursor + "\n");
+        sb.append(">>>>> Dumping cursor ").append(cursor).append('\n');
         if (cursor != null) {
             int startPos = cursor.getPosition();
 
@@ -739,7 +739,7 @@
      */
     public static void dumpCurrentRow(Cursor cursor, StringBuilder sb) {
         String[] cols = cursor.getColumnNames();
-        sb.append("" + cursor.getPosition() + " {\n");
+        sb.append(cursor.getPosition()).append(" {\n");
         int length = cols.length;
         for (int i = 0; i < length; i++) {
             String value;
@@ -750,7 +750,7 @@
                 // representable by a string, e.g. it is a BLOB.
                 value = "<unprintable>";
             }
-            sb.append("   " + cols[i] + '=' + value + "\n");
+            sb.append("   ").append(cols[i]).append('=').append(value).append('\n');
         }
         sb.append("}\n");
     }
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index 2f67f6d..865940e 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -226,7 +226,8 @@
                     NoPreloadHolder.DEBUG_SQL_STATEMENTS, NoPreloadHolder.DEBUG_SQL_TIME,
                     mConfiguration.lookasideSlotSize, mConfiguration.lookasideSlotCount);
         } catch (SQLiteCantOpenDatabaseException e) {
-            String message = String.format("Cannot open database '%s'", file);
+            final StringBuilder message = new StringBuilder("Cannot open database '")
+                    .append(file).append('\'');
 
             try {
                 // Try to diagnose for common reasons. If something fails in here, that's fine;
@@ -236,20 +237,21 @@
                 final Path dir = path.getParent();
 
                 if (!Files.isDirectory(dir)) {
-                    message += ": Directory " + dir + " doesn't exist";
+                    message.append(": Directory ").append(dir).append(" doesn't exist");
                 } else if (!Files.exists(path)) {
-                    message += ": File " + path + " doesn't exist";
+                    message.append(": File ").append(path).append(" doesn't exist");
                 } else if (!Files.isReadable(path)) {
-                    message += ": File " + path + " is not readable";
+                    message.append(": File ").append(path).append(" is not readable");
                 } else if (Files.isDirectory(path)) {
-                    message += ": Path " + path + " is a directory";
+                    message.append(": Path ").append(path).append(" is a directory");
                 } else {
-                    message += ": Unknown reason";
+                    message.append(": Unknown reason");
                 }
             } catch (Throwable th) {
-                message += ": Unknown reason; cannot examine filesystem: " + th.getMessage();
+                message.append(": Unknown reason; cannot examine filesystem: ")
+                        .append(th.getMessage());
             }
-            throw new SQLiteCantOpenDatabaseException(message, e);
+            throw new SQLiteCantOpenDatabaseException(message.toString(), e);
         } finally {
             mRecentOperations.endOperation(cookie);
         }
@@ -1293,11 +1295,11 @@
                 } catch (SQLiteException ex) {
                     // Ignore.
                 }
-                String label = "  (attached) " + name;
+                StringBuilder label = new StringBuilder("  (attached) ").append(name);
                 if (!path.isEmpty()) {
-                    label += ": " + path;
+                    label.append(": ").append(path);
                 }
-                dbStatsList.add(new DbStats(label, pageCount, pageSize, 0, 0, 0, 0));
+                dbStatsList.add(new DbStats(label.toString(), pageCount, pageSize, 0, 0, 0, 0));
             }
         } catch (SQLiteException ex) {
             // Ignore.
@@ -1319,9 +1321,11 @@
     private DbStats getMainDbStatsUnsafe(int lookaside, long pageCount, long pageSize) {
         // The prepared statement cache is thread-safe so we can access its statistics
         // even if we do not own the database connection.
-        String label = mConfiguration.path;
-        if (!mIsPrimaryConnection) {
-            label += " (" + mConnectionId + ")";
+        String label;
+        if (mIsPrimaryConnection) {
+            label = mConfiguration.path;
+        } else {
+            label = mConfiguration.path + " (" + mConnectionId + ")";
         }
         return new DbStats(label, pageCount, pageSize, lookaside,
                 mPreparedStatementCache.hitCount(),
diff --git a/core/java/android/database/sqlite/SQLiteCursor.java b/core/java/android/database/sqlite/SQLiteCursor.java
index 4559e91..3bfbe7e 100644
--- a/core/java/android/database/sqlite/SQLiteCursor.java
+++ b/core/java/android/database/sqlite/SQLiteCursor.java
@@ -147,7 +147,7 @@
         clearOrCreateWindow(getDatabase().getPath());
         try {
             Preconditions.checkArgumentNonnegative(requiredPos,
-                    "requiredPos cannot be negative, but was " + requiredPos);
+                    "requiredPos cannot be negative");
 
             if (mCount == NO_COUNT) {
                 mCount = mQuery.fillWindow(mWindow, requiredPos, requiredPos, true);
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 0efd883..a2cbdd3 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -1692,7 +1692,7 @@
                     sql.append((i > 0) ? ",?" : "?");
                 }
             } else {
-                sql.append(nullColumnHack + ") VALUES (NULL");
+                sql.append(nullColumnHack).append(") VALUES (NULL");
             }
             sql.append(')');
 
@@ -2674,7 +2674,7 @@
                         "lookasideSlotSize cannot be negative");
                 Preconditions.checkArgument(
                         (slotSize > 0 && slotCount > 0) || (slotCount == 0 && slotSize == 0),
-                        "Invalid configuration: " + slotSize + ", " + slotCount);
+                        "Invalid configuration: %d, %d", slotSize, slotCount);
 
                 mLookasideSlotSize = slotSize;
                 mLookasideSlotCount = slotCount;
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 2159905..86031dd 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -2151,7 +2151,7 @@
          *         same as those of this size. {@code false} otherwise.
          */
         @Override
-        public boolean equals(Object obj) {
+        public boolean equals(@Nullable Object obj) {
             if (!(obj instanceof Size)) {
                 return false;
             }
@@ -2222,7 +2222,7 @@
          *         the same as those of this area. {@code false} otherwise.
          */
         @Override
-        public boolean equals(Object obj) {
+        public boolean equals(@Nullable Object obj) {
             if (!(obj instanceof Area)) {
                 return false;
             }
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index a6e8c13..0f3cdfc 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -616,7 +616,7 @@
     public static final String STRING_TYPE_MOTION_DETECT = "android.sensor.motion_detect";
 
     /**
-     * A constant describing a motion detect sensor.
+     * A constant describing a heart beat sensor.
      *
      * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details.
      *
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index 9906331..236fab0 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -550,7 +550,7 @@
      *   <h4>{@link android.hardware.Sensor#TYPE_HEART_BEAT
      * Sensor.TYPE_HEART_BEAT}:</h4>
      *
-     * A sensor of this type returns an event everytime a hear beat peak is
+     * A sensor of this type returns an event everytime a heart beat peak is
      * detected.
      *
      * Peak here ideally corresponds to the positive peak in the QRS complex of
diff --git a/core/java/android/hardware/biometrics/BiometricConstants.java b/core/java/android/hardware/biometrics/BiometricConstants.java
index 3040932..bed9a06 100644
--- a/core/java/android/hardware/biometrics/BiometricConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricConstants.java
@@ -172,8 +172,7 @@
             BIOMETRIC_ERROR_NEGATIVE_BUTTON,
             BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL,
             BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED,
-            BIOMETRIC_PAUSED_REJECTED,
-            BIOMETRIC_ERROR_VENDOR_BASE})
+            BIOMETRIC_PAUSED_REJECTED})
     @Retention(RetentionPolicy.SOURCE)
     @interface Errors {}
 
@@ -182,6 +181,19 @@
     //
 
     /**
+     * @hide
+     */
+    @IntDef({BIOMETRIC_ACQUIRED_GOOD,
+            BIOMETRIC_ACQUIRED_PARTIAL,
+            BIOMETRIC_ACQUIRED_INSUFFICIENT,
+            BIOMETRIC_ACQUIRED_IMAGER_DIRTY,
+            BIOMETRIC_ACQUIRED_TOO_SLOW,
+            BIOMETRIC_ACQUIRED_TOO_FAST,
+            BIOMETRIC_ACQUIRED_VENDOR})
+    @Retention(RetentionPolicy.SOURCE)
+    @interface Acquired {}
+
+    /**
      * The image acquired was good.
      */
     int BIOMETRIC_ACQUIRED_GOOD = 0;
diff --git a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
index 46e8cc0..c7b554b 100644
--- a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
@@ -16,11 +16,15 @@
 
 package android.hardware.biometrics;
 
+import android.annotation.IntDef;
 import android.app.KeyguardManager;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.hardware.biometrics.BiometricManager.Authenticators;
 import android.hardware.fingerprint.FingerprintManager;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Interface containing all of the fingerprint-specific constants.
  *
@@ -36,6 +40,27 @@
     //
 
     /**
+     * @hide
+     */
+    @IntDef({FINGERPRINT_ERROR_HW_UNAVAILABLE,
+            FINGERPRINT_ERROR_UNABLE_TO_PROCESS,
+            FINGERPRINT_ERROR_TIMEOUT,
+            FINGERPRINT_ERROR_NO_SPACE,
+            FINGERPRINT_ERROR_CANCELED,
+            FINGERPRINT_ERROR_UNABLE_TO_REMOVE,
+            FINGERPRINT_ERROR_LOCKOUT,
+            FINGERPRINT_ERROR_VENDOR,
+            FINGERPRINT_ERROR_LOCKOUT_PERMANENT,
+            FINGERPRINT_ERROR_USER_CANCELED,
+            FINGERPRINT_ERROR_NO_FINGERPRINTS,
+            FINGERPRINT_ERROR_HW_NOT_PRESENT,
+            FINGERPRINT_ERROR_NEGATIVE_BUTTON,
+            BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL,
+            BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED})
+    @Retention(RetentionPolicy.SOURCE)
+    @interface FingerprintError {}
+
+    /**
      * The hardware is unavailable. Try again later.
      */
     int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1;
@@ -150,6 +175,20 @@
     //
 
     /**
+     * @hide
+     */
+    @IntDef({FINGERPRINT_ACQUIRED_GOOD,
+            FINGERPRINT_ACQUIRED_PARTIAL,
+            FINGERPRINT_ACQUIRED_INSUFFICIENT,
+            FINGERPRINT_ACQUIRED_IMAGER_DIRTY,
+            FINGERPRINT_ACQUIRED_TOO_SLOW,
+            FINGERPRINT_ACQUIRED_TOO_FAST,
+            FINGERPRINT_ACQUIRED_VENDOR,
+            FINGERPRINT_ACQUIRED_START})
+    @Retention(RetentionPolicy.SOURCE)
+    @interface FingerprintAcquired {}
+
+    /**
      * The image acquired was good.
      */
     int FINGERPRINT_ACQUIRED_GOOD = 0;
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 8d0cc68..25c749b 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -16,14 +16,17 @@
 
 package android.hardware.biometrics;
 
+import static android.Manifest.permission.TEST_BIOMETRIC;
 import static android.Manifest.permission.USE_BIOMETRIC;
 import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
 import static android.Manifest.permission.WRITE_DEVICE_CONFIG;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.content.Context;
 import android.os.RemoteException;
 import android.security.keystore.KeyGenParameterSpec;
@@ -32,6 +35,8 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * A class that contains biometric utilities. For authentication, see {@link BiometricPrompt}.
@@ -196,6 +201,28 @@
     }
 
     /**
+     * @return A list of {@link SensorProperties}
+     * @hide
+     */
+    @TestApi
+    @NonNull
+    @RequiresPermission(TEST_BIOMETRIC)
+    public List<SensorProperties> getSensorProperties() {
+        return new ArrayList<>(); // TODO(169459906)
+    }
+
+    /**
+     * Retrieves a test session for BiometricManager/BiometricPrompt.
+     * @hide
+     */
+    @TestApi
+    @NonNull
+    @RequiresPermission(TEST_BIOMETRIC)
+    public BiometricTestSession createTestSession(int sensorId) {
+        return null; // TODO(169459906)
+    }
+
+    /**
      * Determine if biometrics can be used. In other words, determine if
      * {@link BiometricPrompt} can be expected to be shown (hardware available, templates enrolled,
      * user-enabled). This is the equivalent of {@link #canAuthenticate(int)} with
diff --git a/core/java/android/hardware/biometrics/BiometricTestSession.java b/core/java/android/hardware/biometrics/BiometricTestSession.java
new file mode 100644
index 0000000..802655b
--- /dev/null
+++ b/core/java/android/hardware/biometrics/BiometricTestSession.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.biometrics;
+
+import static android.Manifest.permission.TEST_BIOMETRIC;
+
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.TestApi;
+import android.content.Context;
+import android.hardware.fingerprint.FingerprintManager;
+import android.os.RemoteException;
+import android.util.ArraySet;
+
+/**
+ * Common set of interfaces to test biometric-related APIs, including {@link BiometricPrompt} and
+ * {@link android.hardware.fingerprint.FingerprintManager}.
+ * @hide
+ */
+@TestApi
+public class BiometricTestSession implements AutoCloseable {
+    private final Context mContext;
+    private final ITestSession mTestSession;
+
+    // Keep track of users that were tested, which need to be cleaned up when finishing.
+    private final ArraySet<Integer> mTestedUsers;
+
+    /**
+     * @hide
+     */
+    public BiometricTestSession(@NonNull Context context, @NonNull ITestSession testSession) {
+        mContext = context;
+        mTestSession = testSession;
+        mTestedUsers = new ArraySet<>();
+        enableTestHal(true);
+    }
+
+    /**
+     * Switches the specified sensor to use a test HAL. In this mode, the framework will not invoke
+     * any methods on the real HAL implementation. This allows the framework to test a substantial
+     * portion of the framework code that would otherwise require human interaction. Note that
+     * secure pathways such as HAT/Keystore are not testable, since they depend on the TEE or its
+     * equivalent for the secret key.
+     *
+     * @param enableTestHal If true, enable testing with a fake HAL instead of the real HAL.
+     */
+    @RequiresPermission(TEST_BIOMETRIC)
+    private void enableTestHal(boolean enableTestHal) {
+        try {
+            mTestSession.enableTestHal(enableTestHal);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Starts the enrollment process. This should generally be used when the test HAL is enabled.
+     *
+     * @param userId User that this command applies to.
+     */
+    @RequiresPermission(TEST_BIOMETRIC)
+    public void startEnroll(int userId) {
+        try {
+            mTestedUsers.add(userId);
+            mTestSession.startEnroll(userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Finishes the enrollment process. Simulates the HAL's callback.
+     *
+     * @param userId User that this command applies to.
+     */
+    @RequiresPermission(TEST_BIOMETRIC)
+    public void finishEnroll(int userId) {
+        try {
+            mTestedUsers.add(userId);
+            mTestSession.finishEnroll(userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Simulates a successful authentication, but does not provide a valid HAT.
+     *
+     * @param userId User that this command applies to.
+     */
+    @RequiresPermission(TEST_BIOMETRIC)
+    public void acceptAuthentication(int userId) {
+        try {
+            mTestSession.acceptAuthentication(userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Simulates a rejected attempt.
+     *
+     * @param userId User that this command applies to.
+     */
+    @RequiresPermission(TEST_BIOMETRIC)
+    public void rejectAuthentication(int userId) {
+        try {
+            mTestSession.rejectAuthentication(userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Simulates an acquired message from the HAL.
+     *
+     * @param userId User that this command applies to.
+     * @param acquireInfo See
+     * {@link BiometricPrompt.AuthenticationCallback#onAuthenticationAcquired(int)} and
+     * {@link FingerprintManager.AuthenticationCallback#onAuthenticationAcquired(int)}
+     */
+    @RequiresPermission(TEST_BIOMETRIC)
+    public void notifyAcquired(int userId, int acquireInfo) {
+        try {
+            mTestSession.notifyAcquired(userId, acquireInfo);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Simulates an error message from the HAL.
+     *
+     * @param userId User that this command applies to.
+     * @param errorCode See
+     * {@link BiometricPrompt.AuthenticationCallback#onAuthenticationError(int, CharSequence)} and
+     * {@link FingerprintManager.AuthenticationCallback#onAuthenticationError(int, CharSequence)}
+     */
+    @RequiresPermission(TEST_BIOMETRIC)
+    public void notifyError(int userId, int errorCode) {
+        try {
+            mTestSession.notifyError(userId, errorCode);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Matches the framework's cached enrollments against the HAL's enrollments. Any enrollment
+     * that isn't known by both sides are deleted. This should generally be used when the test
+     * HAL is disabled (e.g. to clean up after a test).
+     *
+     * @param userId User that this command applies to.
+     */
+    @RequiresPermission(TEST_BIOMETRIC)
+    public void cleanupInternalState(int userId) {
+        try {
+            mTestSession.cleanupInternalState(userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @Override
+    @RequiresPermission(TEST_BIOMETRIC)
+    public void close() {
+        for (int user : mTestedUsers) {
+            cleanupInternalState(user);
+        }
+
+        enableTestHal(false);
+    }
+}
diff --git a/core/java/android/hardware/biometrics/ITestSession.aidl b/core/java/android/hardware/biometrics/ITestSession.aidl
new file mode 100644
index 0000000..6112f17
--- /dev/null
+++ b/core/java/android/hardware/biometrics/ITestSession.aidl
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.biometrics;
+
+import android.hardware.biometrics.SensorPropertiesInternal;
+
+/**
+ * A test service for FingerprintManager and BiometricPrompt.
+ * @hide
+ */
+interface ITestSession {
+    // Switches the specified sensor to use a test HAL. In this mode, the framework will not invoke
+    // any methods on the real HAL implementation. This allows the framework to test a substantial
+    // portion of the framework code that would otherwise require human interaction. Note that
+    // secure pathways such as HAT/Keystore are not testable, since they depend on the TEE or its
+    // equivalent for the secret key.
+    void enableTestHal(boolean enableTestHal);
+
+    // Starts the enrollment process. This should generally be used when the test HAL is enabled.
+    void startEnroll(int userId);
+
+    // Finishes the enrollment process. Simulates the HAL's callback.
+    void finishEnroll(int userId);
+
+    // Simulates a successful authentication, but does not provide a valid HAT.
+    void acceptAuthentication(int userId);
+
+    // Simulates a rejected attempt.
+    void rejectAuthentication(int userId);
+
+    // Simulates an acquired message from the HAL.
+    void notifyAcquired(int userId, int acquireInfo);
+
+    // Simulates an error message from the HAL.
+    void notifyError(int userId, int errorCode);
+
+    // Matches the framework's cached enrollments against the HAL's enrollments. Any enrollment
+    // that isn't known by both sides are deleted. This should generally be used when the test
+    // HAL is disabled (e.g. to clean up after a test).
+    void cleanupInternalState(int userId);
+}
diff --git a/core/java/android/hardware/biometrics/SensorProperties.java b/core/java/android/hardware/biometrics/SensorProperties.java
index b3dcb8f..5b1b5e6 100644
--- a/core/java/android/hardware/biometrics/SensorProperties.java
+++ b/core/java/android/hardware/biometrics/SensorProperties.java
@@ -17,6 +17,7 @@
 package android.hardware.biometrics;
 
 import android.annotation.IntDef;
+import android.annotation.TestApi;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -25,19 +26,18 @@
  * The base class containing all modality-agnostic information.
  * @hide
  */
+@TestApi
 public class SensorProperties {
     /**
      * A sensor that meets the requirements for Class 1 biometrics as defined in the CDD. This does
      * not correspond to a public BiometricManager.Authenticators constant. Sensors of this strength
      * are not available to applications via the public API surface.
-     * @hide
      */
     public static final int STRENGTH_CONVENIENCE = 0;
 
     /**
      * A sensor that meets the requirements for Class 2 biometrics as defined in the CDD.
      * Corresponds to BiometricManager.Authenticators.BIOMETRIC_WEAK.
-     * @hide
      */
     public static final int STRENGTH_WEAK = 1;
 
@@ -46,7 +46,6 @@
      * Corresponds to BiometricManager.Authenticators.BIOMETRIC_STRONG.
      *
      * Notably, this is the only strength that allows generation of HardwareAuthToken(s).
-     * @hide
      */
     public static final int STRENGTH_STRONG = 2;
 
@@ -70,7 +69,6 @@
 
     /**
      * @return The sensor's unique identifier.
-     * @hide
      */
     public int getSensorId() {
         return mSensorId;
@@ -78,7 +76,6 @@
 
     /**
      * @return The sensor's strength.
-     * @hide
      */
     @Strength
     public int getSensorStrength() {
diff --git a/media/java/android/media/tv/TvChannelInfo.aidl b/core/java/android/hardware/biometrics/SensorPropertiesInternal.aidl
similarity index 80%
copy from media/java/android/media/tv/TvChannelInfo.aidl
copy to core/java/android/hardware/biometrics/SensorPropertiesInternal.aidl
index 71cd0a7..d85c788 100644
--- a/media/java/android/media/tv/TvChannelInfo.aidl
+++ b/core/java/android/hardware/biometrics/SensorPropertiesInternal.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,7 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package android.hardware.biometrics;
 
-package android.media.tv;
-
-parcelable TvChannelInfo;
+parcelable SensorPropertiesInternal;
\ No newline at end of file
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index decf053..cd13707 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -20,7 +20,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.hardware.camera2.params.InputConfiguration;
 import android.hardware.camera2.params.OutputConfiguration;
 import android.hardware.camera2.params.SessionConfiguration;
@@ -358,7 +357,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final int SESSION_OPERATION_MODE_NORMAL =
             0; // ICameraDeviceUser.NORMAL_MODE;
 
@@ -369,7 +367,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final int SESSION_OPERATION_MODE_CONSTRAINED_HIGH_SPEED =
             1; // ICameraDeviceUser.CONSTRAINED_HIGH_SPEED_MODE;
 
@@ -380,7 +377,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final int SESSION_OPERATION_MODE_VENDOR_START =
             0x8000; // ICameraDeviceUser.VENDOR_MODE_START;
 
@@ -423,7 +419,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @Deprecated
     public abstract void createCustomCaptureSession(
             InputConfiguration inputConfig,
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 0d0bfb3..47dafa0 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -528,7 +528,7 @@
      * @return True if the requests are the same, false otherwise.
      */
     @Override
-    public boolean equals(Object other) {
+    public boolean equals(@Nullable Object other) {
         return other instanceof CaptureRequest
                 && equals((CaptureRequest)other);
     }
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index e21b45a..48ec3fd 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -1587,7 +1587,7 @@
             }
 
             switch (errorCode) {
-                case CameraDeviceCallbacks.ERROR_CAMERA_DISCONNECTED:
+                case CameraDeviceCallbacks.ERROR_CAMERA_DISCONNECTED: {
                     final long ident = Binder.clearCallingIdentity();
                     try {
                         mDeviceExecutor.execute(mCallOnDisconnected);
@@ -1595,6 +1595,7 @@
                         Binder.restoreCallingIdentity(ident);
                     }
                     break;
+                }
                 case CameraDeviceCallbacks.ERROR_CAMERA_REQUEST:
                 case CameraDeviceCallbacks.ERROR_CAMERA_RESULT:
                 case CameraDeviceCallbacks.ERROR_CAMERA_BUFFER:
diff --git a/core/java/android/hardware/camera2/impl/CameraOfflineSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraOfflineSessionImpl.java
index 413caf5..1d8b2a1 100644
--- a/core/java/android/hardware/camera2/impl/CameraOfflineSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraOfflineSessionImpl.java
@@ -147,7 +147,7 @@
                     case CameraDeviceCallbacks.ERROR_CAMERA_BUFFER:
                         onCaptureErrorLocked(errorCode, resultExtras);
                         break;
-                    default:
+                    default: {
                         Runnable errorDispatch = new Runnable() {
                             @Override
                             public void run() {
@@ -164,6 +164,7 @@
                         } finally {
                             Binder.restoreCallingIdentity(ident);
                         }
+                    }
                 }
             }
         }
diff --git a/core/java/android/hardware/camera2/marshal/MarshalRegistry.java b/core/java/android/hardware/camera2/marshal/MarshalRegistry.java
index 1565087..0575f37 100644
--- a/core/java/android/hardware/camera2/marshal/MarshalRegistry.java
+++ b/core/java/android/hardware/camera2/marshal/MarshalRegistry.java
@@ -15,6 +15,7 @@
  */
 package android.hardware.camera2.marshal;
 
+import android.annotation.Nullable;
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.utils.TypeReference;
 
@@ -114,7 +115,7 @@
         private final int hash;
 
         @Override
-        public boolean equals(Object other) {
+        public boolean equals(@Nullable Object other) {
             if (other instanceof MarshalToken<?>) {
                 MarshalToken<?> otherToken = (MarshalToken<?>)other;
                 return typeReference.equals(otherToken.typeReference) &&
diff --git a/core/java/android/hardware/camera2/params/BlackLevelPattern.java b/core/java/android/hardware/camera2/params/BlackLevelPattern.java
index c3dc25f..4405df8 100644
--- a/core/java/android/hardware/camera2/params/BlackLevelPattern.java
+++ b/core/java/android/hardware/camera2/params/BlackLevelPattern.java
@@ -16,6 +16,8 @@
 
 package android.hardware.camera2.params;
 
+import android.annotation.Nullable;
+
 import java.util.Arrays;
 import java.util.Objects;
 
@@ -107,7 +109,7 @@
      * @return {@code true} if the objects were equal, {@code false} otherwise
      */
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (obj == null) {
             return false;
         } else if (this == obj) {
diff --git a/core/java/android/hardware/camera2/params/ColorSpaceTransform.java b/core/java/android/hardware/camera2/params/ColorSpaceTransform.java
index 1e1c4b1..5b3a6d7 100644
--- a/core/java/android/hardware/camera2/params/ColorSpaceTransform.java
+++ b/core/java/android/hardware/camera2/params/ColorSpaceTransform.java
@@ -78,7 +78,7 @@
         mElements = new int[COUNT_INT];
 
         for (int i = 0; i < elements.length; ++i) {
-            checkNotNull(elements, "element[" + i + "] must not be null");
+            checkNotNull(elements, "element[%d] must not be null", i);
             mElements[i * RATIONAL_SIZE + OFFSET_NUMERATOR] = elements[i].getNumerator();
             mElements[i * RATIONAL_SIZE + OFFSET_DENOMINATOR] = elements[i].getDenominator();
         }
@@ -116,7 +116,7 @@
         }
 
         for (int i = 0; i < elements.length; ++i) {
-            checkNotNull(elements, "element " + i + " must not be null");
+            checkNotNull(elements, "element %d must not be null", i);
         }
 
         mElements = Arrays.copyOf(elements, elements.length);
diff --git a/core/java/android/hardware/camera2/params/InputConfiguration.java b/core/java/android/hardware/camera2/params/InputConfiguration.java
index d95f889..0a50f97 100644
--- a/core/java/android/hardware/camera2/params/InputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/InputConfiguration.java
@@ -16,6 +16,7 @@
 
 package android.hardware.camera2.params;
 
+import android.annotation.Nullable;
 import android.hardware.camera2.utils.HashCodeHelpers;
 
 /**
@@ -90,7 +91,7 @@
      * @return {@code true} if the objects were equal, {@code false} otherwise.
      */
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (!(obj instanceof InputConfiguration)) {
             return false;
         }
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index 226b8e5..a20a1bf 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -17,6 +17,8 @@
 
 package android.hardware.camera2.params;
 
+import static com.android.internal.util.Preconditions.*;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -31,8 +33,6 @@
 import android.util.Size;
 import android.view.Surface;
 
-import static com.android.internal.util.Preconditions.*;
-
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -677,7 +677,7 @@
      * @return {@code true} if the objects were equal, {@code false} otherwise
      */
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (obj == null) {
             return false;
         } else if (this == obj) {
diff --git a/core/java/android/hardware/camera2/params/SessionConfiguration.java b/core/java/android/hardware/camera2/params/SessionConfiguration.java
index 47a897c..8fc919f 100644
--- a/core/java/android/hardware/camera2/params/SessionConfiguration.java
+++ b/core/java/android/hardware/camera2/params/SessionConfiguration.java
@@ -22,6 +22,7 @@
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraDevice;
@@ -190,7 +191,7 @@
      * @return {@code true} if the objects were equal, {@code false} otherwise
      */
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (obj == null) {
             return false;
         } else if (this == obj) {
diff --git a/core/java/android/hardware/camera2/params/TonemapCurve.java b/core/java/android/hardware/camera2/params/TonemapCurve.java
index 90e6355..a11d2c7 100644
--- a/core/java/android/hardware/camera2/params/TonemapCurve.java
+++ b/core/java/android/hardware/camera2/params/TonemapCurve.java
@@ -18,6 +18,7 @@
 
 import static com.android.internal.util.Preconditions.*;
 
+import android.annotation.Nullable;
 import android.graphics.PointF;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraDevice;
@@ -249,7 +250,7 @@
      * @return {@code true} if the objects were equal, {@code false} otherwise
      */
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (obj == null) {
             return false;
         }
diff --git a/core/java/android/hardware/camera2/utils/TypeReference.java b/core/java/android/hardware/camera2/utils/TypeReference.java
index 435ed15..3c1d206 100644
--- a/core/java/android/hardware/camera2/utils/TypeReference.java
+++ b/core/java/android/hardware/camera2/utils/TypeReference.java
@@ -18,6 +18,7 @@
 
 import static com.android.internal.util.Preconditions.checkNotNull;
 
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 
 import java.lang.reflect.Array;
@@ -244,7 +245,7 @@
      * is also equal.</p>
      */
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         // Note that this comparison could inaccurately return true when comparing types
         // with nested type variables; therefore we ban type variables in the constructor.
         return o instanceof TypeReference<?> && mType.equals(((TypeReference<?>)o).mType);
diff --git a/core/java/android/hardware/devicestate/DeviceStateManager.java b/core/java/android/hardware/devicestate/DeviceStateManager.java
new file mode 100644
index 0000000..a52f983
--- /dev/null
+++ b/core/java/android/hardware/devicestate/DeviceStateManager.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.devicestate;
+
+import android.annotation.SystemService;
+import android.content.Context;
+
+/**
+ * Manages the state of the system for devices with user-configurable hardware like a foldable
+ * phone.
+ *
+ * @hide
+ */
+@SystemService(Context.DEVICE_STATE_SERVICE)
+public final class DeviceStateManager {
+    /** Invalid device state. */
+    public static final int INVALID_DEVICE_STATE = -1;
+
+    private DeviceStateManagerGlobal mGlobal;
+
+    public DeviceStateManager() {
+        mGlobal = DeviceStateManagerGlobal.getInstance();
+    }
+}
diff --git a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
new file mode 100644
index 0000000..4e7cf4a
--- /dev/null
+++ b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.devicestate;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.IBinder;
+import android.os.ServiceManager;
+
+/**
+ * Provides communication with the device state system service on behalf of applications.
+ *
+ * @see DeviceStateManager
+ * @hide
+ */
+final class DeviceStateManagerGlobal {
+    private static DeviceStateManagerGlobal sInstance;
+
+    /**
+     * Returns an instance of {@link DeviceStateManagerGlobal}. May return {@code null} if a
+     * connection with the device state service couldn't be established.
+     */
+    @Nullable
+    static DeviceStateManagerGlobal getInstance() {
+        synchronized (DeviceStateManagerGlobal.class) {
+            if (sInstance == null) {
+                IBinder b = ServiceManager.getService(Context.DEVICE_STATE_SERVICE);
+                if (b != null) {
+                    sInstance = new DeviceStateManagerGlobal(IDeviceStateManager
+                            .Stub.asInterface(b));
+                }
+            }
+            return sInstance;
+        }
+    }
+
+    @NonNull
+    private final IDeviceStateManager mDeviceStateManager;
+
+    private DeviceStateManagerGlobal(@NonNull IDeviceStateManager deviceStateManager) {
+        mDeviceStateManager = deviceStateManager;
+    }
+}
diff --git a/media/java/android/media/tv/TvChannelInfo.aidl b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
similarity index 72%
copy from media/java/android/media/tv/TvChannelInfo.aidl
copy to core/java/android/hardware/devicestate/IDeviceStateManager.aidl
index 71cd0a7..24913e9 100644
--- a/media/java/android/media/tv/TvChannelInfo.aidl
+++ b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
@@ -1,11 +1,11 @@
-/*
- * Copyright 2020 The Android Open Source Project
+/**
+ * Copyright (c) 2020, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *      http://www.apache.org/licenses/LICENSE-2.0
+ *     http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
-package android.media.tv;
+package android.hardware.devicestate;
 
-parcelable TvChannelInfo;
+/** @hide */
+interface IDeviceStateManager {}
diff --git a/core/java/android/hardware/display/AmbientBrightnessDayStats.java b/core/java/android/hardware/display/AmbientBrightnessDayStats.java
index 26fd265..8aff911 100644
--- a/core/java/android/hardware/display/AmbientBrightnessDayStats.java
+++ b/core/java/android/hardware/display/AmbientBrightnessDayStats.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -36,7 +35,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class AmbientBrightnessDayStats implements Parcelable {
 
     /** The localdate for which brightness stats are being tracked */
diff --git a/core/java/android/hardware/display/BrightnessChangeEvent.java b/core/java/android/hardware/display/BrightnessChangeEvent.java
index a6a44be..e2d836c 100644
--- a/core/java/android/hardware/display/BrightnessChangeEvent.java
+++ b/core/java/android/hardware/display/BrightnessChangeEvent.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -32,7 +31,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class BrightnessChangeEvent implements Parcelable {
     /** Brightness in nits */
     public final float brightness;
diff --git a/core/java/android/hardware/display/BrightnessConfiguration.java b/core/java/android/hardware/display/BrightnessConfiguration.java
index 6412a0c..d9c1063 100644
--- a/core/java/android/hardware/display/BrightnessConfiguration.java
+++ b/core/java/android/hardware/display/BrightnessConfiguration.java
@@ -20,7 +20,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.content.pm.ApplicationInfo;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -44,7 +43,6 @@
 
 /** @hide */
 @SystemApi
-@TestApi
 public final class BrightnessConfiguration implements Parcelable {
     private static final String TAG_BRIGHTNESS_CURVE = "brightness-curve";
     private static final String TAG_BRIGHTNESS_POINT = "brightness-point";
diff --git a/core/java/android/hardware/display/BrightnessCorrection.java b/core/java/android/hardware/display/BrightnessCorrection.java
index 22df778..bbfc45e 100644
--- a/core/java/android/hardware/display/BrightnessCorrection.java
+++ b/core/java/android/hardware/display/BrightnessCorrection.java
@@ -20,7 +20,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.MathUtils;
@@ -44,7 +43,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class BrightnessCorrection implements Parcelable {
 
     private static final int SCALE_AND_TRANSLATE_LOG = 1;
@@ -238,7 +236,7 @@
         }
 
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (o == this) {
                 return true;
             }
diff --git a/core/java/android/hardware/display/DeviceProductInfo.java b/core/java/android/hardware/display/DeviceProductInfo.java
index f8d3675..41126b7 100644
--- a/core/java/android/hardware/display/DeviceProductInfo.java
+++ b/core/java/android/hardware/display/DeviceProductInfo.java
@@ -16,6 +16,7 @@
 
 package android.hardware.display;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -124,7 +125,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
         DeviceProductInfo that = (DeviceProductInfo) o;
@@ -227,7 +228,7 @@
         }
 
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (this == o) return true;
             if (o == null || getClass() != o.getClass()) return false;
             ManufactureDate that = (ManufactureDate) o;
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index dda1890b..68b9d52 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -16,6 +16,8 @@
 
 package android.hardware.display;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -381,6 +383,7 @@
                     addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_EXTERNAL);
                     addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_OVERLAY);
                     addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_VIRTUAL);
+                    addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_INTERNAL);
                 }
                 return mTempDisplays.toArray(new Display[mTempDisplays.size()]);
             } finally {
@@ -401,6 +404,9 @@
     private void addPresentationDisplaysLocked(
             ArrayList<Display> displays, int[] displayIds, int matchType) {
         for (int i = 0; i < displayIds.length; i++) {
+            if (displayIds[i] == DEFAULT_DISPLAY) {
+                continue;
+            }
             Display display = getOrCreateDisplayLocked(displayIds[i], true /*assumeValid*/);
             if (display != null
                     && (display.getFlags() & Display.FLAG_PRESENTATION) != 0
@@ -693,7 +699,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public Point getStableDisplaySize() {
         return mGlobal.getStableDisplaySize();
     }
@@ -703,7 +708,6 @@
      * @hide until we make it a system api.
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(Manifest.permission.BRIGHTNESS_SLIDER_USAGE)
     public List<BrightnessChangeEvent> getBrightnessEvents() {
         return mGlobal.getBrightnessEvents(mContext.getOpPackageName());
@@ -715,7 +719,6 @@
      * @hide until we make it a system api
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(Manifest.permission.ACCESS_AMBIENT_LIGHT_STATS)
     public List<AmbientBrightnessDayStats> getAmbientBrightnessStats() {
         return mGlobal.getAmbientBrightnessStats();
@@ -727,7 +730,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS)
     public void setBrightnessConfiguration(BrightnessConfiguration c) {
         setBrightnessConfigurationForUser(c, mContext.getUserId(), mContext.getPackageName());
@@ -752,7 +754,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS)
     public BrightnessConfiguration getBrightnessConfiguration() {
         return getBrightnessConfigurationForUser(mContext.getUserId());
@@ -778,7 +779,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS)
     @Nullable
     public BrightnessConfiguration getDefaultBrightnessConfiguration() {
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index c7f8915..defcab7 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -372,7 +372,7 @@
         }
 
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             return o instanceof DisplayPowerRequest
                     && equals((DisplayPowerRequest)o);
         }
diff --git a/core/java/android/hardware/display/DisplayViewport.java b/core/java/android/hardware/display/DisplayViewport.java
index 8dcfc94..d2b66c3 100644
--- a/core/java/android/hardware/display/DisplayViewport.java
+++ b/core/java/android/hardware/display/DisplayViewport.java
@@ -105,7 +105,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (o == this) {
             return true;
         }
diff --git a/core/java/android/hardware/display/WifiDisplay.java b/core/java/android/hardware/display/WifiDisplay.java
index 5bbbbf9..39fd0a8 100644
--- a/core/java/android/hardware/display/WifiDisplay.java
+++ b/core/java/android/hardware/display/WifiDisplay.java
@@ -16,6 +16,7 @@
 
 package android.hardware.display;
 
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -135,7 +136,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         return o instanceof WifiDisplay && equals((WifiDisplay)o);
     }
 
diff --git a/core/java/android/hardware/fingerprint/Fingerprint.java b/core/java/android/hardware/fingerprint/Fingerprint.java
index 57e52d9..9ce834ca 100644
--- a/core/java/android/hardware/fingerprint/Fingerprint.java
+++ b/core/java/android/hardware/fingerprint/Fingerprint.java
@@ -31,6 +31,10 @@
         mGroupId = groupId;
     }
 
+    public Fingerprint(CharSequence name, int fingerId, long deviceId) {
+        super(name, fingerId, deviceId);
+    }
+
     private Fingerprint(Parcel in) {
         super(in.readString(), in.readInt(), in.readLong());
         mGroupId = in.readInt();
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 23de303..2aefb1d 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
 import static android.Manifest.permission.MANAGE_FINGERPRINT;
 import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT;
+import static android.Manifest.permission.TEST_BIOMETRIC;
 import static android.Manifest.permission.USE_BIOMETRIC;
 import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
 import static android.Manifest.permission.USE_FINGERPRINT;
@@ -28,6 +29,7 @@
 import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.app.ActivityManager;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
@@ -35,7 +37,9 @@
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricFingerprintConstants;
 import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.BiometricTestSession;
 import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.hardware.biometrics.SensorProperties;
 import android.os.Binder;
 import android.os.CancellationSignal;
 import android.os.CancellationSignal.OnCancelListener;
@@ -94,6 +98,40 @@
     private Fingerprint mRemovalFingerprint;
     private Handler mHandler;
 
+
+    /**
+     * Retrieves a list of properties for all fingerprint sensors on the device.
+     * @hide
+     */
+    @TestApi
+    @NonNull
+    @RequiresPermission(TEST_BIOMETRIC)
+    public List<SensorProperties> getSensorProperties() {
+        final List<SensorProperties> properties = new ArrayList<>();
+        final List<FingerprintSensorPropertiesInternal> internalProperties
+                = getSensorPropertiesInternal();
+        for (FingerprintSensorPropertiesInternal internalProp : internalProperties) {
+            properties.add(FingerprintSensorProperties.from(internalProp));
+        }
+        return properties;
+    }
+
+    /**
+     * Retrieves a test session for FingerprintManager.
+     * @hide
+     */
+    @TestApi
+    @NonNull
+    @RequiresPermission(TEST_BIOMETRIC)
+    public BiometricTestSession createTestSession(int sensorId) {
+        try {
+            return new BiometricTestSession(mContext,
+                    mService.createTestSession(sensorId, mContext.getOpPackageName()));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     private class OnEnrollCancelListener implements OnCancelListener {
         @Override
         public void onCancel() {
@@ -588,10 +626,10 @@
      * @hide
      */
     @RequiresPermission(MANAGE_FINGERPRINT)
-    public void generateChallenge(int sensorId, GenerateChallengeCallback callback) {
+    public void generateChallenge(int sensorId, int userId, GenerateChallengeCallback callback) {
         if (mService != null) try {
             mGenerateChallengeCallback = callback;
-            mService.generateChallenge(mToken, sensorId, mServiceReceiver,
+            mService.generateChallenge(mToken, sensorId, userId, mServiceReceiver,
                     mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -604,7 +642,7 @@
      * @hide
      */
     @RequiresPermission(MANAGE_FINGERPRINT)
-    public void generateChallenge(GenerateChallengeCallback callback) {
+    public void generateChallenge(int userId, GenerateChallengeCallback callback) {
         final List<FingerprintSensorPropertiesInternal> fingerprintSensorProperties =
                 getSensorPropertiesInternal();
         if (fingerprintSensorProperties.isEmpty()) {
@@ -613,17 +651,36 @@
         }
 
         final int sensorId = fingerprintSensorProperties.get(0).sensorId;
-        generateChallenge(sensorId, callback);
+        generateChallenge(sensorId, userId, callback);
     }
 
     /**
-     * Finishes enrollment and cancels the current auth token.
+     * Revokes the current challenge.
      * @hide
      */
     @RequiresPermission(MANAGE_FINGERPRINT)
-    public void revokeChallenge() {
+    public void revokeChallenge(int userId) {
+        // On HALs with only single in-flight challenge such as IBiometricsFingerprint@2.1,
+        // this parameter is ignored.
+        revokeChallenge(userId, 0L);
+    }
+
+    /**
+     * Revokes the specified challenge.
+     * @hide
+     */
+    @RequiresPermission(MANAGE_FINGERPRINT)
+    public void revokeChallenge(int userId, long challenge) {
         if (mService != null) try {
-            mService.revokeChallenge(mToken, mContext.getOpPackageName());
+            final List<FingerprintSensorPropertiesInternal> fingerprintSensorProperties =
+                    getSensorPropertiesInternal();
+            if (fingerprintSensorProperties.isEmpty()) {
+                Slog.e(TAG, "No sensors");
+                return;
+            }
+            final int sensorId = fingerprintSensorProperties.get(0).sensorId;
+            mService.revokeChallenge(mToken, sensorId, userId, mContext.getOpPackageName(),
+                    challenge);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -761,14 +818,14 @@
      * @hide
      */
     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
-    public void onFingerDown(int sensorId, int x, int y, float minor, float major) {
+    public void onPointerDown(int sensorId, int x, int y, float minor, float major) {
         if (mService == null) {
             Slog.w(TAG, "onFingerDown: no fingerprint service");
             return;
         }
 
         try {
-            mService.onFingerDown(sensorId, x, y, minor, major);
+            mService.onPointerDown(sensorId, x, y, minor, major);
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
         }
@@ -778,14 +835,14 @@
      * @hide
      */
     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
-    public void onFingerUp(int sensorId) {
+    public void onPointerUp(int sensorId) {
         if (mService == null) {
             Slog.w(TAG, "onFingerDown: no fingerprint service");
             return;
         }
 
         try {
-            mService.onFingerUp(sensorId);
+            mService.onPointerUp(sensorId);
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
         }
@@ -848,21 +905,6 @@
     }
 
     /**
-     * Retrieves a list of properties for all fingerprint sensors on the device.
-     * @hide
-     */
-    @NonNull
-    public List<FingerprintSensorProperties> getSensorProperties() {
-        final List<FingerprintSensorProperties> properties = new ArrayList<>();
-        final List<FingerprintSensorPropertiesInternal> internalProperties
-                = getSensorPropertiesInternal();
-        for (FingerprintSensorPropertiesInternal internalProp : internalProperties) {
-            properties.add(FingerprintSensorProperties.from(internalProp));
-        }
-        return properties;
-    }
-
-    /**
      * Get statically configured sensor properties.
      * @hide
      */
diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
index d5ce9e3..1f896cd 100644
--- a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
+++ b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
@@ -85,4 +85,9 @@
                 return false;
         }
     }
+
+    @Override
+    public String toString() {
+        return "ID: " + sensorId + ", Strength: " + sensorStrength + ", Type: " + sensorType;
+    }
 }
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 7af7380..9566837 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -17,6 +17,7 @@
 
 import android.hardware.biometrics.IBiometricSensorReceiver;
 import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.hardware.biometrics.ITestSession;
 import android.hardware.fingerprint.IFingerprintClientActiveCallback;
 import android.hardware.fingerprint.IFingerprintServiceReceiver;
 import android.hardware.fingerprint.IUdfpsOverlayController;
@@ -30,6 +31,10 @@
  * @hide
  */
 interface IFingerprintService {
+
+    // Creates a test session with the specified sensorId
+    ITestSession createTestSession(int sensorId, String opPackageName);
+
     // Retrieve static sensor properties for all fingerprint sensors
     List<FingerprintSensorPropertiesInternal> getSensorPropertiesInternal(String opPackageName);
 
@@ -88,10 +93,10 @@
     boolean isHardwareDetected(String opPackageName);
 
     // Get a pre-enrollment authentication token
-    void generateChallenge(IBinder token, int sensorId, IFingerprintServiceReceiver receiver, String opPackageName);
+    void generateChallenge(IBinder token, int sensorId, int userId, IFingerprintServiceReceiver receiver, String opPackageName);
 
     // Finish an enrollment sequence and invalidate the authentication token
-    void revokeChallenge(IBinder token, String opPackageName);
+    void revokeChallenge(IBinder token, int sensorId, int userId, String opPackageName, long challenge);
 
     // Determine if a user has at least one enrolled fingerprint
     boolean hasEnrolledFingerprints(int userId, String opPackageName);
@@ -121,10 +126,10 @@
     void initializeConfiguration(int sensorId, int strength);
 
     // Notifies about a finger touching the sensor area.
-    void onFingerDown(int sensorId, int x, int y, float minor, float major);
+    void onPointerDown(int sensorId, int x, int y, float minor, float major);
 
     // Notifies about a finger leaving the sensor area.
-    void onFingerUp(int sensorId);
+    void onPointerUp(int sensorId);
 
     // Sets the controller for managing the UDFPS overlay.
     void setUdfpsOverlayController(in IUdfpsOverlayController controller);
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index da4d8e0..401bb9d 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -28,7 +28,6 @@
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
-import android.annotation.TestApi;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.Binder;
@@ -60,7 +59,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 @SystemService(Context.HDMI_CONTROL_SERVICE)
 @RequiresFeature(PackageManager.FEATURE_HDMI_CEC)
 public final class HdmiControlManager {
@@ -321,6 +319,29 @@
     /** The HdmiControlService will be disabled to standby. */
     public static final int CONTROL_STATE_CHANGED_REASON_STANDBY = 3;
 
+    // -- Whether the HDMI CEC is enabled or disabled.
+    /**
+     * HDMI CEC enabled.
+     *
+     * @hide
+     */
+    public static final String HDMI_CEC_CONTROL_ENABLED = "1";
+    /**
+     * HDMI CEC disabled.
+     *
+     * @hide
+     */
+    public static final String HDMI_CEC_CONTROL_DISABLED = "0";
+    /**
+     * @hide
+     */
+    @StringDef({
+            HDMI_CEC_CONTROL_ENABLED,
+            HDMI_CEC_CONTROL_DISABLED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface HdmiCecControl {}
+
     // -- Which devices the playback device can send a <Standby> message to upon going to sleep.
     /**
      * Send <Standby> to TV only.
@@ -349,8 +370,91 @@
             SEND_STANDBY_ON_SLEEP_NONE
     })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface StandbyBehavior {
-    }
+    public @interface StandbyBehavior {}
+
+    // -- Which power state action should be taken when Active Source is lost.
+    /**
+     * No action to be taken.
+     *
+     * @hide
+     */
+    public static final String POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_NONE = "none";
+    /**
+     * Go to standby immediately.
+     *
+     * @hide
+     */
+    public static final String POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW = "standby_now";
+    /**
+     * @hide
+     */
+    @StringDef({
+            POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_NONE,
+            POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ActiveSourceLostBehavior {}
+
+    // -- Whether System Audio Mode muting is enabled or disabled.
+    /**
+     * System Audio Mode muting enabled.
+     *
+     * @hide
+     */
+    public static final String SYSTEM_AUDIO_MODE_MUTING_ENABLED = "1";
+    /**
+     * System Audio Mode muting disabled.
+     *
+     * @hide
+     */
+    public static final String SYSTEM_AUDIO_MODE_MUTING_DISABLED = "0";
+    /**
+     * @hide
+     */
+    @StringDef({
+            SYSTEM_AUDIO_MODE_MUTING_ENABLED,
+            SYSTEM_AUDIO_MODE_MUTING_DISABLED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SystemAudioModeMuting {}
+
+    // -- Settings available in the CEC Configuration.
+    /**
+     * Name of a setting deciding whether the CEC is enabled.
+     *
+     * @hide
+     */
+    public static final String CEC_SETTING_NAME_HDMI_CEC_ENABLED = "hdmi_cec_enabled";
+    /**
+     * Name of a setting deciding on the Standby message behaviour on sleep.
+     *
+     * @hide
+     */
+    public static final String CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP = "send_standby_on_sleep";
+    /**
+     * Name of a setting deciding on power state action when losing Active Source.
+     *
+     * @hide
+     */
+    public static final String CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST =
+            "power_state_change_on_active_source_lost";
+    /**
+     * Name of a setting deciding whether System Audio Muting is allowed.
+     *
+     * @hide
+     */
+    public static final String CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING =
+            "system_audio_mode_muting";
+    /**
+     * @hide
+     */
+    @StringDef({
+        CEC_SETTING_NAME_HDMI_CEC_ENABLED,
+        CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP,
+        CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+        CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
+    })
+    public @interface CecSettingName {}
 
     // True if we have a logical device of type playback hosted in the system.
     private final boolean mHasPlaybackDevice;
@@ -1006,8 +1110,12 @@
         return new IHdmiHotplugEventListener.Stub() {
             @Override
             public void onReceived(HdmiHotplugEvent event) {
-                Binder.clearCallingIdentity();
-                executor.execute(() -> listener.onReceived(event));
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(() -> listener.onReceived(event));
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         };
     }
@@ -1098,8 +1206,12 @@
         return new IHdmiControlStatusChangeListener.Stub() {
             @Override
             public void onStatusChange(boolean isCecEnabled, boolean isCecAvailable) {
-                Binder.clearCallingIdentity();
-                executor.execute(() -> listener.onStatusChange(isCecEnabled, isCecAvailable));
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(() -> listener.onStatusChange(isCecEnabled, isCecAvailable));
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         };
     }
@@ -1171,9 +1283,243 @@
         return new android.hardware.hdmi.IHdmiCecVolumeControlFeatureListener.Stub() {
             @Override
             public void onHdmiCecVolumeControlFeature(boolean enabled) {
-                Binder.clearCallingIdentity();
-                executor.execute(() -> listener.onHdmiCecVolumeControlFeature(enabled));
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(() -> listener.onHdmiCecVolumeControlFeature(enabled));
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         };
     }
+
+    /**
+     * Get a set of user-modifiable settings.
+     *
+     * @return a set of user-modifiable settings.
+     * @throws RuntimeException when the HdmiControlService is not available.
+     *
+     * @hide
+     */
+    @NonNull
+    @CecSettingName
+    @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+    public List<String> getUserCecSettings() {
+        if (mService == null) {
+            Log.e(TAG, "HdmiControlService is not available");
+            throw new RuntimeException("HdmiControlService is not available");
+        }
+        try {
+            return mService.getUserCecSettings();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get a set of allowed values for a settings.
+     *
+     * @param name name of the setting
+     * @return a set of allowed values for a settings. {@code null} on failure.
+     * @throws IllegalArgumentException when setting {@code name} does not exist.
+     * @throws RuntimeException when the HdmiControlService is not available.
+     *
+     * @hide
+     */
+    @NonNull
+    @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+    public List<String> getAllowedCecSettingValues(@NonNull @CecSettingName String name) {
+        if (mService == null) {
+            Log.e(TAG, "HdmiControlService is not available");
+            throw new RuntimeException("HdmiControlService is not available");
+        }
+        try {
+            return mService.getAllowedCecSettingValues(name);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Set the 'hdmi_cec_enabled' option.
+     *
+     * @param value the desired value
+     * @throws IllegalArgumentException when the new value is not allowed.
+     * @throws RuntimeException when the HdmiControlService is not available.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+    public void setHdmiCecEnabled(@NonNull @HdmiCecControl String value) {
+        if (mService == null) {
+            Log.e(TAG, "HdmiControlService is not available");
+            throw new RuntimeException("HdmiControlService is not available");
+        }
+        try {
+            mService.setCecSettingValue(CEC_SETTING_NAME_HDMI_CEC_ENABLED, value);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get the value of 'hdmi_cec_enabled' option.
+     *
+     * @return the current value.
+     * @throws RuntimeException when the HdmiControlService is not available.
+     *
+     * @hide
+     */
+    @NonNull
+    @HdmiCecControl
+    @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+    public String getHdmiCecEnabled() {
+        if (mService == null) {
+            Log.e(TAG, "HdmiControlService is not available");
+            throw new RuntimeException("HdmiControlService is not available");
+        }
+        try {
+            return mService.getCecSettingValue(CEC_SETTING_NAME_HDMI_CEC_ENABLED);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Set the 'send_standby_on_sleep' option.
+     *
+     * @param value the desired value
+     * @throws IllegalArgumentException when the new value is not allowed.
+     * @throws RuntimeException when the HdmiControlService is not available.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+    public void setSendStandbyOnSleep(@NonNull @StandbyBehavior String value) {
+        if (mService == null) {
+            Log.e(TAG, "HdmiControlService is not available");
+            throw new RuntimeException("HdmiControlService is not available");
+        }
+        try {
+            mService.setCecSettingValue(CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP, value);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get the value of 'send_standby_on_sleep' option.
+     *
+     * @return the current value.
+     * @throws RuntimeException when the HdmiControlService is not available.
+     *
+     * @hide
+     */
+    @NonNull
+    @StandbyBehavior
+    @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+    public String getSendStandbyOnSleep() {
+        if (mService == null) {
+            Log.e(TAG, "HdmiControlService is not available");
+            throw new RuntimeException("HdmiControlService is not available");
+        }
+        try {
+            return mService.getCecSettingValue(CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Set the 'power_state_change_on_active_source_lost' option.
+     *
+     * @param value the desired value
+     * @throws IllegalArgumentException when the new value is not allowed
+     * @throws RuntimeException when the HdmiControlService is not available.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+    public void setPowerStateChangeOnActiveSourceLost(
+            @NonNull @ActiveSourceLostBehavior String value) {
+        if (mService == null) {
+            Log.e(TAG, "HdmiControlService is not available");
+            throw new RuntimeException("HdmiControlService is not available");
+        }
+        try {
+            mService.setCecSettingValue(
+                    CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST, value);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get the value of 'power_state_change_on_active_source_lost' option.
+     *
+     * @return the current value.
+     * @throws RuntimeException when the HdmiControlService is not available.
+     *
+     * @hide
+     */
+    @NonNull
+    @ActiveSourceLostBehavior
+    @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+    public String getPowerStateChangeOnActiveSourceLost() {
+        if (mService == null) {
+            Log.e(TAG, "HdmiControlService is not available");
+            throw new RuntimeException("HdmiControlService is not available");
+        }
+        try {
+            return mService.getCecSettingValue(
+                    CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Set the 'system_audio_mode_muting' option.
+     *
+     * @param value the desired value
+     * @throws IllegalArgumentException when the new value is not allowed.
+     * @throws RuntimeException when the HdmiControlService is not available.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+    public void setSystemAudioModeMuting(@NonNull @SystemAudioModeMuting String value) {
+        if (mService == null) {
+            Log.e(TAG, "HdmiControlService is not available");
+            throw new RuntimeException("HdmiControlService is not available");
+        }
+        try {
+            mService.setCecSettingValue(CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, value);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get the value of 'system_audio_mode_muting' option.
+     *
+     * @return the current value.
+     * @throws RuntimeException when the HdmiControlService is not available.
+     *
+     * @hide
+     */
+    @NonNull
+    @SystemAudioModeMuting
+    @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+    public String getSystemAudioModeMuting() {
+        if (mService == null) {
+            Log.e(TAG, "HdmiControlService is not available");
+            throw new RuntimeException("HdmiControlService is not available");
+        }
+        try {
+            return mService.getCecSettingValue(CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java b/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
index 0289635..22d4640 100644
--- a/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
+++ b/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.TestApi;
 
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -293,6 +294,26 @@
                 IHdmiCecVolumeControlFeatureListener listener) {
             HdmiControlServiceWrapper.this.removeHdmiCecVolumeControlFeatureListener(listener);
         }
+
+        @Override
+        public List<String> getUserCecSettings() {
+            return HdmiControlServiceWrapper.this.getUserCecSettings();
+        }
+
+        @Override
+        public List<String> getAllowedCecSettingValues(String name) {
+            return HdmiControlServiceWrapper.this.getAllowedCecSettingValues(name);
+        }
+
+        @Override
+        public String getCecSettingValue(String name) {
+            return HdmiControlServiceWrapper.this.getCecSettingValue(name);
+        }
+
+        @Override
+        public void setCecSettingValue(String name, String value) {
+            HdmiControlServiceWrapper.this.setCecSettingValue(name, value);
+        }
     };
 
     @BinderThread
@@ -466,4 +487,22 @@
     /** @hide */
     public void removeHdmiCecVolumeControlFeatureListener(
             IHdmiCecVolumeControlFeatureListener listener) {}
+
+    /** @hide */
+    public List<String> getUserCecSettings() {
+        return new ArrayList<>();
+    }
+
+    /** @hide */
+    public List<String> getAllowedCecSettingValues(String name) {
+        return new ArrayList<>();
+    }
+
+    /** @hide */
+    public String getCecSettingValue(String name) {
+        return "";
+    }
+
+    /** @hide */
+    public void setCecSettingValue(String name, String value) {}
 }
diff --git a/core/java/android/hardware/hdmi/HdmiDeviceInfo.java b/core/java/android/hardware/hdmi/HdmiDeviceInfo.java
index 55b0726..3fd20f1 100644
--- a/core/java/android/hardware/hdmi/HdmiDeviceInfo.java
+++ b/core/java/android/hardware/hdmi/HdmiDeviceInfo.java
@@ -463,7 +463,7 @@
     @NonNull
     @Override
     public String toString() {
-        StringBuffer s = new StringBuffer();
+        StringBuilder s = new StringBuilder();
         switch (mHdmiDeviceType) {
             case HDMI_DEVICE_TYPE_CEC:
                 s.append("CEC: ");
diff --git a/core/java/android/hardware/hdmi/HdmiPortInfo.java b/core/java/android/hardware/hdmi/HdmiPortInfo.java
index 52c3628..e4b311a 100644
--- a/core/java/android/hardware/hdmi/HdmiPortInfo.java
+++ b/core/java/android/hardware/hdmi/HdmiPortInfo.java
@@ -18,7 +18,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -29,7 +28,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class HdmiPortInfo implements Parcelable {
     /** HDMI port type: Input */
     public static final int PORT_INPUT = 0;
@@ -171,7 +169,7 @@
     @NonNull
     @Override
     public String toString() {
-        StringBuffer s = new StringBuffer();
+        StringBuilder s = new StringBuilder();
         s.append("port_id: ").append(mId).append(", ");
         s.append("type: ").append((mType == PORT_INPUT) ? "HDMI_IN" : "HDMI_OUT").append(", ");
         s.append("address: ").append(String.format("0x%04x", mAddress)).append(", ");
diff --git a/core/java/android/hardware/hdmi/HdmiSwitchClient.java b/core/java/android/hardware/hdmi/HdmiSwitchClient.java
index 4685e1e..cbfbe39 100644
--- a/core/java/android/hardware/hdmi/HdmiSwitchClient.java
+++ b/core/java/android/hardware/hdmi/HdmiSwitchClient.java
@@ -18,7 +18,6 @@
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.hardware.hdmi.HdmiControlManager.ControlCallbackResult;
 import android.os.Binder;
 import android.os.RemoteException;
@@ -39,7 +38,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public class HdmiSwitchClient extends HdmiClient {
 
     private static final String TAG = "HdmiSwitchClient";
diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
index 4c724ef..6df164b 100644
--- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
@@ -87,4 +87,8 @@
     boolean isHdmiCecVolumeControlEnabled();
     void reportAudioStatus(int deviceType, int volume, int maxVolume, boolean isMute);
     void setSystemAudioModeOnForAudioOnlySource();
+    List<String> getUserCecSettings();
+    List<String> getAllowedCecSettingValues(String name);
+    String getCecSettingValue(String name);
+    void setCecSettingValue(String name, String value);
 }
diff --git a/core/java/android/hardware/input/InputDeviceIdentifier.java b/core/java/android/hardware/input/InputDeviceIdentifier.java
index 26f2393..c673e7a 100644
--- a/core/java/android/hardware/input/InputDeviceIdentifier.java
+++ b/core/java/android/hardware/input/InputDeviceIdentifier.java
@@ -16,12 +16,13 @@
 
 package android.hardware.input;
 
-import java.util.Objects;
-
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
 
+import java.util.Objects;
+
 /**
  * Wrapper for passing identifying information for input devices.
  *
@@ -69,7 +70,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) return true;
         if (o == null || !(o instanceof InputDeviceIdentifier)) return false;
 
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index dd820fa..25fec32 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -16,26 +16,32 @@
 
 package android.hardware.input;
 
+import android.Manifest;
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
+import android.compat.annotation.ChangeId;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
-import android.media.AudioAttributes;
 import android.os.Binder;
+import android.os.BlockUntrustedTouchesMode;
 import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.InputEventInjectionSync;
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ServiceManager.ServiceNotFoundException;
 import android.os.SystemClock;
+import android.os.VibrationAttributes;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
 import android.provider.Settings;
@@ -48,8 +54,10 @@
 import android.view.MotionEvent;
 import android.view.PointerIcon;
 import android.view.VerifiedInputEvent;
+import android.view.WindowManager.LayoutParams;
 
 import com.android.internal.os.SomeArgs;
+import com.android.internal.util.ArrayUtils;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -69,6 +77,13 @@
     private static final int MSG_DEVICE_REMOVED = 2;
     private static final int MSG_DEVICE_CHANGED = 3;
 
+    /** @hide */
+    public static final int[] BLOCK_UNTRUSTED_TOUCHES_MODES = {
+            BlockUntrustedTouchesMode.DISABLED,
+            BlockUntrustedTouchesMode.PERMISSIVE,
+            BlockUntrustedTouchesMode.BLOCK
+    };
+
     private static InputManager sInstance;
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
@@ -168,11 +183,37 @@
     public static final int DEFAULT_POINTER_SPEED = 0;
 
     /**
+     * The maximum allowed obscuring opacity by UID to propagate touches (0 <= x <= 1).
+     * @hide
+     */
+    public static final float DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH = .8f;
+
+    /**
+     * Default mode of the block untrusted touches mode feature.
+     * @hide
+     */
+    @BlockUntrustedTouchesMode
+    public static final int DEFAULT_BLOCK_UNTRUSTED_TOUCHES_MODE =
+            BlockUntrustedTouchesMode.DISABLED;
+
+    /**
+     * Prevent touches from being consumed by apps if these touches passed through a non-trusted
+     * window from a different UID and are considered unsafe.
+     *
+     * TODO(b/158002302): Turn the feature on by default
+     *
+     * @hide
+     */
+    @TestApi
+    @ChangeId
+    public static final long BLOCK_UNTRUSTED_TOUCHES = 158002302L;
+
+    /**
      * Input Event Injection Synchronization Mode: None.
      * Never blocks.  Injection is asynchronous and is assumed always to be successful.
      * @hide
      */
-    public static final int INJECT_INPUT_EVENT_MODE_ASYNC = 0; // see InputDispatcher.h
+    public static final int INJECT_INPUT_EVENT_MODE_ASYNC = InputEventInjectionSync.NONE;
 
     /**
      * Input Event Injection Synchronization Mode: Wait for result.
@@ -182,7 +223,8 @@
      * by the application.
      * @hide
      */
-    public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT = 1;  // see InputDispatcher.h
+    public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT =
+            InputEventInjectionSync.WAIT_FOR_RESULT;
 
     /**
      * Input Event Injection Synchronization Mode: Wait for finish.
@@ -190,7 +232,8 @@
      * @hide
      */
     @UnsupportedAppUsage
-    public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH = 2;  // see InputDispatcher.h
+    public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH =
+            InputEventInjectionSync.WAIT_FOR_FINISHED;
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
@@ -832,6 +875,103 @@
     }
 
     /**
+     * Returns the maximum allowed obscuring opacity by UID to propagate touches.
+     *
+     * For certain window types (eg. SAWs), the decision of honoring {@link LayoutParams
+     * #FLAG_NOT_TOUCHABLE} or not depends on the combined obscuring opacity of the windows
+     * above the touch-consuming window.
+     *
+     * @see #setMaximumObscuringOpacityForTouch(Context, float)
+     *
+     * @hide
+     */
+    @TestApi
+    public float getMaximumObscuringOpacityForTouch(@NonNull Context context) {
+        return Settings.Global.getFloat(context.getContentResolver(),
+                Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH,
+                DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH);
+    }
+
+    /**
+     * Sets the maximum allowed obscuring opacity by UID to propagate touches.
+     *
+     * For certain window types (eg. SAWs), the decision of honoring {@link LayoutParams
+     * #FLAG_NOT_TOUCHABLE} or not depends on the combined obscuring opacity of the windows
+     * above the touch-consuming window.
+     *
+     * For a certain UID:
+     * <ul>
+     *     <li>If it's the same as the UID of the touch-consuming window, allow it to propagate
+     *     the touch.
+     *     <li>Otherwise take all its windows of eligible window types above the touch-consuming
+     *     window, compute their combined obscuring opacity considering that {@code
+     *     opacity(A, B) = 1 - (1 - opacity(A))*(1 - opacity(B))}. If the computed value is
+     *     lesser than or equal to this setting and there are no other windows preventing the
+     *     touch, allow the UID to propagate the touch.
+     * </ul>
+     *
+     * This value should be between 0 (inclusive) and 1 (inclusive).
+     *
+     * @see #getMaximumObscuringOpacityForTouch(Context)
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+    public void setMaximumObscuringOpacityForTouch(@NonNull Context context, float opacity) {
+        if (opacity < 0 || opacity > 1) {
+            throw new IllegalArgumentException(
+                    "Maximum obscuring opacity for touch should be >= 0 and <= 1");
+        }
+        Settings.Global.putFloat(context.getContentResolver(),
+                Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH, opacity);
+    }
+
+    /**
+     * Returns the current mode of the block untrusted touches feature, one of:
+     * <ul>
+     *     <li>{@link BlockUntrustedTouchesMode#DISABLED}
+     *     <li>{@link BlockUntrustedTouchesMode#PERMISSIVE}
+     *     <li>{@link BlockUntrustedTouchesMode#BLOCK}
+     * </ul>
+     *
+     * @hide
+     */
+    @TestApi
+    @BlockUntrustedTouchesMode
+    public int getBlockUntrustedTouchesMode(@NonNull Context context) {
+        int mode = Settings.Global.getInt(context.getContentResolver(),
+                Settings.Global.BLOCK_UNTRUSTED_TOUCHES_MODE, DEFAULT_BLOCK_UNTRUSTED_TOUCHES_MODE);
+        if (!ArrayUtils.contains(BLOCK_UNTRUSTED_TOUCHES_MODES, mode)) {
+            Log.w(TAG, "Unknown block untrusted touches feature mode " + mode + ", using "
+                    + "default " + DEFAULT_BLOCK_UNTRUSTED_TOUCHES_MODE);
+            return DEFAULT_BLOCK_UNTRUSTED_TOUCHES_MODE;
+        }
+        return mode;
+    }
+
+    /**
+     * Sets the mode of the block untrusted touches feature to one of:
+     * <ul>
+     *     <li>{@link BlockUntrustedTouchesMode#DISABLED}
+     *     <li>{@link BlockUntrustedTouchesMode#PERMISSIVE}
+     *     <li>{@link BlockUntrustedTouchesMode#BLOCK}
+     * </ul>
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+    public void setBlockUntrustedTouchesMode(@NonNull Context context,
+            @BlockUntrustedTouchesMode int mode) {
+        if (!ArrayUtils.contains(BLOCK_UNTRUSTED_TOUCHES_MODES, mode)) {
+            throw new IllegalArgumentException("Invalid feature mode " + mode);
+        }
+        Settings.Global.putInt(context.getContentResolver(),
+                Settings.Global.BLOCK_UNTRUSTED_TOUCHES_MODE, mode);
+    }
+
+    /**
      * Queries the framework about whether any physical keys exist on the
      * any keyboard attached to the device that are capable of producing the given
      * array of key codes.
@@ -885,9 +1025,9 @@
      *
      * @param event The event to inject.
      * @param mode The synchronization mode.  One of:
-     * {@link #INJECT_INPUT_EVENT_MODE_ASYNC},
-     * {@link #INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT}, or
-     * {@link #INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH}.
+     * {@link android.os.InputEventInjectionSync.NONE},
+     * {@link android.os.InputEventInjectionSync.WAIT_FOR_RESULT}, or
+     * {@link android.os.InputEventInjectionSync.WAIT_FOR_FINISHED}.
      * @return True if input event injection succeeded.
      *
      * @hide
@@ -897,9 +1037,9 @@
         if (event == null) {
             throw new IllegalArgumentException("event must not be null");
         }
-        if (mode != INJECT_INPUT_EVENT_MODE_ASYNC
-                && mode != INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH
-                && mode != INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT) {
+        if (mode != InputEventInjectionSync.NONE
+                && mode != InputEventInjectionSync.WAIT_FOR_FINISHED
+                && mode != InputEventInjectionSync.WAIT_FOR_RESULT) {
             throw new IllegalArgumentException("mode is invalid");
         }
 
@@ -1295,8 +1435,8 @@
          * @hide
          */
         @Override
-        public void vibrate(int uid, String opPkg, VibrationEffect effect,
-                String reason, AudioAttributes attributes) {
+        public void vibrate(int uid, String opPkg, @NonNull VibrationEffect effect,
+                String reason, @NonNull VibrationAttributes attributes) {
             try {
                 mIm.vibrate(mDeviceId, effect, mToken);
             } catch (RemoteException ex) {
diff --git a/core/java/android/hardware/input/TouchCalibration.java b/core/java/android/hardware/input/TouchCalibration.java
index 7a6eba2..cec7cfc 100644
--- a/core/java/android/hardware/input/TouchCalibration.java
+++ b/core/java/android/hardware/input/TouchCalibration.java
@@ -16,6 +16,7 @@
 
 package android.hardware.input;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -97,7 +98,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (obj == this) {
             return true;
         } else if (obj instanceof TouchCalibration) {
diff --git a/core/java/android/hardware/lights/Light.java b/core/java/android/hardware/lights/Light.java
index e90b57c..da27018 100644
--- a/core/java/android/hardware/lights/Light.java
+++ b/core/java/android/hardware/lights/Light.java
@@ -17,8 +17,8 @@
 package android.hardware.lights;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -28,7 +28,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class Light implements Parcelable {
     private final int mId;
     private final int mOrdinal;
@@ -78,7 +77,7 @@
             };
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (obj instanceof Light) {
             Light light = (Light) obj;
             return mId == light.mId && mOrdinal == light.mOrdinal && mType == light.mType;
diff --git a/core/java/android/hardware/lights/LightState.java b/core/java/android/hardware/lights/LightState.java
index e55aa70..cd39e6d 100644
--- a/core/java/android/hardware/lights/LightState.java
+++ b/core/java/android/hardware/lights/LightState.java
@@ -19,7 +19,6 @@
 import android.annotation.ColorInt;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -36,7 +35,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class LightState implements Parcelable {
     private final int mColor;
 
diff --git a/core/java/android/hardware/lights/LightsManager.java b/core/java/android/hardware/lights/LightsManager.java
index 8cd2312..33e5fca 100644
--- a/core/java/android/hardware/lights/LightsManager.java
+++ b/core/java/android/hardware/lights/LightsManager.java
@@ -45,7 +45,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 @SystemService(Context.LIGHTS_SERVICE)
 public final class LightsManager {
     private static final String TAG = "LightsManager";
diff --git a/core/java/android/hardware/lights/LightsRequest.java b/core/java/android/hardware/lights/LightsRequest.java
index 5c4fc67..a318992 100644
--- a/core/java/android/hardware/lights/LightsRequest.java
+++ b/core/java/android/hardware/lights/LightsRequest.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.util.SparseArray;
 
 import com.android.internal.util.Preconditions;
@@ -29,7 +28,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class LightsRequest {
 
     /** Visible to {@link LightsManager.Session}. */
diff --git a/core/java/android/hardware/location/GeofenceHardwareImpl.java b/core/java/android/hardware/location/GeofenceHardwareImpl.java
index ee2d43c..3715a61 100644
--- a/core/java/android/hardware/location/GeofenceHardwareImpl.java
+++ b/core/java/android/hardware/location/GeofenceHardwareImpl.java
@@ -16,6 +16,7 @@
 
 package android.hardware.location;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.location.IFusedGeofenceHardware;
@@ -878,7 +879,7 @@
         }
 
         @Override
-        public boolean equals(Object obj) {
+        public boolean equals(@Nullable Object obj) {
             if (obj == null) return false;
             if (obj == this) return true;
 
diff --git a/core/java/android/hardware/radio/ProgramList.java b/core/java/android/hardware/radio/ProgramList.java
index ec318b7..3a042a5 100644
--- a/core/java/android/hardware/radio/ProgramList.java
+++ b/core/java/android/hardware/radio/ProgramList.java
@@ -466,7 +466,7 @@
         }
 
         @Override
-        public boolean equals(Object obj) {
+        public boolean equals(@Nullable Object obj) {
             if (this == obj) return true;
             if (!(obj instanceof Chunk)) return false;
             Chunk other = (Chunk) obj;
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index 0f1c2a5..e58403f 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -67,7 +67,6 @@
  *
  * @hide
  */
-@TestApi
 @SystemApi
 public class SoundTrigger {
     private static final String TAG = "SoundTrigger";
@@ -522,7 +521,7 @@
         }
 
         @Override
-        public boolean equals(Object obj) {
+        public boolean equals(@Nullable Object obj) {
             if (this == obj) {
                 return true;
             }
@@ -699,7 +698,7 @@
         }
 
         @Override
-        public boolean equals(Object obj) {
+        public boolean equals(@Nullable Object obj) {
             if (this == obj) {
                 return true;
             }
@@ -840,7 +839,7 @@
         }
 
         @Override
-        public boolean equals(Object obj) {
+        public boolean equals(@Nullable Object obj) {
             if (this == obj) {
                 return true;
             }
@@ -1527,7 +1526,7 @@
         }
 
         @Override
-        public boolean equals(Object obj) {
+        public boolean equals(@Nullable Object obj) {
             if (this == obj)
                 return true;
             if (obj == null)
@@ -1630,7 +1629,7 @@
         }
 
         @Override
-        public boolean equals(Object obj) {
+        public boolean equals(@Nullable Object obj) {
             if (this == obj)
                 return true;
             if (obj == null)
@@ -1752,7 +1751,7 @@
         }
 
         @Override
-        public boolean equals(Object obj) {
+        public boolean equals(@Nullable Object obj) {
             if (this == obj)
                 return true;
             if (!super.equals(obj))
@@ -1819,7 +1818,7 @@
         }
 
         @Override
-        public boolean equals(Object obj) {
+        public boolean equals(@Nullable Object obj) {
             if (this == obj)
                 return true;
             if (obj == null)
@@ -1909,7 +1908,7 @@
         }
 
         @Override
-        public boolean equals(Object obj) {
+        public boolean equals(@Nullable Object obj) {
             if (this == obj)
                 return true;
             if (obj == null)
diff --git a/core/java/android/hardware/usb/AccessoryFilter.java b/core/java/android/hardware/usb/AccessoryFilter.java
index f22dad4..8345ff3 100644
--- a/core/java/android/hardware/usb/AccessoryFilter.java
+++ b/core/java/android/hardware/usb/AccessoryFilter.java
@@ -17,6 +17,7 @@
 package android.hardware.usb;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.service.usb.UsbAccessoryFilterProto;
 
 import com.android.internal.util.dump.DualDumpOutputStream;
@@ -120,7 +121,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         // can't compare if we have wildcard strings
         if (mManufacturer == null || mModel == null || mVersion == null) {
             return false;
diff --git a/core/java/android/hardware/usb/DeviceFilter.java b/core/java/android/hardware/usb/DeviceFilter.java
index da979c0c..66b0a42 100644
--- a/core/java/android/hardware/usb/DeviceFilter.java
+++ b/core/java/android/hardware/usb/DeviceFilter.java
@@ -17,6 +17,7 @@
 package android.hardware.usb;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.service.usb.UsbDeviceFilterProto;
 import android.util.Slog;
 
@@ -238,7 +239,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         // can't compare if we have wildcard strings
         if (mVendorId == -1 || mProductId == -1 ||
                 mClass == -1 || mSubclass == -1 || mProtocol == -1) {
diff --git a/core/java/android/hardware/usb/UsbAccessory.java b/core/java/android/hardware/usb/UsbAccessory.java
index f4cfc74..47c6978 100644
--- a/core/java/android/hardware/usb/UsbAccessory.java
+++ b/core/java/android/hardware/usb/UsbAccessory.java
@@ -186,7 +186,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (obj instanceof UsbAccessory) {
             UsbAccessory accessory = (UsbAccessory)obj;
             return (compare(mManufacturer, accessory.getManufacturer()) &&
diff --git a/core/java/android/hardware/usb/UsbDevice.java b/core/java/android/hardware/usb/UsbDevice.java
index a0f64cd..71074df 100644
--- a/core/java/android/hardware/usb/UsbDevice.java
+++ b/core/java/android/hardware/usb/UsbDevice.java
@@ -308,7 +308,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (o instanceof UsbDevice) {
             return ((UsbDevice)o).mName.equals(mName);
         } else if (o instanceof String) {
diff --git a/core/java/android/inputmethodservice/AbstractInputMethodService.java b/core/java/android/inputmethodservice/AbstractInputMethodService.java
index d7ca63a..3ca5207 100644
--- a/core/java/android/inputmethodservice/AbstractInputMethodService.java
+++ b/core/java/android/inputmethodservice/AbstractInputMethodService.java
@@ -193,7 +193,17 @@
      * needed for a new client of the input method.
      */
     public abstract AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface();
-    
+
+    /**
+     * Dumps the internal state of IME to a protocol buffer output stream initialized using the
+     * given {@link FileDescriptor}.
+     *
+     * @param fd The file descriptor to which proto dump should be written.
+     * @param args The arguments passed to the dump method.
+     * @hide
+     */
+    abstract void dumpProtoInternal(FileDescriptor fd, String[] args);
+
     /**
      * Implement this to handle {@link android.os.Binder#dump Binder.dump()}
      * calls on your input method.
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index a298c85..0512305 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -16,6 +16,8 @@
 
 package android.inputmethodservice;
 
+import static android.util.imetracing.ImeTracing.PROTO_ARG;
+
 import android.annotation.BinderThread;
 import android.annotation.MainThread;
 import android.annotation.Nullable;
@@ -37,8 +39,8 @@
 import android.view.inputmethod.InputMethodSession;
 import android.view.inputmethod.InputMethodSubtype;
 
-import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
 import com.android.internal.inputmethod.CancellationGroup;
+import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
 import com.android.internal.os.HandlerCaller;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.view.IInlineSuggestionsRequestCallback;
@@ -155,9 +157,20 @@
                     return;
                 }
                 SomeArgs args = (SomeArgs)msg.obj;
+                String[] dumpArgs = (String[]) args.arg3;
+                boolean protoDumpRequested = false;
+                for (String arg : dumpArgs) {
+                    if (arg.equals(PROTO_ARG)) {
+                        protoDumpRequested = true;
+                        break;
+                    }
+                }
                 try {
-                    target.dump((FileDescriptor)args.arg1,
-                            (PrintWriter)args.arg2, (String[])args.arg3);
+                    if (protoDumpRequested) {
+                        target.dumpProtoInternal((FileDescriptor) args.arg1, dumpArgs);
+                    } else {
+                        target.dump((FileDescriptor) args.arg1, (PrintWriter) args.arg2, dumpArgs);
+                    }
                 } catch (RuntimeException e) {
                     ((PrintWriter)args.arg2).println("Exception: " + e);
                 }
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index d62d1e1..78cc71a 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -17,6 +17,37 @@
 package android.inputmethodservice;
 
 import static android.graphics.Color.TRANSPARENT;
+import static android.inputmethodservice.InputMethodServiceProto.CANDIDATES_VIEW_STARTED;
+import static android.inputmethodservice.InputMethodServiceProto.CANDIDATES_VISIBILITY;
+import static android.inputmethodservice.InputMethodServiceProto.CAN_PRE_RENDER;
+import static android.inputmethodservice.InputMethodServiceProto.CONFIGURATION;
+import static android.inputmethodservice.InputMethodServiceProto.DECOR_VIEW_VISIBLE;
+import static android.inputmethodservice.InputMethodServiceProto.DECOR_VIEW_WAS_VISIBLE;
+import static android.inputmethodservice.InputMethodServiceProto.EXTRACTED_TOKEN;
+import static android.inputmethodservice.InputMethodServiceProto.EXTRACT_VIEW_HIDDEN;
+import static android.inputmethodservice.InputMethodServiceProto.FULLSCREEN_APPLIED;
+import static android.inputmethodservice.InputMethodServiceProto.INPUT_BINDING;
+import static android.inputmethodservice.InputMethodServiceProto.INPUT_EDITOR_INFO;
+import static android.inputmethodservice.InputMethodServiceProto.INPUT_STARTED;
+import static android.inputmethodservice.InputMethodServiceProto.INPUT_VIEW_STARTED;
+import static android.inputmethodservice.InputMethodServiceProto.IN_SHOW_WINDOW;
+import static android.inputmethodservice.InputMethodServiceProto.IS_FULLSCREEN;
+import static android.inputmethodservice.InputMethodServiceProto.IS_INPUT_VIEW_SHOWN;
+import static android.inputmethodservice.InputMethodServiceProto.IS_PRE_RENDERED;
+import static android.inputmethodservice.InputMethodServiceProto.InsetsProto.CONTENT_TOP_INSETS;
+import static android.inputmethodservice.InputMethodServiceProto.InsetsProto.TOUCHABLE_INSETS;
+import static android.inputmethodservice.InputMethodServiceProto.InsetsProto.TOUCHABLE_REGION;
+import static android.inputmethodservice.InputMethodServiceProto.InsetsProto.VISIBLE_TOP_INSETS;
+import static android.inputmethodservice.InputMethodServiceProto.LAST_COMPUTED_INSETS;
+import static android.inputmethodservice.InputMethodServiceProto.LAST_SHOW_INPUT_REQUESTED;
+import static android.inputmethodservice.InputMethodServiceProto.SETTINGS_OBSERVER;
+import static android.inputmethodservice.InputMethodServiceProto.SHOW_INPUT_FLAGS;
+import static android.inputmethodservice.InputMethodServiceProto.SHOW_INPUT_REQUESTED;
+import static android.inputmethodservice.InputMethodServiceProto.SOFT_INPUT_WINDOW;
+import static android.inputmethodservice.InputMethodServiceProto.STATUS_ICON;
+import static android.inputmethodservice.InputMethodServiceProto.TOKEN;
+import static android.inputmethodservice.InputMethodServiceProto.VIEWS_CREATED;
+import static android.inputmethodservice.InputMethodServiceProto.WINDOW_VISIBLE;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 import static android.view.WindowInsets.Type.navigationBars;
@@ -59,6 +90,7 @@
 import android.util.Log;
 import android.util.PrintWriterPrinter;
 import android.util.Printer;
+import android.util.proto.ProtoOutputStream;
 import android.view.Gravity;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
@@ -104,6 +136,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Collections;
+import java.util.Objects;
 
 /**
  * InputMethodService provides a standard implementation of an InputMethod,
@@ -1041,6 +1074,15 @@
          * or {@link #TOUCHABLE_INSETS_REGION}.
          */
         public int touchableInsets;
+
+        private void dumpDebug(ProtoOutputStream proto, long fieldId) {
+            final long token = proto.start(fieldId);
+            proto.write(CONTENT_TOP_INSETS, contentTopInsets);
+            proto.write(VISIBLE_TOP_INSETS, visibleTopInsets);
+            proto.write(TOUCHABLE_INSETS, touchableInsets);
+            proto.write(TOUCHABLE_REGION, touchableRegion.toString());
+            proto.end(token);
+        }
     }
 
     /**
@@ -1380,7 +1422,7 @@
     public AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface() {
         return new InputMethodSessionImpl();
     }
-    
+
     public LayoutInflater getLayoutInflater() {
         return mInflater;
     }
@@ -3294,7 +3336,7 @@
         } else {
             p.println("  mInputEditorInfo: null");
         }
-        
+
         p.println("  mShowInputRequested=" + mShowInputRequested
                 + " mLastShowInputRequested=" + mLastShowInputRequested
                 + " mCanPreRender=" + mCanPreRender
@@ -3304,7 +3346,7 @@
                 + " mFullscreenApplied=" + mFullscreenApplied
                 + " mIsFullscreen=" + mIsFullscreen
                 + " mExtractViewHidden=" + mExtractViewHidden);
-        
+
         if (mExtractedText != null) {
             p.println("  mExtractedText:");
             p.println("    text=" + mExtractedText.text.length() + " chars"
@@ -3325,4 +3367,42 @@
                 + " touchableRegion=" + mTmpInsets.touchableRegion);
         p.println(" mSettingsObserver=" + mSettingsObserver);
     }
+
+    /**
+     * @hide
+     */
+    @Override
+    final void dumpProtoInternal(FileDescriptor fd, String[] args) {
+        final ProtoOutputStream proto = new ProtoOutputStream(fd);
+        mWindow.dumpDebug(proto, SOFT_INPUT_WINDOW);
+        proto.write(VIEWS_CREATED, mViewsCreated);
+        proto.write(DECOR_VIEW_VISIBLE, mDecorViewVisible);
+        proto.write(DECOR_VIEW_WAS_VISIBLE, mDecorViewWasVisible);
+        proto.write(WINDOW_VISIBLE, mWindowVisible);
+        proto.write(IN_SHOW_WINDOW, mInShowWindow);
+        proto.write(CONFIGURATION, getResources().getConfiguration().toString());
+        proto.write(TOKEN, Objects.toString(mToken));
+        proto.write(INPUT_BINDING, Objects.toString(mInputBinding));
+        proto.write(INPUT_STARTED, mInputStarted);
+        proto.write(INPUT_VIEW_STARTED, mInputViewStarted);
+        proto.write(CANDIDATES_VIEW_STARTED, mCandidatesViewStarted);
+        if (mInputEditorInfo != null) {
+            mInputEditorInfo.dumpDebug(proto, INPUT_EDITOR_INFO);
+        }
+        proto.write(SHOW_INPUT_REQUESTED, mShowInputRequested);
+        proto.write(LAST_SHOW_INPUT_REQUESTED, mLastShowInputRequested);
+        proto.write(CAN_PRE_RENDER, mCanPreRender);
+        proto.write(IS_PRE_RENDERED, mIsPreRendered);
+        proto.write(SHOW_INPUT_FLAGS, mShowInputFlags);
+        proto.write(CANDIDATES_VISIBILITY, mCandidatesVisibility);
+        proto.write(FULLSCREEN_APPLIED, mFullscreenApplied);
+        proto.write(IS_FULLSCREEN, mIsFullscreen);
+        proto.write(EXTRACT_VIEW_HIDDEN, mExtractViewHidden);
+        proto.write(EXTRACTED_TOKEN, mExtractedToken);
+        proto.write(IS_INPUT_VIEW_SHOWN, mIsInputViewShown);
+        proto.write(STATUS_ICON, mStatusIcon);
+        mTmpInsets.dumpDebug(proto, LAST_COMPUTED_INSETS);
+        proto.write(SETTINGS_OBSERVER, Objects.toString(mSettingsObserver));
+        proto.flush();
+    }
 }
diff --git a/core/java/android/inputmethodservice/SoftInputWindow.java b/core/java/android/inputmethodservice/SoftInputWindow.java
index 6efd03c..bc0b37e 100644
--- a/core/java/android/inputmethodservice/SoftInputWindow.java
+++ b/core/java/android/inputmethodservice/SoftInputWindow.java
@@ -16,6 +16,13 @@
 
 package android.inputmethodservice;
 
+import static android.inputmethodservice.SoftInputWindowProto.BOUNDS;
+import static android.inputmethodservice.SoftInputWindowProto.GRAVITY;
+import static android.inputmethodservice.SoftInputWindowProto.NAME;
+import static android.inputmethodservice.SoftInputWindowProto.TAKES_FOCUS;
+import static android.inputmethodservice.SoftInputWindowProto.WINDOW_STATE;
+import static android.inputmethodservice.SoftInputWindowProto.WINDOW_TYPE;
+
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
 import android.annotation.IntDef;
@@ -25,6 +32,7 @@
 import android.os.Debug;
 import android.os.IBinder;
 import android.util.Log;
+import android.util.proto.ProtoOutputStream;
 import android.view.Gravity;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
@@ -362,4 +370,15 @@
                 throw new IllegalStateException("Unknown state=" + state);
         }
     }
+
+    void dumpDebug(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        proto.write(NAME, mName);
+        proto.write(WINDOW_TYPE, mWindowType);
+        proto.write(GRAVITY, mGravity);
+        proto.write(TAKES_FOCUS, mTakesFocus);
+        mBounds.dumpDebug(proto, BOUNDS);
+        proto.write(WINDOW_STATE, mWindowState);
+        proto.end(token);
+    }
 }
diff --git a/core/java/android/metrics/LogMaker.java b/core/java/android/metrics/LogMaker.java
index d8a2082..a19eb56 100644
--- a/core/java/android/metrics/LogMaker.java
+++ b/core/java/android/metrics/LogMaker.java
@@ -16,7 +16,6 @@
 package android.metrics;
 
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.content.ComponentName;
 import android.util.Log;
 import android.util.SparseArray;
@@ -32,7 +31,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public class LogMaker {
     private static final String TAG = "LogBuilder";
 
diff --git a/core/java/android/metrics/MetricsReader.java b/core/java/android/metrics/MetricsReader.java
index 27f9a5d..5f356ca 100644
--- a/core/java/android/metrics/MetricsReader.java
+++ b/core/java/android/metrics/MetricsReader.java
@@ -16,7 +16,6 @@
 package android.metrics;
 
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.util.EventLog;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -36,7 +35,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public class MetricsReader {
     private Queue<LogMaker> mPendingQueue = new LinkedList<>();
     private Queue<LogMaker> mSeenQueue = new LinkedList<>();
diff --git a/core/java/android/net/CaptivePortal.java b/core/java/android/net/CaptivePortal.java
index 8afeb30..c2586fa 100644
--- a/core/java/android/net/CaptivePortal.java
+++ b/core/java/android/net/CaptivePortal.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -42,7 +41,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final int APP_RETURN_DISMISSED    = 0;
     /**
      * Response code from the captive portal application, indicating that the user did not login and
@@ -52,7 +50,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final int APP_RETURN_UNWANTED     = 1;
     /**
      * Response code from the captive portal application, indicating that the user does not wish to
@@ -62,7 +59,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final int APP_RETURN_WANTED_AS_IS = 2;
     /** Event offset of request codes from captive portal application. */
     private static final int APP_REQUEST_BASE = 100;
@@ -74,7 +70,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final int APP_REQUEST_REEVALUATION_REQUIRED = APP_REQUEST_BASE + 0;
 
     private final IBinder mBinder;
@@ -154,7 +149,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public void useNetwork() {
         try {
             ICaptivePortal.Stub.asInterface(mBinder).appResponse(APP_RETURN_WANTED_AS_IS);
@@ -167,7 +161,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
     public void reevaluateNetwork() {
         try {
@@ -183,7 +176,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public void logEvent(@EventId int eventId, @NonNull String packageName) {
         try {
             ICaptivePortal.Stub.asInterface(mBinder).logEvent(eventId, packageName);
diff --git a/core/java/android/net/CaptivePortalData.java b/core/java/android/net/CaptivePortalData.java
index 1357803..59e62a6 100644
--- a/core/java/android/net/CaptivePortalData.java
+++ b/core/java/android/net/CaptivePortalData.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -30,7 +29,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class CaptivePortalData implements Parcelable {
     private final long mRefreshTimeMillis;
     @Nullable
@@ -254,7 +252,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (!(obj instanceof CaptivePortalData)) return false;
         final CaptivePortalData other = (CaptivePortalData) obj;
         return mRefreshTimeMillis == other.mRefreshTimeMillis
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 5d4003a..1012f47 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -269,7 +269,6 @@
      * {@hide}
      */
     @SystemApi
-    @TestApi
     public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC =
             "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC";
 
@@ -278,7 +277,6 @@
      * {@hide}
      */
     @SystemApi
-    @TestApi
     public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT =
             "android.net.extra.CAPTIVE_PORTAL_USER_AGENT";
 
@@ -899,6 +897,18 @@
     }
 
     /**
+     * @hide
+     * TODO: Expose for SystemServer when becomes a module.
+     */
+    public void systemReady() {
+        try {
+            mService.systemReady();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Checks if a given type uses the cellular data connection.
      * This should be replaced in the future by a network property.
      * @param networkType the type to check
@@ -4401,7 +4411,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK)
     public void startCaptivePortalApp(@NonNull Network network, @NonNull Bundle appExtras) {
         try {
diff --git a/core/java/android/net/DataUsageRequest.java b/core/java/android/net/DataUsageRequest.java
index 0ac8f7e7..b06d515 100644
--- a/core/java/android/net/DataUsageRequest.java
+++ b/core/java/android/net/DataUsageRequest.java
@@ -16,6 +16,7 @@
 
 package android.net;
 
+import android.annotation.Nullable;
 import android.net.NetworkTemplate;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -95,7 +96,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (obj instanceof DataUsageRequest == false) return false;
         DataUsageRequest that = (DataUsageRequest) obj;
         return that.requestId == this.requestId
diff --git a/core/java/android/net/DhcpResults.java b/core/java/android/net/DhcpResults.java
index 1ef4f17..6819c34 100644
--- a/core/java/android/net/DhcpResults.java
+++ b/core/java/android/net/DhcpResults.java
@@ -159,7 +159,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (this == obj) return true;
 
         if (!(obj instanceof DhcpResults)) return false;
diff --git a/core/java/android/net/EthernetManager.java b/core/java/android/net/EthernetManager.java
index d975017..5860e20 100644
--- a/core/java/android/net/EthernetManager.java
+++ b/core/java/android/net/EthernetManager.java
@@ -37,7 +37,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 @SystemService(Context.ETHERNET_SERVICE)
 public class EthernetManager {
     private static final String TAG = "EthernetManager";
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index d7f178c..059ec28 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -233,4 +233,6 @@
 
     void simulateDataStall(int detectionMethod, long timestampMillis, in Network network,
                 in PersistableBundle extras);
+
+    void systemReady();
 }
diff --git a/core/java/android/net/Ikev2VpnProfile.java b/core/java/android/net/Ikev2VpnProfile.java
index c8ca618e..9876fc6 100644
--- a/core/java/android/net/Ikev2VpnProfile.java
+++ b/core/java/android/net/Ikev2VpnProfile.java
@@ -364,7 +364,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (!(obj instanceof Ikev2VpnProfile)) {
             return false;
         }
diff --git a/core/java/android/net/IpConfiguration.java b/core/java/android/net/IpConfiguration.java
index 23d5ff7..fa31b80 100644
--- a/core/java/android/net/IpConfiguration.java
+++ b/core/java/android/net/IpConfiguration.java
@@ -166,7 +166,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (o == this) {
             return true;
         }
diff --git a/core/java/android/net/IpPrefix.java b/core/java/android/net/IpPrefix.java
index 8cfe6df..bcb65fa 100644
--- a/core/java/android/net/IpPrefix.java
+++ b/core/java/android/net/IpPrefix.java
@@ -18,8 +18,8 @@
 
 import android.annotation.IntRange;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Pair;
@@ -88,7 +88,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public IpPrefix(@NonNull InetAddress address, @IntRange(from = 0, to = 128) int prefixLength) {
         // We don't reuse the (byte[], int) constructor because it calls clone() on the byte array,
         // which is unnecessary because getAddress() already returns a clone.
@@ -107,7 +106,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public IpPrefix(@NonNull String prefix) {
         // We don't reuse the (InetAddress, int) constructor because "error: call to this must be
         // first statement in constructor". We could factor out setting the member variables to an
@@ -127,7 +125,7 @@
      * @return {@code true} if both objects are equal, {@code false} otherwise.
      */
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (!(obj instanceof IpPrefix)) {
             return false;
         }
diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java
index 38d9883..e89451e 100644
--- a/core/java/android/net/IpSecAlgorithm.java
+++ b/core/java/android/net/IpSecAlgorithm.java
@@ -17,6 +17,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.StringDef;
+import android.content.res.Resources;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -27,6 +28,12 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
 
 /**
  * This class represents a single algorithm that can be used by an {@link IpSecTransform}.
@@ -52,6 +59,27 @@
     public static final String CRYPT_AES_CBC = "cbc(aes)";
 
     /**
+     * AES-CTR Encryption/Ciphering Algorithm.
+     *
+     * <p>Valid lengths for keying material are {160, 224, 288}.
+     *
+     * <p>As per <a href="https://tools.ietf.org/html/rfc3686#section-5.1">RFC3686 (Section
+     * 5.1)</a>, keying material consists of a 128, 192, or 256 bit AES key followed by a 32-bit
+     * nonce. RFC compliance requires that the nonce must be unique per security association.
+     *
+     * <p>This algorithm may be available on the device. Caller MUST check if it is supported before
+     * using it by calling {@link #getSupportedAlgorithms()} and checking if this algorithm is
+     * included in the returned algorithm set. The returned algorithm set will not change unless the
+     * device is rebooted. {@link IllegalArgumentException} will be thrown if this algorithm is
+     * requested on an unsupported device.
+     *
+     * <p>@see {@link #getSupportedAlgorithms()}
+     */
+    // This algorithm may be available on devices released before Android 12, and is guaranteed
+    // to be available on devices first shipped with Android 12 or later.
+    public static final String CRYPT_AES_CTR = "rfc3686(ctr(aes))";
+
+    /**
      * MD5 HMAC Authentication/Integrity Algorithm. <b>This algorithm is not recommended for use in
      * new applications and is provided for legacy compatibility with 3gpp infrastructure.</b>
      *
@@ -99,6 +127,25 @@
     public static final String AUTH_HMAC_SHA512 = "hmac(sha512)";
 
     /**
+     * AES-XCBC Authentication/Integrity Algorithm.
+     *
+     * <p>Keys for this algorithm must be 128 bits in length.
+     *
+     * <p>The only valid truncation length is 96 bits.
+     *
+     * <p>This algorithm may be available on the device. Caller MUST check if it is supported before
+     * using it by calling {@link #getSupportedAlgorithms()} and checking if this algorithm is
+     * included in the returned algorithm set. The returned algorithm set will not change unless the
+     * device is rebooted. {@link IllegalArgumentException} will be thrown if this algorithm is
+     * requested on an unsupported device.
+     *
+     * <p>@see {@link #getSupportedAlgorithms()}
+     */
+    // This algorithm may be available on devices released before Android 12, and is guaranteed
+    // to be available on devices first shipped with Android 12 or later.
+    public static final String AUTH_AES_XCBC = "xcbc(aes)";
+
+    /**
      * AES-GCM Authentication/Integrity + Encryption/Ciphering Algorithm.
      *
      * <p>Valid lengths for keying material are {160, 224, 288}.
@@ -111,19 +158,69 @@
      */
     public static final String AUTH_CRYPT_AES_GCM = "rfc4106(gcm(aes))";
 
+    /**
+     * ChaCha20-Poly1305 Authentication/Integrity + Encryption/Ciphering Algorithm.
+     *
+     * <p>Keys for this algorithm must be 288 bits in length.
+     *
+     * <p>As per <a href="https://tools.ietf.org/html/rfc7634#section-2">RFC7634 (Section 2)</a>,
+     * keying material consists of a 256 bit key followed by a 32-bit salt. The salt is fixed per
+     * security association.
+     *
+     * <p>The only valid ICV (truncation) length is 128 bits.
+     *
+     * <p>This algorithm may be available on the device. Caller MUST check if it is supported before
+     * using it by calling {@link #getSupportedAlgorithms()} and checking if this algorithm is
+     * included in the returned algorithm set. The returned algorithm set will not change unless the
+     * device is rebooted. {@link IllegalArgumentException} will be thrown if this algorithm is
+     * requested on an unsupported device.
+     *
+     * <p>@see {@link #getSupportedAlgorithms()}
+     */
+    // This algorithm may be available on devices released before Android 12, and is guaranteed
+    // to be available on devices first shipped with Android 12 or later.
+    public static final String AUTH_CRYPT_CHACHA20_POLY1305 = "rfc7539esp(chacha20,poly1305)";
+
     /** @hide */
     @StringDef({
         CRYPT_AES_CBC,
+        CRYPT_AES_CTR,
         AUTH_HMAC_MD5,
         AUTH_HMAC_SHA1,
         AUTH_HMAC_SHA256,
         AUTH_HMAC_SHA384,
         AUTH_HMAC_SHA512,
-        AUTH_CRYPT_AES_GCM
+        AUTH_AES_XCBC,
+        AUTH_CRYPT_AES_GCM,
+        AUTH_CRYPT_CHACHA20_POLY1305
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface AlgorithmName {}
 
+    /** @hide */
+    @VisibleForTesting
+    public static final Map<String, Integer> ALGO_TO_REQUIRED_FIRST_SDK = new HashMap<>();
+
+    private static final int SDK_VERSION_ZERO = 0;
+
+    static {
+        ALGO_TO_REQUIRED_FIRST_SDK.put(CRYPT_AES_CBC, SDK_VERSION_ZERO);
+        ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_HMAC_MD5, SDK_VERSION_ZERO);
+        ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_HMAC_SHA1, SDK_VERSION_ZERO);
+        ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_HMAC_SHA256, SDK_VERSION_ZERO);
+        ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_HMAC_SHA384, SDK_VERSION_ZERO);
+        ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_HMAC_SHA512, SDK_VERSION_ZERO);
+        ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_CRYPT_AES_GCM, SDK_VERSION_ZERO);
+
+        // STOPSHIP: b/170424293 Use Build.VERSION_CODES.S when it is defined
+        ALGO_TO_REQUIRED_FIRST_SDK.put(CRYPT_AES_CTR, Build.VERSION_CODES.R + 1);
+        ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_AES_XCBC, Build.VERSION_CODES.R + 1);
+        ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_CRYPT_CHACHA20_POLY1305, Build.VERSION_CODES.R + 1);
+    }
+
+    private static final Set<String> ENABLED_ALGOS =
+            Collections.unmodifiableSet(loadAlgos(Resources.getSystem()));
+
     private final String mName;
     private final byte[] mKey;
     private final int mTruncLenBits;
@@ -137,6 +234,7 @@
      *
      * @param algorithm name of the algorithm.
      * @param key key padded to a multiple of 8 bits.
+     * @throws IllegalArgumentException if algorithm or key length is invalid.
      */
     public IpSecAlgorithm(@NonNull @AlgorithmName String algorithm, @NonNull byte[] key) {
         this(algorithm, key, 0);
@@ -152,6 +250,7 @@
      * @param algorithm name of the algorithm.
      * @param key key padded to a multiple of 8 bits.
      * @param truncLenBits number of bits of output hash to use.
+     * @throws IllegalArgumentException if algorithm, key length or truncation length is invalid.
      */
     public IpSecAlgorithm(
             @NonNull @AlgorithmName String algorithm, @NonNull byte[] key, int truncLenBits) {
@@ -206,13 +305,59 @@
                 }
             };
 
-    private static void checkValidOrThrow(String name, int keyLen, int truncLen) {
-        boolean isValidLen = true;
-        boolean isValidTruncLen = true;
+    /**
+     * Returns supported IPsec algorithms for the current device.
+     *
+     * <p>Some algorithms may not be supported on old devices. Callers MUST check if an algorithm is
+     * supported before using it.
+     */
+    @NonNull
+    public static Set<String> getSupportedAlgorithms() {
+        return ENABLED_ALGOS;
+    }
 
-        switch(name) {
+    /** @hide */
+    @VisibleForTesting
+    public static Set<String> loadAlgos(Resources systemResources) {
+        final Set<String> enabledAlgos = new HashSet<>();
+
+        // Load and validate the optional algorithm resource. Undefined or duplicate algorithms in
+        // the resource are not allowed.
+        final String[] resourceAlgos = systemResources.getStringArray(
+                com.android.internal.R.array.config_optionalIpSecAlgorithms);
+        for (String str : resourceAlgos) {
+            if (!ALGO_TO_REQUIRED_FIRST_SDK.containsKey(str) || !enabledAlgos.add(str)) {
+                // This error should be caught by CTS and never be thrown to API callers
+                throw new IllegalArgumentException("Invalid or repeated algorithm " + str);
+            }
+        }
+
+        for (Entry<String, Integer> entry : ALGO_TO_REQUIRED_FIRST_SDK.entrySet()) {
+            if (Build.VERSION.FIRST_SDK_INT >= entry.getValue()) {
+                enabledAlgos.add(entry.getKey());
+            }
+        }
+
+        return enabledAlgos;
+    }
+
+    private static void checkValidOrThrow(String name, int keyLen, int truncLen) {
+        final boolean isValidLen;
+        final boolean isValidTruncLen;
+
+        if (!getSupportedAlgorithms().contains(name)) {
+            throw new IllegalArgumentException("Unsupported algorithm: " + name);
+        }
+
+        switch (name) {
             case CRYPT_AES_CBC:
                 isValidLen = keyLen == 128 || keyLen == 192 || keyLen == 256;
+                isValidTruncLen = true;
+                break;
+            case CRYPT_AES_CTR:
+                // The keying material for AES-CTR is a key plus a 32-bit salt
+                isValidLen = keyLen == 128 + 32 || keyLen == 192 + 32 || keyLen == 256 + 32;
+                isValidTruncLen = true;
                 break;
             case AUTH_HMAC_MD5:
                 isValidLen = keyLen == 128;
@@ -234,12 +379,22 @@
                 isValidLen = keyLen == 512;
                 isValidTruncLen = truncLen >= 256 && truncLen <= 512;
                 break;
+            case AUTH_AES_XCBC:
+                isValidLen = keyLen == 128;
+                isValidTruncLen = truncLen == 96;
+                break;
             case AUTH_CRYPT_AES_GCM:
                 // The keying material for GCM is a key plus a 32-bit salt
                 isValidLen = keyLen == 128 + 32 || keyLen == 192 + 32 || keyLen == 256 + 32;
                 isValidTruncLen = truncLen == 64 || truncLen == 96 || truncLen == 128;
                 break;
+            case AUTH_CRYPT_CHACHA20_POLY1305:
+                // The keying material for ChaCha20Poly1305 is a key plus a 32-bit salt
+                isValidLen = keyLen == 256 + 32;
+                isValidTruncLen = truncLen == 128;
+                break;
             default:
+                // Should never hit here.
                 throw new IllegalArgumentException("Couldn't find an algorithm: " + name);
         }
 
@@ -260,6 +415,7 @@
             case AUTH_HMAC_SHA256:
             case AUTH_HMAC_SHA384:
             case AUTH_HMAC_SHA512:
+            case AUTH_AES_XCBC:
                 return true;
             default:
                 return false;
@@ -268,12 +424,24 @@
 
     /** @hide */
     public boolean isEncryption() {
-        return getName().equals(CRYPT_AES_CBC);
+        switch (getName()) {
+            case CRYPT_AES_CBC: // fallthrough
+            case CRYPT_AES_CTR:
+                return true;
+            default:
+                return false;
+        }
     }
 
     /** @hide */
     public boolean isAead() {
-        return getName().equals(AUTH_CRYPT_AES_GCM);
+        switch (getName()) {
+            case AUTH_CRYPT_AES_GCM: // fallthrough
+            case AUTH_CRYPT_CHACHA20_POLY1305:
+                return true;
+            default:
+                return false;
+        }
     }
 
     // Because encryption keys are sensitive and userdebug builds are used by large user pools
diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java
index 44a0a4f..fa1497d 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/core/java/android/net/IpSecTransform.java
@@ -21,6 +21,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
@@ -153,7 +154,7 @@
     /**
      * Standard equals.
      */
-    public boolean equals(Object other) {
+    public boolean equals(@Nullable Object other) {
         if (this == other) return true;
         if (!(other instanceof IpSecTransform)) return false;
         final IpSecTransform rhs = (IpSecTransform) other;
diff --git a/core/java/android/net/KeepalivePacketData.java b/core/java/android/net/KeepalivePacketData.java
index e21cb44..5877f1f 100644
--- a/core/java/android/net/KeepalivePacketData.java
+++ b/core/java/android/net/KeepalivePacketData.java
@@ -22,9 +22,10 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.net.util.IpUtils;
 import android.util.Log;
 
+import com.android.net.module.util.IpUtils;
+
 import java.net.InetAddress;
 
 /**
diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java
index a9d7f17..d1bdaa0 100644
--- a/core/java/android/net/LinkAddress.java
+++ b/core/java/android/net/LinkAddress.java
@@ -30,7 +30,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Parcel;
@@ -158,7 +157,6 @@
      * @return true if the address is IPv6.
      * @hide
      */
-    @TestApi
     @SystemApi
     public boolean isIpv6() {
         return address instanceof Inet6Address;
@@ -180,7 +178,6 @@
      * @return true if the address is IPv4 or is a mapped IPv4 address.
      * @hide
      */
-    @TestApi
     @SystemApi
     public boolean isIpv4() {
         return address instanceof Inet4Address;
@@ -243,7 +240,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public LinkAddress(@NonNull InetAddress address, @IntRange(from = 0, to = 128) int prefixLength,
             int flags, int scope) {
         init(address, prefixLength, flags, scope, LIFETIME_UNKNOWN, LIFETIME_UNKNOWN);
@@ -275,7 +271,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public LinkAddress(@NonNull InetAddress address, @IntRange(from = 0, to = 128) int prefixLength,
                        int flags, int scope, long deprecationTime, long expirationTime) {
         init(address, prefixLength, flags, scope, deprecationTime, expirationTime);
@@ -289,7 +284,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public LinkAddress(@NonNull InetAddress address,
             @IntRange(from = 0, to = 128) int prefixLength) {
         this(address, prefixLength, 0, 0);
@@ -314,7 +308,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public LinkAddress(@NonNull String address) {
         this(address, 0, 0);
         this.scope = scopeForUnicastAddress(this.address);
@@ -329,7 +322,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public LinkAddress(@NonNull String address, int flags, int scope) {
         // This may throw an IllegalArgumentException; catching it is the caller's responsibility.
         // TODO: consider rejecting mapped IPv4 addresses such as "::ffff:192.0.2.5/24".
@@ -357,7 +349,7 @@
      * @return {@code true} if both objects are equal, {@code false} otherwise.
      */
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (!(obj instanceof LinkAddress)) {
             return false;
         }
@@ -389,7 +381,6 @@
      * otherwise.
      * @hide
      */
-    @TestApi
     @SystemApi
     public boolean isSameAddressAs(@Nullable LinkAddress other) {
         if (other == null) {
@@ -469,7 +460,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public long getDeprecationTime() {
         return deprecationTime;
     }
@@ -485,7 +475,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public long getExpirationTime() {
         return expirationTime;
     }
@@ -496,7 +485,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     public boolean isGlobalPreferred() {
         /**
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 651494d..25a76f4 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -19,10 +19,8 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.net.util.LinkPropertiesUtils;
-import android.net.util.LinkPropertiesUtils.CompareResult;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -162,7 +160,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public LinkProperties(@Nullable LinkProperties source) {
         this(source, false /* parcelSensitiveFields */);
     }
@@ -178,7 +175,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public LinkProperties(@Nullable LinkProperties source, boolean parcelSensitiveFields) {
         mParcelSensitiveFields = parcelSensitiveFields;
         if (source == null) return;
@@ -293,7 +289,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public boolean addLinkAddress(@NonNull LinkAddress address) {
         if (address == null) {
             return false;
@@ -322,7 +317,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public boolean removeLinkAddress(@NonNull LinkAddress toRemove) {
         int i = findLinkAddressIndex(toRemove);
         if (i >= 0) {
@@ -376,7 +370,6 @@
      * @return true if the DNS server was added, false if it was already present.
      * @hide
      */
-    @TestApi
     @SystemApi
     public boolean addDnsServer(@NonNull InetAddress dnsServer) {
         if (dnsServer != null && !mDnses.contains(dnsServer)) {
@@ -393,7 +386,6 @@
      * @return true if the DNS server was removed, false if it did not exist.
      * @hide
      */
-    @TestApi
     @SystemApi
     public boolean removeDnsServer(@NonNull InetAddress dnsServer) {
         return mDnses.remove(dnsServer);
@@ -428,7 +420,6 @@
      * @param usePrivateDns The private DNS state.
      * @hide
      */
-    @TestApi
     @SystemApi
     public void setUsePrivateDns(boolean usePrivateDns) {
         mUsePrivateDns = usePrivateDns;
@@ -455,7 +446,6 @@
      * @param privateDnsServerName The private DNS server name.
      * @hide
      */
-    @TestApi
     @SystemApi
     public void setPrivateDnsServerName(@Nullable String privateDnsServerName) {
         mPrivateDnsServerName = privateDnsServerName;
@@ -534,7 +524,6 @@
      *        object.
      * @hide
      */
-    @TestApi
     @SystemApi
     public void setValidatedPrivateDnsServers(@NonNull Collection<InetAddress> dnsServers) {
         mValidatedPrivateDnses.clear();
@@ -551,7 +540,6 @@
      *         DNS servers on this link.
      * @hide
      */
-    @TestApi
     @SystemApi
     public @NonNull List<InetAddress> getValidatedPrivateDnsServers() {
         return Collections.unmodifiableList(mValidatedPrivateDnses);
@@ -592,7 +580,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public void setPcscfServers(@NonNull Collection<InetAddress> pcscfServers) {
         mPcscfs.clear();
         for (InetAddress pcscfServer: pcscfServers) {
@@ -608,7 +595,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public @NonNull List<InetAddress> getPcscfServers() {
         return Collections.unmodifiableList(mPcscfs);
     }
@@ -662,7 +648,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     public void setTcpBufferSizes(@Nullable String tcpBufferSizes) {
         mTcpBufferSizes = tcpBufferSizes;
@@ -675,7 +660,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     public @Nullable String getTcpBufferSizes() {
         return mTcpBufferSizes;
@@ -744,7 +728,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     public boolean removeRoute(@NonNull RouteInfo route) {
         return Objects.equals(mIfaceName, route.getInterface()) && mRoutes.remove(route);
@@ -1021,7 +1004,6 @@
      * @return {@code true} if there is an IPv4 address, {@code false} otherwise.
      * @hide
      */
-    @TestApi
     @SystemApi
     public boolean hasIpv4Address() {
         for (LinkAddress address : mLinkAddresses) {
@@ -1062,7 +1044,6 @@
      * @return {@code true} if there is a global preferred IPv6 address, {@code false} otherwise.
      * @hide
      */
-    @TestApi
     @SystemApi
     public boolean hasGlobalIpv6Address() {
         for (LinkAddress address : mLinkAddresses) {
@@ -1149,7 +1130,6 @@
      * @return {@code true} if there is an IPv6 default route, {@code false} otherwise.
      * @hide
      */
-    @TestApi
     @SystemApi
     public boolean hasIpv6DefaultRoute() {
         for (RouteInfo r : mRoutes) {
@@ -1265,7 +1245,6 @@
      * @return {@code true} if the link is provisioned, {@code false} otherwise.
      * @hide
      */
-    @TestApi
     @SystemApi
     public boolean isIpv4Provisioned() {
         return (hasIpv4Address()
@@ -1280,7 +1259,6 @@
      * @return {@code true} if the link is provisioned, {@code false} otherwise.
      * @hide
      */
-    @TestApi
     @SystemApi
     public boolean isIpv6Provisioned() {
         return (hasGlobalIpv6Address()
@@ -1308,7 +1286,6 @@
      * @return {@code true} if the link is provisioned, {@code false} otherwise.
      * @hide
      */
-    @TestApi
     @SystemApi
     public boolean isProvisioned() {
         return (isIpv4Provisioned() || isIpv6Provisioned());
@@ -1321,7 +1298,6 @@
      *         {@code false} otherwise.
      * @hide
      */
-    @TestApi
     @SystemApi
     public boolean isReachable(@NonNull InetAddress ip) {
         final List<RouteInfo> allRoutes = getAllRoutes();
@@ -1578,7 +1554,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public void setCaptivePortalApiUrl(@Nullable Uri url) {
         mCaptivePortalApiUrl = url;
     }
@@ -1593,7 +1568,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @Nullable
     public Uri getCaptivePortalApiUrl() {
         return mCaptivePortalApiUrl;
@@ -1604,7 +1578,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public void setCaptivePortalData(@Nullable CaptivePortalData data) {
         mCaptivePortalData = data;
     }
@@ -1618,7 +1591,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @Nullable
     public CaptivePortalData getCaptivePortalData() {
         return mCaptivePortalData;
@@ -1639,7 +1611,7 @@
      * @return {@code true} if both objects are equal, {@code false} otherwise.
      */
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (this == obj) return true;
 
         if (!(obj instanceof LinkProperties)) return false;
@@ -1669,78 +1641,6 @@
     }
 
     /**
-     * Compares the DNS addresses in this LinkProperties with another
-     * LinkProperties, examining only DNS addresses on the base link.
-     *
-     * @param target a LinkProperties with the new list of dns addresses
-     * @return the differences between the DNS addresses.
-     * @hide
-     */
-    public @NonNull CompareResult<InetAddress> compareDnses(@Nullable LinkProperties target) {
-        /*
-         * Duplicate the InetAddresses into removed, we will be removing
-         * dns address which are common between mDnses and target
-         * leaving the addresses that are different. And dns address which
-         * are in target but not in mDnses are placed in the
-         * addedAddresses.
-         */
-        return new CompareResult<>(mDnses, target != null ? target.getDnsServers() : null);
-    }
-
-    /**
-     * Compares the validated private DNS addresses in this LinkProperties with another
-     * LinkProperties.
-     *
-     * @param target a LinkProperties with the new list of validated private dns addresses
-     * @return the differences between the DNS addresses.
-     * @hide
-     */
-    public @NonNull CompareResult<InetAddress> compareValidatedPrivateDnses(
-            @Nullable LinkProperties target) {
-        return new CompareResult<>(mValidatedPrivateDnses,
-                target != null ? target.getValidatedPrivateDnsServers() : null);
-    }
-
-    /**
-     * Compares all routes in this LinkProperties with another LinkProperties,
-     * examining both the the base link and all stacked links.
-     *
-     * @param target a LinkProperties with the new list of routes
-     * @return the differences between the routes.
-     * @hide
-     */
-    public @NonNull CompareResult<RouteInfo> compareAllRoutes(@Nullable LinkProperties target) {
-        /*
-         * Duplicate the RouteInfos into removed, we will be removing
-         * routes which are common between mRoutes and target
-         * leaving the routes that are different. And route address which
-         * are in target but not in mRoutes are placed in added.
-         */
-        return new CompareResult<>(getAllRoutes(), target != null ? target.getAllRoutes() : null);
-    }
-
-    /**
-     * Compares all interface names in this LinkProperties with another
-     * LinkProperties, examining both the the base link and all stacked links.
-     *
-     * @param target a LinkProperties with the new list of interface names
-     * @return the differences between the interface names.
-     * @hide
-     */
-    public @NonNull CompareResult<String> compareAllInterfaceNames(
-            @Nullable LinkProperties target) {
-        /*
-         * Duplicate the interface names into removed, we will be removing
-         * interface names which are common between this and target
-         * leaving the interface names that are different. And interface names which
-         * are in target but not in this are placed in added.
-         */
-        return new CompareResult<>(getAllInterfaceNames(),
-                target != null ? target.getAllInterfaceNames() : null);
-    }
-
-
-    /**
      * Generate hashcode based on significant fields
      *
      * Equal objects must produce the same hash code, while unequal objects
diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java
index 0eb3c1e..6949bf2 100644
--- a/core/java/android/net/MacAddress.java
+++ b/core/java/android/net/MacAddress.java
@@ -160,7 +160,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         return (o instanceof MacAddress) && ((MacAddress) o).mAddr == mAddr;
     }
 
diff --git a/core/java/android/net/MatchAllNetworkSpecifier.java b/core/java/android/net/MatchAllNetworkSpecifier.java
index 70c4a72..84985b6 100644
--- a/core/java/android/net/MatchAllNetworkSpecifier.java
+++ b/core/java/android/net/MatchAllNetworkSpecifier.java
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -55,7 +56,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         return o instanceof MatchAllNetworkSpecifier;
     }
 
diff --git a/core/java/android/net/NattKeepalivePacketData.java b/core/java/android/net/NattKeepalivePacketData.java
index 22288b6..c4f8fc2 100644
--- a/core/java/android/net/NattKeepalivePacketData.java
+++ b/core/java/android/net/NattKeepalivePacketData.java
@@ -22,11 +22,12 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.net.util.IpUtils;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.system.OsConstants;
 
+import com.android.net.module.util.IpUtils;
+
 import java.net.Inet4Address;
 import java.net.InetAddress;
 import java.nio.ByteBuffer;
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index b872617..3e4f735 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -17,8 +17,8 @@
 package android.net;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -127,7 +127,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public Network(@NonNull Network that) {
         this(that.netId, that.mPrivateDnsBypass);
     }
@@ -164,7 +163,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     public @NonNull Network getPrivateDnsBypassingCopy() {
         return new Network(netId, true);
@@ -175,7 +173,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     public int getNetId() {
         return netId;
@@ -500,7 +497,7 @@
     };
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (!(obj instanceof Network)) return false;
         Network other = (Network)obj;
         return this.netId == other.netId;
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 004f844..be33f4e 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -850,7 +850,6 @@
      * @return an array of transport type values for this instance.
      * @hide
      */
-    @TestApi
     @SystemApi
     @NonNull public @Transport int[] getTransportTypes() {
         return BitUtils.unpackBits(mTransportTypes);
@@ -1025,7 +1024,6 @@
      */
     @NonNull
     @SystemApi
-    @TestApi
     public int[] getAdministratorUids() {
         return Arrays.copyOf(mAdministratorUids, mAdministratorUids.length);
     }
@@ -1506,7 +1504,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public @Nullable String getSsid() {
         return mSSID;
     }
@@ -1590,7 +1587,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     public boolean satisfiedByNetworkCapabilities(@Nullable NetworkCapabilities nc) {
         return satisfiedByNetworkCapabilities(nc, false);
@@ -2136,7 +2132,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final class Builder {
         private final NetworkCapabilities mCaps;
 
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index 0948a4da..9c10388 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -20,6 +20,7 @@
 import static android.net.ConnectivityManager.getNetworkTypeName;
 import static android.net.ConnectivityManager.isNetworkTypeMobile;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
@@ -69,7 +70,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (obj instanceof NetworkIdentity) {
             final NetworkIdentity ident = (NetworkIdentity) obj;
             return mType == ident.mType && mSubType == ident.mSubType && mRoaming == ident.mRoaming
diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java
index 4f05c9b..8a0211c 100644
--- a/core/java/android/net/NetworkPolicy.java
+++ b/core/java/android/net/NetworkPolicy.java
@@ -16,6 +16,7 @@
 
 package android.net;
 
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -205,7 +206,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (obj instanceof NetworkPolicy) {
             final NetworkPolicy other = (NetworkPolicy) obj;
             return warningBytes == other.warningBytes
diff --git a/core/java/android/net/NetworkProvider.java b/core/java/android/net/NetworkProvider.java
index 75086cf..d31218d 100644
--- a/core/java/android/net/NetworkProvider.java
+++ b/core/java/android/net/NetworkProvider.java
@@ -30,7 +30,7 @@
 
 /**
  * Base class for network providers such as telephony or Wi-Fi. NetworkProviders connect the device
- * to networks and makes them available to to the core network stack by creating
+ * to networks and makes them available to the core network stack by creating
  * {@link NetworkAgent}s. The networks can then provide connectivity to apps and can be interacted
  * with via networking APIs such as {@link ConnectivityManager}.
  *
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index 473e6c5..1d6e507 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -553,7 +553,7 @@
         proto.end(token);
     }
 
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (obj instanceof NetworkRequest == false) return false;
         NetworkRequest that = (NetworkRequest)obj;
         return (that.legacyType == this.legacyType &&
diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java
index a190c47..0ba2663 100644
--- a/core/java/android/net/NetworkScoreManager.java
+++ b/core/java/android/net/NetworkScoreManager.java
@@ -511,18 +511,26 @@
 
         @Override
         public void updateScores(@NonNull List<ScoredNetwork> networks) {
-            Binder.clearCallingIdentity();
-            mExecutor.execute(() -> {
-                mCallback.onScoresUpdated(networks);
-            });
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> {
+                    mCallback.onScoresUpdated(networks);
+                });
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
         }
 
         @Override
         public void clearScores() {
-            Binder.clearCallingIdentity();
-            mExecutor.execute(() -> {
-                mCallback.onScoresInvalidated();
-            });
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> {
+                    mCallback.onScoresInvalidated();
+                });
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
         }
     }
 
diff --git a/core/java/android/net/NetworkScorerAppData.java b/core/java/android/net/NetworkScorerAppData.java
index 116e39e..caa6e45 100644
--- a/core/java/android/net/NetworkScorerAppData.java
+++ b/core/java/android/net/NetworkScorerAppData.java
@@ -110,7 +110,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
         NetworkScorerAppData that = (NetworkScorerAppData) o;
diff --git a/core/java/android/net/NetworkSpecifier.java b/core/java/android/net/NetworkSpecifier.java
index 160259e..6ef496b 100644
--- a/core/java/android/net/NetworkSpecifier.java
+++ b/core/java/android/net/NetworkSpecifier.java
@@ -22,10 +22,14 @@
 /**
  * Describes specific properties of a requested network for use in a {@link NetworkRequest}.
  *
- * Applications cannot instantiate this class by themselves, but can obtain instances of
- * subclasses of this class via other APIs.
+ * This as an abstract class. Applications shouldn't instantiate this class by themselves, but can
+ * obtain instances of subclasses of this class via other APIs.
  */
 public abstract class NetworkSpecifier {
+    /**
+     * Create a placeholder object. Please use subclasses of this class in a {@link NetworkRequest}
+     * to request a network.
+     */
     public NetworkSpecifier() {}
 
     /**
diff --git a/core/java/android/net/NetworkStack.java b/core/java/android/net/NetworkStack.java
index 86f3dfd..79f9e6e 100644
--- a/core/java/android/net/NetworkStack.java
+++ b/core/java/android/net/NetworkStack.java
@@ -33,7 +33,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public class NetworkStack {
     /**
      * Permission granted only to the NetworkStack APK, defined in NetworkStackStub with signature
@@ -41,7 +40,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final String PERMISSION_MAINLINE_NETWORK_STACK =
             "android.permission.MAINLINE_NETWORK_STACK";
 
@@ -54,7 +52,6 @@
      */
     @Nullable
     @SystemApi
-    @TestApi
     public static IBinder getService() {
         final IBinder mockService = sMockService;
         if (mockService != null) return mockService;
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 34e48eb..cbee010 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -411,7 +411,7 @@
 
         /** @hide */
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (o instanceof Entry) {
                 final Entry e = (Entry) o;
                 return uid == e.uid && set == e.set && tag == e.tag && metered == e.metered
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index cd26079..a95ba12f 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -327,7 +327,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (obj instanceof NetworkTemplate) {
             final NetworkTemplate other = (NetworkTemplate) obj;
             return mMatchRule == other.mMatchRule
diff --git a/core/java/android/net/ProxyInfo.java b/core/java/android/net/ProxyInfo.java
index a32b41f..de5a180 100644
--- a/core/java/android/net/ProxyInfo.java
+++ b/core/java/android/net/ProxyInfo.java
@@ -275,7 +275,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (!(o instanceof ProxyInfo)) return false;
         ProxyInfo p = (ProxyInfo)o;
         // If PAC URL is present in either then they must be equal.
diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java
index 9876076..93ad41f7 100644
--- a/core/java/android/net/RouteInfo.java
+++ b/core/java/android/net/RouteInfo.java
@@ -20,7 +20,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.net.util.NetUtils;
 import android.os.Build;
@@ -87,17 +86,14 @@
 
     /** Unicast route. @hide */
     @SystemApi
-    @TestApi
     public static final int RTN_UNICAST = 1;
 
     /** Unreachable route. @hide */
     @SystemApi
-    @TestApi
     public static final int RTN_UNREACHABLE = 7;
 
     /** Throw route. @hide */
     @SystemApi
-    @TestApi
     public static final int RTN_THROW = 9;
 
     /**
@@ -135,7 +131,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public RouteInfo(@Nullable IpPrefix destination, @Nullable InetAddress gateway,
             @Nullable String iface, @RouteType int type) {
         this(destination, gateway, iface, type, 0);
@@ -397,7 +392,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     @RouteType
     public int getType() {
@@ -539,7 +533,7 @@
      * Compares this RouteInfo object against the specified object and indicates if they are equal.
      * @return {@code true} if the objects are equal, {@code false} otherwise.
      */
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (this == obj) return true;
 
         if (!(obj instanceof RouteInfo)) return false;
@@ -575,7 +569,7 @@
         }
 
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (!(o instanceof RouteKey)) {
                 return false;
             }
diff --git a/core/java/android/net/StaticIpConfiguration.java b/core/java/android/net/StaticIpConfiguration.java
index a973455..f56d656 100644
--- a/core/java/android/net/StaticIpConfiguration.java
+++ b/core/java/android/net/StaticIpConfiguration.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -52,7 +51,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class StaticIpConfiguration implements Parcelable {
     /** @hide */
     @UnsupportedAppUsage
diff --git a/core/java/android/net/StringNetworkSpecifier.java b/core/java/android/net/StringNetworkSpecifier.java
index 3f2aa17..012410b 100644
--- a/core/java/android/net/StringNetworkSpecifier.java
+++ b/core/java/android/net/StringNetworkSpecifier.java
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
@@ -45,7 +46,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (!(o instanceof StringNetworkSpecifier)) return false;
         return TextUtils.equals(specifier, ((StringNetworkSpecifier) o).specifier);
     }
diff --git a/core/java/android/net/TEST_MAPPING b/core/java/android/net/TEST_MAPPING
new file mode 100644
index 0000000..abac811
--- /dev/null
+++ b/core/java/android/net/TEST_MAPPING
@@ -0,0 +1,20 @@
+{
+  "imports": [
+    {
+      // Also includes cts/tests/tests/net
+      "path": "frameworks/base/tests/net"
+    },
+    {
+      "path": "packages/modules/NetworkStack"
+    },
+    {
+      "path": "packages/modules/CaptivePortalLogin"
+    },
+    {
+      "path": "frameworks/base/packages/Tethering"
+    },
+    {
+      "path": "frameworks/opt/net/wifi"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/core/java/android/net/TelephonyNetworkSpecifier.java b/core/java/android/net/TelephonyNetworkSpecifier.java
index 33c71d5..3348233 100644
--- a/core/java/android/net/TelephonyNetworkSpecifier.java
+++ b/core/java/android/net/TelephonyNetworkSpecifier.java
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -74,7 +75,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (this == obj) {
             return true;
         }
diff --git a/core/java/android/net/UidRange.java b/core/java/android/net/UidRange.java
index d75c43d..3bc0f9c 100644
--- a/core/java/android/net/UidRange.java
+++ b/core/java/android/net/UidRange.java
@@ -18,6 +18,7 @@
 
 import static android.os.UserHandle.PER_USER_RANGE;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -81,7 +82,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) {
             return true;
         }
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index efe2903..815e4f0 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -354,7 +354,7 @@
      * default port explicitly and the other leaves it implicit, they will not
      * be considered equal.
      */
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (!(o instanceof Uri)) {
             return false;
         }
diff --git a/core/java/android/net/apf/ApfCapabilities.java b/core/java/android/net/apf/ApfCapabilities.java
index 92c5432..bf5b26e 100644
--- a/core/java/android/net/apf/ApfCapabilities.java
+++ b/core/java/android/net/apf/ApfCapabilities.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.content.res.Resources;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -36,7 +35,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class ApfCapabilities implements Parcelable {
     /**
      * Version of APF instruction set supported for packet filtering. 0 indicates no support for
diff --git a/core/java/android/net/metrics/ApfProgramEvent.java b/core/java/android/net/metrics/ApfProgramEvent.java
index f93907a..c50bae9 100644
--- a/core/java/android/net/metrics/ApfProgramEvent.java
+++ b/core/java/android/net/metrics/ApfProgramEvent.java
@@ -20,7 +20,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -40,7 +39,6 @@
  * the APF program in place with a new APF program.
  * {@hide}
  */
-@TestApi
 @SystemApi
 public final class ApfProgramEvent implements IpConnectivityLog.Event {
 
diff --git a/core/java/android/net/metrics/ApfStats.java b/core/java/android/net/metrics/ApfStats.java
index b221cb9..2a601b2 100644
--- a/core/java/android/net/metrics/ApfStats.java
+++ b/core/java/android/net/metrics/ApfStats.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -29,7 +28,6 @@
  * {@hide}
  */
 @SystemApi
-@TestApi
 public final class ApfStats implements IpConnectivityLog.Event {
 
     /**
@@ -126,7 +124,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final class Builder {
         private long mDurationMs;
         private int mReceivedRas;
diff --git a/core/java/android/net/metrics/DhcpClientEvent.java b/core/java/android/net/metrics/DhcpClientEvent.java
index 8fc1ef8..e0a93dd 100644
--- a/core/java/android/net/metrics/DhcpClientEvent.java
+++ b/core/java/android/net/metrics/DhcpClientEvent.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -30,7 +29,6 @@
  * {@hide}
  */
 @SystemApi
-@TestApi
 public final class DhcpClientEvent implements IpConnectivityLog.Event {
 
     // Names for recording DhcpClient pseudo-state transitions.
diff --git a/core/java/android/net/metrics/DhcpErrorEvent.java b/core/java/android/net/metrics/DhcpErrorEvent.java
index 32efb5a..de3129d 100644
--- a/core/java/android/net/metrics/DhcpErrorEvent.java
+++ b/core/java/android/net/metrics/DhcpErrorEvent.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.SparseArray;
@@ -30,7 +29,6 @@
  * {@hide}
  */
 @SystemApi
-@TestApi
 public final class DhcpErrorEvent implements IpConnectivityLog.Event {
     public static final int L2_ERROR   = 1;
     public static final int L3_ERROR   = 2;
diff --git a/core/java/android/net/metrics/IpConnectivityLog.java b/core/java/android/net/metrics/IpConnectivityLog.java
index 680c015..a008d85 100644
--- a/core/java/android/net/metrics/IpConnectivityLog.java
+++ b/core/java/android/net/metrics/IpConnectivityLog.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.net.ConnectivityMetricsEvent;
 import android.net.IIpConnectivityMetrics;
 import android.net.Network;
@@ -35,7 +34,6 @@
  * {@hide}
  */
 @SystemApi
-@TestApi
 public class IpConnectivityLog {
     private static final String TAG = IpConnectivityLog.class.getSimpleName();
     private static final boolean DBG = false;
@@ -52,7 +50,6 @@
 
     /** @hide */
     @SystemApi
-    @TestApi
     public IpConnectivityLog() {
     }
 
diff --git a/core/java/android/net/metrics/IpManagerEvent.java b/core/java/android/net/metrics/IpManagerEvent.java
index f14abb8..4f7f326 100644
--- a/core/java/android/net/metrics/IpManagerEvent.java
+++ b/core/java/android/net/metrics/IpManagerEvent.java
@@ -20,7 +20,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.SparseArray;
@@ -36,7 +35,6 @@
  * {@hide}
  */
 @SystemApi
-@TestApi
 public final class IpManagerEvent implements IpConnectivityLog.Event {
 
     public static final int PROVISIONING_OK                       = 1;
diff --git a/core/java/android/net/metrics/IpReachabilityEvent.java b/core/java/android/net/metrics/IpReachabilityEvent.java
index 79e01d7..d5003ba 100644
--- a/core/java/android/net/metrics/IpReachabilityEvent.java
+++ b/core/java/android/net/metrics/IpReachabilityEvent.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.SparseArray;
@@ -32,7 +31,6 @@
  * {@hide}
  */
 @SystemApi
-@TestApi
 public final class IpReachabilityEvent implements IpConnectivityLog.Event {
 
     // Event types.
diff --git a/core/java/android/net/metrics/NetworkEvent.java b/core/java/android/net/metrics/NetworkEvent.java
index fe603cf..8c28f7a 100644
--- a/core/java/android/net/metrics/NetworkEvent.java
+++ b/core/java/android/net/metrics/NetworkEvent.java
@@ -20,7 +20,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.SparseArray;
@@ -34,7 +33,6 @@
  * {@hide}
  */
 @SystemApi
-@TestApi
 public final class NetworkEvent implements IpConnectivityLog.Event {
 
     public static final int NETWORK_CONNECTED            = 1;
diff --git a/core/java/android/net/metrics/RaEvent.java b/core/java/android/net/metrics/RaEvent.java
index 661f648..b54874f 100644
--- a/core/java/android/net/metrics/RaEvent.java
+++ b/core/java/android/net/metrics/RaEvent.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -28,7 +27,6 @@
  * {@hide}
  */
 @SystemApi
-@TestApi
 public final class RaEvent implements IpConnectivityLog.Event {
 
     private static final long NO_LIFETIME = -1L;
diff --git a/core/java/android/net/metrics/ValidationProbeEvent.java b/core/java/android/net/metrics/ValidationProbeEvent.java
index 8fab64a..7f4e4a7 100644
--- a/core/java/android/net/metrics/ValidationProbeEvent.java
+++ b/core/java/android/net/metrics/ValidationProbeEvent.java
@@ -20,7 +20,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.SparseArray;
@@ -35,7 +34,6 @@
  * {@hide}
  */
 @SystemApi
-@TestApi
 public final class ValidationProbeEvent implements IpConnectivityLog.Event {
 
     public static final int PROBE_DNS       = 0;
diff --git a/core/java/android/net/util/IpUtils.java b/core/java/android/net/util/IpUtils.java
deleted file mode 100644
index e037c40..0000000
--- a/core/java/android/net/util/IpUtils.java
+++ /dev/null
@@ -1,151 +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.
- */
-
-package android.net.util;
-
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.nio.BufferOverflowException;
-import java.nio.BufferUnderflowException;
-import java.nio.ByteBuffer;
-import java.nio.ShortBuffer;
-
-import static android.system.OsConstants.IPPROTO_TCP;
-import static android.system.OsConstants.IPPROTO_UDP;
-
-/**
- * @hide
- */
-public class IpUtils {
-    /**
-     * Converts a signed short value to an unsigned int value.  Needed
-     * because Java does not have unsigned types.
-     */
-    private static int intAbs(short v) {
-        return v & 0xFFFF;
-    }
-
-    /**
-     * Performs an IP checksum (used in IP header and across UDP
-     * payload) on the specified portion of a ByteBuffer.  The seed
-     * allows the checksum to commence with a specified value.
-     */
-    private static int checksum(ByteBuffer buf, int seed, int start, int end) {
-        int sum = seed;
-        final int bufPosition = buf.position();
-
-        // set position of original ByteBuffer, so that the ShortBuffer
-        // will be correctly initialized
-        buf.position(start);
-        ShortBuffer shortBuf = buf.asShortBuffer();
-
-        // re-set ByteBuffer position
-        buf.position(bufPosition);
-
-        final int numShorts = (end - start) / 2;
-        for (int i = 0; i < numShorts; i++) {
-            sum += intAbs(shortBuf.get(i));
-        }
-        start += numShorts * 2;
-
-        // see if a singleton byte remains
-        if (end != start) {
-            short b = buf.get(start);
-
-            // make it unsigned
-            if (b < 0) {
-                b += 256;
-            }
-
-            sum += b * 256;
-        }
-
-        sum = ((sum >> 16) & 0xFFFF) + (sum & 0xFFFF);
-        sum = ((sum + ((sum >> 16) & 0xFFFF)) & 0xFFFF);
-        int negated = ~sum;
-        return intAbs((short) negated);
-    }
-
-    private static int pseudoChecksumIPv4(
-            ByteBuffer buf, int headerOffset, int protocol, int transportLen) {
-        int partial = protocol + transportLen;
-        partial += intAbs(buf.getShort(headerOffset + 12));
-        partial += intAbs(buf.getShort(headerOffset + 14));
-        partial += intAbs(buf.getShort(headerOffset + 16));
-        partial += intAbs(buf.getShort(headerOffset + 18));
-        return partial;
-    }
-
-    private static int pseudoChecksumIPv6(
-            ByteBuffer buf, int headerOffset, int protocol, int transportLen) {
-        int partial = protocol + transportLen;
-        for (int offset = 8; offset < 40; offset += 2) {
-            partial += intAbs(buf.getShort(headerOffset + offset));
-        }
-        return partial;
-    }
-
-    private static byte ipversion(ByteBuffer buf, int headerOffset) {
-        return (byte) ((buf.get(headerOffset) & (byte) 0xf0) >> 4);
-   }
-
-    public static short ipChecksum(ByteBuffer buf, int headerOffset) {
-        byte ihl = (byte) (buf.get(headerOffset) & 0x0f);
-        return (short) checksum(buf, 0, headerOffset, headerOffset + ihl * 4);
-    }
-
-    private static short transportChecksum(ByteBuffer buf, int protocol,
-            int ipOffset, int transportOffset, int transportLen) {
-        if (transportLen < 0) {
-            throw new IllegalArgumentException("Transport length < 0: " + transportLen);
-        }
-        int sum;
-        byte ver = ipversion(buf, ipOffset);
-        if (ver == 4) {
-            sum = pseudoChecksumIPv4(buf, ipOffset, protocol, transportLen);
-        } else if (ver == 6) {
-            sum = pseudoChecksumIPv6(buf, ipOffset, protocol, transportLen);
-        } else {
-            throw new UnsupportedOperationException("Checksum must be IPv4 or IPv6");
-        }
-
-        sum = checksum(buf, sum, transportOffset, transportOffset + transportLen);
-        if (protocol == IPPROTO_UDP && sum == 0) {
-            sum = (short) 0xffff;
-        }
-        return (short) sum;
-    }
-
-    public static short udpChecksum(ByteBuffer buf, int ipOffset, int transportOffset) {
-        int transportLen = intAbs(buf.getShort(transportOffset + 4));
-        return transportChecksum(buf, IPPROTO_UDP, ipOffset, transportOffset, transportLen);
-    }
-
-    public static short tcpChecksum(ByteBuffer buf, int ipOffset, int transportOffset,
-            int transportLen) {
-        return transportChecksum(buf, IPPROTO_TCP, ipOffset, transportOffset, transportLen);
-    }
-
-    public static String addressAndPortToString(InetAddress address, int port) {
-        return String.format(
-                (address instanceof Inet6Address) ? "[%s]:%d" : "%s:%d",
-                address.getHostAddress(), port);
-    }
-
-    public static boolean isValidUdpOrTcpPort(int port) {
-        return port > 0 && port < 65536;
-    }
-}
diff --git a/core/java/android/net/util/SocketUtils.java b/core/java/android/net/util/SocketUtils.java
index 6967084..e64060f 100644
--- a/core/java/android/net/util/SocketUtils.java
+++ b/core/java/android/net/util/SocketUtils.java
@@ -22,7 +22,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.net.NetworkUtils;
 import android.system.ErrnoException;
 import android.system.NetlinkSocketAddress;
@@ -40,7 +39,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class SocketUtils {
     /**
      * Create a raw datagram socket that is bound to an interface.
diff --git a/core/java/android/nfc/NdefMessage.java b/core/java/android/nfc/NdefMessage.java
index 35dae55..553f6c0 100644
--- a/core/java/android/nfc/NdefMessage.java
+++ b/core/java/android/nfc/NdefMessage.java
@@ -16,6 +16,7 @@
 
 package android.nfc;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.proto.ProtoOutputStream;
@@ -23,7 +24,6 @@
 import java.nio.ByteBuffer;
 import java.util.Arrays;
 
-
 /**
  * Represents an immutable NDEF Message.
  * <p>
@@ -239,7 +239,7 @@
      * identical NDEF Records.
      */
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (this == obj) return true;
         if (obj == null) return false;
         if (getClass() != obj.getClass()) return false;
diff --git a/core/java/android/nfc/NdefRecord.java b/core/java/android/nfc/NdefRecord.java
index 74f8cb4..421eb333 100644
--- a/core/java/android/nfc/NdefRecord.java
+++ b/core/java/android/nfc/NdefRecord.java
@@ -16,6 +16,7 @@
 
 package android.nfc;
 
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Intent;
 import android.net.Uri;
@@ -1032,7 +1033,7 @@
      * identical tnf, type, id and payload fields.
      */
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (this == obj) return true;
         if (obj == null) return false;
         if (getClass() != obj.getClass()) return false;
diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
index d7c2e05..e3f996b 100644
--- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -16,6 +16,7 @@
 
 package android.nfc.cardemulation;
 
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.pm.PackageManager;
@@ -507,7 +508,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) return true;
         if (!(o instanceof ApduServiceInfo)) return false;
         ApduServiceInfo thatService = (ApduServiceInfo) o;
diff --git a/core/java/android/nfc/cardemulation/NfcFServiceInfo.java b/core/java/android/nfc/cardemulation/NfcFServiceInfo.java
index 885a0b5..c2b33dd 100644
--- a/core/java/android/nfc/cardemulation/NfcFServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/NfcFServiceInfo.java
@@ -16,6 +16,7 @@
 
 package android.nfc.cardemulation;
 
+import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
@@ -250,7 +251,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) return true;
         if (!(o instanceof NfcFServiceInfo)) return false;
         NfcFServiceInfo thatService = (NfcFServiceInfo) o;
diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java
index 12ec0a0..9a16d3f 100644
--- a/core/java/android/os/BatteryManager.java
+++ b/core/java/android/os/BatteryManager.java
@@ -20,7 +20,6 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
-import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.Intent;
@@ -392,7 +391,6 @@
      */
     @RequiresPermission(permission.POWER_SAVER)
     @SystemApi
-    @TestApi
     public boolean setChargingStateUpdateDelayMillis(int delayMillis) {
         try {
             return mBatteryStats.setChargingStateUpdateDelayMillis(delayMillis);
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index d889b155..b550c7d 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1499,7 +1499,7 @@
         }
 
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (this == o) return true;
             if (o == null || getClass() != o.getClass()) return false;
 
@@ -4049,7 +4049,8 @@
         if (cpuFreqs != null) {
             sb.setLength(0);
             for (int i = 0; i < cpuFreqs.length; ++i) {
-                sb.append((i == 0 ? "" : ",") + cpuFreqs[i]);
+                if (i != 0) sb.append(',');
+                sb.append(cpuFreqs[i]);
             }
             dumpLine(pw, 0 /* uid */, category, GLOBAL_CPU_FREQ_DATA, sb.toString());
         }
@@ -4368,12 +4369,13 @@
                 if (cpuFreqTimeMs != null && cpuFreqTimeMs.length == cpuFreqs.length) {
                     sb.setLength(0);
                     for (int i = 0; i < cpuFreqTimeMs.length; ++i) {
-                        sb.append((i == 0 ? "" : ",") + cpuFreqTimeMs[i]);
+                        if (i != 0) sb.append(',');
+                        sb.append(cpuFreqTimeMs[i]);
                     }
                     final long[] screenOffCpuFreqTimeMs = u.getScreenOffCpuFreqTimes(which);
                     if (screenOffCpuFreqTimeMs != null) {
                         for (int i = 0; i < screenOffCpuFreqTimeMs.length; ++i) {
-                            sb.append("," + screenOffCpuFreqTimeMs[i]);
+                            sb.append(',').append(screenOffCpuFreqTimeMs[i]);
                         }
                     } else {
                         for (int i = 0; i < cpuFreqTimeMs.length; ++i) {
@@ -4389,13 +4391,14 @@
                     if (timesMs != null && timesMs.length == cpuFreqs.length) {
                         sb.setLength(0);
                         for (int i = 0; i < timesMs.length; ++i) {
-                            sb.append((i == 0 ? "" : ",") + timesMs[i]);
+                            if (i != 0) sb.append(',');
+                            sb.append(timesMs[i]);
                         }
                         final long[] screenOffTimesMs = u.getScreenOffCpuFreqTimes(
                                 which, procState);
                         if (screenOffTimesMs != null) {
                             for (int i = 0; i < screenOffTimesMs.length; ++i) {
-                                sb.append("," + screenOffTimesMs[i]);
+                                sb.append(',').append(screenOffTimesMs[i]);
                             }
                         } else {
                             for (int i = 0; i < timesMs.length; ++i) {
@@ -5427,7 +5430,7 @@
             sb.setLength(0);
             sb.append("  CPU freqs:");
             for (int i = 0; i < cpuFreqs.length; ++i) {
-                sb.append(" " + cpuFreqs[i]);
+                sb.append(' ').append(cpuFreqs[i]);
             }
             pw.println(sb.toString());
             pw.println();
@@ -6036,7 +6039,7 @@
                 sb.setLength(0);
                 sb.append("    Total cpu time per freq:");
                 for (int i = 0; i < cpuFreqTimes.length; ++i) {
-                    sb.append(" " + cpuFreqTimes[i]);
+                    sb.append(' ').append(cpuFreqTimes[i]);
                 }
                 pw.println(sb.toString());
             }
@@ -6045,7 +6048,7 @@
                 sb.setLength(0);
                 sb.append("    Total screen-off cpu time per freq:");
                 for (int i = 0; i < screenOffCpuFreqTimes.length; ++i) {
-                    sb.append(" " + screenOffCpuFreqTimes[i]);
+                    sb.append(' ').append(screenOffCpuFreqTimes[i]);
                 }
                 pw.println(sb.toString());
             }
@@ -6054,8 +6057,8 @@
                 final long[] cpuTimes = u.getCpuFreqTimes(which, procState);
                 if (cpuTimes != null) {
                     sb.setLength(0);
-                    sb.append("    Cpu times per freq at state "
-                            + Uid.PROCESS_STATE_NAMES[procState] + ":");
+                    sb.append("    Cpu times per freq at state ")
+                            .append(Uid.PROCESS_STATE_NAMES[procState]).append(':');
                     for (int i = 0; i < cpuTimes.length; ++i) {
                         sb.append(" " + cpuTimes[i]);
                     }
@@ -6065,8 +6068,8 @@
                 final long[] screenOffCpuTimes = u.getScreenOffCpuFreqTimes(which, procState);
                 if (screenOffCpuTimes != null) {
                     sb.setLength(0);
-                    sb.append("   Screen-off cpu times per freq at state "
-                            + Uid.PROCESS_STATE_NAMES[procState] + ":");
+                    sb.append("   Screen-off cpu times per freq at state ")
+                            .append(Uid.PROCESS_STATE_NAMES[procState]).append(':');
                     for (int i = 0; i < screenOffCpuTimes.length; ++i) {
                         sb.append(" " + screenOffCpuTimes[i]);
                     }
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index bec96f9..fce3437 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -379,6 +379,7 @@
      *
      * @see #clearCallingIdentity
      */
+    @CriticalNative
     public static final native void restoreCallingIdentity(long token);
 
     /**
@@ -391,8 +392,8 @@
      * @hide
      */
     public static final void withCleanCallingIdentity(@NonNull ThrowingRunnable action) {
-        long callingIdentity = clearCallingIdentity();
         Throwable throwableToPropagate = null;
+        final long callingIdentity = clearCallingIdentity();
         try {
             action.runOrThrow();
         } catch (Throwable throwable) {
@@ -415,8 +416,8 @@
      * @hide
      */
     public static final <T> T withCleanCallingIdentity(@NonNull ThrowingSupplier<T> action) {
-        long callingIdentity = clearCallingIdentity();
         Throwable throwableToPropagate = null;
+        final long callingIdentity = clearCallingIdentity();
         try {
             return action.getOrThrow();
         } catch (Throwable throwable) {
diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java
index 9e996d1..fe4d729 100644
--- a/core/java/android/os/BugreportManager.java
+++ b/core/java/android/os/BugreportManager.java
@@ -24,13 +24,13 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
-import android.annotation.TestApi;
 import android.app.ActivityManager;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Handler;
 import android.util.Log;
 import android.widget.Toast;
+
 import com.android.internal.R;
 import com.android.internal.util.Preconditions;
 
@@ -48,7 +48,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 @SystemService(Context.BUGREPORT_SERVICE)
 public final class BugreportManager {
 
diff --git a/core/java/android/os/BugreportParams.java b/core/java/android/os/BugreportParams.java
index c8347813..279ccae 100644
--- a/core/java/android/os/BugreportParams.java
+++ b/core/java/android/os/BugreportParams.java
@@ -18,7 +18,6 @@
 
 import android.annotation.IntDef;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -29,7 +28,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class BugreportParams {
     private final int mMode;
 
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 78ba7f0..bd18150 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -18,6 +18,7 @@
 
 import android.Manifest;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SuppressAutoDoc;
 import android.annotation.SystemApi;
@@ -1215,7 +1216,7 @@
         }
 
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (!(o instanceof Partition)) {
                 return false;
             }
diff --git a/media/java/android/media/tv/TvChannelInfo.aidl b/core/java/android/os/CombinedVibrationEffect.aidl
similarity index 83%
copy from media/java/android/media/tv/TvChannelInfo.aidl
copy to core/java/android/os/CombinedVibrationEffect.aidl
index 71cd0a7..330733c 100644
--- a/media/java/android/media/tv/TvChannelInfo.aidl
+++ b/core/java/android/os/CombinedVibrationEffect.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open 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,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.media.tv;
+package android.os;
 
-parcelable TvChannelInfo;
+parcelable CombinedVibrationEffect;
diff --git a/core/java/android/os/CombinedVibrationEffect.java b/core/java/android/os/CombinedVibrationEffect.java
new file mode 100644
index 0000000..77bfa57
--- /dev/null
+++ b/core/java/android/os/CombinedVibrationEffect.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.os;
+
+import android.annotation.NonNull;
+
+import java.util.Objects;
+
+/**
+ * A CombinedVibrationEffect describes a haptic effect to be performed by one or more {@link
+ * Vibrator Vibrators}.
+ *
+ * These effects may be any number of things, from single shot vibrations to complex waveforms.
+ *
+ * @hide
+ * @see VibrationEffect
+ */
+public abstract class CombinedVibrationEffect implements Parcelable {
+    private static final int PARCEL_TOKEN_MONO = 1;
+
+    /** @hide to prevent subclassing from outside of the framework */
+    public CombinedVibrationEffect() {
+    }
+
+    /**
+     * Create a synced vibration effect.
+     *
+     * A synced vibration effect should be performed by multiple vibrators at the same time.
+     *
+     * @param effect The {@link VibrationEffect} to perform
+     * @return The desired combined effect.
+     */
+    @NonNull
+    public static CombinedVibrationEffect createSynced(@NonNull VibrationEffect effect) {
+        CombinedVibrationEffect combined = new Mono(effect);
+        combined.validate();
+        return combined;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** @hide */
+    public abstract void validate();
+
+    /**
+     * Represents a single {@link VibrationEffect} that should be executed in all vibrators in sync.
+     *
+     * @hide
+     */
+    public static final class Mono extends CombinedVibrationEffect {
+        private final VibrationEffect mEffect;
+
+        public Mono(Parcel in) {
+            mEffect = VibrationEffect.CREATOR.createFromParcel(in);
+        }
+
+        public Mono(@NonNull VibrationEffect effect) {
+            mEffect = effect;
+        }
+
+        public VibrationEffect getEffect() {
+            return mEffect;
+        }
+
+        /** @hide */
+        @Override
+        public void validate() {
+            mEffect.validate();
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (!(o instanceof CombinedVibrationEffect.Mono)) {
+                return false;
+            }
+            CombinedVibrationEffect.Mono other = (CombinedVibrationEffect.Mono) o;
+            return other.mEffect.equals(other.mEffect);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mEffect);
+        }
+
+        @Override
+        public String toString() {
+            return "Mono{mEffect=" + mEffect + '}';
+        }
+
+        @Override
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeInt(PARCEL_TOKEN_MONO);
+            mEffect.writeToParcel(out, flags);
+        }
+
+        @NonNull
+        public static final Parcelable.Creator<Mono> CREATOR =
+                new Parcelable.Creator<Mono>() {
+                    @Override
+                    public Mono createFromParcel(@NonNull Parcel in) {
+                        // Skip the type token
+                        in.readInt();
+                        return new Mono(in);
+                    }
+
+                    @Override
+                    @NonNull
+                    public Mono[] newArray(int size) {
+                        return new Mono[size];
+                    }
+                };
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<CombinedVibrationEffect> CREATOR =
+            new Parcelable.Creator<CombinedVibrationEffect>() {
+                @Override
+                public CombinedVibrationEffect createFromParcel(Parcel in) {
+                    int token = in.readInt();
+                    if (token == PARCEL_TOKEN_MONO) {
+                        return new CombinedVibrationEffect.Mono(in);
+                    } else {
+                        throw new IllegalStateException(
+                                "Unexpected combined vibration event type token in parcel.");
+                    }
+                }
+
+                @Override
+                public CombinedVibrationEffect[] newArray(int size) {
+                    return new CombinedVibrationEffect[size];
+                }
+            };
+}
diff --git a/core/java/android/os/CoolingDevice.java b/core/java/android/os/CoolingDevice.java
index 0e86a38..4babd4b 100644
--- a/core/java/android/os/CoolingDevice.java
+++ b/core/java/android/os/CoolingDevice.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.hardware.thermal.V2_0.CoolingType;
 
 import com.android.internal.util.Preconditions;
@@ -128,7 +129,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (!(o instanceof CoolingDevice)) {
             return false;
         }
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index a2e53e2..4fed932 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -2468,7 +2468,7 @@
     @UnsupportedAppUsage
     public static String getCallers(final int depth) {
         final StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
-        StringBuffer sb = new StringBuffer();
+        StringBuilder sb = new StringBuilder();
         for (int i = 0; i < depth; i++) {
             sb.append(getCaller(callStack, i)).append(" ");
         }
@@ -2483,7 +2483,7 @@
      */
     public static String getCallers(final int start, int depth) {
         final StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
-        StringBuffer sb = new StringBuffer();
+        StringBuilder sb = new StringBuilder();
         depth += start;
         for (int i = start; i < depth; i++) {
             sb.append(getCaller(callStack, i)).append(" ");
@@ -2501,7 +2501,7 @@
      */
     public static String getCallers(final int depth, String linePrefix) {
         final StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
-        StringBuffer sb = new StringBuffer();
+        StringBuilder sb = new StringBuilder();
         for (int i = 0; i < depth; i++) {
             sb.append(linePrefix).append(getCaller(callStack, i)).append("\n");
         }
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 085681d..5745187 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -256,7 +256,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static @NonNull File getOemDirectory() {
         return DIR_OEM_ROOT;
     }
@@ -268,7 +267,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static @NonNull File getOdmDirectory() {
         return DIR_ODM_ROOT;
     }
@@ -279,7 +277,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static @NonNull File getVendorDirectory() {
         return DIR_VENDOR_ROOT;
     }
@@ -291,7 +288,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static @NonNull File getProductDirectory() {
         return DIR_PRODUCT_ROOT;
     }
@@ -318,7 +314,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static @NonNull File getSystemExtDirectory() {
         return DIR_SYSTEM_EXT_ROOT;
     }
diff --git a/core/java/android/os/ExternalVibration.java b/core/java/android/os/ExternalVibration.java
index e62244f..7fd02116 100644
--- a/core/java/android/os/ExternalVibration.java
+++ b/core/java/android/os/ExternalVibration.java
@@ -17,6 +17,7 @@
 package android.os;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.media.AudioAttributes;
 import android.util.Slog;
 
@@ -137,7 +138,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (o == null || !(o instanceof ExternalVibration)) {
             return false;
         }
diff --git a/core/java/android/os/FileObserver.java b/core/java/android/os/FileObserver.java
index ca303d9..25bffbc 100644
--- a/core/java/android/os/FileObserver.java
+++ b/core/java/android/os/FileObserver.java
@@ -21,6 +21,7 @@
 import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.util.Log;
+import android.util.SparseArray;
 
 import java.io.File;
 import java.lang.annotation.Retention;
@@ -101,7 +102,9 @@
     private static final String LOG_TAG = "FileObserver";
 
     private static class ObserverThread extends Thread {
+        /** Temporarily retained; appears to be missing UnsupportedAppUsage annotation */
         private HashMap<Integer, WeakReference> m_observers = new HashMap<Integer, WeakReference>();
+        private SparseArray<WeakReference> mRealObservers = new SparseArray<>();
         private int m_fd;
 
         public ObserverThread() {
@@ -127,10 +130,10 @@
 
             final WeakReference<FileObserver> fileObserverWeakReference =
                     new WeakReference<>(observer);
-            synchronized (m_observers) {
+            synchronized (mRealObservers) {
                 for (int wfd : wfds) {
                     if (wfd >= 0) {
-                        m_observers.put(wfd, fileObserverWeakReference);
+                        mRealObservers.put(wfd, fileObserverWeakReference);
                     }
                 }
             }
@@ -147,12 +150,12 @@
             // look up our observer, fixing up the map if necessary...
             FileObserver observer = null;
 
-            synchronized (m_observers) {
-                WeakReference weak = m_observers.get(wfd);
+            synchronized (mRealObservers) {
+                WeakReference weak = mRealObservers.get(wfd);
                 if (weak != null) {  // can happen with lots of events from a dead wfd
                     observer = (FileObserver) weak.get();
                     if (observer == null) {
-                        m_observers.remove(wfd);
+                        mRealObservers.remove(wfd);
                     }
                 }
             }
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 70c924a..bbafc7b 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -40,9 +40,16 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
+import android.app.AppGlobals;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ContentResolver;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.content.res.AssetFileDescriptor;
+import android.net.Uri;
 import android.provider.DocumentsContract.Document;
+import android.provider.MediaStore;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.StructStat;
@@ -118,6 +125,7 @@
 
     // non-final so it can be toggled by Robolectric's ShadowFileUtils
     private static boolean sEnableCopyOptimizations = true;
+    private static volatile int sMediaProviderAppId = -1;
 
     private static final long COPY_CHECKPOINT_BYTES = 524288;
 
@@ -1425,6 +1433,54 @@
     }
 
     /** {@hide} */
+    public static FileDescriptor convertToModernFd(FileDescriptor fd) {
+        try {
+            Context context = AppGlobals.getInitialApplication();
+            if (UserHandle.getAppId(Process.myUid()) == getMediaProviderAppId(context)) {
+                // Never convert modern fd for MediaProvider, because this requires
+                // MediaStore#scanFile and can cause infinite loops when MediaProvider scans
+                return null;
+            }
+            File realFile = ParcelFileDescriptor.getFile(fd);
+            Log.i(TAG, "Changing to modern format dataSource for: " + realFile);
+            ContentResolver resolver = context.getContentResolver();
+
+            Uri uri = MediaStore.scanFile(resolver, realFile);
+            if (uri != null) {
+                Bundle opts = new Bundle();
+                // TODO(b/158465539): Use API constant
+                opts.putBoolean("android.provider.extra.ACCEPT_ORIGINAL_MEDIA_FORMAT", true);
+                AssetFileDescriptor afd = resolver.openTypedAssetFileDescriptor(uri, "*/*", opts);
+                Log.i(TAG, "Changed to modern format dataSource for: " + realFile);
+                return afd.getFileDescriptor();
+            } else {
+                Log.i(TAG, "Failed to change to modern format dataSource for: " + realFile);
+            }
+        } catch (Exception e) {
+            Log.w(TAG, "Failed to change to modern format dataSource");
+        }
+        return null;
+    }
+
+    private static int getMediaProviderAppId(Context context) {
+        if (sMediaProviderAppId != -1) {
+            return sMediaProviderAppId;
+        }
+
+        PackageManager pm = context.getPackageManager();
+        ProviderInfo provider = context.getPackageManager().resolveContentProvider(
+                MediaStore.AUTHORITY, PackageManager.MATCH_DIRECT_BOOT_AWARE
+                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+                | PackageManager.MATCH_SYSTEM_ONLY);
+        if (provider == null) {
+            return -1;
+        }
+
+        sMediaProviderAppId = UserHandle.getAppId(provider.applicationInfo.uid);
+        return sMediaProviderAppId;
+    }
+
+    /** {@hide} */
     @VisibleForTesting
     public static class MemoryPipe extends Thread implements AutoCloseable {
         private final FileDescriptor[] pipe;
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 6ba1627..be21fea 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -109,11 +109,11 @@
     private static final int UPDATABLE_DRIVER_GLOBAL_OPT_IN_PRERELEASE_DRIVER = 2;
     private static final int UPDATABLE_DRIVER_GLOBAL_OPT_IN_OFF = 3;
 
-    // Values for GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE
+    // Values for ANGLE_GL_DRIVER_ALL_ANGLE
     private static final int ANGLE_GL_DRIVER_ALL_ANGLE_ON = 1;
     private static final int ANGLE_GL_DRIVER_ALL_ANGLE_OFF = 0;
 
-    // Values for GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES
+    // Values for ANGLE_GL_DRIVER_SELECTION_VALUES
     private static final String ANGLE_GL_DRIVER_CHOICE_DEFAULT = "default";
     private static final String ANGLE_GL_DRIVER_CHOICE_ANGLE = "angle";
     private static final String ANGLE_GL_DRIVER_CHOICE_NATIVE = "native";
@@ -382,11 +382,11 @@
         final int allUseAngle;
         if (bundle != null) {
             allUseAngle =
-                    bundle.getInt(Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE);
+                    bundle.getInt(Settings.Global.ANGLE_GL_DRIVER_ALL_ANGLE);
         } else {
             ContentResolver contentResolver = context.getContentResolver();
             allUseAngle = Settings.Global.getInt(contentResolver,
-                    Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE,
+                    Settings.Global.ANGLE_GL_DRIVER_ALL_ANGLE,
                     ANGLE_GL_DRIVER_ALL_ANGLE_OFF);
         }
         if (allUseAngle == ANGLE_GL_DRIVER_ALL_ANGLE_ON) {
@@ -402,10 +402,10 @@
         final ContentResolver contentResolver = context.getContentResolver();
         final List<String> optInPackages =
                 getGlobalSettingsString(contentResolver, bundle,
-                        Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS);
+                        Settings.Global.ANGLE_GL_DRIVER_SELECTION_PKGS);
         final List<String> optInValues =
                 getGlobalSettingsString(contentResolver, bundle,
-                        Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES);
+                        Settings.Global.ANGLE_GL_DRIVER_SELECTION_VALUES);
 
         // Make sure we have good settings to use
         if (optInPackages.size() != optInValues.size()) {
@@ -462,11 +462,11 @@
 
         if (coreSettings != null) {
             debugPackage =
-                    coreSettings.getString(Settings.Global.GLOBAL_SETTINGS_ANGLE_DEBUG_PACKAGE);
+                    coreSettings.getString(Settings.Global.ANGLE_DEBUG_PACKAGE);
         } else {
             ContentResolver contentResolver = context.getContentResolver();
             debugPackage = Settings.Global.getString(contentResolver,
-                    Settings.Global.GLOBAL_SETTINGS_ANGLE_DEBUG_PACKAGE);
+                    Settings.Global.ANGLE_DEBUG_PACKAGE);
         }
         if (TextUtils.isEmpty(debugPackage)) {
             return "";
@@ -578,7 +578,7 @@
         final ContentResolver contentResolver = context.getContentResolver();
         final List<String> angleAllowlist =
                 getGlobalSettingsString(contentResolver, bundle,
-                    Settings.Global.GLOBAL_SETTINGS_ANGLE_ALLOWLIST);
+                    Settings.Global.ANGLE_ALLOWLIST);
 
         if (DEBUG) Log.v(TAG, "ANGLE allowlist: " + angleAllowlist);
 
@@ -678,7 +678,7 @@
         try {
             ContentResolver contentResolver = context.getContentResolver();
             final int showDialogBox = Settings.Global.getInt(contentResolver,
-                    Settings.Global.GLOBAL_SETTINGS_SHOW_ANGLE_IN_USE_DIALOG_BOX);
+                    Settings.Global.SHOW_ANGLE_IN_USE_DIALOG_BOX);
 
             return (showDialogBox == 1);
         } catch (Settings.SettingNotFoundException | SecurityException e) {
diff --git a/core/java/android/os/HidlMemory.java b/core/java/android/os/HidlMemory.java
index 02d1e0c..26fc6f0 100644
--- a/core/java/android/os/HidlMemory.java
+++ b/core/java/android/os/HidlMemory.java
@@ -20,7 +20,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 
 import java.io.Closeable;
 import java.io.IOException;
@@ -41,7 +40,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public class HidlMemory implements Closeable {
     private final @NonNull String mName;
     private final long mSize;
diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java
index 64ab1d7..0d2bfdf 100644
--- a/core/java/android/os/HwBinder.java
+++ b/core/java/android/os/HwBinder.java
@@ -17,7 +17,6 @@
 package android.os;
 
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 
 import libcore.util.NativeAllocationRegistry;
@@ -26,7 +25,6 @@
 
 /** @hide */
 @SystemApi
-@TestApi
 public abstract class HwBinder implements IHwBinder {
     private static final String TAG = "HwBinder";
 
diff --git a/core/java/android/os/HwBlob.java b/core/java/android/os/HwBlob.java
index 154227b2..a43fbdb 100644
--- a/core/java/android/os/HwBlob.java
+++ b/core/java/android/os/HwBlob.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 
 import libcore.util.NativeAllocationRegistry;
 
@@ -30,7 +29,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public class HwBlob {
     private static final String TAG = "HwBlob";
 
diff --git a/core/java/android/os/HwParcel.java b/core/java/android/os/HwParcel.java
index 228548a..9fd37d4 100644
--- a/core/java/android/os/HwParcel.java
+++ b/core/java/android/os/HwParcel.java
@@ -20,7 +20,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 
 import dalvik.annotation.optimization.FastNative;
@@ -34,7 +33,6 @@
 
 /** @hide */
 @SystemApi
-@TestApi
 public class HwParcel {
     private static final String TAG = "HwParcel";
 
diff --git a/core/java/android/os/IHwBinder.java b/core/java/android/os/IHwBinder.java
index 46fa6ef..249eb3a 100644
--- a/core/java/android/os/IHwBinder.java
+++ b/core/java/android/os/IHwBinder.java
@@ -17,11 +17,9 @@
 package android.os;
 
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 
 /** @hide */
 @SystemApi
-@TestApi
 public interface IHwBinder {
     /**
      * Process a hwbinder transaction.
diff --git a/core/java/android/os/IHwInterface.java b/core/java/android/os/IHwInterface.java
index 0a5a715..f21f6e3 100644
--- a/core/java/android/os/IHwInterface.java
+++ b/core/java/android/os/IHwInterface.java
@@ -17,11 +17,9 @@
 package android.os;
 
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 
 /** @hide */
 @SystemApi
-@TestApi
 public interface IHwInterface {
     /**
      * @return the binder object that corresponds to this interface.
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 07363ed..6fe5777 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -86,6 +86,7 @@
     Bundle getApplicationRestrictionsForUser(in String packageName, int userId);
     void setDefaultGuestRestrictions(in Bundle restrictions);
     Bundle getDefaultGuestRestrictions();
+    int removeUserOrSetEphemeral(int userId);
     boolean markGuestForDeletion(int userId);
     UserInfo findCurrentGuestUser();
     boolean isQuietModeEnabled(int userId);
diff --git a/core/java/android/os/IVibratorManagerService.aidl b/core/java/android/os/IVibratorManagerService.aidl
index e821e31..08d2019 100644
--- a/core/java/android/os/IVibratorManagerService.aidl
+++ b/core/java/android/os/IVibratorManagerService.aidl
@@ -16,9 +16,13 @@
 
 package android.os;
 
+import android.os.CombinedVibrationEffect;
 import android.os.VibrationAttributes;
 
 /** {@hide} */
 interface IVibratorManagerService {
     int[] getVibratorIds();
+    void vibrate(int uid, String opPkg, in CombinedVibrationEffect effect,
+            in VibrationAttributes attributes, String reason, IBinder token);
+    void cancelVibrate(IBinder token);
 }
diff --git a/core/java/android/os/IncidentManager.java b/core/java/android/os/IncidentManager.java
index 565d31a..a543a2d 100644
--- a/core/java/android/os/IncidentManager.java
+++ b/core/java/android/os/IncidentManager.java
@@ -23,7 +23,6 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
-import android.annotation.TestApi;
 import android.content.Context;
 import android.net.Uri;
 import android.util.Slog;
@@ -45,7 +44,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 @SystemService(Context.INCIDENT_SERVICE)
 public class IncidentManager {
     private static final String TAG = "IncidentManager";
@@ -159,7 +157,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static class PendingReport {
         /**
          * Encoded data.
@@ -277,7 +274,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static class IncidentReport implements Parcelable, Closeable {
         private final long mTimestampNs;
         private final int mPrivacyPolicy;
diff --git a/core/java/android/os/IncidentReportArgs.java b/core/java/android/os/IncidentReportArgs.java
index 7e858e1..73e4914 100644
--- a/core/java/android/os/IncidentReportArgs.java
+++ b/core/java/android/os/IncidentReportArgs.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.IntArray;
@@ -30,7 +29,6 @@
  * {@hide}
  */
 @SystemApi
-@TestApi
 public final class IncidentReportArgs implements Parcelable {
 
     private final IntArray mSections = new IntArray();
diff --git a/core/java/android/os/LocaleList.java b/core/java/android/os/LocaleList.java
index 9c0bc45..ee64551 100644
--- a/core/java/android/os/LocaleList.java
+++ b/core/java/android/os/LocaleList.java
@@ -93,7 +93,7 @@
     }
 
     @Override
-    public boolean equals(Object other) {
+    public boolean equals(@Nullable Object other) {
         if (other == this)
             return true;
         if (!(other instanceof LocaleList))
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index b05ea39..c39fd4d 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -155,6 +155,7 @@
     /**
      * Poll and deliver single message, return true if the outer loop should continue.
      */
+    @SuppressWarnings("AndroidFrameworkBinderIdentity")
     private static boolean loopOnce(final Looper me,
             final long ident, final int thresholdOverride) {
         Message msg = me.mQueue.next(); // might block
@@ -255,6 +256,7 @@
      * Run the message queue in this thread. Be sure to call
      * {@link #quit()} to end the loop.
      */
+    @SuppressWarnings("AndroidFrameworkBinderIdentity")
     public static void loop() {
         final Looper me = myLooper();
         if (me == null) {
diff --git a/core/java/android/os/Messenger.java b/core/java/android/os/Messenger.java
index ed5c470..ed851b3 100644
--- a/core/java/android/os/Messenger.java
+++ b/core/java/android/os/Messenger.java
@@ -16,6 +16,8 @@
 
 package android.os;
 
+import android.annotation.Nullable;
+
 /**
  * Reference to a Handler, which others can use to send messages to it.
  * This allows for the implementation of message-based communication across
@@ -71,7 +73,7 @@
      * Comparison operator on two Messenger objects, such that true
      * is returned then they both point to the same Handler.
      */
-    public boolean equals(Object otherObj) {
+    public boolean equals(@Nullable Object otherObj) {
         if (otherObj == null) {
             return false;
         }
diff --git a/core/java/android/os/NativeHandle.java b/core/java/android/os/NativeHandle.java
index 8d341b6..a26873a 100644
--- a/core/java/android/os/NativeHandle.java
+++ b/core/java/android/os/NativeHandle.java
@@ -20,7 +20,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.system.ErrnoException;
 import android.system.Os;
 
@@ -33,7 +32,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class NativeHandle implements Closeable {
     // whether this object owns mFds
     private boolean mOwn = false;
diff --git a/core/java/android/os/NullVibrator.java b/core/java/android/os/NullVibrator.java
index 6d8ab6d..6bb0165 100644
--- a/core/java/android/os/NullVibrator.java
+++ b/core/java/android/os/NullVibrator.java
@@ -16,8 +16,6 @@
 
 package android.os;
 
-import android.media.AudioAttributes;
-
 /**
  * Vibrator implementation that does nothing.
  *
@@ -50,7 +48,7 @@
 
     @Override
     public void vibrate(int uid, String opPkg, VibrationEffect effect,
-            String reason, AudioAttributes attributes) {
+            String reason, VibrationAttributes attributes) {
     }
 
     @Override
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 13d5f6a..765ef48 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -2010,13 +2010,13 @@
      * A map used by {@link #readSquashed} to cache parcelables. It's a map from
      * an absolute position in a Parcel to the parcelable stored at the position.
      */
-    private ArrayMap<Integer, Parcelable> mReadSquashableParcelables;
+    private SparseArray<Parcelable> mReadSquashableParcelables;
 
     private void ensureReadSquashableParcelables() {
         if (mReadSquashableParcelables != null) {
             return;
         }
-        mReadSquashableParcelables = new ArrayMap<>();
+        mReadSquashableParcelables = new SparseArray<>();
     }
 
     /**
@@ -2112,9 +2112,13 @@
 
         final Parcelable p = mReadSquashableParcelables.get(firstAbsolutePos);
         if (p == null) {
+            final StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < mReadSquashableParcelables.size(); i++) {
+                sb.append(mReadSquashableParcelables.keyAt(i)).append(' ');
+            }
             Slog.wtfStack(TAG, "Map doesn't contain offset "
                     + firstAbsolutePos
-                    + " : contains=" + new ArrayList<>(mReadSquashableParcelables.keySet()));
+                    + " : contains=" + sb.toString());
         }
         return (T) p;
     }
diff --git a/core/java/android/os/ParcelUuid.java b/core/java/android/os/ParcelUuid.java
index 0b52c75..b529694 100644
--- a/core/java/android/os/ParcelUuid.java
+++ b/core/java/android/os/ParcelUuid.java
@@ -16,6 +16,7 @@
 
 package android.os;
 
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 
 import java.util.UUID;
@@ -91,7 +92,7 @@
     *         or {@code false} if not.
     */
    @Override
-   public boolean equals(Object object) {
+   public boolean equals(@Nullable Object object) {
        if (object == null) {
            return false;
        }
diff --git a/core/java/android/os/Parcelable.java b/core/java/android/os/Parcelable.java
index f14f66b..7a624e1 100644
--- a/core/java/android/os/Parcelable.java
+++ b/core/java/android/os/Parcelable.java
@@ -120,7 +120,7 @@
      * @see ParcelableHolder
      * @hide
      */
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
     public static final int PARCELABLE_STABILITY_LOCAL = 0x0000;
     /**
      * Something that is meant to be used between system and vendor.
@@ -128,7 +128,7 @@
      * @see ParcelableHolder
      * @hide
      */
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
     public static final int PARCELABLE_STABILITY_VINTF = 0x0001;
 
     /**
diff --git a/core/java/android/os/ParcelableHolder.java b/core/java/android/os/ParcelableHolder.java
index 181f94b..95c07b6 100644
--- a/core/java/android/os/ParcelableHolder.java
+++ b/core/java/android/os/ParcelableHolder.java
@@ -18,12 +18,54 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.util.MathUtils;
 
 /**
- * Parcelable containing the other Parcelable object.
+ * ParcelableHolder is a Parcelable which can contain another Parcelable.
+ * The main use case of ParcelableHolder is to make a Parcelable extensible.
+ * For example, an AOSP-defined Parcelable <code>AospDefinedParcelable</code>
+ * is expected to be extended by device implementers for their value-add features.
+ * Previously without ParcelableHolder, the device implementers had to
+ * directly modify the Parcelable to add more fields:
+ * <pre> {@code
+ * parcelable AospDefinedParcelable {
+ *   int a;
+ *   String b;
+ *   String x; // added by a device implementer
+ *   int[] y; // added by a device implementer
+ * }}</pre>
+ *
+ * This practice is very error-prone because the fields added by the device implementer
+ * might have a conflict when the Parcelable is revisioned in the next releases of Android.
+ *
+ * Using ParcelableHolder, one can define an extension point in a Parcelable.
+ * <pre> {@code
+ * parcelable AospDefinedParcelable {
+ *   int a;
+ *   String b;
+ *   ParcelableHolder extension;
+ * }}</pre>
+ * Then the device implementers can define their own Parcelable for their extension.
+ *
+ * <pre> {@code
+ * parcelable OemDefinedParcelable {
+ *   String x;
+ *   int[] y;
+ * }}</pre>
+ * Finally, the new Parcelable can be attached to the original Parcelable via
+ * the ParcelableHolder field.
+ *
+ * <pre> {@code
+ * AospDefinedParcelable ap = ...;
+ * OemDefinedParcelable op = new OemDefinedParcelable();
+ * op.x = ...;
+ * op.y = ...;
+ * ap.extension.setParcelable(op);}</pre>
+ *
  * @hide
  */
+@SystemApi
 public final class ParcelableHolder implements Parcelable {
     /**
      * This is set by {@link #setParcelable}.
@@ -80,7 +122,7 @@
      * Write a parcelable into ParcelableHolder, the previous parcelable will be removed.
      * @return {@code false} if the parcelable's stability is more unstable ParcelableHolder.
      */
-    public synchronized boolean setParcelable(@Nullable Parcelable p) {
+    public boolean setParcelable(@Nullable Parcelable p) {
         // a ParcelableHolder can only hold things at its stability or higher
         if (p != null && this.getStability() > p.getStability()) {
             return false;
@@ -99,7 +141,7 @@
      *         the type written by (@link #setParcelable}.
      */
     @Nullable
-    public synchronized <T extends Parcelable> T getParcelable(@NonNull Class<T> clazz) {
+    public <T extends Parcelable> T getParcelable(@NonNull Class<T> clazz) {
         if (mParcel == null) {
             if (!clazz.isInstance(mParcelable)) {
                 return null;
@@ -123,7 +165,7 @@
     /**
      * Read ParcelableHolder from a parcel.
      */
-    public synchronized void readFromParcel(@NonNull Parcel parcel) {
+    public void readFromParcel(@NonNull Parcel parcel) {
         this.mStability = parcel.readInt();
 
         mParcelable = null;
@@ -145,7 +187,7 @@
     }
 
     @Override
-    public synchronized void writeToParcel(@NonNull Parcel parcel, int flags) {
+    public void writeToParcel(@NonNull Parcel parcel, int flags) {
         parcel.writeInt(this.mStability);
 
         if (mParcel != null) {
@@ -166,7 +208,7 @@
     }
 
     @Override
-    public synchronized int describeContents() {
+    public int describeContents() {
         if (mParcel != null) {
             return mParcel.hasFileDescriptors() ? Parcelable.CONTENTS_FILE_DESCRIPTOR : 0;
         }
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 50f0c28..e736e30 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -1565,7 +1565,6 @@
      * @see #isPowerSaveMode()
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(anyOf = {
             android.Manifest.permission.DEVICE_POWER,
             android.Manifest.permission.POWER_SAVER
@@ -1610,7 +1609,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(permission.POWER_SAVER)
     public boolean setDynamicPowerSaveHint(boolean powerSaveHint, int disableThreshold) {
         try {
@@ -1670,7 +1668,6 @@
      *  @hide
      */
     @SystemApi
-    @TestApi
     public static final int POWER_SAVE_MODE_TRIGGER_PERCENTAGE = 0;
 
     /**
@@ -1683,7 +1680,6 @@
      *  @hide
      */
     @SystemApi
-    @TestApi
     public static final int POWER_SAVE_MODE_TRIGGER_DYNAMIC = 1;
 
     /** @hide */
@@ -1708,7 +1704,6 @@
      */
     @AutoPowerSaveModeTriggers
     @SystemApi
-    @TestApi
     @RequiresPermission(android.Manifest.permission.POWER_SAVER)
     public int getPowerSaveModeTrigger() {
         try {
@@ -1732,7 +1727,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
     public void setBatteryDischargePrediction(@NonNull Duration timeRemaining,
             boolean isPersonalized) {
@@ -2005,7 +1999,7 @@
         Preconditions.checkNotNull(listener, "listener cannot be null");
         Preconditions.checkNotNull(executor, "executor cannot be null");
         Preconditions.checkArgument(!mListenerMap.containsKey(listener),
-                "Listener already registered: " + listener);
+                "Listener already registered: %s", listener);
         IThermalStatusListener internalListener = new IThermalStatusListener.Stub() {
             @Override
             public void onStatusChange(int status) {
diff --git a/core/java/android/os/RemoteCallback.java b/core/java/android/os/RemoteCallback.java
index 373060f..49f84adf 100644
--- a/core/java/android/os/RemoteCallback.java
+++ b/core/java/android/os/RemoteCallback.java
@@ -19,14 +19,12 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * @hide
  */
 @SystemApi
-@TestApi
 public final class RemoteCallback implements Parcelable {
 
     public interface OnResultListener {
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 6c5b04a6..0fba895 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -60,6 +60,7 @@
 import android.util.Printer;
 import android.util.Singleton;
 import android.util.Slog;
+import android.util.SparseLongArray;
 import android.view.IWindowManager;
 
 import com.android.internal.annotations.GuardedBy;
@@ -1525,7 +1526,9 @@
         // Map from violation stacktrace hashcode -> uptimeMillis of
         // last violation.  No locking needed, as this is only
         // accessed by the same thread.
+        /** Temporarily retained; appears to be missing UnsupportedAppUsage annotation */
         private ArrayMap<Integer, Long> mLastViolationTime;
+        private SparseLongArray mRealLastViolationTime;
 
         public AndroidBlockGuardPolicy(@ThreadPolicyMask int threadPolicyMask) {
             mThreadPolicyMask = threadPolicyMask;
@@ -1759,17 +1762,17 @@
             long lastViolationTime = 0;
             long now = SystemClock.uptimeMillis();
             if (sLogger == LOGCAT_LOGGER) { // Don't throttle it if there is a non-default logger
-                if (mLastViolationTime != null) {
-                    Long vtime = mLastViolationTime.get(crashFingerprint);
+                if (mRealLastViolationTime != null) {
+                    Long vtime = mRealLastViolationTime.get(crashFingerprint);
                     if (vtime != null) {
                         lastViolationTime = vtime;
                     }
-                    clampViolationTimeMap(mLastViolationTime, Math.max(MIN_LOG_INTERVAL_MS,
+                    clampViolationTimeMap(mRealLastViolationTime, Math.max(MIN_LOG_INTERVAL_MS,
                                 Math.max(MIN_DIALOG_INTERVAL_MS, MIN_DROPBOX_INTERVAL_MS)));
                 } else {
-                    mLastViolationTime = new ArrayMap<>(1);
+                    mRealLastViolationTime = new SparseLongArray(1);
                 }
-                mLastViolationTime.put(crashFingerprint, now);
+                mRealLastViolationTime.put(crashFingerprint, now);
             }
             long timeSinceLastViolationMillis =
                     lastViolationTime == 0 ? Long.MAX_VALUE : (now - lastViolationTime);
@@ -2165,16 +2168,17 @@
         }
 
         final int uid = android.os.Process.myUid();
-        String msg = "Detected cleartext network traffic from UID " + uid;
+        final StringBuilder msg = new StringBuilder("Detected cleartext network traffic from UID ")
+                .append(uid);
         if (rawAddr != null) {
             try {
-                msg += " to " + InetAddress.getByAddress(rawAddr);
+                msg.append(" to ").append(InetAddress.getByAddress(rawAddr));
             } catch (UnknownHostException ignored) {
             }
         }
-        msg += HexDump.dumpHexString(firstPacket).trim() + " ";
+        msg.append(HexDump.dumpHexString(firstPacket).trim()).append(' ');
         final boolean forceDeath = (sVmPolicy.mask & PENALTY_DEATH_ON_CLEARTEXT_NETWORK) != 0;
-        onVmPolicyViolation(new CleartextNetworkViolation(msg), forceDeath);
+        onVmPolicyViolation(new CleartextNetworkViolation(msg.toString()), forceDeath);
     }
 
     /** @hide */
@@ -2231,18 +2235,19 @@
     // Map from VM violation fingerprint to uptime millis.
     @UnsupportedAppUsage
     private static final HashMap<Integer, Long> sLastVmViolationTime = new HashMap<>();
+    private static final SparseLongArray sRealLastVmViolationTime = new SparseLongArray();
 
     /**
      * Clamp the given map by removing elements with timestamp older than the given retainSince.
      */
-    private static void clampViolationTimeMap(final @NonNull Map<Integer, Long> violationTime,
+    private static void clampViolationTimeMap(final @NonNull SparseLongArray violationTime,
             final long retainSince) {
-        final Iterator<Map.Entry<Integer, Long>> iterator = violationTime.entrySet().iterator();
-        while (iterator.hasNext()) {
-            Map.Entry<Integer, Long> e = iterator.next();
-            if (e.getValue() < retainSince) {
+        for (int i = 0; i < violationTime.size(); ) {
+            if (violationTime.valueAt(i) < retainSince) {
                 // Remove stale entries
-                iterator.remove();
+                violationTime.removeAt(i);
+            } else {
+                i++;
             }
         }
         // Ideally we'd cap the total size of the map, though it'll involve quickselect of topK,
@@ -2273,15 +2278,15 @@
         long lastViolationTime;
         long timeSinceLastViolationMillis = Long.MAX_VALUE;
         if (sLogger == LOGCAT_LOGGER) { // Don't throttle it if there is a non-default logger
-            synchronized (sLastVmViolationTime) {
-                if (sLastVmViolationTime.containsKey(fingerprint)) {
-                    lastViolationTime = sLastVmViolationTime.get(fingerprint);
+            synchronized (sRealLastVmViolationTime) {
+                if (sRealLastVmViolationTime.indexOfKey(fingerprint) >= 0) {
+                    lastViolationTime = sRealLastVmViolationTime.get(fingerprint);
                     timeSinceLastViolationMillis = now - lastViolationTime;
                 }
                 if (timeSinceLastViolationMillis > MIN_VM_INTERVAL_MS) {
-                    sLastVmViolationTime.put(fingerprint, now);
+                    sRealLastVmViolationTime.put(fingerprint, now);
                 }
-                clampViolationTimeMap(sLastVmViolationTime,
+                clampViolationTimeMap(sRealLastVmViolationTime,
                         now - Math.max(MIN_VM_INTERVAL_MS, MIN_LOG_INTERVAL_MS));
             }
         }
diff --git a/core/java/android/os/SystemConfigManager.java b/core/java/android/os/SystemConfigManager.java
index 12a1ffa..3f0632b 100644
--- a/core/java/android/os/SystemConfigManager.java
+++ b/core/java/android/os/SystemConfigManager.java
@@ -20,7 +20,6 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
-import android.annotation.TestApi;
 import android.content.Context;
 import android.util.ArraySet;
 import android.util.Log;
@@ -40,7 +39,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 @SystemService(Context.SYSTEM_CONFIG_SERVICE)
 public class SystemConfigManager {
     private static final String TAG = SystemConfigManager.class.getSimpleName();
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java
index c5e5cc4..a164527 100644
--- a/core/java/android/os/SystemProperties.java
+++ b/core/java/android/os/SystemProperties.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.util.Log;
 import android.util.MutableInt;
@@ -52,7 +51,6 @@
  * {@hide}
  */
 @SystemApi
-@TestApi
 public class SystemProperties {
     private static final String TAG = "SystemProperties";
     private static final boolean TRACK_KEY_ACCESS = false;
@@ -146,7 +144,6 @@
      */
     @NonNull
     @SystemApi
-    @TestApi
     public static String get(@NonNull String key) {
         if (TRACK_KEY_ACCESS) onKeyAccess(key);
         return native_get(key);
@@ -163,7 +160,6 @@
      */
     @NonNull
     @SystemApi
-    @TestApi
     public static String get(@NonNull String key, @Nullable String def) {
         if (TRACK_KEY_ACCESS) onKeyAccess(key);
         return native_get(key, def);
@@ -179,7 +175,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static int getInt(@NonNull String key, int def) {
         if (TRACK_KEY_ACCESS) onKeyAccess(key);
         return native_get_int(key, def);
@@ -195,7 +190,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static long getLong(@NonNull String key, long def) {
         if (TRACK_KEY_ACCESS) onKeyAccess(key);
         return native_get_long(key, def);
@@ -216,7 +210,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static boolean getBoolean(@NonNull String key, boolean def) {
         if (TRACK_KEY_ACCESS) onKeyAccess(key);
         return native_get_boolean(key, def);
diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java
index 2dba8dc..5c9067a 100644
--- a/core/java/android/os/SystemVibrator.java
+++ b/core/java/android/os/SystemVibrator.java
@@ -221,18 +221,14 @@
     }
 
     @Override
-    public void vibrate(int uid, String opPkg, VibrationEffect effect,
-            String reason, AudioAttributes attributes) {
+    public void vibrate(int uid, String opPkg, @NonNull VibrationEffect effect,
+            String reason, @NonNull VibrationAttributes attributes) {
         if (mService == null) {
             Log.w(TAG, "Failed to vibrate; no vibrator service.");
             return;
         }
         try {
-            if (attributes == null) {
-                attributes = new AudioAttributes.Builder().build();
-            }
-            VibrationAttributes atr = new VibrationAttributes.Builder(attributes, effect).build();
-            mService.vibrate(uid, opPkg, effect, atr, reason, mToken);
+            mService.vibrate(uid, opPkg, effect, attributes, reason, mToken);
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to vibrate.", e);
         }
diff --git a/core/java/android/os/Temperature.java b/core/java/android/os/Temperature.java
index 7caffcd..55785f3 100644
--- a/core/java/android/os/Temperature.java
+++ b/core/java/android/os/Temperature.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.hardware.thermal.V2_0.TemperatureType;
 import android.hardware.thermal.V2_0.ThrottlingSeverity;
 
@@ -171,7 +172,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (!(o instanceof Temperature)) {
             return false;
         }
diff --git a/core/java/android/os/TimestampedValue.java b/core/java/android/os/TimestampedValue.java
index 4c4335b..3d8a550 100644
--- a/core/java/android/os/TimestampedValue.java
+++ b/core/java/android/os/TimestampedValue.java
@@ -60,7 +60,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) {
             return true;
         }
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index ff1bcf6..d39c532 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -18,6 +18,7 @@
 
 import android.annotation.AppIdInt;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.annotation.UserIdInt;
@@ -46,7 +47,6 @@
 
     /** @hide A user handle to indicate all users on the device */
     @SystemApi
-    @TestApi
     public static final @NonNull UserHandle ALL = new UserHandle(USER_ALL);
 
     /** @hide A user id to indicate the currently active user */
@@ -55,7 +55,6 @@
 
     /** @hide A user handle to indicate the current user of the device */
     @SystemApi
-    @TestApi
     public static final @NonNull UserHandle CURRENT = new UserHandle(USER_CURRENT);
 
     /** @hide A user id to indicate that we would like to send to the current
@@ -106,7 +105,6 @@
 
     /** @hide A user handle to indicate the "system" user of the device */
     @SystemApi
-    @TestApi
     public static final @NonNull UserHandle SYSTEM = new UserHandle(USER_SYSTEM);
 
     /**
@@ -279,7 +277,6 @@
     }
 
     /** @hide */
-    @TestApi
     @SystemApi
     public static UserHandle of(@UserIdInt int userId) {
         if (userId == USER_SYSTEM) {
@@ -324,7 +321,6 @@
      * Returns the app id (or base uid) for a given uid, stripping out the user id from it.
      * @hide
      */
-    @TestApi
     @SystemApi
     public static @AppIdInt int getAppId(int uid) {
         return uid % PER_USER_RANGE;
@@ -482,7 +478,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static @UserIdInt int myUserId() {
         return getUserId(Process.myUid());
     }
@@ -521,7 +516,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public @UserIdInt int getIdentifier() {
         return mHandle;
     }
@@ -532,7 +526,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         try {
             if (obj != null) {
                 UserHandle other = (UserHandle)obj;
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index ddc21ab..b0e76e3 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1407,8 +1407,7 @@
      *
      * @hide
      */
-    @SystemApi
-    @TestApi // To allow seeing it from CTS.
+    @SystemApi // To allow seeing it from CTS.
     public static final String ACTION_USER_RESTRICTIONS_CHANGED =
             "android.os.action.USER_RESTRICTIONS_CHANGED";
 
@@ -1468,6 +1467,48 @@
     public @interface UserSwitchabilityResult {}
 
     /**
+     * A response code from {@link #removeUserOrSetEphemeral(int)} indicating that the specified
+     * user has been successfully removed.
+     * @hide
+     */
+    public static final int REMOVE_RESULT_REMOVED = 0;
+
+    /**
+     * A response code from {@link #removeUserOrSetEphemeral(int)} indicating that the specified
+     * user has had its {@link UserInfo#FLAG_EPHEMERAL} flag set to {@code true}, so that it will be
+     * removed when the user is stopped or on boot.
+     * @hide
+     */
+    public static final int REMOVE_RESULT_SET_EPHEMERAL = 1;
+
+    /**
+     * A response code from {@link #removeUserOrSetEphemeral(int)} indicating that the specified
+     * user is already in the process of being removed.
+     * @hide
+     */
+    public static final int REMOVE_RESULT_ALREADY_BEING_REMOVED = 2;
+
+    /**
+     * A response code from {@link #removeUserOrSetEphemeral(int)} indicating that an error occurred
+     * that prevented the user from being removed or set as ephemeral.
+     * @hide
+     */
+    public static final int REMOVE_RESULT_ERROR = 3;
+
+    /**
+     * Possible response codes from {@link #removeUserOrSetEphemeral(int)}.
+     * @hide
+     */
+    @IntDef(prefix = { "REMOVE_RESULT_" }, value = {
+            REMOVE_RESULT_REMOVED,
+            REMOVE_RESULT_SET_EPHEMERAL,
+            REMOVE_RESULT_ALREADY_BEING_REMOVED,
+            REMOVE_RESULT_ERROR,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface RemoveResult {}
+
+    /**
      * Indicates user operation is successful.
      */
     public static final int USER_OPERATION_SUCCESS = 0;
@@ -3978,6 +4019,23 @@
     }
 
     /**
+     * Immediately removes the user or, if the user cannot be removed, such as when the user is
+     * the current user, then set the user as ephemeral so that it will be removed when it is
+     * stopped.
+     *
+     * @return the {@link RemoveResult} code
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    public @RemoveResult int removeUserOrSetEphemeral(@UserIdInt int userId) {
+        try {
+            return mService.removeUserOrSetEphemeral(userId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Updates the user's name.
      *
      * @param userId the user's integer id
diff --git a/core/java/android/os/VibrationAttributes.java b/core/java/android/os/VibrationAttributes.java
index 8a7cf60..2093077 100644
--- a/core/java/android/os/VibrationAttributes.java
+++ b/core/java/android/os/VibrationAttributes.java
@@ -140,13 +140,12 @@
 
     private final int mUsage;
     private final int mFlags;
+    private final int mOriginalAudioUsage;
 
-    private final AudioAttributes mAudioAttributes;
-
-    private VibrationAttributes(int usage, int flags, @NonNull AudioAttributes audio) {
+    private VibrationAttributes(int usage, int audioUsage, int flags) {
         mUsage = usage;
+        mOriginalAudioUsage = audioUsage;
         mFlags = flags & FLAG_ALL_SUPPORTED;
-        mAudioAttributes = audio;
     }
 
     /**
@@ -182,14 +181,31 @@
     }
 
     /**
-     * Return AudioAttributes equivalent to this VibrationAttributes.
-     * @deprecated Temporary support of AudioAttributes, will be removed when out of WIP
+     * Return {@link AudioAttributes} usage equivalent to {@link #getUsage()}.
+     * @return one of {@link AudioAttributes#SDK_USAGES} that represents {@link #getUsage()}
      * @hide
      */
-    @Deprecated
     @TestApi
-    public @NonNull AudioAttributes getAudioAttributes() {
-        return mAudioAttributes;
+    public int getAudioUsage() {
+        if (mOriginalAudioUsage != AudioAttributes.USAGE_UNKNOWN) {
+            // Return same audio usage set in the Builder.
+            return mOriginalAudioUsage;
+        }
+        // Return correct audio usage based on the vibration usage set in the Builder.
+        switch (mUsage) {
+            case USAGE_NOTIFICATION:
+                return AudioAttributes.USAGE_NOTIFICATION;
+            case USAGE_COMMUNICATION_REQUEST:
+                return AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST;
+            case USAGE_RINGTONE:
+                return AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
+            case USAGE_TOUCH:
+                return AudioAttributes.USAGE_ASSISTANCE_SONIFICATION;
+            case USAGE_ALARM:
+                return AudioAttributes.USAGE_ALARM;
+            default:
+                return AudioAttributes.USAGE_UNKNOWN;
+        }
     }
 
     @Override
@@ -200,15 +216,14 @@
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeInt(mUsage);
+        dest.writeInt(mOriginalAudioUsage);
         dest.writeInt(mFlags);
-        dest.writeParcelable(mAudioAttributes, flags);
     }
 
     private VibrationAttributes(Parcel src) {
         mUsage = src.readInt();
+        mOriginalAudioUsage = src.readInt();
         mFlags = src.readInt();
-        mAudioAttributes = (AudioAttributes) src.readParcelable(
-                AudioAttributes.class.getClassLoader());
     }
 
     public static final @NonNull Parcelable.Creator<VibrationAttributes>
@@ -222,7 +237,7 @@
             };
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) {
             return true;
         }
@@ -230,18 +245,20 @@
             return false;
         }
         VibrationAttributes rhs = (VibrationAttributes) o;
-        return mUsage == rhs.mUsage && mFlags == rhs.mFlags;
+        return mUsage == rhs.mUsage && mOriginalAudioUsage == rhs.mOriginalAudioUsage
+                && mFlags == rhs.mFlags;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mUsage, mFlags);
+        return Objects.hash(mUsage, mOriginalAudioUsage, mFlags);
     }
 
     @Override
     public String toString() {
         return "VibrationAttributes:"
                 + " Usage=" + usageToString()
+                + " Audio Usage= " + AudioAttributes.usageToString(mOriginalAudioUsage)
                 + " Flags=" + mFlags;
     }
 
@@ -280,10 +297,9 @@
      */
     public static final class Builder {
         private int mUsage = USAGE_UNKNOWN;
+        private int mOriginalAudioUsage = AudioAttributes.USAGE_UNKNOWN;
         private int mFlags = 0x0;
 
-        private AudioAttributes mAudioAttributes = new AudioAttributes.Builder().build();
-
         /**
          * Constructs a new Builder with the defaults.
          */
@@ -296,8 +312,8 @@
         public Builder(@Nullable VibrationAttributes vib) {
             if (vib != null) {
                 mUsage = vib.mUsage;
+                mOriginalAudioUsage = vib.mOriginalAudioUsage;
                 mFlags = vib.mFlags;
-                mAudioAttributes = vib.mAudioAttributes;
             }
         }
 
@@ -306,9 +322,7 @@
          * @hide
          */
         @TestApi
-        public Builder(@NonNull AudioAttributes audio,
-                @Nullable VibrationEffect effect) {
-            mAudioAttributes = audio;
+        public Builder(@NonNull AudioAttributes audio, @Nullable VibrationEffect effect) {
             setUsage(audio);
             setFlags(audio);
             applyHapticFeedbackHeuristics(effect);
@@ -342,6 +356,7 @@
         }
 
         private void setUsage(@NonNull AudioAttributes audio) {
+            mOriginalAudioUsage = audio.getUsage();
             switch (audio.getUsage()) {
                 case AudioAttributes.USAGE_NOTIFICATION:
                 case AudioAttributes.USAGE_NOTIFICATION_EVENT:
@@ -379,8 +394,7 @@
          * @return a new {@link VibrationAttributes} object
          */
         public @NonNull VibrationAttributes build() {
-            VibrationAttributes ans = new VibrationAttributes(mUsage, mFlags,
-                    mAudioAttributes);
+            VibrationAttributes ans = new VibrationAttributes(mUsage, mOriginalAudioUsage, mFlags);
             return ans;
         }
 
@@ -396,6 +410,7 @@
          * @return the same Builder instance.
          */
         public @NonNull Builder setUsage(int usage) {
+            mOriginalAudioUsage = AudioAttributes.USAGE_UNKNOWN;
             mUsage = usage;
             return this;
         }
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index e02fd28..21ad38b 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -539,7 +539,7 @@
         }
 
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (!(o instanceof VibrationEffect.OneShot)) {
                 return false;
             }
@@ -705,7 +705,7 @@
         }
 
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (!(o instanceof VibrationEffect.Waveform)) {
                 return false;
             }
@@ -872,7 +872,7 @@
         }
 
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (!(o instanceof VibrationEffect.Prebaked)) {
                 return false;
             }
@@ -983,6 +983,8 @@
                 Composition.checkPrimitive(effect.id);
                 Preconditions.checkArgumentInRange(
                         effect.scale, 0.0f, 1.0f, "scale");
+                Preconditions.checkArgumentNonNegative(effect.delay,
+                        "Primitive delay must be zero or positive");
             }
         }
 
@@ -998,7 +1000,7 @@
         }
 
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (this == o) return true;
             if (o == null || getClass() != o.getClass()) return false;
             Composed composed = (Composed) o;
@@ -1244,7 +1246,7 @@
             }
 
             @Override
-            public boolean equals(Object o) {
+            public boolean equals(@Nullable Object o) {
                 if (this == o) return true;
                 if (o == null || getClass() != o.getClass()) return false;
                 PrimitiveEffect that = (PrimitiveEffect) o;
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index 86d009e..7d85d13 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -23,7 +23,6 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
-import android.annotation.TestApi;
 import android.app.ActivityThread;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
@@ -344,8 +343,24 @@
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.VIBRATE)
-    public abstract void vibrate(int uid, String opPkg, VibrationEffect vibe,
-            String reason, AudioAttributes attributes);
+    public final void vibrate(int uid, String opPkg, VibrationEffect vibe,
+            String reason, AudioAttributes attributes) {
+        if (attributes == null) {
+            attributes = new AudioAttributes.Builder().build();
+        }
+        VibrationAttributes attr = new VibrationAttributes.Builder(attributes, vibe).build();
+        vibrate(uid, opPkg, vibe, reason, attr);
+    }
+
+    /**
+     * Like {@link #vibrate(int, String, VibrationEffect, String, AudioAttributes)}, but allows the
+     * caller to specify {@link VibrationAttributes} instead of {@link AudioAttributes}.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.VIBRATE)
+    public abstract void vibrate(int uid, String opPkg, @NonNull VibrationEffect vibe,
+            String reason, @NonNull VibrationAttributes attributes);
 
     /**
      * Query whether the vibrator supports the given effects.
@@ -453,7 +468,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE)
     public boolean isVibrating() {
         return false;
@@ -467,7 +481,6 @@
     * @hide
     */
     @SystemApi
-    @TestApi
     public interface OnVibratorStateChangedListener  {
         /**
          * Called when the vibrator state has changed.
@@ -486,7 +499,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE)
     public void addVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
     }
@@ -500,7 +512,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE)
     public void addVibratorStateListener(
             @NonNull @CallbackExecutor Executor executor,
@@ -515,7 +526,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE)
     public void removeVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
     }
diff --git a/core/java/android/os/WorkSource.java b/core/java/android/os/WorkSource.java
index a1b4dc3..e0927eb 100644
--- a/core/java/android/os/WorkSource.java
+++ b/core/java/android/os/WorkSource.java
@@ -98,7 +98,6 @@
      * @param uid the uid performing the work
      * @hide
      */
-    @TestApi
     @SystemApi
     public WorkSource(int uid) {
         mNum = 1;
@@ -152,7 +151,6 @@
      * Returns the number of uids in this work source.
      * @hide
      */
-    @TestApi
     @SystemApi
     public int size() {
         return mNum;
@@ -173,7 +171,6 @@
      * If {@code index} < 0 or {@code index} >= {@link #size() N}, then the behavior is undefined.
      * @hide
      */
-    @TestApi
     @SystemApi
     public int getUid(int index) {
         return mUids[index];
@@ -209,7 +206,6 @@
      * If {@code index} < 0 or {@code index} >= {@link #size() N}, then the behavior is undefined.
      * @hide
      */
-    @TestApi
     @SystemApi
     @Nullable
     public String getPackageName(int index) {
@@ -455,7 +451,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @NonNull
     public WorkSource withoutNames() {
         final WorkSource copy = new WorkSource(this);
@@ -582,7 +577,6 @@
      * @hide for internal use only.
      */
     @SystemApi
-    @TestApi
     public boolean isEmpty() {
         return mNum == 0 && (mChains == null || mChains.isEmpty());
     }
diff --git a/core/java/android/os/connectivity/CellularBatteryStats.java b/core/java/android/os/connectivity/CellularBatteryStats.java
index 121fd33..fc17002 100644
--- a/core/java/android/os/connectivity/CellularBatteryStats.java
+++ b/core/java/android/os/connectivity/CellularBatteryStats.java
@@ -109,7 +109,7 @@
                         CellSignalStrength.getNumSignalStrengthLevels()));
         mTxTimeMs = Arrays.copyOfRange(
                 txTimeMs, 0,
-                Math.min(txTimeMs.length, ModemActivityInfo.TX_POWER_LEVELS));
+                Math.min(txTimeMs.length, ModemActivityInfo.getNumTxPowerLevels()));
         mMonitoredRailChargeConsumedMaMs = monitoredRailChargeConsumedMaMs;
     }
 
diff --git a/core/java/android/os/image/DynamicSystemClient.java b/core/java/android/os/image/DynamicSystemClient.java
index 50d8d80..58268e2 100644
--- a/core/java/android/os/image/DynamicSystemClient.java
+++ b/core/java/android/os/image/DynamicSystemClient.java
@@ -22,7 +22,6 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -68,7 +67,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public class DynamicSystemClient {
     /** @hide */
     @IntDef(prefix = { "STATUS_" }, value = {
@@ -286,7 +284,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public DynamicSystemClient(@NonNull Context context) {
         mContext = context;
         mConnection = new DynSystemServiceConnection();
@@ -322,7 +319,6 @@
      */
     @RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM)
     @SystemApi
-    @TestApi
     public void bind() {
         if (!featureFlagEnabled()) {
             Slog.w(TAG, FeatureFlagUtils.DYNAMIC_SYSTEM + " not enabled; bind() aborted.");
@@ -345,7 +341,6 @@
      */
     @RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM)
     @SystemApi
-    @TestApi
     public void unbind() {
         if (!mBound) {
             return;
@@ -381,7 +376,6 @@
      */
     @RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM)
     @SystemApi
-    @TestApi
     public void start(@NonNull Uri systemUrl, @BytesLong long systemSize) {
         start(systemUrl, systemSize, 0 /* Use the default userdata size */);
     }
diff --git a/core/java/android/os/incremental/IIncrementalService.aidl b/core/java/android/os/incremental/IIncrementalService.aidl
index 52475e9..ca92ad5 100644
--- a/core/java/android/os/incremental/IIncrementalService.aidl
+++ b/core/java/android/os/incremental/IIncrementalService.aidl
@@ -144,4 +144,14 @@
      * Stop listening for the loading progress change for a storage.
      */
     boolean unregisterLoadingProgressListener(int storageId);
+
+    /**
+     * Register storage health status listener.
+     */
+    boolean registerStorageHealthListener(int storageId, in StorageHealthCheckParams params, in IStorageHealthListener listener);
+
+    /**
+     * Register storage health status listener.
+     */
+    void unregisterStorageHealthListener(int storageId);
 }
diff --git a/core/java/android/os/incremental/IStorageHealthListener.aidl b/core/java/android/os/incremental/IStorageHealthListener.aidl
index 9f93ede..c71e73f 100644
--- a/core/java/android/os/incremental/IStorageHealthListener.aidl
+++ b/core/java/android/os/incremental/IStorageHealthListener.aidl
@@ -26,9 +26,15 @@
     /** There are reads pending for params.blockedTimeoutMs, waiting till
     *   params.unhealthyTimeoutMs to confirm unhealthy state. */
     const int HEALTH_STATUS_BLOCKED = 2;
-    /** There are reads pending for params.unhealthyTimeoutMs>,
-    *   marking storage as unhealthy. */
+    /** There are reads pending for params.unhealthyTimeoutMs,
+    *   marking storage as unhealthy due to unknown issues. */
     const int HEALTH_STATUS_UNHEALTHY = 3;
+    /** There are reads pending for params.unhealthyTimeoutMs,
+     *  due to data transportation issues. */
+    const int HEALTH_STATUS_UNHEALTHY_TRANSPORT = 4;
+    /** There are reads pending for params.unhealthyTimeoutMs,
+     *  due to limited storage space. */
+    const int HEALTH_STATUS_UNHEALTHY_STORAGE = 5;
 
     /** Health status callback. */
     void onHealthStatus(in int storageId, in int status);
diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java
index 768ef97..fb47ef0 100644
--- a/core/java/android/os/incremental/IncrementalManager.java
+++ b/core/java/android/os/incremental/IncrementalManager.java
@@ -283,6 +283,7 @@
                 return;
             }
             mLoadingProgressCallbacks.cleanUpCallbacks(storage);
+            unregisterHealthListener(codePath);
             mService.deleteStorage(storage.getId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -297,7 +298,7 @@
      * @param callback To report loading progress to.
      * @return True if the package name and associated storage id are valid. False otherwise.
      */
-    public boolean registerCallback(@NonNull String codePath,
+    public boolean registerLoadingProgressCallback(@NonNull String codePath,
             @NonNull IPackageLoadingProgressCallback callback) {
         final IncrementalStorage storage = openStorage(codePath);
         if (storage == null) {
@@ -314,7 +315,7 @@
      * @param codePath Path of the installed package
      * @return True if the package name and associated storage id are valid. False otherwise.
      */
-    public boolean unregisterCallback(@NonNull String codePath,
+    public boolean unregisterLoadingProgressCallback(@NonNull String codePath,
             @NonNull IPackageLoadingProgressCallback callback) {
         final IncrementalStorage storage = openStorage(codePath);
         if (storage == null) {
@@ -414,6 +415,38 @@
         }
     }
 
+    /**
+     * Specify the health check params and listener for listening to Incremental Storage health
+     * status changes. Notice that this will overwrite the previously registered listener.
+     * @param codePath Path of the installed package. This path is on an Incremental Storage.
+     * @param healthCheckParams The params for health state change timeouts.
+     * @param listener To report health status change.
+     * @return True if listener was successfully registered.
+     */
+    public boolean registerHealthListener(@NonNull String codePath,
+            @NonNull StorageHealthCheckParams healthCheckParams,
+            @NonNull IStorageHealthListener.Stub listener) {
+        final IncrementalStorage storage = openStorage(codePath);
+        if (storage == null) {
+            // storage does not exist, package not installed
+            return false;
+        }
+        return storage.registerStorageHealthListener(healthCheckParams, listener);
+    }
+
+    /**
+     * Stop listening to health status changes on an Incremental Storage.
+     * @param codePath Path of the installed package. This path is on an Incremental Storage.
+     */
+    public void unregisterHealthListener(@NonNull String codePath) {
+        final IncrementalStorage storage = openStorage(codePath);
+        if (storage == null) {
+            // storage does not exist, package not installed
+            return;
+        }
+        storage.unregisterStorageHealthListener();
+    }
+
     /* Native methods */
     private static native boolean nativeIsEnabled();
     private static native boolean nativeIsIncrementalPath(@NonNull String path);
diff --git a/core/java/android/os/incremental/IncrementalStorage.java b/core/java/android/os/incremental/IncrementalStorage.java
index a1c3cc6..b913faf 100644
--- a/core/java/android/os/incremental/IncrementalStorage.java
+++ b/core/java/android/os/incremental/IncrementalStorage.java
@@ -545,4 +545,31 @@
             return false;
         }
     }
+
+    /**
+     * Register to listen to the status changes of the storage health.
+     * @param healthCheckParams Params to specify status change timeouts.
+     * @param listener To report health status change from Incremental Service to the caller.
+     */
+    public boolean registerStorageHealthListener(StorageHealthCheckParams healthCheckParams,
+            IStorageHealthListener listener) {
+        try {
+            return mService.registerStorageHealthListener(mId, healthCheckParams, listener);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return false;
+        }
+    }
+
+    /**
+     * Stops listening to the status changes of the storage health.
+     */
+    public void unregisterStorageHealthListener() {
+        try {
+            mService.unregisterStorageHealthListener(mId);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return;
+        }
+    }
 }
diff --git a/core/java/android/os/storage/DiskInfo.java b/core/java/android/os/storage/DiskInfo.java
index df3c4d5..67317c7 100644
--- a/core/java/android/os/storage/DiskInfo.java
+++ b/core/java/android/os/storage/DiskInfo.java
@@ -184,7 +184,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (o instanceof DiskInfo) {
             return Objects.equals(id, ((DiskInfo) o).id);
         } else {
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 6b5eb16..270115b 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -43,7 +43,6 @@
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
-import android.annotation.TestApi;
 import android.annotation.WorkerThread;
 import android.app.Activity;
 import android.app.ActivityThread;
@@ -1701,7 +1700,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static boolean hasIsolatedStorage() {
         // Prefer to use snapshot for current boot when available
         return SystemProperties.getBoolean(PROP_ISOLATED_STORAGE_SNAPSHOT,
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index a63b82e..eed36d7 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -435,7 +435,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (obj instanceof StorageVolume && mPath != null) {
             StorageVolume volume = (StorageVolume)obj;
             return (mPath.equals(volume.mPath));
diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java
index d7aaa4d6..74c0ecb 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -520,7 +520,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (o instanceof VolumeInfo) {
             return Objects.equals(id, ((VolumeInfo) o).id);
         } else {
diff --git a/core/java/android/os/storage/VolumeRecord.java b/core/java/android/os/storage/VolumeRecord.java
index 60df981..0f58a71 100644
--- a/core/java/android/os/storage/VolumeRecord.java
+++ b/core/java/android/os/storage/VolumeRecord.java
@@ -16,6 +16,7 @@
 
 package android.os.storage;
 
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.os.Environment;
@@ -149,7 +150,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (o instanceof VolumeRecord) {
             return Objects.equals(fsUuid, ((VolumeRecord) o).fsUuid);
         } else {
diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
index 17a78a8..0ba09fd 100644
--- a/core/java/android/permission/PermissionControllerManager.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -79,7 +79,6 @@
  *
  * @hide
  */
-@TestApi
 @SystemApi
 @SystemService(Context.PERMISSION_CONTROLLER_SERVICE)
 public final class PermissionControllerManager {
@@ -308,7 +307,7 @@
                     revokeRuntimePermissionsResult);
             return revokeRuntimePermissionsResult;
         }).whenCompleteAsync((revoked, err) -> {
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 if (err != null) {
                     Log.e(TAG, "Failure when revoking runtime permissions " + revoked, err);
@@ -358,7 +357,7 @@
                     setRuntimePermissionGrantStateResult);
             return setRuntimePermissionGrantStateResult;
         }).whenCompleteAsync((setRuntimePermissionGrantStateResult, err) -> {
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 if (err != null) {
                     Log.e(TAG, "Error setting permissions state for device admin " + packageName,
@@ -477,7 +476,7 @@
                     applyStagedRuntimePermissionBackupResult);
             return applyStagedRuntimePermissionBackupResult;
         }).whenCompleteAsync((applyStagedRuntimePermissionBackupResult, err) -> {
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 if (err != null) {
                     Log.e(TAG, "Error restoring delayed permissions for " + packageName, err);
@@ -623,7 +622,7 @@
                 Log.e(TAG, "Error getting permission usages", err);
                 callback.onPermissionUsageResult(Collections.emptyList());
             } else {
-                long token = Binder.clearCallingIdentity();
+                final long token = Binder.clearCallingIdentity();
                 try {
                     callback.onPermissionUsageResult(
                             CollectionUtils.emptyIfNull(getPermissionUsagesResult));
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index d80a7e7..e4220dd 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -16,6 +16,8 @@
 
 package android.permission;
 
+import static android.os.Build.VERSION_CODES.S;
+
 import android.Manifest;
 import android.annotation.CallbackExecutor;
 import android.annotation.IntRange;
@@ -24,12 +26,13 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
-import android.annotation.TestApi;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.ActivityThread;
 import android.app.IActivityManager;
 import android.app.PropertyInvalidatedCache;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
 import android.content.Context;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
@@ -57,7 +60,6 @@
  *
  * @hide
  */
-@TestApi
 @SystemApi
 @SystemService(Context.PERMISSION_SERVICE)
 public final class PermissionManager {
@@ -70,6 +72,17 @@
     public static final String KILL_APP_REASON_GIDS_CHANGED =
             "permission grant or revoke changed gids";
 
+    /**
+     * Refuse to install package if groups of permissions are bad
+     * - Permission groups should only be shared between apps sharing a certificate
+     * - If a permission belongs to a group that group should be defined
+     *
+     * @hide
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = S)
+    public static final long CANNOT_INSTALL_WITH_BAD_PERMISSION_GROUPS = 146211400;
+
     private final @NonNull Context mContext;
 
     private final IPackageManager mPackageManager;
@@ -115,7 +128,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     @RequiresPermission(anyOf = {
             Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY,
@@ -136,7 +148,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     @RequiresPermission(anyOf = {
             Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY,
@@ -587,7 +598,7 @@
         }
 
         @Override
-        public boolean equals(Object rval) {
+        public boolean equals(@Nullable Object rval) {
             // N.B. pid doesn't count toward equality!
             if (rval == null) {
                 return false;
@@ -609,7 +620,7 @@
     /** @hide */
     private static final PropertyInvalidatedCache<PermissionQuery, Integer> sPermissionCache =
             new PropertyInvalidatedCache<PermissionQuery, Integer>(
-                    16, CACHE_KEY_PACKAGE_INFO, "checkPermission") {
+                    2048, CACHE_KEY_PACKAGE_INFO, "checkPermission") {
                 @Override
                 protected Integer recompute(PermissionQuery query) {
                     return checkPermissionUncached(query.permission, query.pid, query.uid);
@@ -660,7 +671,7 @@
         }
 
         @Override
-        public boolean equals(Object rval) {
+        public boolean equals(@Nullable Object rval) {
             if (rval == null) {
                 return false;
             }
diff --git a/core/java/android/permission/Permissions.md b/core/java/android/permission/Permissions.md
index d8ab618..4224b7a 100644
--- a/core/java/android/permission/Permissions.md
+++ b/core/java/android/permission/Permissions.md
@@ -203,7 +203,7 @@
 
 During development and testing a runtime permission can be granted via the `pm` shell command or by
 using the `UiAutomator.grantRuntimePermission` API call. Please note that this does _not_ grant the
-[app-op](#runtime-permissions-and-app-ops) synchronously. Unless the app needs to test the actual
+[app-op](#runtime-permissions-and-app_ops) synchronously. Unless the app needs to test the actual
 permission grant flow it is recommended to grant the runtime permissions during install using
 `adb install -g /my/package.apk`.
 
@@ -262,7 +262,7 @@
 silently fail.
 
 A secondary use case of the `AppOpsManager.noteOp` calls is to
-[track](../app/AppOps.md#Appops-for-tracking) which apps perform what runtime protected actions.
+[track](../app/AppOps.md#app_ops-for-tracking) which apps perform what runtime protected actions.
 
 #### Verifying an app has a runtime time permission
 
@@ -471,7 +471,7 @@
 
 ##### Location
 
-As described [above](#runtime-permissions-and-app-ops) the app-op mode for granted permissions is
+As described [above](#runtime-permissions-and-app_ops) the app-op mode for granted permissions is
 `MODE_ALLOWED` to allow access or `MODE_IGNORED` to suppress access.
 
 The important case is the case where the permission is granted and the app-op is `MODE_IGNORED`. In
@@ -848,7 +848,7 @@
 
 @Test
 fun onlySomeAppsAreAllowedToHavePermissionGranted() {
-    assertThat(whitelistedPkgs).containsAllIn(
+    assertThat(whitelistedPkgs).containsAtLeastElementsIn(
             context.packageManager.getInstalledPackages(MATCH_ALL)
                     .filter { pkg ->
                         context.checkPermission(android.Manifest.permission.MY_PRIVILEGED_PERMISSION, -1,
diff --git a/core/java/android/permission/RuntimePermissionPresentationInfo.java b/core/java/android/permission/RuntimePermissionPresentationInfo.java
index d696fea..4fce14c 100644
--- a/core/java/android/permission/RuntimePermissionPresentationInfo.java
+++ b/core/java/android/permission/RuntimePermissionPresentationInfo.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -33,7 +32,6 @@
  *
  * @hide
  */
-@TestApi
 @SystemApi
 public final class RuntimePermissionPresentationInfo implements Parcelable {
     private static final int FLAG_GRANTED = 1 << 0;
diff --git a/core/java/android/print/PageRange.java b/core/java/android/print/PageRange.java
index 1345038..f5f6613 100644
--- a/core/java/android/print/PageRange.java
+++ b/core/java/android/print/PageRange.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntRange;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -128,7 +129,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (this == obj) {
             return true;
         }
diff --git a/core/java/android/print/PrintAttributes.java b/core/java/android/print/PrintAttributes.java
index e607ced..934e642 100644
--- a/core/java/android/print/PrintAttributes.java
+++ b/core/java/android/print/PrintAttributes.java
@@ -370,7 +370,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (this == obj) {
             return true;
         }
@@ -1019,7 +1019,7 @@
         }
 
         @Override
-        public boolean equals(Object obj) {
+        public boolean equals(@Nullable Object obj) {
             if (this == obj) {
                 return true;
             }
@@ -1180,7 +1180,7 @@
         }
 
         @Override
-        public boolean equals(Object obj) {
+        public boolean equals(@Nullable Object obj) {
             if (this == obj) {
                 return true;
             }
@@ -1304,7 +1304,7 @@
         }
 
         @Override
-        public boolean equals(Object obj) {
+        public boolean equals(@Nullable Object obj) {
             if (this == obj) {
                 return true;
             }
diff --git a/core/java/android/print/PrintDocumentInfo.java b/core/java/android/print/PrintDocumentInfo.java
index b988461..a817a7d 100644
--- a/core/java/android/print/PrintDocumentInfo.java
+++ b/core/java/android/print/PrintDocumentInfo.java
@@ -19,9 +19,11 @@
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
+
 import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
@@ -235,7 +237,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (this == obj) {
             return true;
         }
diff --git a/core/java/android/print/PrintJob.java b/core/java/android/print/PrintJob.java
index 66181e0..9c8e61f 100644
--- a/core/java/android/print/PrintJob.java
+++ b/core/java/android/print/PrintJob.java
@@ -184,7 +184,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (this == obj) {
             return true;
         }
diff --git a/core/java/android/print/PrintJobId.java b/core/java/android/print/PrintJobId.java
index 606cbb8..35bec5c 100644
--- a/core/java/android/print/PrintJobId.java
+++ b/core/java/android/print/PrintJobId.java
@@ -17,6 +17,7 @@
 package android.print;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -59,7 +60,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (this == obj) {
             return true;
         }
diff --git a/core/java/android/print/PrinterCapabilitiesInfo.java b/core/java/android/print/PrinterCapabilitiesInfo.java
index e465d3b..8f1b864 100644
--- a/core/java/android/print/PrinterCapabilitiesInfo.java
+++ b/core/java/android/print/PrinterCapabilitiesInfo.java
@@ -17,6 +17,7 @@
 package android.print;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.print.PrintAttributes.ColorMode;
@@ -24,6 +25,7 @@
 import android.print.PrintAttributes.Margins;
 import android.print.PrintAttributes.MediaSize;
 import android.print.PrintAttributes.Resolution;
+
 import com.android.internal.util.Preconditions;
 
 import java.util.ArrayList;
@@ -271,7 +273,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (this == obj) {
             return true;
         }
diff --git a/core/java/android/print/PrinterId.java b/core/java/android/print/PrinterId.java
index 75ca750..25260c4 100644
--- a/core/java/android/print/PrinterId.java
+++ b/core/java/android/print/PrinterId.java
@@ -17,6 +17,7 @@
 package android.print;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.os.Parcel;
@@ -85,7 +86,7 @@
     }
 
     @Override
-    public boolean equals(Object object) {
+    public boolean equals(@Nullable Object object) {
         if (this == object) {
             return true;
         }
diff --git a/core/java/android/print/PrinterInfo.java b/core/java/android/print/PrinterInfo.java
index 4d5ccc0..8e03e3e 100644
--- a/core/java/android/print/PrinterInfo.java
+++ b/core/java/android/print/PrinterInfo.java
@@ -360,7 +360,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (this == obj) {
             return true;
         }
diff --git a/core/java/android/printservice/PrintJob.java b/core/java/android/printservice/PrintJob.java
index 3226f34..d0d41e6 100644
--- a/core/java/android/printservice/PrintJob.java
+++ b/core/java/android/printservice/PrintJob.java
@@ -27,6 +27,7 @@
 import android.print.PrintJobInfo;
 import android.text.TextUtils;
 import android.util.Log;
+
 import com.android.internal.util.Preconditions;
 
 /**
@@ -436,7 +437,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (this == obj) {
             return true;
         }
diff --git a/core/java/android/provider/CalendarContract.java b/core/java/android/provider/CalendarContract.java
index e1aa21e..8ac1d84e 100644
--- a/core/java/android/provider/CalendarContract.java
+++ b/core/java/android/provider/CalendarContract.java
@@ -2618,7 +2618,8 @@
             intent.setData(ContentUris.withAppendedId(CalendarContract.CONTENT_URI, alarmTime));
             intent.putExtra(ALARM_TIME, alarmTime);
             intent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
-            PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0);
+            PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent,
+                    PendingIntent.FLAG_IMMUTABLE);
             manager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, alarmTime, pi);
         }
 
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 99ffee3..0315b56 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -55,7 +55,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class DeviceConfig {
     /**
      * The content:// style URL for the config table.
@@ -116,7 +115,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final String NAMESPACE_AUTOFILL = "autofill";
 
     /**
@@ -150,7 +148,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture";
 
     /**
@@ -250,7 +247,7 @@
      *
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String NAMESPACE_ROLLBACK = "rollback";
 
     /**
@@ -258,7 +255,7 @@
      *
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String NAMESPACE_ROLLBACK_BOOT = "rollback_boot";
 
     /**
@@ -403,7 +400,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final String NAMESPACE_PRIVACY = "privacy";
 
     /**
@@ -412,7 +408,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final String NAMESPACE_BIOMETRICS = "biometrics";
 
     /**
@@ -421,7 +416,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final String NAMESPACE_PERMISSIONS = "permissions";
 
     /**
@@ -467,7 +461,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(READ_DEVICE_CONFIG)
     public static String getProperty(@NonNull String namespace, @NonNull String name) {
         // Fetch all properties for the namespace at once and cache them in the local process, so we
@@ -496,7 +489,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @NonNull
     @RequiresPermission(READ_DEVICE_CONFIG)
     public static Properties getProperties(@NonNull String namespace, @NonNull String ... names) {
@@ -516,7 +508,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(READ_DEVICE_CONFIG)
     public static String getString(@NonNull String namespace, @NonNull String name,
             @Nullable String defaultValue) {
@@ -535,7 +526,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(READ_DEVICE_CONFIG)
     public static boolean getBoolean(@NonNull String namespace, @NonNull String name,
             boolean defaultValue) {
@@ -554,7 +544,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(READ_DEVICE_CONFIG)
     public static int getInt(@NonNull String namespace, @NonNull String name, int defaultValue) {
         String value = getProperty(namespace, name);
@@ -580,7 +569,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(READ_DEVICE_CONFIG)
     public static long getLong(@NonNull String namespace, @NonNull String name, long defaultValue) {
         String value = getProperty(namespace, name);
@@ -606,7 +594,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(READ_DEVICE_CONFIG)
     public static float getFloat(@NonNull String namespace, @NonNull String name,
             float defaultValue) {
@@ -642,7 +629,6 @@
      * @see #resetToDefaults(int, String).
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(WRITE_DEVICE_CONFIG)
     public static boolean setProperty(@NonNull String namespace, @NonNull String name,
             @Nullable String value, boolean makeDefault) {
@@ -666,7 +652,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(WRITE_DEVICE_CONFIG)
     public static boolean setProperties(@NonNull Properties properties) throws BadConfigException {
         ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
@@ -686,7 +671,6 @@
      * @see #setProperty(String, String, String, boolean)
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(WRITE_DEVICE_CONFIG)
     public static void resetToDefaults(@ResetMode int resetMode, @Nullable String namespace) {
         ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
@@ -708,7 +692,6 @@
      * @see #removeOnPropertiesChangedListener(OnPropertiesChangedListener)
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(READ_DEVICE_CONFIG)
     public static void addOnPropertiesChangedListener(
             @NonNull String namespace,
@@ -743,7 +726,6 @@
      * @see #addOnPropertiesChangedListener(String, Executor, OnPropertiesChangedListener)
      */
     @SystemApi
-    @TestApi
     public static void removeOnPropertiesChangedListener(
             @NonNull OnPropertiesChangedListener onPropertiesChangedListener) {
         Preconditions.checkNotNull(onPropertiesChangedListener);
@@ -878,7 +860,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public interface OnPropertiesChangedListener {
         /**
          * Called when one or more properties have changed, providing a Properties object with all
@@ -899,7 +880,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static class BadConfigException extends Exception {}
 
     /**
@@ -908,7 +888,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static class Properties {
         private final String mNamespace;
         private final HashMap<String, String> mMap;
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index da9794d..062d929 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -22,7 +22,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ContentProvider;
 import android.content.ContentResolver;
@@ -1304,7 +1303,6 @@
      * {@hide}
      */
     @SystemApi
-    @TestApi
     public static @NonNull Uri setManageMode(@NonNull Uri uri) {
         Preconditions.checkNotNull(uri, "uri can not be null");
         return uri.buildUpon().appendQueryParameter(PARAM_MANAGE, "true").build();
@@ -1316,7 +1314,6 @@
      * {@hide}
      */
     @SystemApi
-    @TestApi
     public static boolean isManageMode(@NonNull Uri uri) {
         Preconditions.checkNotNull(uri, "uri can not be null");
         return uri.getBooleanQueryParameter(PARAM_MANAGE, false);
@@ -1811,7 +1808,7 @@
         }
 
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (this == o) {
                 return true;
             }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 97acd2f..59934ac 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -81,6 +81,7 @@
 import android.util.Log;
 import android.util.MemoryIntArray;
 import android.view.Display;
+import android.view.WindowManager.LayoutParams;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
@@ -228,7 +229,6 @@
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     @SystemApi
-    @TestApi
     public static final String ACTION_TETHER_PROVISIONING_UI =
             "android.settings.TETHER_PROVISIONING_UI";
 
@@ -326,6 +326,21 @@
             "android.settings.ACCESSIBILITY_DETAILS_SETTINGS";
 
     /**
+     * Activity Action: Show settings to allow configuration of Reduce Bright Colors.
+     * <p>
+     * In some cases, a matching Activity may not exist, so ensure you
+     * safeguard against this.
+     * <p>
+     * Input: Nothing.
+     * <p>
+     * Output: Nothing.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_REDUCE_BRIGHT_COLORS_SETTINGS =
+            "android.settings.REDUCE_BRIGHT_COLORS_SETTINGS";
+
+    /**
      * Activity Action: Show settings to control access to usage information.
      * <p>
      * In some cases, a matching Activity may not exist, so ensure you
@@ -1037,7 +1052,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_MANAGE_APP_OVERLAY_PERMISSION =
@@ -2001,7 +2015,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_ENTERPRISE_PRIVACY_SETTINGS
             = "android.settings.ENTERPRISE_PRIVACY_SETTINGS";
@@ -2084,7 +2097,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_REQUEST_ENABLE_CONTENT_CAPTURE =
             "android.settings.REQUEST_ENABLE_CONTENT_CAPTURE";
@@ -5564,7 +5576,6 @@
          * @hide
          */
         @SystemApi
-        @TestApi
         @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
         public static void resetToDefaults(@NonNull ContentResolver resolver,
                 @Nullable String tag) {
@@ -6035,7 +6046,6 @@
          * @hide
          */
         @SystemApi
-        @TestApi
         public static final String AUTOFILL_FEATURE_FIELD_CLASSIFICATION =
                 "autofill_field_classification";
 
@@ -6069,7 +6079,6 @@
          * @hide
          */
         @SystemApi
-        @TestApi
         public static final String AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE =
                 "autofill_user_data_max_user_data_size";
 
@@ -6080,7 +6089,6 @@
          * @hide
          */
         @SystemApi
-        @TestApi
         public static final String AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE =
                 "autofill_user_data_max_field_classification_size";
 
@@ -6091,7 +6099,6 @@
          * @hide
          */
         @SystemApi
-        @TestApi
         public static final String AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT =
                 "autofill_user_data_max_category_count";
 
@@ -6101,7 +6108,6 @@
          * @hide
          */
         @SystemApi
-        @TestApi
         public static final String AUTOFILL_USER_DATA_MAX_VALUE_LENGTH =
                 "autofill_user_data_max_value_length";
 
@@ -6111,7 +6117,6 @@
          * @hide
          */
         @SystemApi
-        @TestApi
         public static final String AUTOFILL_USER_DATA_MIN_VALUE_LENGTH =
                 "autofill_user_data_min_value_length";
 
@@ -6161,7 +6166,6 @@
          * @hide
          */
         @SystemApi
-        @TestApi
         public static final String USER_SETUP_COMPLETE = "user_setup_complete";
 
         /**
@@ -6537,7 +6541,6 @@
          * @hide
          */
         @SystemApi
-        @TestApi
         public static final String LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS =
                 "lock_screen_allow_private_notifications";
 
@@ -6811,6 +6814,12 @@
         /**
          * Whether to draw text in bold.
          *
+         * <p>Values:
+         *  1 - Text is not displayed in bold. (Default)
+         *  2 - Text is displayed in bold.
+         *
+         * @see Configuration#FORCE_BOLD_TEXT_NO
+         * @see Configuration#FORCE_BOLD_TEXT_YES
          * @hide
          */
         public static final String FORCE_BOLD_TEXT = "force_bold_text";
@@ -7123,6 +7132,33 @@
         public static final String ACCESSIBILITY_INTERACTIVE_UI_TIMEOUT_MS =
                 "accessibility_interactive_ui_timeout_ms";
 
+
+        /**
+         * Setting that specifies whether Reduce Bright Colors, or brightness dimming by color
+         * adjustment, is enabled.
+         *
+         * @hide
+         */
+        public static final String REDUCE_BRIGHT_COLORS_ACTIVATED =
+                "reduce_bright_colors_activated";
+
+        /**
+         * Setting that specifies the level of Reduce Bright Colors in intensity. The range is
+         * [0, 100].
+         *
+         * @hide
+         */
+        public static final String REDUCE_BRIGHT_COLORS_LEVEL =
+                "reduce_bright_colors_level";
+
+        /**
+         * Setting that specifies whether Reduce Bright Colors should persist across reboots.
+         *
+         * @hide
+         */
+        public static final String REDUCE_BRIGHT_COLORS_PERSIST_ACROSS_REBOOTS =
+                "reduce_bright_colors_persist_across_reboots";
+
         /**
          * List of the enabled print services.
          *
@@ -7792,7 +7828,6 @@
          * @hide
          */
         @SystemApi
-        @TestApi
         public static final String DOZE_ALWAYS_ON = "doze_always_on";
 
         /**
@@ -8225,7 +8260,6 @@
          * @hide
          */
         @SystemApi
-        @TestApi
         public static final String LOCK_SCREEN_SHOW_NOTIFICATIONS =
                 "lock_screen_show_notifications";
 
@@ -8912,7 +8946,6 @@
          * @hide
          */
         @SystemApi
-        @TestApi
         public static final String LOCATION_ACCESS_CHECK_INTERVAL_MILLIS =
                 "location_access_check_interval_millis";
 
@@ -8921,7 +8954,6 @@
          * @hide
          */
         @SystemApi
-        @TestApi
         public static final String LOCATION_ACCESS_CHECK_DELAY_MILLIS =
                 "location_access_check_delay_millis";
 
@@ -9039,6 +9071,7 @@
          * @see#ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW
          * @hide
          */
+        @TestApi
         public static final String ACCESSIBILITY_MAGNIFICATION_MODE =
                 "accessibility_magnification_mode";
 
@@ -9046,12 +9079,14 @@
          * Magnification mode value that magnifies whole display.
          * @hide
          */
+        @TestApi
         public static final int ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN = 0x1;
 
         /**
          * Magnification mode value that magnifies magnify particular region in a window
          * @hide
          */
+        @TestApi
         public static final int ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW = 0x2;
 
         /**
@@ -9059,6 +9094,7 @@
          * region in a window.
          * @hide
          */
+        @TestApi
         public static final int ACCESSIBILITY_MAGNIFICATION_MODE_ALL = 0x3;
 
         /**
@@ -9070,10 +9106,20 @@
          * @see#ACCESSIBILITY_MAGNIFICATION_MODE_ALL
          * @hide
          */
+        @TestApi
         public static final String ACCESSIBILITY_MAGNIFICATION_CAPABILITY =
                 "accessibility_magnification_capability";
 
         /**
+         *  Whether to show the window magnification prompt dialog when the user uses full-screen
+         *  magnification first time after database is upgraded .
+         *
+         * @hide
+         */
+        public static final String ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT =
+                "accessibility_show_window_magnification_prompt";
+
+        /**
          * Whether the Adaptive connectivity option is enabled.
          *
          * @hide
@@ -10328,7 +10374,6 @@
          * @hide
          */
         @SystemApi
-        @TestApi
         public static final String TETHER_OFFLOAD_DISABLED = "tether_offload_disabled";
 
         /**
@@ -10389,14 +10434,6 @@
                 "webview_data_reduction_proxy_key";
 
        /**
-        * Whether or not the WebView fallback mechanism should be enabled.
-        * 0=disabled, 1=enabled.
-        * @hide
-        */
-       public static final String WEBVIEW_FALLBACK_LOGIC_ENABLED =
-               "webview_fallback_logic_enabled";
-
-       /**
         * Name of the package used as WebView provider (if unset the provider is instead determined
         * by the system).
         * @hide
@@ -12304,37 +12341,34 @@
          * to dumpable apps that opt-in.
          * @hide
          */
-        public static final String GLOBAL_SETTINGS_ANGLE_DEBUG_PACKAGE =
-                "angle_debug_package";
+        public static final String ANGLE_DEBUG_PACKAGE = "angle_debug_package";
 
         /**
          * Force all PKGs to use ANGLE, regardless of any other settings
          * The value is a boolean (1 or 0).
          * @hide
          */
-        public static final String GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE =
-                "angle_gl_driver_all_angle";
+        public static final String ANGLE_GL_DRIVER_ALL_ANGLE = "angle_gl_driver_all_angle";
 
         /**
          * List of PKGs that have an OpenGL driver selected
          * @hide
          */
-        public static final String GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS =
+        public static final String ANGLE_GL_DRIVER_SELECTION_PKGS =
                 "angle_gl_driver_selection_pkgs";
 
         /**
          * List of selected OpenGL drivers, corresponding to the PKGs in GLOBAL_SETTINGS_DRIVER_PKGS
          * @hide
          */
-        public static final String GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES =
+        public static final String ANGLE_GL_DRIVER_SELECTION_VALUES =
                 "angle_gl_driver_selection_values";
 
         /**
          * List of package names that should check ANGLE rules
          * @hide
          */
-        public static final String GLOBAL_SETTINGS_ANGLE_ALLOWLIST =
-                "angle_allowlist";
+        public static final String ANGLE_ALLOWLIST = "angle_allowlist";
 
         /**
          * Lists of ANGLE EGL features for debugging.
@@ -12350,8 +12384,7 @@
          * 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";
+        public static final String SHOW_ANGLE_IN_USE_DIALOG_BOX = "show_angle_in_use_dialog_box";
 
         /**
          * Updatable driver global preference for all Apps.
@@ -13310,7 +13343,6 @@
          * @hide
          */
         @SystemApi
-        @TestApi
         public static final String AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES =
                 "autofill_compat_mode_allowed_packages";
 
@@ -13358,6 +13390,7 @@
          *
          * @hide
          */
+        @TestApi
         public static final String HIDDEN_API_POLICY = "hidden_api_policy";
 
         /**
@@ -14485,6 +14518,80 @@
          * @hide
          */
         public static final String SHOW_PEOPLE_SPACE = "show_people_space";
+
+        /**
+         * Which types of conversations to show in People Space.
+         * Values are:
+         * 0: All conversations (default)
+         * 1: Priority conversations only
+         * @hide
+         */
+        public static final String PEOPLE_SPACE_CONVERSATION_TYPE =
+                "people_space_conversation_type";
+
+        /**
+         * Whether to show new lockscreen & AOD UI.
+         * Values are:
+         * 0: Disabled (default)
+         * 1: Enabled
+         * @hide
+         */
+        public static final String SHOW_NEW_LOCKSCREEN = "show_new_lockscreen";
+
+        /**
+         * Block untrusted touches mode.
+         *
+         * Can be one of:
+         * <ul>
+         *      <li>0 = {@link BlockUntrustedTouchesMode#DISABLED}: Feature is off.
+         *      <li>1 = {@link BlockUntrustedTouchesMode#PERMISSIVE}: Untrusted touches are flagged
+         *          but not blocked
+         *      <li>2 = {@link BlockUntrustedTouchesMode#BLOCK}: Untrusted touches are blocked
+         * </ul>
+         *
+         * @hide
+         */
+        public static final String BLOCK_UNTRUSTED_TOUCHES_MODE = "block_untrusted_touches";
+
+        /**
+         * The maximum allowed obscuring opacity by UID to propagate touches.
+         *
+         * For certain window types (eg. SAWs), the decision of honoring {@link LayoutParams
+         * #FLAG_NOT_TOUCHABLE} or not depends on the combined obscuring opacity of the windows
+         * above the touch-consuming window.
+         *
+         * For a certain UID:
+         * <ul>
+         *     <li>If it's the same as the UID of the touch-consuming window, allow it to propagate
+         *     the touch.
+         *     <li>Otherwise take all its windows of eligible window types above the touch-consuming
+         *     window, compute their combined obscuring opacity considering that {@code
+         *     opacity(A, B) = 1 - (1 - opacity(A))*(1 - opacity(B))}. If the computed value is
+         *     lesser than or equal to this setting and there are no other windows preventing the
+         *     touch, allow the UID to propagate the touch.
+         * </ul>
+         *
+         * @see android.hardware.input.InputManager#getMaximumObscuringOpacityForTouch(Context)
+         * @see android.hardware.input.InputManager#setMaximumObscuringOpacityForTouch(Context,
+         * float)
+         *
+         * @hide
+         */
+        public static final String MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH =
+                "maximum_obscuring_opacity_for_touch";
+
+        /**
+         * LatencyTracker settings.
+         *
+         * The following strings are supported as keys:
+         * <pre>
+         *     enabled              (boolean)
+         *     sampling_interval    (int)
+         * </pre>
+         *
+         * @hide
+         */
+        public static final String LATENCY_TRACKER = "latency_tracker";
     }
 
     /**
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 79d6bb4..2c735fd 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -4045,7 +4045,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final class CellBroadcasts implements BaseColumns {
 
         /**
diff --git a/core/java/android/security/net/config/Domain.java b/core/java/android/security/net/config/Domain.java
index 5bb727a3..c87c173 100644
--- a/core/java/android/security/net/config/Domain.java
+++ b/core/java/android/security/net/config/Domain.java
@@ -16,7 +16,10 @@
 
 package android.security.net.config;
 
+import android.annotation.Nullable;
+
 import java.util.Locale;
+
 /** @hide */
 public final class Domain {
     /**
@@ -43,7 +46,7 @@
     }
 
     @Override
-    public boolean equals(Object other) {
+    public boolean equals(@Nullable Object other) {
         if (other == this) {
             return true;
         }
diff --git a/core/java/android/security/net/config/Pin.java b/core/java/android/security/net/config/Pin.java
index 94520e2..7ac0ab0 100644
--- a/core/java/android/security/net/config/Pin.java
+++ b/core/java/android/security/net/config/Pin.java
@@ -16,6 +16,8 @@
 
 package android.security.net.config;
 
+import android.annotation.Nullable;
+
 import java.util.Arrays;
 
 /** @hide */
@@ -56,7 +58,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (this == obj) {
             return true;
         }
diff --git a/core/java/android/service/appprediction/AppPredictionService.java b/core/java/android/service/appprediction/AppPredictionService.java
index be20570..2d8aee5 100644
--- a/core/java/android/service/appprediction/AppPredictionService.java
+++ b/core/java/android/service/appprediction/AppPredictionService.java
@@ -22,7 +22,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.app.Service;
 import android.app.prediction.AppPredictionContext;
 import android.app.prediction.AppPredictionSessionId;
@@ -52,7 +51,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public abstract class AppPredictionService extends Service {
 
     private static final String TAG = "AppPredictionService";
diff --git a/core/java/android/service/autofill/AutofillFieldClassificationService.java b/core/java/android/service/autofill/AutofillFieldClassificationService.java
index 28842a7..9d9b881 100644
--- a/core/java/android/service/autofill/AutofillFieldClassificationService.java
+++ b/core/java/android/service/autofill/AutofillFieldClassificationService.java
@@ -20,7 +20,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.app.Service;
 import android.content.Intent;
 import android.os.Bundle;
@@ -53,7 +52,6 @@
  * {@hide}
  */
 @SystemApi
-@TestApi
 public abstract class AutofillFieldClassificationService extends Service {
 
     private static final String TAG = "AutofillFieldClassificationService";
@@ -121,7 +119,6 @@
 
     /** @hide */
     @SystemApi
-    @TestApi
     public AutofillFieldClassificationService() {
     }
 
diff --git a/core/java/android/service/autofill/BatchUpdates.java b/core/java/android/service/autofill/BatchUpdates.java
index e0b1c2f..d15514d 100644
--- a/core/java/android/service/autofill/BatchUpdates.java
+++ b/core/java/android/service/autofill/BatchUpdates.java
@@ -117,7 +117,7 @@
         public Builder transformChild(int id, @NonNull Transformation transformation) {
             throwIfDestroyed();
             Preconditions.checkArgument((transformation instanceof InternalTransformation),
-                    "not provided by Android System: " + transformation);
+                    "not provided by Android System: %s", transformation);
             if (mTransformations == null) {
                 mTransformations = new ArrayList<>();
             }
diff --git a/core/java/android/service/autofill/CustomDescription.java b/core/java/android/service/autofill/CustomDescription.java
index e274460..6df0154 100644
--- a/core/java/android/service/autofill/CustomDescription.java
+++ b/core/java/android/service/autofill/CustomDescription.java
@@ -180,7 +180,7 @@
         public Builder addChild(int id, @NonNull Transformation transformation) {
             throwIfDestroyed();
             Preconditions.checkArgument((transformation instanceof InternalTransformation),
-                    "not provided by Android System: " + transformation);
+                    "not provided by Android System: %s", transformation);
             if (mTransformations == null) {
                 mTransformations = new ArrayList<>();
             }
@@ -275,7 +275,7 @@
         public Builder batchUpdate(@NonNull Validator condition, @NonNull BatchUpdates updates) {
             throwIfDestroyed();
             Preconditions.checkArgument((condition instanceof InternalValidator),
-                    "not provided by Android System: " + condition);
+                    "not provided by Android System: %s", condition);
             Preconditions.checkNotNull(updates);
             if (mUpdates == null) {
                 mUpdates = new ArrayList<>();
@@ -329,7 +329,7 @@
         public Builder addOnClickAction(int id, @NonNull OnClickAction action) {
             throwIfDestroyed();
             Preconditions.checkArgument((action instanceof InternalOnClickAction),
-                    "not provided by Android System: " + action);
+                    "not provided by Android System: %s", action);
             if (mActions == null) {
                 mActions = new SparseArray<InternalOnClickAction>();
             }
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index 2d99c41..18d7992 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -21,7 +21,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.content.IntentSender;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -249,7 +248,6 @@
          * @hide
          */
         @SystemApi
-        @TestApi
         public Builder(@NonNull InlinePresentation inlinePresentation) {
             Preconditions.checkNotNull(inlinePresentation, "inlinePresentation must be non-null");
             mInlinePresentation = inlinePresentation;
@@ -604,7 +602,6 @@
          * @hide
          */
         @SystemApi
-        @TestApi
         public @NonNull Builder setFieldInlinePresentation(@NonNull AutofillId id,
                 @Nullable AutofillValue value, @Nullable Pattern filter,
                 @NonNull InlinePresentation inlinePresentation) {
diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java
index 1cd2d62..f226528 100644
--- a/core/java/android/service/autofill/FillEventHistory.java
+++ b/core/java/android/service/autofill/FillEventHistory.java
@@ -159,6 +159,7 @@
                     FieldClassification.writeArrayToParcel(parcel,
                             event.mDetectedFieldClassifications);
                 }
+                parcel.writeInt(event.mSaveDialogNotShowReason);
             }
         }
     }
@@ -243,6 +244,40 @@
         @Retention(RetentionPolicy.SOURCE)
         @interface EventIds{}
 
+        /** No reason for save dialog. */
+        public static final int NO_SAVE_REASON_NONE = 0;
+
+        /** The SaveInfo associated with the FillResponse is null. */
+        public static final int NO_SAVE_REASON_NO_SAVE_INFO = 1;
+
+        /** The service asked to delay save. */
+        public static final int NO_SAVE_REASON_WITH_DELAY_SAVE_FLAG = 2;
+
+        /** There was empty value for required ids. */
+        public static final int NO_SAVE_REASON_HAS_EMPTY_REQUIRED = 3;
+
+        /** No value has been changed. */
+        public static final int NO_SAVE_REASON_NO_VALUE_CHANGED = 4;
+
+        /** Fields failed validation. */
+        public static final int NO_SAVE_REASON_FIELD_VALIDATION_FAILED = 5;
+
+        /** All fields matched contents of datasets. */
+        public static final int NO_SAVE_REASON_DATASET_MATCH = 6;
+
+        /** @hide */
+        @IntDef(prefix = { "NO_SAVE_REASON_" }, value = {
+                NO_SAVE_REASON_NONE,
+                NO_SAVE_REASON_NO_SAVE_INFO,
+                NO_SAVE_REASON_WITH_DELAY_SAVE_FLAG,
+                NO_SAVE_REASON_HAS_EMPTY_REQUIRED,
+                NO_SAVE_REASON_NO_VALUE_CHANGED,
+                NO_SAVE_REASON_FIELD_VALIDATION_FAILED,
+                NO_SAVE_REASON_DATASET_MATCH
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface NoSaveReason{}
+
         @EventIds private final int mEventType;
         @Nullable private final String mDatasetId;
         @Nullable private final Bundle mClientState;
@@ -261,6 +296,8 @@
         @Nullable private final AutofillId[] mDetectedFieldIds;
         @Nullable private final FieldClassification[] mDetectedFieldClassifications;
 
+        @NoSaveReason private final int mSaveDialogNotShowReason;
+
         /**
          * Returns the type of the event.
          *
@@ -448,6 +485,18 @@
         }
 
         /**
+         * Returns the reason why a save dialog was not shown.
+         *
+         * <p><b>Note: </b>Only set on events of type {@link #TYPE_CONTEXT_COMMITTED}. For the other
+         * event types, the reason is set to NO_SAVE_REASON_NONE.
+         *
+         * @return The reason why a save dialog was not shown.
+         */
+        public int getNoSaveReason() {
+            return mSaveDialogNotShowReason;
+        }
+
+        /**
          * Creates a new event.
          *
          * @param eventType The type of the event
@@ -481,6 +530,48 @@
                 @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
                 @Nullable AutofillId[] detectedFieldIds,
                 @Nullable FieldClassification[] detectedFieldClassifications) {
+            this(eventType, datasetId, clientState, selectedDatasetIds, ignoredDatasetIds,
+                    changedFieldIds, changedDatasetIds, manuallyFilledFieldIds,
+                    manuallyFilledDatasetIds, detectedFieldIds, detectedFieldClassifications,
+                    NO_SAVE_REASON_NONE);
+        }
+
+        /**
+         * Creates a new event.
+         *
+         * @param eventType The type of the event
+         * @param datasetId The dataset the event was on, or {@code null} if the event was on the
+         *                  whole response.
+         * @param clientState The client state associated with the event.
+         * @param selectedDatasetIds The ids of datasets selected by the user.
+         * @param ignoredDatasetIds The ids of datasets NOT select by the user.
+         * @param changedFieldIds The ids of fields changed by the user.
+         * @param changedDatasetIds The ids of the datasets that havd values matching the
+         * respective entry on {@code changedFieldIds}.
+         * @param manuallyFilledFieldIds The ids of fields that were manually entered by the user
+         * and belonged to datasets.
+         * @param manuallyFilledDatasetIds The ids of datasets that had values matching the
+         * respective entry on {@code manuallyFilledFieldIds}.
+         * @param detectedFieldClassifications the field classification matches.
+         * @param saveDialogNotShowReason The reason why a save dialog was not shown.
+         *
+         * @throws IllegalArgumentException If the length of {@code changedFieldIds} and
+         * {@code changedDatasetIds} doesn't match.
+         * @throws IllegalArgumentException If the length of {@code manuallyFilledFieldIds} and
+         * {@code manuallyFilledDatasetIds} doesn't match.
+         *
+         * @hide
+         */
+        public Event(int eventType, @Nullable String datasetId, @Nullable Bundle clientState,
+                @Nullable List<String> selectedDatasetIds,
+                @Nullable ArraySet<String> ignoredDatasetIds,
+                @Nullable ArrayList<AutofillId> changedFieldIds,
+                @Nullable ArrayList<String> changedDatasetIds,
+                @Nullable ArrayList<AutofillId> manuallyFilledFieldIds,
+                @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
+                @Nullable AutofillId[] detectedFieldIds,
+                @Nullable FieldClassification[] detectedFieldClassifications,
+                int saveDialogNotShowReason) {
             mEventType = Preconditions.checkArgumentInRange(eventType, 0, TYPE_DATASETS_SHOWN,
                     "eventType");
             mDatasetId = datasetId;
@@ -506,6 +597,10 @@
 
             mDetectedFieldIds = detectedFieldIds;
             mDetectedFieldClassifications = detectedFieldClassifications;
+
+            mSaveDialogNotShowReason = Preconditions.checkArgumentInRange(saveDialogNotShowReason,
+                    NO_SAVE_REASON_NONE, NO_SAVE_REASON_DATASET_MATCH,
+                    "saveDialogNotShowReason");
         }
 
         @Override
@@ -521,6 +616,7 @@
                     + ", detectedFieldIds=" + Arrays.toString(mDetectedFieldIds)
                     + ", detectedFieldClassifications ="
                         + Arrays.toString(mDetectedFieldClassifications)
+                    + ", saveDialogNotShowReason=" + mSaveDialogNotShowReason
                     + "]";
         }
     }
@@ -562,12 +658,14 @@
                                 (detectedFieldIds != null)
                                 ? FieldClassification.readArrayFromParcel(parcel)
                                 : null;
+                        final int saveDialogNotShowReason = parcel.readInt();
 
                         selection.addEvent(new Event(eventType, datasetId, clientState,
                                 selectedDatasetIds, ignoredDatasets,
                                 changedFieldIds, changedDatasetIds,
                                 manuallyFilledFieldIds, manuallyFilledDatasetIds,
-                                detectedFieldIds, detectedFieldClassifications));
+                                detectedFieldIds, detectedFieldClassifications,
+                                saveDialogNotShowReason));
                     }
                     return selection;
                 }
diff --git a/core/java/android/service/autofill/InlineSuggestionRenderService.java b/core/java/android/service/autofill/InlineSuggestionRenderService.java
index 839caff..cdcd6591 100644
--- a/core/java/android/service/autofill/InlineSuggestionRenderService.java
+++ b/core/java/android/service/autofill/InlineSuggestionRenderService.java
@@ -20,7 +20,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.app.Service;
 import android.content.Intent;
 import android.content.IntentSender;
@@ -51,7 +50,6 @@
  * {@hide}
  */
 @SystemApi
-@TestApi
 public abstract class InlineSuggestionRenderService extends Service {
 
     private static final String TAG = "InlineSuggestionRenderService";
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index e640eec..619bfa2 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -687,7 +687,7 @@
         public @NonNull Builder setValidator(@NonNull Validator validator) {
             throwIfDestroyed();
             Preconditions.checkArgument((validator instanceof InternalValidator),
-                    "not provided by Android System: " + validator);
+                    "not provided by Android System: %s", validator);
             mValidator = (InternalValidator) validator;
             return this;
         }
@@ -734,7 +734,7 @@
             throwIfDestroyed();
             Preconditions.checkArgument(!ArrayUtils.isEmpty(ids), "ids cannot be empty or null");
             Preconditions.checkArgument((sanitizer instanceof InternalSanitizer),
-                    "not provided by Android System: " + sanitizer);
+                    "not provided by Android System: %s", sanitizer);
 
             if (mSanitizers == null) {
                 mSanitizers = new ArrayMap<>();
diff --git a/core/java/android/service/autofill/UserData.java b/core/java/android/service/autofill/UserData.java
index 7814f70..eaffc92 100644
--- a/core/java/android/service/autofill/UserData.java
+++ b/core/java/android/service/autofill/UserData.java
@@ -344,11 +344,11 @@
             if (!mUniqueCategoryIds.contains(categoryId)) {
                 // New category - check size
                 Preconditions.checkState(mUniqueCategoryIds.size() < getMaxCategoryCount(),
-                        "already added " + mUniqueCategoryIds.size() + " unique category ids");
+                        "already added %d unique category ids", mUniqueCategoryIds.size());
             }
 
             Preconditions.checkState(mValues.size() < getMaxUserDataSize(),
-                    "already added " + mValues.size() + " elements");
+                    "already added %d elements", mValues.size());
             addMapping(value, categoryId);
 
             return this;
diff --git a/core/java/android/service/autofill/Validators.java b/core/java/android/service/autofill/Validators.java
index 0f1ba98..0a21158 100644
--- a/core/java/android/service/autofill/Validators.java
+++ b/core/java/android/service/autofill/Validators.java
@@ -67,7 +67,7 @@
     @NonNull
     public static Validator not(@NonNull Validator validator) {
         Preconditions.checkArgument(validator instanceof InternalValidator,
-                "validator not provided by Android System: " + validator);
+                "validator not provided by Android System: %s", validator);
         return new NegationValidator((InternalValidator) validator);
     }
 
@@ -78,7 +78,7 @@
 
         for (int i = 0; i < validators.length; i++) {
             Preconditions.checkArgument((validators[i] instanceof InternalValidator),
-                    "element " + i + " not provided by Android System: " + validators[i]);
+                    "element %d not provided by Android System: %s", i, validators[i]);
             internals[i] = (InternalValidator) validators[i];
         }
         return internals;
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index 620c457..b34c2dc 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -25,7 +25,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.app.Service;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -70,7 +69,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public abstract class AugmentedAutofillService extends Service {
 
     private static final String TAG = AugmentedAutofillService.class.getSimpleName();
diff --git a/core/java/android/service/autofill/augmented/FillCallback.java b/core/java/android/service/autofill/augmented/FillCallback.java
index fc3baf1..9fc7f34 100644
--- a/core/java/android/service/autofill/augmented/FillCallback.java
+++ b/core/java/android/service/autofill/augmented/FillCallback.java
@@ -20,7 +20,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Bundle;
 import android.service.autofill.Dataset;
 import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
@@ -34,7 +33,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 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 7d552d6..7cd674e 100644
--- a/core/java/android/service/autofill/augmented/FillController.java
+++ b/core/java/android/service/autofill/augmented/FillController.java
@@ -19,7 +19,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.RemoteException;
 import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
 import android.util.Log;
@@ -37,7 +36,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class FillController {
     private static final String TAG = FillController.class.getSimpleName();
 
diff --git a/core/java/android/service/autofill/augmented/FillResponse.java b/core/java/android/service/autofill/augmented/FillResponse.java
index f72eb78..53484cf 100644
--- a/core/java/android/service/autofill/augmented/FillResponse.java
+++ b/core/java/android/service/autofill/augmented/FillResponse.java
@@ -18,7 +18,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Bundle;
 import android.service.autofill.Dataset;
 
@@ -33,7 +32,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 @DataClass(
         genBuilder = true,
         genHiddenGetters = true)
diff --git a/core/java/android/service/autofill/augmented/FillWindow.java b/core/java/android/service/autofill/augmented/FillWindow.java
index 8e86646..d4f7e11 100644
--- a/core/java/android/service/autofill/augmented/FillWindow.java
+++ b/core/java/android/service/autofill/augmented/FillWindow.java
@@ -23,7 +23,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.graphics.Rect;
 import android.os.Handler;
 import android.os.Looper;
@@ -64,7 +63,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class FillWindow implements AutoCloseable {
     private static final String TAG = FillWindow.class.getSimpleName();
 
diff --git a/core/java/android/service/autofill/augmented/PresentationParams.java b/core/java/android/service/autofill/augmented/PresentationParams.java
index 8b3a001..fe78d22 100644
--- a/core/java/android/service/autofill/augmented/PresentationParams.java
+++ b/core/java/android/service/autofill/augmented/PresentationParams.java
@@ -18,7 +18,6 @@
 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.view.View;
@@ -36,7 +35,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public abstract class PresentationParams {
 
     // /** @hide */
@@ -61,7 +59,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public abstract static class Area {
 
         /** @hide */
diff --git a/core/java/android/service/contentcapture/ActivityEvent.java b/core/java/android/service/contentcapture/ActivityEvent.java
index b741cff..1188a3f 100644
--- a/core/java/android/service/contentcapture/ActivityEvent.java
+++ b/core/java/android/service/contentcapture/ActivityEvent.java
@@ -19,7 +19,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.app.usage.UsageEvents.Event;
 import android.content.ComponentName;
 import android.os.Parcel;
@@ -34,7 +33,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class ActivityEvent implements Parcelable {
 
     /**
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index 84f6028..3c44cfd 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -26,7 +26,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.app.Service;
 import android.content.ComponentName;
 import android.content.ContentCaptureOptions;
@@ -74,7 +73,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public abstract class ContentCaptureService extends Service {
 
     private static final String TAG = ContentCaptureService.class.getSimpleName();
@@ -344,7 +342,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public void onDataShareRequest(@NonNull DataShareRequest request,
             @NonNull DataShareCallback callback) {
         if (sVerbose) Log.v(TAG, "onDataShareRequest()");
diff --git a/core/java/android/service/contentcapture/DataShareCallback.java b/core/java/android/service/contentcapture/DataShareCallback.java
index 5df8a4b..e3c7bb3 100644
--- a/core/java/android/service/contentcapture/DataShareCallback.java
+++ b/core/java/android/service/contentcapture/DataShareCallback.java
@@ -19,7 +19,6 @@
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 
 import java.util.concurrent.Executor;
 
@@ -33,7 +32,6 @@
  * @hide
  **/
 @SystemApi
-@TestApi
 public interface DataShareCallback {
 
     /** Accept the data share.
diff --git a/core/java/android/service/contentcapture/DataShareReadAdapter.java b/core/java/android/service/contentcapture/DataShareReadAdapter.java
index 8cd9eea..4526aba 100644
--- a/core/java/android/service/contentcapture/DataShareReadAdapter.java
+++ b/core/java/android/service/contentcapture/DataShareReadAdapter.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.ParcelFileDescriptor;
 import android.view.contentcapture.ContentCaptureManager.DataShareError;
 
@@ -29,7 +28,6 @@
  * @hide
  **/
 @SystemApi
-@TestApi
 public interface DataShareReadAdapter {
 
     /**
diff --git a/core/java/android/service/contentcapture/SnapshotData.java b/core/java/android/service/contentcapture/SnapshotData.java
index 5b3930a..bf469b4 100644
--- a/core/java/android/service/contentcapture/SnapshotData.java
+++ b/core/java/android/service/contentcapture/SnapshotData.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.app.assist.AssistContent;
 import android.app.assist.AssistStructure;
 import android.os.Bundle;
@@ -32,7 +31,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class SnapshotData implements Parcelable {
 
     private final @NonNull Bundle mAssistData;
diff --git a/core/java/android/service/dreams/DreamActivity.java b/core/java/android/service/dreams/DreamActivity.java
index 09d1bb9..96bbf8e 100644
--- a/core/java/android/service/dreams/DreamActivity.java
+++ b/core/java/android/service/dreams/DreamActivity.java
@@ -19,7 +19,6 @@
 import android.annotation.Nullable;
 import android.app.Activity;
 import android.os.Bundle;
-import android.view.WindowInsets;
 
 import com.android.internal.R;
 
@@ -63,8 +62,6 @@
     @Override
     public void onResume() {
         super.onResume();
-        // Hide all insets (nav bar, status bar, etc) when the dream is showing
-        getWindow().getInsetsController().hide(WindowInsets.Type.systemBars());
         overridePendingTransition(R.anim.dream_activity_open_enter,
                                   R.anim.dream_activity_open_exit);
     }
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index d2dfb29..859bb51 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -48,6 +48,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.Window;
+import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
 import android.view.accessibility.AccessibilityEvent;
@@ -1061,11 +1062,17 @@
                     | (mFullscreen ? WindowManager.LayoutParams.FLAG_FULLSCREEN : 0)
                     | (mScreenBright ? WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON : 0)
                     );
+        lp.layoutInDisplayCutoutMode =
+                WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
         mWindow.setAttributes(lp);
         // Workaround: Currently low-profile and in-window system bar backgrounds don't go
         // along well. Dreams usually don't need such bars anyways, so disable them by default.
         mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
 
+        // Hide all insets  when the dream is showing
+        mWindow.getDecorView().getWindowInsetsController().hide(WindowInsets.Type.systemBars());
+        mWindow.setDecorFitsSystemWindows(false);
+
         mWindow.getDecorView().addOnAttachStateChangeListener(
                 new View.OnAttachStateChangeListener() {
                     @Override
diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java
index 8464c6d..4b25c88 100644
--- a/core/java/android/service/notification/Adjustment.java
+++ b/core/java/android/service/notification/Adjustment.java
@@ -19,7 +19,6 @@
 import android.annotation.Nullable;
 import android.annotation.StringDef;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.app.Notification;
 import android.os.Bundle;
 import android.os.Parcel;
@@ -42,7 +41,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class Adjustment implements Parcelable {
     private final String mPackage;
     private final String mKey;
@@ -148,7 +146,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public Adjustment(String pkg, String key, Bundle signals, CharSequence explanation, int user) {
         mPackage = pkg;
         mKey = key;
@@ -232,7 +229,6 @@
 
     /** @hide */
     @SystemApi
-    @TestApi
     public int getUser() {
         return mUser;
     }
diff --git a/core/java/android/service/notification/Condition.java b/core/java/android/service/notification/Condition.java
index cf57e25..4f324f9 100644
--- a/core/java/android/service/notification/Condition.java
+++ b/core/java/android/service/notification/Condition.java
@@ -17,6 +17,7 @@
 package android.service.notification;
 
 import android.annotation.IntDef;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.net.Uri;
 import android.os.Parcel;
@@ -183,7 +184,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (!(o instanceof Condition)) return false;
         if (o == this) return true;
         final Condition other = (Condition) o;
diff --git a/core/java/android/service/notification/ConversationChannelWrapper.java b/core/java/android/service/notification/ConversationChannelWrapper.java
index ab465ab..3d0984c 100644
--- a/core/java/android/service/notification/ConversationChannelWrapper.java
+++ b/core/java/android/service/notification/ConversationChannelWrapper.java
@@ -16,6 +16,7 @@
 
 package android.service.notification;
 
+import android.annotation.Nullable;
 import android.app.NotificationChannel;
 import android.content.pm.ShortcutInfo;
 import android.graphics.drawable.Drawable;
@@ -126,7 +127,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
         ConversationChannelWrapper that = (ConversationChannelWrapper) o;
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index 975e75c..6320149 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -23,7 +23,6 @@
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
@@ -66,7 +65,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public abstract class NotificationAssistantService extends NotificationListenerService {
     private static final String TAG = "NotificationAssistants";
 
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 5d34c47..25f140f 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -22,7 +22,6 @@
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.app.ActivityManager;
 import android.app.INotificationManager;
 import android.app.Notification;
@@ -64,6 +63,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 
@@ -451,7 +451,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     public void onNotificationRemoved(@NonNull StatusBarNotification sbn,
             @NonNull RankingMap rankingMap, @NonNull NotificationStats stats, int reason) {
@@ -1802,7 +1801,7 @@
          * {@link NotificationAssistantService}
          */
         public @NonNull List<Notification.Action> getSmartActions() {
-            return mSmartActions;
+            return mSmartActions == null ? Collections.emptyList() : mSmartActions;
         }
 
         /**
@@ -1810,7 +1809,7 @@
          * {@link NotificationAssistantService}
          */
         public @NonNull List<CharSequence> getSmartReplies() {
-            return mSmartReplies;
+            return mSmartReplies == null ? Collections.emptyList() : mSmartReplies;
         }
 
         /**
@@ -1864,8 +1863,9 @@
         }
 
         /**
-         * Returns whether this notification is a conversation notification.
-         * @hide
+         * Returns whether this notification is a conversation notification, and would appear
+         * in the conversation section of the notification shade, on devices that separate that
+         * type of notification.
          */
         public boolean isConversation() {
             return mIsConversation;
@@ -1880,7 +1880,10 @@
         }
 
         /**
-         * @hide
+         * Returns the shortcut information associated with this notification, if it is a
+         * {@link #isConversation() conversation notification}.
+         * <p>This might be null even if the notification is a conversation notification, if
+         * the posting app hasn't opted into the full conversation feature set yet.</p>
          */
         public @Nullable ShortcutInfo getShortcutInfo() {
             return mShortcutInfo;
@@ -2000,7 +2003,7 @@
         }
 
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (this == o) return true;
             if (o == null || getClass() != o.getClass()) return false;
 
@@ -2076,7 +2079,7 @@
         }
 
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (this == o) return true;
             if (o == null || getClass() != o.getClass()) return false;
 
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index 675c5cd..c64f4c46 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -15,6 +15,7 @@
  */
 package android.service.notification;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -42,7 +43,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
 
diff --git a/core/java/android/service/notification/NotificationStats.java b/core/java/android/service/notification/NotificationStats.java
index 2cd8b8b..206e4fa 100644
--- a/core/java/android/service/notification/NotificationStats.java
+++ b/core/java/android/service/notification/NotificationStats.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.app.RemoteInput;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -31,7 +30,6 @@
  * Information about how the user has interacted with a given notification.
  * @hide
  */
-@TestApi
 @SystemApi
 public final class NotificationStats implements Parcelable {
 
diff --git a/core/java/android/service/notification/NotifyingApp.java b/core/java/android/service/notification/NotifyingApp.java
index a4fc5fd..930d144 100644
--- a/core/java/android/service/notification/NotifyingApp.java
+++ b/core/java/android/service/notification/NotifyingApp.java
@@ -16,6 +16,7 @@
 package android.service.notification;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -99,7 +100,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
         NotifyingApp that = (NotifyingApp) o;
diff --git a/core/java/android/service/notification/SnoozeCriterion.java b/core/java/android/service/notification/SnoozeCriterion.java
index eb624c9..d3da07a 100644
--- a/core/java/android/service/notification/SnoozeCriterion.java
+++ b/core/java/android/service/notification/SnoozeCriterion.java
@@ -17,7 +17,6 @@
 
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -27,7 +26,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class SnoozeCriterion implements Parcelable {
     private final String mId;
     private final CharSequence mExplanation;
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index 08d9905..579a8bf 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -16,7 +16,7 @@
 
 package android.service.notification;
 
-import static android.app.NotificationChannel.PLACEHOLDER_CONVERSATION_ID;
+import static android.text.TextUtils.formatSimple;
 
 import android.annotation.NonNull;
 import android.app.Notification;
@@ -31,8 +31,6 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.UserHandle;
-import android.provider.Settings;
-import android.text.TextUtils;
 
 import com.android.internal.logging.InstanceId;
 import com.android.internal.logging.nano.MetricsProto;
@@ -258,7 +256,7 @@
 
     @Override
     public String toString() {
-        return String.format(
+        return formatSimple(
                 "StatusBarNotification(pkg=%s user=%s id=%d tag=%s key=%s: %s)",
                 this.pkg, this.user, this.id, this.tag,
                 this.key, this.notification);
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 49bc65b..a9ab33c 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -23,6 +23,7 @@
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
 
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.AlarmManager;
 import android.app.NotificationManager;
@@ -430,7 +431,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (!(o instanceof ZenModeConfig)) return false;
         if (o == this) return true;
         final ZenModeConfig other = (ZenModeConfig) o;
@@ -1527,7 +1528,7 @@
         }
 
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (!(o instanceof ScheduleInfo)) return false;
             final ScheduleInfo other = (ScheduleInfo) o;
             return toDayList(days).equals(toDayList(other.days))
@@ -1629,7 +1630,7 @@
         }
 
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (!(o instanceof EventInfo)) return false;
             final EventInfo other = (EventInfo) o;
             return userId == other.userId
@@ -1934,7 +1935,7 @@
         }
 
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (!(o instanceof ZenRule)) return false;
             if (o == this) return true;
             final ZenRule other = (ZenRule) o;
diff --git a/core/java/android/service/notification/ZenPolicy.java b/core/java/android/service/notification/ZenPolicy.java
index 6d0bcff..ed3a9ac 100644
--- a/core/java/android/service/notification/ZenPolicy.java
+++ b/core/java/android/service/notification/ZenPolicy.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.os.Parcel;
@@ -950,7 +951,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (!(o instanceof ZenPolicy)) return false;
         if (o == this) return true;
         final ZenPolicy other = (ZenPolicy) o;
diff --git a/core/java/android/service/textclassifier/TextClassifierService.java b/core/java/android/service/textclassifier/TextClassifierService.java
index 93faa58..1fb18fa 100644
--- a/core/java/android/service/textclassifier/TextClassifierService.java
+++ b/core/java/android/service/textclassifier/TextClassifierService.java
@@ -22,7 +22,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.app.Service;
 import android.content.ComponentName;
 import android.content.Context;
@@ -88,7 +87,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public abstract class TextClassifierService extends Service {
 
     private static final String LOG_TAG = "TextClassifierService";
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 8f8e6cc..b94031a 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -379,16 +379,23 @@
      * Callbacks for always-on hotword detection.
      */
     public static abstract class Callback {
+
         /**
-         * Called when the hotword availability changes.
-         * This indicates a change in the availability of recognition for the given keyphrase.
-         * It's called at least once with the initial availability.<p/>
+         * Updates the availability state of the active keyphrase and locale on every keyphrase
+         * sound model change.
          *
-         * Availability implies whether the hardware on this system is capable of listening for
-         * the given keyphrase or not. <p/>
+         * <p>This API is called whenever there's a possibility that the keyphrase associated
+         * with this detector has been updated. It is not guaranteed that there is in fact any
+         * change, as it may be called for other reasons.</p>
+         *
+         * <p>This API is also guaranteed to be called right after an AlwaysOnHotwordDetector
+         * instance is created to updated the current availability state.</p>
+         *
+         * <p>Availability implies the current enrollment state of the given keyphrase. If the
+         * hardware on this system is not capable of listening for the given keyphrase,
+         * {@link AlwaysOnHotwordDetector#STATE_HARDWARE_UNAVAILABLE} will be returned.
          *
          * @see AlwaysOnHotwordDetector#STATE_HARDWARE_UNAVAILABLE
-         * @see AlwaysOnHotwordDetector#STATE_KEYPHRASE_UNSUPPORTED
          * @see AlwaysOnHotwordDetector#STATE_KEYPHRASE_UNENROLLED
          * @see AlwaysOnHotwordDetector#STATE_KEYPHRASE_ENROLLED
          */
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 0341b6d..0b6d371 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -2073,7 +2073,7 @@
         }
 
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (this == o) {
                 return true;
             }
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 0f46ffc..4249e5c 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -20,6 +20,7 @@
 import static android.graphics.Matrix.MSCALE_Y;
 import static android.graphics.Matrix.MSKEW_X;
 import static android.graphics.Matrix.MSKEW_Y;
+import static android.view.View.SYSTEM_UI_FLAG_VISIBLE;
 
 import android.annotation.FloatRange;
 import android.annotation.Nullable;
@@ -33,6 +34,7 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.Intent;
+import android.content.res.Configuration;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
@@ -227,9 +229,6 @@
 
         SurfaceControl mSurfaceControl = new SurfaceControl();
 
-        // Unused relayout out-param
-        SurfaceControl mTmpSurfaceControl = new SurfaceControl();
-
         final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() {
             {
                 mRequestedFormat = PixelFormat.RGBX_8888;
@@ -880,9 +879,9 @@
                         InputChannel inputChannel = new InputChannel();
 
                         if (mSession.addToDisplay(mWindow, mLayout, View.VISIBLE,
-                                mDisplay.getDisplayId(), mWinFrames.frame, mWinFrames.contentInsets,
-                                mWinFrames.stableInsets, mWinFrames.displayCutout, inputChannel,
-                                mInsetsState, mTempControls) < 0) {
+                                mDisplay.getDisplayId(), mInsetsState, mWinFrames.frame,
+                                mWinFrames.displayCutout, inputChannel, mInsetsState,
+                                mTempControls) < 0) {
                             Log.w(TAG, "Failed to add window while updating wallpaper surface.");
                             return;
                         }
@@ -905,7 +904,7 @@
                     final int relayoutResult = mSession.relayout(
                             mWindow, mLayout, mWidth, mHeight,
                             View.VISIBLE, 0, -1, mWinFrames, mMergedConfiguration, mSurfaceControl,
-                            mInsetsState, mTempControls, mSurfaceSize, mTmpSurfaceControl);
+                            mInsetsState, mTempControls, mSurfaceSize);
                     if (mSurfaceControl.isValid()) {
                         mSurfaceHolder.mSurface.copyFrom(mSurfaceControl);
                     }
@@ -916,20 +915,22 @@
                     int w = mWinFrames.frame.width();
                     int h = mWinFrames.frame.height();
 
+                    final DisplayCutout rawCutout = mWinFrames.displayCutout.get();
+                    final Configuration config = getResources().getConfiguration();
+                    final Rect visibleFrame = new Rect(mWinFrames.frame);
+                    visibleFrame.intersect(mInsetsState.getDisplayFrame());
+                    WindowInsets windowInsets = mInsetsState.calculateInsets(visibleFrame,
+                            null /* ignoringVisibilityState */, config.isScreenRound(),
+                            false /* alwaysConsumeSystemBars */, rawCutout, mLayout.softInputMode,
+                            mLayout.flags, SYSTEM_UI_FLAG_VISIBLE, mLayout.type,
+                            config.windowConfiguration.getWindowingMode(), null /* typeSideMap */);
+
                     if (!fixedSize) {
                         final Rect padding = mIWallpaperEngine.mDisplayPadding;
                         w += padding.left + padding.right;
                         h += padding.top + padding.bottom;
-                        mWinFrames.contentInsets.left += padding.left;
-                        mWinFrames.contentInsets.top += padding.top;
-                        mWinFrames.contentInsets.right += padding.right;
-                        mWinFrames.contentInsets.bottom += padding.bottom;
-                        mWinFrames.stableInsets.left += padding.left;
-                        mWinFrames.stableInsets.top += padding.top;
-                        mWinFrames.stableInsets.right += padding.right;
-                        mWinFrames.stableInsets.bottom += padding.bottom;
-                        mWinFrames.displayCutout.set(mWinFrames.displayCutout.get().inset(
-                                -padding.left, -padding.top, -padding.right, -padding.bottom));
+                        windowInsets = windowInsets.insetUnchecked(
+                                -padding.left, -padding.top, -padding.right, -padding.bottom);
                     } else {
                         w = myWidth;
                         h = myHeight;
@@ -948,9 +949,12 @@
                         Log.v(TAG, "Wallpaper size has changed: (" + mCurWidth + ", " + mCurHeight);
                     }
 
-                    final DisplayCutout displayCutout = mWinFrames.displayCutout.get();
-                    insetsChanged |= !mDispatchedContentInsets.equals(mWinFrames.contentInsets);
-                    insetsChanged |= !mDispatchedStableInsets.equals(mWinFrames.stableInsets);
+                    final Rect contentInsets = windowInsets.getSystemWindowInsets().toRect();
+                    final Rect stableInsets = windowInsets.getStableInsets().toRect();
+                    final DisplayCutout displayCutout = windowInsets.getDisplayCutout() != null
+                            ? windowInsets.getDisplayCutout() : rawCutout;
+                    insetsChanged |= !mDispatchedContentInsets.equals(contentInsets);
+                    insetsChanged |= !mDispatchedStableInsets.equals(stableInsets);
                     insetsChanged |= !mDispatchedDisplayCutout.equals(displayCutout);
 
                     mSurfaceHolder.setSurfaceFrameSize(w, h);
@@ -1010,18 +1014,13 @@
                         }
 
                         if (insetsChanged) {
-                            mDispatchedContentInsets.set(mWinFrames.contentInsets);
-                            mDispatchedStableInsets.set(mWinFrames.stableInsets);
+                            mDispatchedContentInsets.set(contentInsets);
+                            mDispatchedStableInsets.set(stableInsets);
                             mDispatchedDisplayCutout = displayCutout;
-                            mFinalStableInsets.set(mDispatchedStableInsets);
-                            WindowInsets insets = new WindowInsets(mFinalSystemInsets,
-                                    mFinalStableInsets,
-                                    getResources().getConfiguration().isScreenRound(), false,
-                                    mDispatchedDisplayCutout);
                             if (DEBUG) {
-                                Log.v(TAG, "dispatching insets=" + insets);
+                                Log.v(TAG, "dispatching insets=" + windowInsets);
                             }
-                            onApplyWindowInsets(insets);
+                            onApplyWindowInsets(windowInsets);
                         }
 
                         if (redrawNeeded) {
diff --git a/core/java/android/service/watchdog/ExplicitHealthCheckService.java b/core/java/android/service/watchdog/ExplicitHealthCheckService.java
index b1647fe..49e00d6 100644
--- a/core/java/android/service/watchdog/ExplicitHealthCheckService.java
+++ b/core/java/android/service/watchdog/ExplicitHealthCheckService.java
@@ -66,7 +66,6 @@
  * </pre>
  * @hide
  */
-@TestApi
 @SystemApi
 public abstract class ExplicitHealthCheckService extends Service {
 
@@ -195,7 +194,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     public static final class PackageConfig implements Parcelable {
         private static final long DEFAULT_HEALTH_CHECK_TIMEOUT_MILLIS = TimeUnit.HOURS.toMillis(1);
diff --git a/core/java/android/speech/tts/Voice.java b/core/java/android/speech/tts/Voice.java
index fefc35e..7ffe5eb 100644
--- a/core/java/android/speech/tts/Voice.java
+++ b/core/java/android/speech/tts/Voice.java
@@ -16,6 +16,7 @@
 
 package android.speech.tts;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -208,7 +209,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (this == obj) {
             return true;
         }
diff --git a/core/java/android/telephony/CellBroadcastIntents.java b/core/java/android/telephony/CellBroadcastIntents.java
index e07f69a..c3ca286 100644
--- a/core/java/android/telephony/CellBroadcastIntents.java
+++ b/core/java/android/telephony/CellBroadcastIntents.java
@@ -45,7 +45,8 @@
     private static final String EXTRA_MESSAGE = "message";
 
     /**
-     * Broadcast intent action for notifying area information has been updated. The information
+     * Broadcast intent action for notifying area information has been updated. broadcast is also
+     * sent when the user turns off area info alerts. The information
      * can be retrieved by {@link CellBroadcastService#getCellBroadcastAreaInfo(int)}. The
      * associated SIM slot index of updated area information can be retrieved through the extra
      * {@link SubscriptionManager#EXTRA_SLOT_INDEX}.
diff --git a/core/java/android/telephony/DataConnectionRealTimeInfo.java b/core/java/android/telephony/DataConnectionRealTimeInfo.java
index 8106f5f..8767133 100644
--- a/core/java/android/telephony/DataConnectionRealTimeInfo.java
+++ b/core/java/android/telephony/DataConnectionRealTimeInfo.java
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -116,7 +117,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (this == obj) {
             return true;
         }
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index 1682686..6a5d5c6 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -20,7 +20,6 @@
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Binder;
@@ -437,7 +436,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
     public static final int LISTEN_OUTGOING_EMERGENCY_CALL                  = 0x10000000;
 
@@ -450,7 +448,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
     public static final int LISTEN_OUTGOING_EMERGENCY_SMS                   = 0x20000000;
 
@@ -936,18 +933,21 @@
     /**
      * Callback invoked when the current emergency number list has changed on the registered
      * subscription.
-     * Note, the registration subId comes from {@link TelephonyManager} object which registers
-     * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+     *
+     * Note, the registered subscription is associated with {@link TelephonyManager} object
+     * on which {@link TelephonyManager#listen(PhoneStateListener, int)} was called.
      * If this TelephonyManager object was created with
      * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-     * subId. Otherwise, this callback applies to
+     * given subId. Otherwise, this callback applies to
      * {@link SubscriptionManager#getDefaultSubscriptionId()}.
      *
-     * @param emergencyNumberList Map including the key as the active subscription ID
-     *                           (Note: if there is no active subscription, the key is
-     *                           {@link SubscriptionManager#getDefaultSubscriptionId})
-     *                           and the value as the list of {@link EmergencyNumber};
-     *                           null if this information is not available.
+     * @param emergencyNumberList Map associating all active subscriptions on the device with the
+     *                            list of emergency numbers originating from that subscription.
+     *                            If there are no active subscriptions, the map will contain a
+     *                            single entry with
+     *                            {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} as
+     *                            the key and a list of emergency numbers as the value. If no
+     *                            emergency number information is available, the value will be null.
      */
     public void onEmergencyNumberListChanged(
             @NonNull Map<Integer, List<EmergencyNumber>> emergencyNumberList) {
@@ -966,7 +966,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @Deprecated
     public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber) {
         // default implementation empty
@@ -991,7 +990,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber,
             int subscriptionId) {
         // Default implementation for backwards compatibility
@@ -1008,7 +1006,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @Deprecated
     public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber) {
         // default implementation empty
@@ -1030,7 +1027,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber,
             int subscriptionId) {
         // Default implementation for backwards compatibility
diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java
index ac27e3d..c5f7f581 100644
--- a/core/java/android/text/SpannableStringBuilder.java
+++ b/core/java/android/text/SpannableStringBuilder.java
@@ -1607,7 +1607,7 @@
 
     // Same as SpannableStringInternal
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (o instanceof Spanned &&
                 toString().equals(o.toString())) {
             final Spanned other = (Spanned) o;
diff --git a/core/java/android/text/SpannableStringInternal.java b/core/java/android/text/SpannableStringInternal.java
index 4c9328a..0fe9b6a 100644
--- a/core/java/android/text/SpannableStringInternal.java
+++ b/core/java/android/text/SpannableStringInternal.java
@@ -16,6 +16,7 @@
 
 package android.text;
 
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 
 import com.android.internal.util.ArrayUtils;
@@ -501,7 +502,7 @@
 
     // Same as SpannableStringBuilder
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (o instanceof Spanned &&
                 toString().equals(o.toString())) {
             final Spanned other = (Spanned) o;
diff --git a/core/java/android/text/StyledTextShaper.java b/core/java/android/text/StyledTextShaper.java
deleted file mode 100644
index bf90614..0000000
--- a/core/java/android/text/StyledTextShaper.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.text;
-
-import android.annotation.NonNull;
-import android.graphics.Paint;
-import android.graphics.text.PositionedGlyphs;
-import android.graphics.text.TextShaper;
-
-import java.util.List;
-
-/**
- * Provides text shaping for multi-styled text.
- *
- * @see TextShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint)
- * @see TextShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)
- * @see StyledTextShaper#shapeText(CharSequence, int, int, TextDirectionHeuristic, TextPaint)
- */
-public class StyledTextShaper {
-    private StyledTextShaper() {}
-
-
-    /**
-     * Shape multi-styled text.
-     *
-     * @param text a styled text.
-     * @param start a start index of shaping target in the text.
-     * @param count a length of shaping target in the text.
-     * @param dir a text direction.
-     * @param paint a paint
-     * @return a shape result.
-     */
-    public static @NonNull List<PositionedGlyphs> shapeText(
-            @NonNull CharSequence text, int start, int count,
-            @NonNull TextDirectionHeuristic dir, @NonNull TextPaint paint) {
-        MeasuredParagraph mp = MeasuredParagraph.buildForBidi(
-                text, start, start + count, dir, null);
-        TextLine tl = TextLine.obtain();
-        try {
-            tl.set(paint, text, start, start + count,
-                    mp.getParagraphDir(),
-                    mp.getDirections(start, start + count),
-                    false /* tabstop is not supported */,
-                    null,
-                    -1, -1 // ellipsis is not supported.
-            );
-            return tl.shape();
-        } finally {
-            TextLine.recycle(tl);
-        }
-    }
-
-}
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index b826832..6318c47 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -24,7 +24,7 @@
 import android.graphics.Paint;
 import android.graphics.Paint.FontMetricsInt;
 import android.graphics.text.PositionedGlyphs;
-import android.graphics.text.TextShaper;
+import android.graphics.text.TextRunShaper;
 import android.os.Build;
 import android.text.Layout.Directions;
 import android.text.Layout.TabStops;
@@ -37,7 +37,6 @@
 import com.android.internal.util.ArrayUtils;
 
 import java.util.ArrayList;
-import java.util.List;
 
 /**
  * Represents a line of styled text, for measuring in visual order and
@@ -312,8 +311,7 @@
     /**
      * Shape the TextLine.
      */
-    List<PositionedGlyphs> shape() {
-        List<PositionedGlyphs> glyphs = new ArrayList<>();
+    void shape(TextShaper.GlyphsConsumer consumer) {
         float horizontal = 0;
         float x = 0;
         final int runCount = mDirections.getRunCount();
@@ -326,7 +324,7 @@
             int segStart = runStart;
             for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) {
                 if (j == runLimit || charAt(j) == TAB_CHAR) {
-                    horizontal += shapeRun(glyphs, segStart, j, runIsRtl, x + horizontal,
+                    horizontal += shapeRun(consumer, segStart, j, runIsRtl, x + horizontal,
                             runIndex != (runCount - 1) || j != mLen);
 
                     if (j != runLimit) {  // charAt(j) == TAB_CHAR
@@ -336,7 +334,6 @@
                 }
             }
         }
-        return glyphs;
     }
 
     /**
@@ -546,7 +543,7 @@
     /**
      * Shape a unidirectional (but possibly multi-styled) run of text.
      *
-     * @param glyphs the output positioned glyphs list
+     * @param consumer the consumer of the shape result
      * @param start the line-relative start
      * @param limit the line-relative limit
      * @param runIsRtl true if the run is right-to-left
@@ -555,16 +552,17 @@
      * @return the signed width of the run, based on the paragraph direction.
      * Only valid if needWidth is true.
      */
-    private float shapeRun(List<PositionedGlyphs> glyphs, int start,
+    private float shapeRun(TextShaper.GlyphsConsumer consumer, int start,
             int limit, boolean runIsRtl, float x, boolean needWidth) {
 
         if ((mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) {
             float w = -measureRun(start, limit, limit, runIsRtl, null);
-            handleRun(start, limit, limit, runIsRtl, null, glyphs, x + w, 0, 0, 0, null, false);
+            handleRun(start, limit, limit, runIsRtl, null, consumer, x + w, 0, 0, 0, null, false);
             return w;
         }
 
-        return handleRun(start, limit, limit, runIsRtl, null, glyphs, x, 0, 0, 0, null, needWidth);
+        return handleRun(start, limit, limit, runIsRtl, null, consumer, x, 0, 0, 0, null,
+                needWidth);
     }
 
 
@@ -899,7 +897,7 @@
      * @param end the end of the text
      * @param runIsRtl true if the run is right-to-left
      * @param c the canvas, can be null if rendering is not needed
-     * @param glyphs the output positioned glyph list, can be null if not necessary
+     * @param consumer the output positioned glyph list, can be null if not necessary
      * @param x the edge of the run closest to the leading margin
      * @param top the top of the line
      * @param y the baseline
@@ -913,7 +911,7 @@
      */
     private float handleText(TextPaint wp, int start, int end,
             int contextStart, int contextEnd, boolean runIsRtl,
-            Canvas c, List<PositionedGlyphs> glyphs, float x, int top, int y, int bottom,
+            Canvas c, TextShaper.GlyphsConsumer consumer, float x, int top, int y, int bottom,
             FontMetricsInt fmi, boolean needWidth, int offset,
             @Nullable ArrayList<DecorationInfo> decorations) {
 
@@ -933,7 +931,8 @@
         float totalWidth = 0;
 
         final int numDecorations = decorations == null ? 0 : decorations.size();
-        if (needWidth || (c != null && (wp.bgColor != 0 || numDecorations != 0 || runIsRtl))) {
+        if (needWidth || ((c != null || consumer != null) && (wp.bgColor != 0
+                || numDecorations != 0 || runIsRtl))) {
             totalWidth = getRunAdvance(wp, start, end, contextStart, contextEnd, runIsRtl, offset);
         }
 
@@ -946,8 +945,8 @@
             rightX = x + totalWidth;
         }
 
-        if (glyphs != null) {
-            shapeTextRun(glyphs, wp, start, end, contextStart, contextEnd, runIsRtl, leftX);
+        if (consumer != null) {
+            shapeTextRun(consumer, wp, start, end, contextStart, contextEnd, runIsRtl, leftX);
         }
 
         if (c != null) {
@@ -1135,7 +1134,7 @@
      * @param limit the limit of the run
      * @param runIsRtl true if the run is right-to-left
      * @param c the canvas, can be null
-     * @param glyphs the output positioned glyphs, can be null
+     * @param consumer the output positioned glyphs, can be null
      * @param x the end of the run closest to the leading margin
      * @param top the top of the line
      * @param y the baseline
@@ -1147,7 +1146,7 @@
      */
     private float handleRun(int start, int measureLimit,
             int limit, boolean runIsRtl, Canvas c,
-            List<PositionedGlyphs> glyphs, float x, int top, int y,
+            TextShaper.GlyphsConsumer consumer, float x, int top, int y,
             int bottom, FontMetricsInt fmi, boolean needWidth) {
 
         if (measureLimit < start || measureLimit > limit) {
@@ -1180,7 +1179,7 @@
             wp.set(mPaint);
             wp.setStartHyphenEdit(adjustStartHyphenEdit(start, wp.getStartHyphenEdit()));
             wp.setEndHyphenEdit(adjustEndHyphenEdit(limit, wp.getEndHyphenEdit()));
-            return handleText(wp, start, limit, start, limit, runIsRtl, c, glyphs, x, top,
+            return handleText(wp, start, limit, start, limit, runIsRtl, c, consumer, x, top,
                     y, bottom, fmi, needWidth, measureLimit, null);
         }
 
@@ -1262,7 +1261,7 @@
                     activePaint.setEndHyphenEdit(
                             adjustEndHyphenEdit(activeEnd, mPaint.getEndHyphenEdit()));
                     x += handleText(activePaint, activeStart, activeEnd, i, inext, runIsRtl, c,
-                            glyphs, x, top, y, bottom, fmi, needWidth || activeEnd < measureLimit,
+                            consumer, x, top, y, bottom, fmi, needWidth || activeEnd < measureLimit,
                             Math.min(activeEnd, mlimit), mDecorations);
 
                     activeStart = j;
@@ -1288,7 +1287,7 @@
                     adjustStartHyphenEdit(activeStart, mPaint.getStartHyphenEdit()));
             activePaint.setEndHyphenEdit(
                     adjustEndHyphenEdit(activeEnd, mPaint.getEndHyphenEdit()));
-            x += handleText(activePaint, activeStart, activeEnd, i, inext, runIsRtl, c, glyphs, x,
+            x += handleText(activePaint, activeStart, activeEnd, i, inext, runIsRtl, c, consumer, x,
                     top, y, bottom, fmi, needWidth || activeEnd < measureLimit,
                     Math.min(activeEnd, mlimit), mDecorations);
         }
@@ -1327,7 +1326,7 @@
     /**
      * Shape a text run with the set-up paint.
      *
-     * @param glyphs the output positioned glyphs list
+     * @param consumer the output positioned glyphs list
      * @param paint the paint used to render the text
      * @param start the start of the run
      * @param end the end of the run
@@ -1336,30 +1335,32 @@
      * @param runIsRtl true if the run is right-to-left
      * @param x the x position of the left edge of the run
      */
-    private void shapeTextRun(List<PositionedGlyphs> glyphs, TextPaint paint,
+    private void shapeTextRun(TextShaper.GlyphsConsumer consumer, TextPaint paint,
             int start, int end, int contextStart, int contextEnd, boolean runIsRtl, float x) {
 
         int count = end - start;
         int contextCount = contextEnd - contextStart;
+        PositionedGlyphs glyphs;
         if (mCharsValid) {
-            glyphs.add(TextShaper.shapeTextRun(
+            glyphs = TextRunShaper.shapeTextRun(
                     mChars,
                     start, count,
                     contextStart, contextCount,
                     x, 0f,
                     runIsRtl,
                     paint
-            ));
+            );
         } else {
-            glyphs.add(TextShaper.shapeTextRun(
+            glyphs = TextRunShaper.shapeTextRun(
                     mText,
                     mStart + start, count,
                     mStart + contextStart, contextCount,
                     x, 0f,
                     runIsRtl,
                     paint
-            ));
+            );
         }
+        consumer.accept(start, count, glyphs, paint);
     }
 
 
diff --git a/core/java/android/text/TextShaper.java b/core/java/android/text/TextShaper.java
new file mode 100644
index 0000000..02fd7b4
--- /dev/null
+++ b/core/java/android/text/TextShaper.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.text;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.graphics.Paint;
+import android.graphics.text.PositionedGlyphs;
+import android.graphics.text.TextRunShaper;
+
+/**
+ * Provides text shaping for multi-styled text.
+ *
+ * Here is an example of animating text size and letter spacing for simple text.
+ * <pre>
+ * <code>
+ * // In this example, shape the text once for start and end state, then animate between two shape
+ * // result without re-shaping in each frame.
+ * class SimpleAnimationView @JvmOverloads constructor(
+ *         context: Context,
+ *         attrs: AttributeSet? = null,
+ *         defStyleAttr: Int = 0
+ * ) : View(context, attrs, defStyleAttr) {
+ *     private val textDir = TextDirectionHeuristics.LOCALE
+ *     private val text = "Hello, World."  // The text to be displayed
+ *
+ *     // Class for keeping drawing parameters.
+ *     data class DrawStyle(val textSize: Float, val alpha: Int)
+ *
+ *     // The start and end text shaping result. This class will animate between these two.
+ *     private val start = mutableListOf&lt;Pair&lt;PositionedGlyphs, DrawStyle&gt;&gt;()
+ *     private val end = mutableListOf&lt;Pair&lt;PositionedGlyphs, DrawStyle&gt;&gt;()
+ *
+ *     init {
+ *         val startPaint = TextPaint().apply {
+ *             alpha = 0 // Alpha only affect text drawing but not text shaping
+ *             textSize = 36f // TextSize affect both text shaping and drawing.
+ *             letterSpacing = 0f // Letter spacing only affect text shaping but not drawing.
+ *         }
+ *
+ *         val endPaint = TextPaint().apply {
+ *             alpha = 255
+ *             textSize =128f
+ *             letterSpacing = 0.1f
+ *         }
+ *
+ *         TextShaper.shapeText(text, 0, text.length, textDir, startPaint) { _, _, glyphs, paint ->
+ *             start.add(Pair(glyphs, DrawStyle(paint.textSize, paint.alpha)))
+ *         }
+ *         TextShaper.shapeText(text, 0, text.length, textDir, endPaint) { _, _, glyphs, paint ->
+ *             end.add(Pair(glyphs, DrawStyle(paint.textSize, paint.alpha)))
+ *         }
+ *     }
+ *
+ *     override fun onDraw(canvas: Canvas) {
+ *         super.onDraw(canvas)
+ *
+ *         // Set the baseline to the vertical center of the view.
+ *         canvas.translate(0f, height / 2f)
+ *
+ *         // Assume the number of PositionedGlyphs are the same. If different, you may want to
+ *         // animate in a different way, e.g. cross fading.
+ *         start.zip(end) { (startGlyphs, startDrawStyle), (endGlyphs, endDrawStyle) ->
+ *             // Tween the style and set to paint.
+ *             paint.textSize = lerp(startDrawStyle.textSize, endDrawStyle.textSize, progress)
+ *             paint.alpha = lerp(startDrawStyle.alpha, endDrawStyle.alpha, progress)
+ *
+ *             // Assume the number of glyphs are the same. If different, you may want to animate in
+ *             // a different way, e.g. cross fading.
+ *             require(startGlyphs.glyphCount() == endGlyphs.glyphCount())
+ *
+ *             if (startGlyphs.glyphCount() == 0) return@zip
+ *
+ *             var curFont = startGlyphs.getFont(0)
+ *             var drawStart = 0
+ *             for (i in 1 until startGlyphs.glyphCount()) {
+ *                 // Assume the pair of Glyph ID and font is the same. If different, you may want
+ *                 // to animate in a different way, e.g. cross fading.
+ *                 require(startGlyphs.getGlyphId(i) == endGlyphs.getGlyphId(i))
+ *                 require(startGlyphs.getFont(i) === endGlyphs.getFont(i))
+ *
+ *                 val font = startGlyphs.getFont(i)
+ *                 if (curFont != font) {
+ *                     drawGlyphs(canvas, startGlyphs, endGlyphs, drawStart, i, curFont, paint)
+ *                     curFont = font
+ *                     drawStart = i
+ *                 }
+ *             }
+ *             if (drawStart != startGlyphs.glyphCount() - 1) {
+ *                 drawGlyphs(canvas, startGlyphs, endGlyphs, drawStart, startGlyphs.glyphCount(),
+ *                         curFont, paint)
+ *             }
+ *         }
+ *     }
+ *
+ *     // Draws Glyphs for the same font run.
+ *     private fun drawGlyphs(canvas: Canvas, startGlyph: PositionedGlyphs,
+ *                            endGlyph: PositionedGlyphs, start: Int, end: Int, font: Font,
+ *                            paint: Paint) {
+ *         var cacheIndex = 0
+ *         for (i in start until end) {
+ *             intArrayCache[cacheIndex] = startGlyph.getGlyphId(i)
+ *             // The glyph positions are different from start to end since they are shaped
+ *             // with different letter spacing. Use linear interpolation for positions
+ *             // during animation.
+ *             floatArrayCache[cacheIndex * 2] =
+ *                     lerp(startGlyph.getGlyphX(i), endGlyph.getGlyphX(i), progress)
+ *             floatArrayCache[cacheIndex * 2 + 1] =
+ *                     lerp(startGlyph.getGlyphY(i), endGlyph.getGlyphY(i), progress)
+ *             if (cacheIndex == CACHE_SIZE) {  // Cached int array is full. Flashing.
+ *                 canvas.drawGlyphs(
+ *                         intArrayCache, 0, // glyphID array and its starting offset
+ *                         floatArrayCache, 0, // position array and its starting offset
+ *                         cacheIndex, // glyph count
+ *                         font,
+ *                         paint
+ *                 )
+ *                 cacheIndex = 0
+ *             }
+ *             cacheIndex++
+ *         }
+ *         if (cacheIndex != 0) {
+ *             canvas.drawGlyphs(
+ *                     intArrayCache, 0, // glyphID array and its starting offset
+ *                     floatArrayCache, 0, // position array and its starting offset
+ *                     cacheIndex, // glyph count
+ *                     font,
+ *                     paint
+ *             )
+ *         }
+ *     }
+ *
+ *     // Linear Interpolator
+ *     private fun lerp(start: Float, end: Float, t: Float) = start * (1f - t) + end * t
+ *     private fun lerp(start: Int, end: Int, t: Float) = (start * (1f - t) + end * t).toInt()
+ *
+ *     // The animation progress.
+ *     var progress: Float = 0f
+ *         set(value) {
+ *             field = value
+ *             invalidate()
+ *         }
+ *
+ *     // working copy of paint.
+ *     private val paint = Paint()
+ *
+ *     // Array cache for reducing allocation during drawing.
+ *     private var intArrayCache = IntArray(CACHE_SIZE)
+ *     private var floatArrayCache = FloatArray(CACHE_SIZE * 2)
+ * }
+ * </code>
+ * </pre>
+ * @see TextRunShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint)
+ * @see TextRunShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)
+ * @see TextShaper#shapeText(CharSequence, int, int, TextDirectionHeuristic, TextPaint,
+ * GlyphsConsumer)
+ */
+public class TextShaper {
+    private TextShaper() {}
+
+    /**
+     * An consumer interface for accepting text shape result.
+     */
+    public interface GlyphsConsumer {
+        /**
+         * Accept text shape result.
+         *
+         * The implementation must not keep reference of paint since it will be mutated for the
+         * subsequent styles. Also, for saving heap size, keep only necessary members in the
+         * {@link TextPaint} instead of copying {@link TextPaint} object.
+         *
+         * @param start The start index of the shaped text.
+         * @param count The length of the shaped text.
+         * @param glyphs The shape result.
+         * @param paint The paint to be used for drawing.
+         */
+        void accept(
+                @IntRange(from = 0) int start,
+                @IntRange(from = 0) int count,
+                @NonNull PositionedGlyphs glyphs,
+                @NonNull TextPaint paint);
+    }
+
+    /**
+     * Shape multi-styled text.
+     *
+     * In the LTR context, the shape result will go from left to right, thus you may want to draw
+     * glyphs from left most position of the canvas. In the RTL context, the shape result will go
+     * from right to left, thus you may want to draw glyphs from right most position of the canvas.
+     *
+     * @param text a styled text.
+     * @param start a start index of shaping target in the text.
+     * @param count a length of shaping target in the text.
+     * @param dir a text direction.
+     * @param paint a paint
+     * @param consumer a consumer of the shape result.
+     */
+    public static void shapeText(
+            @NonNull CharSequence text, @IntRange(from = 0) int start,
+            @IntRange(from = 0) int count, @NonNull TextDirectionHeuristic dir,
+            @NonNull TextPaint paint, @NonNull GlyphsConsumer consumer) {
+        MeasuredParagraph mp = MeasuredParagraph.buildForBidi(
+                text, start, start + count, dir, null);
+        TextLine tl = TextLine.obtain();
+        try {
+            tl.set(paint, text, start, start + count,
+                    mp.getParagraphDir(),
+                    mp.getDirections(0, count),
+                    false /* tabstop is not supported */,
+                    null,
+                    -1, -1 // ellipsis is not supported.
+            );
+            tl.shape(consumer);
+        } finally {
+            TextLine.recycle(tl);
+        }
+    }
+
+}
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 984acfd..d0fd2b3 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -2080,6 +2080,119 @@
     }
 
     /**
+     * Simple alternative to {@link String#format} which purposefully supports
+     * only a small handful of substitutions to improve execution speed.
+     * Benchmarking reveals this optimized alternative performs 6.5x faster for
+     * a typical format string.
+     * <p>
+     * Below is a summary of the limited grammar supported by this method; if
+     * you need advanced features, please continue using {@link String#format}.
+     * <ul>
+     * <li>{@code %b} for {@code boolean}
+     * <li>{@code %c} for {@code char}
+     * <li>{@code %d} for {@code int} or {@code long}
+     * <li>{@code %f} for {@code float} or {@code double}
+     * <li>{@code %s} for {@code String}
+     * <li>{@code %x} for hex representation of {@code int} or {@code long}
+     * <li>{@code %%} for literal {@code %}
+     * <li>{@code %04d} style grammar to specify the argument width, such as
+     * {@code %04d} to prefix an {@code int} with zeros or {@code %10b} to
+     * prefix a {@code boolean} with spaces
+     * </ul>
+     *
+     * @throws IllegalArgumentException if the format string or arguments don't
+     *             match the supported grammar described above.
+     * @hide
+     */
+    public static @NonNull String formatSimple(@NonNull String format, Object... args) {
+        final StringBuilder sb = new StringBuilder(format);
+        int j = 0;
+        for (int i = 0; i < sb.length(); ) {
+            if (sb.charAt(i) == '%') {
+                char code = sb.charAt(i + 1);
+
+                // Decode any argument width request
+                char prefixChar = '\0';
+                int prefixLen = 0;
+                int consume = 2;
+                while ('0' <= code && code <= '9') {
+                    if (prefixChar == '\0') {
+                        prefixChar = (code == '0') ? '0' : ' ';
+                    }
+                    prefixLen *= 10;
+                    prefixLen += Character.digit(code, 10);
+                    consume += 1;
+                    code = sb.charAt(i + consume - 1);
+                }
+
+                final String repl;
+                switch (code) {
+                    case 'b': {
+                        if (j == args.length) {
+                            throw new IllegalArgumentException("Too few arguments");
+                        }
+                        final Object arg = args[j++];
+                        if (arg instanceof Boolean) {
+                            repl = Boolean.toString((boolean) arg);
+                        } else {
+                            repl = Boolean.toString(arg != null);
+                        }
+                        break;
+                    }
+                    case 'c':
+                    case 'd':
+                    case 'f':
+                    case 's': {
+                        if (j == args.length) {
+                            throw new IllegalArgumentException("Too few arguments");
+                        }
+                        final Object arg = args[j++];
+                        repl = String.valueOf(arg);
+                        break;
+                    }
+                    case 'x': {
+                        if (j == args.length) {
+                            throw new IllegalArgumentException("Too few arguments");
+                        }
+                        final Object arg = args[j++];
+                        if (arg instanceof Integer) {
+                            repl = Integer.toHexString((int) arg);
+                        } else if (arg instanceof Long) {
+                            repl = Long.toHexString((long) arg);
+                        } else {
+                            throw new IllegalArgumentException(
+                                    "Unsupported hex type " + arg.getClass());
+                        }
+                        break;
+                    }
+                    case '%': {
+                        repl = "%";
+                        break;
+                    }
+                    default: {
+                        throw new IllegalArgumentException("Unsupported format code " + code);
+                    }
+                }
+
+                sb.replace(i, i + consume, repl);
+
+                // Apply any argument width request
+                final int prefixInsert = (prefixChar == '0' && repl.charAt(0) == '-') ? 1 : 0;
+                for (int k = repl.length(); k < prefixLen; k++) {
+                    sb.insert(i + prefixInsert, prefixChar);
+                }
+                i += Math.max(repl.length(), prefixLen);
+            } else {
+                i++;
+            }
+        }
+        if (j != args.length) {
+            throw new IllegalArgumentException("Too many arguments");
+        }
+        return sb.toString();
+    }
+
+    /**
      * Returns whether or not the specified spanned text has a style span.
      * @hide
      */
@@ -2173,7 +2286,7 @@
     public static <T extends CharSequence> T trimToLengthWithEllipsis(@Nullable T text,
             @IntRange(from = 1) int size) {
         T trimmed = trimToSize(text, size);
-        if (trimmed.length() < text.length()) {
+        if (text != null && trimmed.length() < text.length()) {
             trimmed = (T) (trimmed.toString() + "...");
         }
         return trimmed;
diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java
index 38e3b39..c2e3a80 100755
--- a/core/java/android/text/format/DateFormat.java
+++ b/core/java/android/text/format/DateFormat.java
@@ -17,14 +17,19 @@
 package android.text.format;
 
 import android.annotation.NonNull;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.icu.text.DateFormatSymbols;
 import android.icu.text.DateTimePatternGenerator;
+import android.os.Build;
 import android.provider.Settings;
 import android.text.SpannableStringBuilder;
 import android.text.Spanned;
 import android.text.SpannedString;
+import android.text.TextUtils;
 
 import java.text.SimpleDateFormat;
 import java.util.Calendar;
@@ -159,6 +164,16 @@
     private static boolean sIs24Hour;
 
     /**
+     * {@link #getBestDateTimePattern(Locale, String)} does not allow non-consecutive repeated
+     * symbol in the skeleton. For example, please use a skeleton of {@code "jmm"} or
+     * {@code "hmma"} instead of {@code "ahmma"} or {@code "jmma"}, because the field 'j' could
+     * mean using 12-hour in some locales and, in this case, is duplicated as the 'a' field.
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
+    static final long DISALLOW_DUPLICATE_FIELD_IN_SKELETON = 170233598L;
+
+    /**
      * Returns true if times should be formatted as 24 hour times, false if times should be
      * formatted as 12 hour (AM/PM) times. Based on the user's chosen locale and other preferences.
      * @param context the context to use for the content resolver
@@ -251,7 +266,9 @@
      */
     public static String getBestDateTimePattern(Locale locale, String skeleton) {
         DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance(locale);
-        return dtpg.getBestPattern(skeleton);
+        boolean allowDuplicateFields = !CompatChanges.isChangeEnabled(
+                DISALLOW_DUPLICATE_FIELD_IN_SKELETON);
+        return dtpg.getBestPattern(skeleton, allowDuplicateFields);
     }
 
     /**
@@ -681,7 +698,7 @@
     }
 
     private static String zeroPad(int inValue, int inMinDigits) {
-        return String.format(Locale.getDefault(), "%0" + inMinDigits + "d", inValue);
+        return TextUtils.formatSimple("%0" + inMinDigits + "d", inValue);
     }
 
     /**
diff --git a/core/java/android/text/style/LineHeightSpan.java b/core/java/android/text/style/LineHeightSpan.java
index 610cf2c..ae565d1 100644
--- a/core/java/android/text/style/LineHeightSpan.java
+++ b/core/java/android/text/style/LineHeightSpan.java
@@ -89,7 +89,7 @@
          * Set the line height of the paragraph to <code>height</code> physical pixels.
          */
         public Standard(@Px @IntRange(from = 1) int height) {
-            Preconditions.checkArgument(height > 0, "Height:" + height + "must be positive");
+            Preconditions.checkArgument(height > 0, "Height: %d must be positive", height);
             mHeight = height;
         }
 
diff --git a/core/java/android/text/style/SuggestionSpan.java b/core/java/android/text/style/SuggestionSpan.java
index be01bfb..9378636 100644
--- a/core/java/android/text/style/SuggestionSpan.java
+++ b/core/java/android/text/style/SuggestionSpan.java
@@ -71,6 +71,12 @@
     public static final int FLAG_AUTO_CORRECTION = 0x0004;
 
     /**
+     * Sets this flag if the suggestions apply to a grammar error. This type of suggestion is
+     * rendered differently to highlight the error.
+     */
+    public static final int FLAG_GRAMMAR_ERROR = 0x0008;
+
+    /**
      * This action is deprecated in {@link android.os.Build.VERSION_CODES#Q}.
      *
      * @deprecated For IMEs to receive this kind of user interaction signals, implement IMEs' own
@@ -136,6 +142,9 @@
     private float mAutoCorrectionUnderlineThickness;
     private int mAutoCorrectionUnderlineColor;
 
+    private float mGrammarErrorUnderlineThickness;
+    private int mGrammarErrorUnderlineColor;
+
     /**
      * @param context Context for the application
      * @param suggestions Suggestions for the string under the span
@@ -190,9 +199,11 @@
     private void initStyle(Context context) {
         if (context == null) {
             mMisspelledUnderlineThickness = 0;
+            mGrammarErrorUnderlineThickness = 0;
             mEasyCorrectUnderlineThickness = 0;
             mAutoCorrectionUnderlineThickness = 0;
             mMisspelledUnderlineColor = Color.BLACK;
+            mGrammarErrorUnderlineColor = Color.BLACK;
             mEasyCorrectUnderlineColor = Color.BLACK;
             mAutoCorrectionUnderlineColor = Color.BLACK;
             return;
@@ -206,6 +217,14 @@
         mMisspelledUnderlineColor = typedArray.getColor(
                 com.android.internal.R.styleable.SuggestionSpan_textUnderlineColor, Color.BLACK);
 
+        defStyleAttr = com.android.internal.R.attr.textAppearanceGrammarErrorSuggestion;
+        typedArray = context.obtainStyledAttributes(
+                null, com.android.internal.R.styleable.SuggestionSpan, defStyleAttr, 0);
+        mGrammarErrorUnderlineThickness = typedArray.getDimension(
+                com.android.internal.R.styleable.SuggestionSpan_textUnderlineThickness, 0);
+        mGrammarErrorUnderlineColor = typedArray.getColor(
+                com.android.internal.R.styleable.SuggestionSpan_textUnderlineColor, Color.BLACK);
+
         defStyleAttr = com.android.internal.R.attr.textAppearanceEasyCorrectSuggestion;
         typedArray = context.obtainStyledAttributes(
                 null, com.android.internal.R.styleable.SuggestionSpan, defStyleAttr, 0);
@@ -235,6 +254,8 @@
         mMisspelledUnderlineThickness = src.readFloat();
         mAutoCorrectionUnderlineColor = src.readInt();
         mAutoCorrectionUnderlineThickness = src.readFloat();
+        mGrammarErrorUnderlineColor = src.readInt();
+        mGrammarErrorUnderlineThickness = src.readFloat();
     }
 
     /**
@@ -313,6 +334,8 @@
         dest.writeFloat(mMisspelledUnderlineThickness);
         dest.writeInt(mAutoCorrectionUnderlineColor);
         dest.writeFloat(mAutoCorrectionUnderlineThickness);
+        dest.writeInt(mGrammarErrorUnderlineColor);
+        dest.writeFloat(mGrammarErrorUnderlineThickness);
     }
 
     @Override
@@ -326,7 +349,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (o instanceof SuggestionSpan) {
             return ((SuggestionSpan)o).hashCode() == mHashCode;
         }
@@ -362,13 +385,19 @@
         final boolean misspelled = (mFlags & FLAG_MISSPELLED) != 0;
         final boolean easy = (mFlags & FLAG_EASY_CORRECT) != 0;
         final boolean autoCorrection = (mFlags & FLAG_AUTO_CORRECTION) != 0;
+        final boolean grammarError = (mFlags & FLAG_GRAMMAR_ERROR) != 0;
         if (easy) {
-            if (!misspelled) {
+            if (!misspelled && !grammarError) {
                 tp.setUnderlineText(mEasyCorrectUnderlineColor, mEasyCorrectUnderlineThickness);
             } else if (tp.underlineColor == 0) {
                 // Spans are rendered in an arbitrary order. Since misspelled is less prioritary
                 // than just easy, do not apply misspelled if an easy (or a mispelled) has been set
-                tp.setUnderlineText(mMisspelledUnderlineColor, mMisspelledUnderlineThickness);
+                if (grammarError) {
+                    tp.setUnderlineText(
+                            mGrammarErrorUnderlineColor, mGrammarErrorUnderlineThickness);
+                } else {
+                    tp.setUnderlineText(mMisspelledUnderlineColor, mMisspelledUnderlineThickness);
+                }
             }
         } else if (autoCorrection) {
             tp.setUnderlineText(mAutoCorrectionUnderlineColor, mAutoCorrectionUnderlineThickness);
@@ -384,11 +413,14 @@
         final boolean misspelled = (mFlags & FLAG_MISSPELLED) != 0;
         final boolean easy = (mFlags & FLAG_EASY_CORRECT) != 0;
         final boolean autoCorrection = (mFlags & FLAG_AUTO_CORRECTION) != 0;
+        final boolean grammarError = (mFlags & FLAG_GRAMMAR_ERROR) != 0;
         if (easy) {
-            if (!misspelled) {
-                return mEasyCorrectUnderlineColor;
-            } else {
+            if (grammarError) {
+                return mGrammarErrorUnderlineColor;
+            } else if (misspelled) {
                 return mMisspelledUnderlineColor;
+            } else {
+                return mEasyCorrectUnderlineColor;
             }
         } else if (autoCorrection) {
             return mAutoCorrectionUnderlineColor;
diff --git a/core/java/android/text/util/Rfc822Token.java b/core/java/android/text/util/Rfc822Token.java
index 058757a..2f207db 100644
--- a/core/java/android/text/util/Rfc822Token.java
+++ b/core/java/android/text/util/Rfc822Token.java
@@ -191,7 +191,7 @@
         }
     }
 
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (!(o instanceof Rfc822Token)) {
             return false;
         }
diff --git a/core/java/android/timezone/CountryTimeZones.java b/core/java/android/timezone/CountryTimeZones.java
index 44d1402..d0bcfda 100644
--- a/core/java/android/timezone/CountryTimeZones.java
+++ b/core/java/android/timezone/CountryTimeZones.java
@@ -66,7 +66,7 @@
         }
 
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (this == o) {
                 return true;
             }
@@ -120,7 +120,7 @@
         }
 
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (this == o) {
                 return true;
             }
@@ -272,7 +272,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) {
             return true;
         }
diff --git a/core/java/android/timezone/TelephonyNetwork.java b/core/java/android/timezone/TelephonyNetwork.java
index 3b65c6f..161ecb4 100644
--- a/core/java/android/timezone/TelephonyNetwork.java
+++ b/core/java/android/timezone/TelephonyNetwork.java
@@ -17,6 +17,7 @@
 package android.timezone;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 
 import java.util.Objects;
 
@@ -59,7 +60,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) {
             return true;
         }
diff --git a/core/java/android/timezone/TzDataSetVersion.java b/core/java/android/timezone/TzDataSetVersion.java
index e1fb932..edcbbb3 100644
--- a/core/java/android/timezone/TzDataSetVersion.java
+++ b/core/java/android/timezone/TzDataSetVersion.java
@@ -17,6 +17,7 @@
 package android.timezone;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -128,7 +129,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) {
             return true;
         }
diff --git a/core/java/android/transition/ChangeTransform.java b/core/java/android/transition/ChangeTransform.java
index 5303855..02d0a6a 100644
--- a/core/java/android/transition/ChangeTransform.java
+++ b/core/java/android/transition/ChangeTransform.java
@@ -20,6 +20,7 @@
 import android.animation.FloatArrayEvaluator;
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Matrix;
@@ -450,7 +451,7 @@
         }
 
         @Override
-        public boolean equals(Object that) {
+        public boolean equals(@Nullable Object that) {
             if (!(that instanceof Transforms)) {
                 return false;
             }
diff --git a/core/java/android/transition/TransitionValues.java b/core/java/android/transition/TransitionValues.java
index 85fa67d..bb68b4c 100644
--- a/core/java/android/transition/TransitionValues.java
+++ b/core/java/android/transition/TransitionValues.java
@@ -17,6 +17,7 @@
 package android.transition;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.util.ArrayMap;
 import android.view.View;
 import android.view.ViewGroup;
@@ -73,7 +74,7 @@
     final ArrayList<Transition> targetedTransitions = new ArrayList<Transition>();
 
     @Override
-    public boolean equals(Object other) {
+    public boolean equals(@Nullable Object other) {
         if (other instanceof TransitionValues) {
             if (view == ((TransitionValues) other).view) {
                 if (values.equals(((TransitionValues) other).values)) {
diff --git a/core/java/android/util/ArrayMap.java b/core/java/android/util/ArrayMap.java
index 4cf0a36..4edff27 100644
--- a/core/java/android/util/ArrayMap.java
+++ b/core/java/android/util/ArrayMap.java
@@ -16,6 +16,7 @@
 
 package android.util;
 
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 
 import com.android.internal.util.ArrayUtils;
@@ -826,7 +827,7 @@
      * equal, the method returns false, otherwise it returns true.
      */
     @Override
-    public boolean equals(Object object) {
+    public boolean equals(@Nullable Object object) {
         if (this == object) {
             return true;
         }
diff --git a/core/java/android/util/ArraySet.java b/core/java/android/util/ArraySet.java
index 7f652ba..f53548a 100644
--- a/core/java/android/util/ArraySet.java
+++ b/core/java/android/util/ArraySet.java
@@ -778,7 +778,7 @@
      * returns true.
      */
     @Override
-    public boolean equals(Object object) {
+    public boolean equals(@Nullable Object object) {
         if (this == object) {
             return true;
         }
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index 9f6065e..0a3e6b1 100755
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -16,10 +16,10 @@
 
 package android.util;
 
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.SystemProperties;
 
-
 /**
  * A structure describing general information about a display, such as its
  * size, density, and font scaling.
@@ -362,7 +362,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         return o instanceof DisplayMetrics && equals((DisplayMetrics)o);
     }
 
diff --git a/core/java/android/util/EventLog.java b/core/java/android/util/EventLog.java
index ead4e46..ee98b65 100644
--- a/core/java/android/util/EventLog.java
+++ b/core/java/android/util/EventLog.java
@@ -307,7 +307,7 @@
          * @hide
          */
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             // Not using ByteBuffer.equals since it takes buffer position into account and we
             // always use absolute positions here.
             if (this == o) return true;
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 9244647..c20b063 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -36,7 +36,6 @@
     public static final String FFLAG_PREFIX = "sys.fflag.";
     public static final String FFLAG_OVERRIDE_PREFIX = FFLAG_PREFIX + "override.";
     public static final String PERSIST_PREFIX = "persist." + FFLAG_OVERRIDE_PREFIX;
-    public static final String SEAMLESS_TRANSFER = "settings_seamless_transfer";
     public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid";
     public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press";
     public static final String DYNAMIC_SYSTEM = "settings_dynamic_system";
@@ -54,7 +53,6 @@
         DEFAULT_FLAGS.put("settings_audio_switcher", "true");
         DEFAULT_FLAGS.put("settings_systemui_theme", "true");
         DEFAULT_FLAGS.put(DYNAMIC_SYSTEM, "false");
-        DEFAULT_FLAGS.put(SEAMLESS_TRANSFER, "false");
         DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false");
         DEFAULT_FLAGS.put(SCREENRECORD_LONG_PRESS, "false");
         DEFAULT_FLAGS.put("settings_wifi_details_datausage_header", "false");
diff --git a/core/java/android/util/IconDrawableFactory.java b/core/java/android/util/IconDrawableFactory.java
index 721e6b3..5eeb122 100644
--- a/core/java/android/util/IconDrawableFactory.java
+++ b/core/java/android/util/IconDrawableFactory.java
@@ -48,7 +48,7 @@
     }
 
     protected boolean needsBadging(ApplicationInfo appInfo, @UserIdInt int userId) {
-        return appInfo.isInstantApp() || mUm.isManagedProfile(userId);
+        return appInfo.isInstantApp() || mUm.hasBadge(userId);
     }
 
     @UnsupportedAppUsage
diff --git a/core/java/android/util/LocalLog.java b/core/java/android/util/LocalLog.java
index fda5e0d..bf9a838 100644
--- a/core/java/android/util/LocalLog.java
+++ b/core/java/android/util/LocalLog.java
@@ -60,10 +60,9 @@
         }
         final String logLine;
         if (mUseLocalTimestamps) {
-            logLine = String.format("%s - %s", LocalDateTime.now(), msg);
+            logLine = LocalDateTime.now() + " - " + msg;
         } else {
-            logLine = String.format(
-                    "%s / %s - %s", SystemClock.elapsedRealtime(), Instant.now(), msg);
+            logLine = SystemClock.elapsedRealtime() + " / " + Instant.now() + " - " + msg;
         }
         append(logLine);
     }
diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java
index 7a117f1..12bcd8b 100644
--- a/core/java/android/util/Log.java
+++ b/core/java/android/util/Log.java
@@ -44,9 +44,7 @@
  * You can then <a href="{@docRoot}studio/debug/am-logcat.html">view the logs in logcat</a>.
  *
  * <p>The order in terms of verbosity, from least to most is
- * ERROR, WARN, INFO, DEBUG, VERBOSE.  Verbose should never be compiled
- * into an application except during development.  Debug logs are compiled
- * in but stripped at runtime.  Error, warning and info logs are always kept.
+ * ERROR, WARN, INFO, DEBUG, VERBOSE.
  *
  * <p><b>Tip:</b> A good convention is to declare a <code>TAG</code> constant
  * in your class:
diff --git a/core/java/android/util/MapCollections.java b/core/java/android/util/MapCollections.java
index a521268..7ab3fca 100644
--- a/core/java/android/util/MapCollections.java
+++ b/core/java/android/util/MapCollections.java
@@ -16,6 +16,8 @@
 
 package android.util;
 
+import android.annotation.Nullable;
+
 import java.lang.reflect.Array;
 import java.util.Collection;
 import java.util.Iterator;
@@ -249,7 +251,7 @@
         }
 
         @Override
-        public boolean equals(Object object) {
+        public boolean equals(@Nullable Object object) {
             return equalsSetHelper(this, object);
         }
 
@@ -339,7 +341,7 @@
         }
 
         @Override
-        public boolean equals(Object object) {
+        public boolean equals(@Nullable Object object) {
             return equalsSetHelper(this, object);
         }
 
diff --git a/core/java/android/util/MemoryIntArray.java b/core/java/android/util/MemoryIntArray.java
index 7d287e3..9073d3a 100644
--- a/core/java/android/util/MemoryIntArray.java
+++ b/core/java/android/util/MemoryIntArray.java
@@ -16,13 +16,15 @@
 
 package android.util;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
 
-import libcore.io.IoUtils;
 import dalvik.system.CloseGuard;
 
+import libcore.io.IoUtils;
+
 import java.io.Closeable;
 import java.io.IOException;
 import java.util.UUID;
@@ -183,7 +185,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (obj == null) {
             return false;
         }
diff --git a/core/java/android/util/MergedConfiguration.java b/core/java/android/util/MergedConfiguration.java
index 2399ada..3ac44aa 100644
--- a/core/java/android/util/MergedConfiguration.java
+++ b/core/java/android/util/MergedConfiguration.java
@@ -17,6 +17,7 @@
 package android.util;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.res.Configuration;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -167,7 +168,7 @@
     }
 
     @Override
-    public boolean equals(Object that) {
+    public boolean equals(@Nullable Object that) {
         if (!(that instanceof MergedConfiguration)) {
             return false;
         }
diff --git a/core/java/android/util/Pair.java b/core/java/android/util/Pair.java
index f96da72..b0866b4 100644
--- a/core/java/android/util/Pair.java
+++ b/core/java/android/util/Pair.java
@@ -16,6 +16,8 @@
 
 package android.util;
 
+import android.annotation.Nullable;
+
 import java.util.Objects;
 
 /**
@@ -47,7 +49,7 @@
      *         equal
      */
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (!(o instanceof Pair)) {
             return false;
         }
diff --git a/core/java/android/util/Range.java b/core/java/android/util/Range.java
index f31ddd9..9fd0ab9 100644
--- a/core/java/android/util/Range.java
+++ b/core/java/android/util/Range.java
@@ -18,6 +18,7 @@
 
 import static com.android.internal.util.Preconditions.*;
 
+import android.annotation.Nullable;
 import android.hardware.camera2.utils.HashCodeHelpers;
 
 /**
@@ -146,7 +147,7 @@
      * @return {@code true} if the ranges are equal, {@code false} otherwise
      */
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (obj == null) {
             return false;
         } else if (this == obj) {
diff --git a/core/java/android/util/Rational.java b/core/java/android/util/Rational.java
index 5930000..aade620 100644
--- a/core/java/android/util/Rational.java
+++ b/core/java/android/util/Rational.java
@@ -17,6 +17,7 @@
 
 import static com.android.internal.util.Preconditions.checkNotNull;
 
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 
 import java.io.IOException;
@@ -240,7 +241,7 @@
      * @return A boolean that determines whether or not the two Rational objects are equal.
      */
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         return obj instanceof Rational && equals((Rational) obj);
     }
 
diff --git a/core/java/android/util/RecurrenceRule.java b/core/java/android/util/RecurrenceRule.java
index a570e5e..0f2d8bc 100644
--- a/core/java/android/util/RecurrenceRule.java
+++ b/core/java/android/util/RecurrenceRule.java
@@ -16,6 +16,7 @@
 
 package android.util;
 
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -130,7 +131,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (obj instanceof RecurrenceRule) {
             final RecurrenceRule other = (RecurrenceRule) obj;
             return Objects.equals(start, other.start)
diff --git a/core/java/android/util/SparseBooleanArray.java b/core/java/android/util/SparseBooleanArray.java
index 846df39..c145b20 100644
--- a/core/java/android/util/SparseBooleanArray.java
+++ b/core/java/android/util/SparseBooleanArray.java
@@ -16,6 +16,7 @@
 
 package android.util;
 
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 
 import com.android.internal.util.ArrayUtils;
@@ -289,7 +290,7 @@
     }
 
     @Override
-    public boolean equals(Object that) {
+    public boolean equals(@Nullable Object that) {
       if (this == that) {
           return true;
       }
diff --git a/core/java/android/util/apk/VerbatimX509Certificate.java b/core/java/android/util/apk/VerbatimX509Certificate.java
index 391c5fc..d9a00b2 100644
--- a/core/java/android/util/apk/VerbatimX509Certificate.java
+++ b/core/java/android/util/apk/VerbatimX509Certificate.java
@@ -16,6 +16,8 @@
 
 package android.util.apk;
 
+import android.annotation.Nullable;
+
 import java.security.cert.CertificateEncodingException;
 import java.security.cert.X509Certificate;
 import java.util.Arrays;
@@ -39,7 +41,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) return true;
         if (!(o instanceof VerbatimX509Certificate)) return false;
 
diff --git a/core/java/android/util/jar/StrictJarManifest.java b/core/java/android/util/jar/StrictJarManifest.java
index faec099..5c2fd9e 100644
--- a/core/java/android/util/jar/StrictJarManifest.java
+++ b/core/java/android/util/jar/StrictJarManifest.java
@@ -17,6 +17,10 @@
 
 package android.util.jar;
 
+import android.annotation.Nullable;
+
+import libcore.io.Streams;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -29,7 +33,6 @@
 import java.util.Iterator;
 import java.util.Map;
 import java.util.jar.Attributes;
-import libcore.io.Streams;
 
 /**
  * The {@code StrictJarManifest} class is used to obtain attribute information for a
@@ -219,7 +222,7 @@
      * @return {@code true} if the manifests are equal, {@code false} otherwise
      */
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (o == null) {
             return false;
         }
diff --git a/core/java/android/util/proto/EncodedBuffer.java b/core/java/android/util/proto/EncodedBuffer.java
index 56a0bfa..2a8f405 100644
--- a/core/java/android/util/proto/EncodedBuffer.java
+++ b/core/java/android/util/proto/EncodedBuffer.java
@@ -648,7 +648,7 @@
      * Print the internal buffer chunks.
      */
     private static int dumpByteString(String tag, String prefix, int start, byte[] buf) {
-        StringBuffer sb = new StringBuffer();
+        StringBuilder sb = new StringBuilder();
         final int length = buf.length;
         final int lineLen = 16;
         int i;
@@ -656,7 +656,7 @@
             if (i % lineLen == 0) {
                 if (i != 0) {
                     Log.d(tag, sb.toString());
-                    sb = new StringBuffer();
+                    sb = new StringBuilder();
                 }
                 sb.append(prefix);
                 sb.append('[');
diff --git a/core/java/android/util/proto/ProtoInputStream.java b/core/java/android/util/proto/ProtoInputStream.java
index aa70d07..9789b10 100644
--- a/core/java/android/util/proto/ProtoInputStream.java
+++ b/core/java/android/util/proto/ProtoInputStream.java
@@ -16,10 +16,11 @@
 
 package android.util.proto;
 
+import android.util.LongArray;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
 
 /**
  * Class to read to a protobuf stream.
@@ -98,7 +99,7 @@
     /**
      * Keeps track of the currently read nested Objects, for end object checking and debug
      */
-    private ArrayList<Long> mExpectedObjectTokenStack = null;
+    private LongArray mExpectedObjectTokenStack = null;
 
     /**
      * Current nesting depth of start calls.
@@ -498,7 +499,7 @@
         int messageSize = (int) readVarint();
 
         if (mExpectedObjectTokenStack == null) {
-            mExpectedObjectTokenStack = new ArrayList<>();
+            mExpectedObjectTokenStack = new LongArray();
         }
         if (++mDepth == mExpectedObjectTokenStack.size()) {
             // Create a token to keep track of nested Object and extend the object stack
diff --git a/core/java/android/uwb/AngleMeasurement.java b/core/java/android/uwb/AngleMeasurement.java
new file mode 100644
index 0000000..c3e2ecc
--- /dev/null
+++ b/core/java/android/uwb/AngleMeasurement.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.uwb;
+
+import android.annotation.FloatRange;
+
+/**
+ * Angle measurement
+ *
+ * <p>The actual angle is interpreted as:
+ *   {@link #getRadians()} +/- {@link #getErrorRadians()} ()} at {@link #getConfidenceLevel()}
+ *
+ * @hide
+ */
+public final class AngleMeasurement {
+    private final double mRadians;
+    private final double mErrorRadians;
+    private final double mConfidenceLevel;
+
+    private AngleMeasurement(double radians, double errorRadians, double confidenceLevel) {
+        mRadians = radians;
+        mErrorRadians = errorRadians;
+        mConfidenceLevel = confidenceLevel;
+    }
+
+    /**
+     * Angle measurement in radians
+    *
+     * @return angle in radians
+     */
+    @FloatRange(from = -Math.PI, to = +Math.PI)
+    public double getRadians() {
+        return mRadians;
+    }
+
+    /**
+     * Error of angle measurement in radians
+     *
+     * <p>Must be a positive value
+     *
+     * @return angle measurement error in radians
+     */
+    @FloatRange(from = 0.0, to = +Math.PI)
+    public double getErrorRadians() {
+        return mErrorRadians;
+    }
+
+    /**
+     * Angle measurement confidence level expressed as a value between
+     * 0.0 to 1.0.
+     *
+     * <p>A value of 0.0 indicates there is no confidence in the measurement. A value of 1.0
+     * indicates there is maximum confidence in the measurement.
+     *
+     * @return the confidence level of the angle measurement
+     */
+    @FloatRange(from = 0.0, to = 1.0)
+    public double getConfidenceLevel() {
+        return mConfidenceLevel;
+    }
+
+    /**
+     * Builder class for {@link AngleMeasurement}.
+     */
+    public static final class Builder {
+        private double mRadians = Double.NaN;
+        private double mErrorRadians = Double.NaN;
+        private double mConfidenceLevel = Double.NaN;
+
+        /**
+         * Set the angle in radians
+         *
+         * @param radians angle in radians
+         * @throws IllegalArgumentException if angle exceeds allowed limits of [-Math.PI, +Math.PI]
+         */
+        public Builder setRadians(double radians) {
+            if (radians < -Math.PI || radians > Math.PI) {
+                throw new IllegalArgumentException("Invalid radians: " + radians);
+            }
+            mRadians = radians;
+            return this;
+        }
+
+        /**
+         * Set the angle error in radians
+         *
+         * @param errorRadians error of the angle in radians
+         * @throws IllegalArgumentException if the error exceeds the allowed limits of [0, +Math.PI]
+         */
+        public Builder setErrorRadians(double errorRadians) {
+            if (errorRadians < 0.0 || errorRadians > Math.PI) {
+                throw new IllegalArgumentException(
+                        "Invalid error radians: " + errorRadians);
+            }
+            mErrorRadians = errorRadians;
+            return this;
+        }
+
+        /**
+         * Set the angle confidence level
+         *
+         * @param confidenceLevel level of confidence of the angle measurement
+         * @throws IllegalArgumentException if the error exceeds the allowed limits of [0.0, 1.0]
+         */
+        public Builder setConfidenceLevel(double confidenceLevel) {
+            if (confidenceLevel < 0.0 || confidenceLevel > 1.0) {
+                throw new IllegalArgumentException(
+                        "Invalid confidence level: " + confidenceLevel);
+            }
+            mConfidenceLevel = confidenceLevel;
+            return this;
+        }
+
+        /**
+         * Build the {@link AngleMeasurement} object
+         *
+         * @throws IllegalStateException if angle, error, or confidence values are missing
+         */
+        public AngleMeasurement build() {
+            if (Double.isNaN(mRadians)) {
+                throw new IllegalStateException("Angle is not set");
+            }
+
+            if (Double.isNaN(mErrorRadians)) {
+                throw new IllegalStateException("Angle error is not set");
+            }
+
+            if (Double.isNaN(mConfidenceLevel)) {
+                throw new IllegalStateException("Angle confidence level is not set");
+            }
+
+            return new AngleMeasurement(mRadians, mErrorRadians, mConfidenceLevel);
+        }
+    }
+}
diff --git a/core/java/android/uwb/AngleOfArrivalMeasurement.java b/core/java/android/uwb/AngleOfArrivalMeasurement.java
new file mode 100644
index 0000000..a7b5eae
--- /dev/null
+++ b/core/java/android/uwb/AngleOfArrivalMeasurement.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.uwb;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+/**
+ * Represents an angle of arrival measurement between two devices using Ultra Wideband
+ *
+ * @hide
+ */
+public final class AngleOfArrivalMeasurement {
+    private final AngleMeasurement mAzimuthAngleMeasurement;
+    private final AngleMeasurement mAltitudeAngleMeasurement;
+
+    private AngleOfArrivalMeasurement(@NonNull AngleMeasurement azimuthAngleMeasurement,
+            @Nullable AngleMeasurement altitudeAngleMeasurement) {
+        mAzimuthAngleMeasurement = azimuthAngleMeasurement;
+        mAltitudeAngleMeasurement = altitudeAngleMeasurement;
+    }
+
+    /**
+     * Azimuth angle measurement
+     * <p>Azimuth {@link AngleMeasurement} of remote device in horizontal coordinate system, this is
+     * the angle clockwise from the meridian when viewing above the north pole.
+     *
+     * <p>See: https://en.wikipedia.org/wiki/Horizontal_coordinate_system
+     *
+     * <p>On an Android device, azimuth north is defined as the angle perpendicular away from the
+     * back of the device when holding it in portrait mode upright.
+     *
+     * <p>Azimuth angle must be supported when Angle of Arrival is supported
+     *
+     * @return the azimuth {@link AngleMeasurement}
+     */
+    @NonNull
+    public AngleMeasurement getAzimuth() {
+        return mAzimuthAngleMeasurement;
+    }
+
+    /**
+     * Altitude angle measurement
+     * <p>Altitude {@link AngleMeasurement} of remote device in horizontal coordinate system, this
+     * is the angle above the equator when the north pole is up.
+     *
+     * <p>See: https://en.wikipedia.org/wiki/Horizontal_coordinate_system
+     *
+     * <p>On an Android device, altitude is defined as the angle vertical from ground when holding
+     * the device in portrait mode upright.
+     *
+     * @return altitude {@link AngleMeasurement} or null when this is not available
+     */
+    @Nullable
+    public AngleMeasurement getAltitude() {
+        return mAltitudeAngleMeasurement;
+    }
+
+    /**
+     * Builder class for {@link AngleOfArrivalMeasurement}.
+     */
+    public static final class Builder {
+        private AngleMeasurement mAzimuthAngleMeasurement = null;
+        private AngleMeasurement mAltitudeAngleMeasurement = null;
+
+        /**
+         * Set the azimuth angle
+         *
+         * @param azimuthAngle azimuth angle
+         */
+        public Builder setAzimuthAngleMeasurement(@NonNull AngleMeasurement azimuthAngle) {
+            mAzimuthAngleMeasurement = azimuthAngle;
+            return this;
+        }
+
+        /**
+         * Set the altitude angle
+         *
+         * @param altitudeAngle altitude angle
+         */
+        public Builder setAltitudeAngleMeasurement(@NonNull AngleMeasurement altitudeAngle) {
+            mAltitudeAngleMeasurement = altitudeAngle;
+            return this;
+        }
+
+        /**
+         * Build the {@link AngleOfArrivalMeasurement} object
+         *
+         * @throws IllegalStateException if the required azimuth angle is not provided
+         */
+        public AngleOfArrivalMeasurement build() {
+            if (mAzimuthAngleMeasurement == null) {
+                throw new IllegalStateException("Azimuth angle measurement is not set");
+            }
+
+            return new AngleOfArrivalMeasurement(mAzimuthAngleMeasurement,
+                    mAltitudeAngleMeasurement);
+        }
+    }
+}
diff --git a/core/java/android/uwb/DistanceMeasurement.java b/core/java/android/uwb/DistanceMeasurement.java
new file mode 100644
index 0000000..4cd5d83
--- /dev/null
+++ b/core/java/android/uwb/DistanceMeasurement.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.uwb;
+
+import android.annotation.FloatRange;
+
+/**
+ * A data point for the distance measurement
+ *
+ * <p>The actual distance is interpreted as:
+ *   {@link #getMeters()} +/- {@link #getErrorMeters()} at {@link #getConfidenceLevel()}
+ *
+ * @hide
+ */
+public final class DistanceMeasurement {
+    private final double mMeters;
+    private final double mErrorMeters;
+    private final double mConfidenceLevel;
+
+    private DistanceMeasurement(double meters, double errorMeters, double confidenceLevel) {
+        mMeters = meters;
+        mErrorMeters = errorMeters;
+        mConfidenceLevel = confidenceLevel;
+    }
+
+    /**
+     * Distance measurement in meters
+     *
+     * @return distance in meters
+     */
+    public double getMeters() {
+        return mMeters;
+    }
+
+    /**
+     * Error of distance measurement in meters
+     * <p>Must be positive
+     *
+     * @return error of distance measurement in meters
+     */
+    public double getErrorMeters() {
+        return mErrorMeters;
+    }
+
+    /**
+     * Distance measurement confidence level expressed as a value between 0.0 to 1.0.
+     *
+     * <p>A value of 0.0 indicates no confidence in the measurement. A value of 1.0 represents
+     * maximum confidence in the measurement
+     *
+     * @return confidence level
+     */
+    @FloatRange(from = 0.0, to = 1.0)
+    public double getConfidenceLevel() {
+        return mConfidenceLevel;
+    }
+
+    /**
+     * Builder to get a {@link DistanceMeasurement} object.
+     */
+    public static final class Builder {
+        private double mMeters = Double.NaN;
+        private double mErrorMeters = Double.NaN;
+        private double mConfidenceLevel = Double.NaN;
+
+        /**
+         * Set the distance measurement in meters
+         *
+         * @param meters distance in meters
+         * @throws IllegalArgumentException if meters is NaN
+         */
+        public Builder setMeters(double meters) {
+            if (Double.isNaN(meters)) {
+                throw new IllegalArgumentException("meters cannot be NaN");
+            }
+            mMeters = meters;
+            return this;
+        }
+
+        /**
+         * Set the distance error in meters
+         *
+         * @param errorMeters distance error in meters
+         * @throws IllegalArgumentException if error is negative or NaN
+         */
+        public Builder setErrorMeters(double errorMeters) {
+            if (Double.isNaN(errorMeters) || errorMeters < 0.0) {
+                throw new IllegalArgumentException(
+                        "errorMeters must be >= 0.0 and not NaN: " + errorMeters);
+            }
+            mErrorMeters = errorMeters;
+            return this;
+        }
+
+        /**
+         * Set the confidence level
+         *
+         * @param confidenceLevel the confidence level in the distance measurement
+         * @throws IllegalArgumentException if confidence level is not in the range of [0.0, 1.0]
+         */
+        public Builder setConfidenceLevel(double confidenceLevel) {
+            if (confidenceLevel < 0.0 || confidenceLevel > 1.0) {
+                throw new IllegalArgumentException(
+                        "confidenceLevel must be in the range [0.0, 1.0]: " + confidenceLevel);
+            }
+            mConfidenceLevel = confidenceLevel;
+            return this;
+        }
+
+        /**
+         * Builds the {@link DistanceMeasurement} object
+         *
+         * @throws IllegalStateException if meters, error, or confidence are not set
+         */
+        public DistanceMeasurement build() {
+            if (Double.isNaN(mMeters)) {
+                throw new IllegalStateException("Meters cannot be NaN");
+            }
+
+            if (Double.isNaN(mErrorMeters)) {
+                throw new IllegalStateException("Error meters cannot be NaN");
+            }
+
+            if (Double.isNaN(mConfidenceLevel)) {
+                throw new IllegalStateException("Confidence level cannot be NaN");
+            }
+
+            return new DistanceMeasurement(mMeters, mErrorMeters, mConfidenceLevel);
+        }
+    }
+}
diff --git a/core/java/android/uwb/RangingMeasurement.java b/core/java/android/uwb/RangingMeasurement.java
new file mode 100644
index 0000000..33a34e3
--- /dev/null
+++ b/core/java/android/uwb/RangingMeasurement.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.uwb;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.os.SystemClock;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Representation of a ranging measurement between the local device and a remote device
+ *
+ * @hide
+ */
+public final class RangingMeasurement {
+    private final UwbAddress mRemoteDeviceAddress;
+    private final @Status int mStatus;
+    private final long mElapsedRealtimeNanos;
+    private final DistanceMeasurement mDistanceMeasurement;
+    private final AngleOfArrivalMeasurement mAngleOfArrivalMeasurement;
+
+    private RangingMeasurement(@NonNull UwbAddress remoteDeviceAddress, @Status int status,
+            long elapsedRealtimeNanos, @Nullable DistanceMeasurement distanceMeasurement,
+            @Nullable AngleOfArrivalMeasurement angleOfArrivalMeasurement) {
+        mRemoteDeviceAddress = remoteDeviceAddress;
+        mStatus = status;
+        mElapsedRealtimeNanos = elapsedRealtimeNanos;
+        mDistanceMeasurement = distanceMeasurement;
+        mAngleOfArrivalMeasurement = angleOfArrivalMeasurement;
+    }
+
+    /**
+     * Get the remote device's {@link UwbAddress}
+     *
+     * @return the remote device's {@link UwbAddress}
+     */
+    @NonNull
+    public UwbAddress getRemoteDeviceAddress() {
+        return mRemoteDeviceAddress;
+    }
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            RANGING_STATUS_SUCCESS,
+            RANGING_STATUS_FAILURE_OUT_OF_RANGE,
+            RANGING_STATUS_FAILURE_UNKNOWN_ERROR})
+    public @interface Status {}
+
+    /**
+     * Ranging attempt was successful for this device
+     */
+    public static final int RANGING_STATUS_SUCCESS = 0;
+
+    /**
+     * Ranging failed for this device because it is out of range
+     */
+    public static final int RANGING_STATUS_FAILURE_OUT_OF_RANGE = 1;
+
+    /**
+     * Ranging failed for this device because of unknown error
+     */
+    public static final int RANGING_STATUS_FAILURE_UNKNOWN_ERROR = -1;
+
+    /**
+     * Get the status of this ranging measurement
+     *
+     * <p>Possible values are
+     * {@link #RANGING_STATUS_SUCCESS},
+     * {@link #RANGING_STATUS_FAILURE_OUT_OF_RANGE},
+     * {@link #RANGING_STATUS_FAILURE_UNKNOWN_ERROR}.
+     *
+     * @return the status of the ranging measurement
+     */
+    @Status
+    public int getStatus() {
+        return mStatus;
+    }
+
+    /**
+     * Timestamp of this ranging measurement in time since boot nanos in the same namespace as
+     * {@link SystemClock#elapsedRealtimeNanos()}
+     *
+     * @return timestamp of ranging measurement in nanoseconds
+     */
+    @SuppressLint("MethodNameUnits")
+    public long getElapsedRealtimeNanos() {
+        return mElapsedRealtimeNanos;
+    }
+
+    /**
+     * Get the distance measurement
+     *
+     * @return a {@link DistanceMeasurement} or null if {@link #getStatus()} !=
+     *         {@link #RANGING_STATUS_SUCCESS}
+     */
+    @Nullable
+    public DistanceMeasurement getDistance() {
+        return mDistanceMeasurement;
+    }
+
+    /**
+     * Get the angle of arrival measurement
+     *
+     * @return an {@link AngleOfArrivalMeasurement} or null if {@link #getStatus()} !=
+     *         {@link #RANGING_STATUS_SUCCESS}
+     */
+    @Nullable
+    public AngleOfArrivalMeasurement getAngleOfArrival() {
+        return mAngleOfArrivalMeasurement;
+    }
+
+    /**
+     * Builder for a {@link RangingMeasurement} object.
+     */
+    public static final class Builder {
+        private UwbAddress mRemoteDeviceAddress = null;
+        private @Status int mStatus = RANGING_STATUS_FAILURE_UNKNOWN_ERROR;
+        private long mElapsedRealtimeNanos = -1L;
+        private DistanceMeasurement mDistanceMeasurement = null;
+        private AngleOfArrivalMeasurement mAngleOfArrivalMeasurement = null;
+
+        /**
+         * Set the remote device address that this measurement is for
+         *
+         * @param remoteDeviceAddress remote device's address
+         */
+        public Builder setRemoteDeviceAddress(@NonNull UwbAddress remoteDeviceAddress) {
+            mRemoteDeviceAddress = remoteDeviceAddress;
+            return this;
+        }
+
+        /**
+         * Set the status of ranging measurement
+         *
+         * @param status the status of the ranging measurement
+         */
+        public Builder setStatus(@Status int status) {
+            mStatus = status;
+            return this;
+        }
+
+        /**
+         * Set the elapsed realtime in nanoseconds when the ranging measurement occurred
+         *
+         * @param elapsedRealtimeNanos time the ranging measurement occurred
+         */
+        public Builder setElapsedRealtimeNanos(long elapsedRealtimeNanos) {
+            if (elapsedRealtimeNanos < 0) {
+                throw new IllegalArgumentException("elapsedRealtimeNanos must be >= 0");
+            }
+            mElapsedRealtimeNanos = elapsedRealtimeNanos;
+            return this;
+        }
+
+        /**
+         * Set the {@link DistanceMeasurement}
+         *
+         * @param distanceMeasurement the distance measurement for this ranging measurement
+         */
+        public Builder setDistanceMeasurement(@NonNull DistanceMeasurement distanceMeasurement) {
+            mDistanceMeasurement = distanceMeasurement;
+            return this;
+        }
+
+        /**
+         * Set the {@link AngleOfArrivalMeasurement}
+         *
+         * @param angleOfArrivalMeasurement the angle of arrival measurement for this ranging
+         *                                  measurement
+         */
+        public Builder setAngleOfArrivalMeasurement(
+                @NonNull AngleOfArrivalMeasurement angleOfArrivalMeasurement) {
+            mAngleOfArrivalMeasurement = angleOfArrivalMeasurement;
+            return this;
+        }
+
+        /**
+         * Build the {@link RangingMeasurement} object
+         *
+         * @throws IllegalStateException if a distance or angle of arrival measurement is provided
+         *                               but the measurement was not successful, if the
+         *                               elapsedRealtimeNanos of the measurement is invalid, or
+         *                               if no remote device address is set
+         */
+        public RangingMeasurement build() {
+            if (mStatus != RANGING_STATUS_SUCCESS) {
+                if (mDistanceMeasurement != null) {
+                    throw new IllegalStateException(
+                            "Distance Measurement must be null if ranging is not successful");
+                }
+
+                if (mAngleOfArrivalMeasurement != null) {
+                    throw new IllegalStateException(
+                            "Angle of Arrival must be null if ranging is not successful");
+                }
+            }
+
+            if (mRemoteDeviceAddress == null) {
+                throw new IllegalStateException("No remote device address was set");
+            }
+
+            if (mElapsedRealtimeNanos < 0) {
+                throw new IllegalStateException(
+                        "elapsedRealtimeNanos must be >=0: " + mElapsedRealtimeNanos);
+            }
+
+            return new RangingMeasurement(mRemoteDeviceAddress, mStatus, mElapsedRealtimeNanos,
+                    mDistanceMeasurement, mAngleOfArrivalMeasurement);
+        }
+    }
+}
diff --git a/core/java/android/uwb/RangingParams.java b/core/java/android/uwb/RangingParams.java
new file mode 100644
index 0000000..a50de3e6
--- /dev/null
+++ b/core/java/android/uwb/RangingParams.java
@@ -0,0 +1,381 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.uwb;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.PersistableBundle;
+import android.util.Duration;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * An object used when requesting to open a new {@link RangingSession}.
+ * <p>Use {@link RangingParams.Builder} to create an instance of this class.
+ *
+ *  @hide
+ */
+public final class RangingParams {
+    private final boolean mIsInitiator;
+    private final boolean mIsController;
+    private final Duration mSamplePeriod;
+    private final UwbAddress mLocalDeviceAddress;
+    private final List<UwbAddress> mRemoteDeviceAddresses;
+    private final int mChannelNumber;
+    private final int mTransmitPreambleCodeIndex;
+    private final int mReceivePreambleCodeIndex;
+    private final int mStsPhyPacketType;
+    private final PersistableBundle mSpecificationParameters;
+
+    private RangingParams(boolean isInitiator, boolean isController,
+            @NonNull Duration samplingPeriod, @NonNull UwbAddress localDeviceAddress,
+            @NonNull List<UwbAddress> remoteDeviceAddresses, int channelNumber,
+            int transmitPreambleCodeIndex, int receivePreambleCodeIndex,
+            @StsPhyPacketType int stsPhyPacketType,
+            @NonNull PersistableBundle specificationParameters) {
+        mIsInitiator = isInitiator;
+        mIsController = isController;
+        mSamplePeriod = samplingPeriod;
+        mLocalDeviceAddress = localDeviceAddress;
+        mRemoteDeviceAddresses = remoteDeviceAddresses;
+        mChannelNumber = channelNumber;
+        mTransmitPreambleCodeIndex = transmitPreambleCodeIndex;
+        mReceivePreambleCodeIndex = receivePreambleCodeIndex;
+        mStsPhyPacketType = stsPhyPacketType;
+        mSpecificationParameters = specificationParameters;
+    }
+
+    /**
+     * Get if the local device is the initiator
+     *
+     * @return true if the device is the initiator
+     */
+    public boolean isInitiator() {
+        return mIsInitiator;
+    }
+
+    /**
+     * Get if the local device is the controller
+     *
+     * @return true if the device is the controller
+     */
+    public boolean isController() {
+        return mIsController;
+    }
+
+    /**
+     * The desired amount of time between two adjacent samples of measurement
+     *
+     * @return the ranging sample period
+     */
+    @NonNull
+    public Duration getSamplingPeriod() {
+        return mSamplePeriod;
+    }
+
+    /**
+     * Local device's {@link UwbAddress}
+     *
+     * <p>Simultaneous {@link RangingSession}s on the same device can have different results for
+     * {@link #getLocalDeviceAddress()}.
+     *
+     * @return the local device's {@link UwbAddress}
+     */
+    @NonNull
+    public UwbAddress getLocalDeviceAddress() {
+        return mLocalDeviceAddress;
+    }
+
+    /**
+     * Gets a list of all remote device's {@link UwbAddress}
+     *
+     * @return a {@link List} of {@link UwbAddress} representing the remote devices
+     */
+    @NonNull
+    public List<UwbAddress> getRemoteDeviceAddresses() {
+        return mRemoteDeviceAddresses;
+    }
+
+    /**
+     * Channel number used between this device pair as defined by 802.15.4z
+     *
+     * Range: -1, 0-15
+     *
+     * @return the channel to use
+     */
+    public int getChannelNumber() {
+        return mChannelNumber;
+    }
+
+    /**
+     * Preamble index used between this device pair as defined by 802.15.4z
+     *
+     * Range: 0, 0-32
+     *
+     * @return the preamble index to use for transmitting
+     */
+    public int getTxPreambleIndex() {
+        return mTransmitPreambleCodeIndex;
+    }
+
+    /**
+     * preamble index used between this device pair as defined by 802.15.4z
+     *
+     * Range: 0, 13-16, 21-32
+     *
+     * @return the preamble index to use for receiving
+     */
+    public int getRxPreambleIndex() {
+        return mReceivePreambleCodeIndex;
+    }
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            STS_PHY_PACKET_TYPE_SP0,
+            STS_PHY_PACKET_TYPE_SP1,
+            STS_PHY_PACKET_TYPE_SP2,
+            STS_PHY_PACKET_TYPE_SP3})
+    public @interface StsPhyPacketType {}
+
+    /**
+     * PHY packet type SP0 when STS is used as defined by 802.15.4z
+     */
+    public static final int STS_PHY_PACKET_TYPE_SP0 = 0;
+
+    /**
+     * PHY packet type SP1 when STS is used as defined by 802.15.4z
+     */
+    public static final int STS_PHY_PACKET_TYPE_SP1 = 1;
+
+    /**
+     * PHY packet type SP2 when STS is used as defined by 802.15.4z
+     */
+    public static final int STS_PHY_PACKET_TYPE_SP2 = 2;
+
+    /**
+     * PHY packet type SP3 when STS is used as defined by 802.15.4z
+     */
+    public static final int STS_PHY_PACKET_TYPE_SP3 = 3;
+
+    /**
+     * Get the type of PHY packet when STS is used as defined by 802.15.4z
+     *
+     * @return the {@link StsPhyPacketType} to use
+     */
+    @StsPhyPacketType
+    public int getStsPhyPacketType() {
+        return mStsPhyPacketType;
+    }
+
+    /**
+     * Parameters for a specific UWB protocol constructed using a support library.
+     *
+     * <p>Android reserves the '^android.*' namespace
+     *
+     * @return a {@link PersistableBundle} copy of protocol specific parameters
+     */
+    public @Nullable PersistableBundle getSpecificationParameters() {
+        return new PersistableBundle(mSpecificationParameters);
+    }
+
+    /**
+     * Builder class for {@link RangingParams}.
+     */
+    public static final class Builder {
+        private boolean mIsInitiator = false;
+        private boolean mIsController = false;
+        private Duration mSamplePeriod = null;
+        private UwbAddress mLocalDeviceAddress = null;
+        private List<UwbAddress> mRemoteDeviceAddresses = new ArrayList<>();
+        private int mChannelNumber = 0;
+        private int mTransmitPreambleCodeIndex = 0;
+        private int mReceivePreambleCodeIndex = 0;
+        private int mStsPhyPacketType = STS_PHY_PACKET_TYPE_SP0;
+        private PersistableBundle mSpecificationParameters = new PersistableBundle();
+
+        /**
+         * Set whether the device is the initiator or responder as defined by IEEE 802.15.4z
+         *
+         * @param isInitiator whether the device is the initiator (true) or responder (false)
+         */
+        public Builder setIsInitiator(boolean isInitiator) {
+            mIsInitiator = isInitiator;
+            return this;
+        }
+
+        /**
+         * Set whether the local device is the controller or controlee as defined by IEEE 802.15.4z
+         *
+         * @param isController whether the device is the controller (true) or controlee (false)
+         */
+        public Builder setIsController(boolean isController) {
+            mIsController = isController;
+            return this;
+        }
+
+        /**
+         * Set the time between ranging samples
+         *
+         * @param samplePeriod the time between ranging samples
+         */
+        public Builder setSamplePeriod(@NonNull Duration samplePeriod) {
+            mSamplePeriod = samplePeriod;
+            return this;
+        }
+
+        /**
+         * Set the local device address
+         *
+         * @param localDeviceAddress the local device's address for the {@link RangingSession}
+         */
+        public Builder setLocalDeviceAddress(@NonNull UwbAddress localDeviceAddress) {
+            mLocalDeviceAddress = localDeviceAddress;
+            return this;
+        }
+
+        /**
+         * Add a remote device's address to the ranging session
+         *
+         * @param remoteDeviceAddress a remote device's address for the {@link RangingSession}
+         * @throws IllegalArgumentException if {@code remoteDeviceAddress} is already present.
+         */
+        public Builder addRemoteDeviceAddress(@NonNull UwbAddress remoteDeviceAddress) {
+            if (mRemoteDeviceAddresses.contains(remoteDeviceAddress)) {
+                throw new IllegalArgumentException(
+                        "Remote device address already added: " + remoteDeviceAddress.toString());
+            }
+            mRemoteDeviceAddresses.add(remoteDeviceAddress);
+            return this;
+        }
+
+        /**
+         * Set the IEEE 802.15.4z channel to use for the {@link RangingSession}
+         * <p>Valid values are in the range [-1, 15]
+         *
+         * @param channelNumber the channel to use for the {@link RangingSession}
+         * @throws IllegalArgumentException if {@code channelNumber} is invalid.
+         */
+        public Builder setChannelNumber(int channelNumber) {
+            if (channelNumber < -1 || channelNumber > 15) {
+                throw new IllegalArgumentException("Invalid channel number");
+            }
+            mChannelNumber = channelNumber;
+            return this;
+        }
+
+        private static final Set<Integer> VALID_TX_PREAMBLE_CODES = new HashSet<Integer>(
+                Arrays.asList(0, 13, 14, 15, 16, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32));
+
+        /**
+         * Set the IEEE 802.15.4z preamble code index to use when transmitting
+         *
+         * <p>Valid values are in the ranges: [0], [13-16], [21-32]
+         *
+         * @param transmitPreambleCodeIndex preamble code index to use for transmitting
+         * @throws IllegalArgumentException if {@code transmitPreambleCodeIndex} is invalid.
+         */
+        public Builder setTransmitPreambleCodeIndex(int transmitPreambleCodeIndex) {
+            if (!VALID_TX_PREAMBLE_CODES.contains(transmitPreambleCodeIndex)) {
+                throw new IllegalArgumentException(
+                        "Invalid transmit preamble: " + transmitPreambleCodeIndex);
+            }
+            mTransmitPreambleCodeIndex = transmitPreambleCodeIndex;
+            return this;
+        }
+
+        private static final Set<Integer> VALID_RX_PREAMBLE_CODES = new HashSet<Integer>(
+                Arrays.asList(0, 16, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32));
+
+        /**
+         * Set the IEEE 802.15.4z preamble code index to use when receiving
+         *
+         * Valid values are in the ranges: [0], [16-32]
+         *
+         * @param receivePreambleCodeIndex preamble code index to use for receiving
+         * @throws IllegalArgumentException if {@code receivePreambleCodeIndex} is invalid.
+         */
+        public Builder setReceivePreambleCodeIndex(int receivePreambleCodeIndex) {
+            if (!VALID_RX_PREAMBLE_CODES.contains(receivePreambleCodeIndex)) {
+                throw new IllegalArgumentException(
+                        "Invalid receive preamble: " + receivePreambleCodeIndex);
+            }
+            mReceivePreambleCodeIndex = receivePreambleCodeIndex;
+            return this;
+        }
+
+        /**
+         * Set the IEEE 802.15.4z PHY packet type when STS is used
+         *
+         * @param stsPhyPacketType PHY packet type when STS is used
+         * @throws IllegalArgumentException if {@code stsPhyPacketType} is invalid.
+         */
+        public Builder setStsPhPacketType(@StsPhyPacketType int stsPhyPacketType) {
+            if (stsPhyPacketType != STS_PHY_PACKET_TYPE_SP0
+                    && stsPhyPacketType != STS_PHY_PACKET_TYPE_SP1
+                    && stsPhyPacketType != STS_PHY_PACKET_TYPE_SP2
+                    && stsPhyPacketType != STS_PHY_PACKET_TYPE_SP3) {
+                throw new IllegalArgumentException("unknown StsPhyPacketType: " + stsPhyPacketType);
+            }
+
+            mStsPhyPacketType = stsPhyPacketType;
+            return this;
+        }
+
+        /**
+         * Set the specification parameters
+         *
+         * <p>Creates a copy of the parameters
+         *
+         * @param parameters specification parameters built from support library
+         */
+        public Builder setSpecificationParameters(@NonNull PersistableBundle parameters) {
+            mSpecificationParameters = new PersistableBundle(parameters);
+            return this;
+        }
+
+        /**
+         * Build the {@link RangingParams} object.
+         *
+         * @throws IllegalStateException if required parameters are missing
+         */
+        public RangingParams build() {
+            if (mSamplePeriod == null) {
+                throw new IllegalStateException("No sample period provided");
+            }
+
+            if (mLocalDeviceAddress == null) {
+                throw new IllegalStateException("Local device address not provided");
+            }
+
+            if (mRemoteDeviceAddresses.size() == 0) {
+                throw new IllegalStateException("No remote device address(es) provided");
+            }
+
+            return new RangingParams(mIsInitiator, mIsController, mSamplePeriod,
+                    mLocalDeviceAddress, mRemoteDeviceAddresses, mChannelNumber,
+                    mTransmitPreambleCodeIndex, mReceivePreambleCodeIndex, mStsPhyPacketType,
+                    mSpecificationParameters);
+        }
+    }
+}
diff --git a/core/java/android/uwb/RangingReport.java b/core/java/android/uwb/RangingReport.java
new file mode 100644
index 0000000..5aca12a
--- /dev/null
+++ b/core/java/android/uwb/RangingReport.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.uwb;
+
+import android.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class contains the UWB ranging data
+ *
+ * @hide
+ */
+public final class RangingReport {
+    private final List<RangingMeasurement> mRangingMeasurements;
+
+    private RangingReport(@NonNull List<RangingMeasurement> rangingMeasurements) {
+        mRangingMeasurements = rangingMeasurements;
+    }
+
+    /**
+     * Get a {@link List} of {@link RangingMeasurement} objects in the last measurement interval
+     * <p>The underlying UWB adapter may choose to do multiple measurements in each ranging
+     * interval.
+     *
+     * <p>The entries in the {@link List} are ordered in ascending order based on
+     * {@link RangingMeasurement#getElapsedRealtimeNanos()}
+     *
+     * @return a {@link List} of {@link RangingMeasurement} objects
+     */
+    @NonNull
+    public List<RangingMeasurement> getMeasurements() {
+        return mRangingMeasurements;
+    }
+
+    /**
+     * Builder for {@link RangingReport} object
+     */
+    public static final class Builder {
+        List<RangingMeasurement> mMeasurements = new ArrayList<>();
+
+        /**
+         * Add a single {@link RangingMeasurement}
+         *
+         * @param rangingMeasurement a ranging measurement
+         */
+        public Builder addMeasurement(@NonNull RangingMeasurement rangingMeasurement) {
+            mMeasurements.add(rangingMeasurement);
+            return this;
+        }
+
+        /**
+         * Add a {@link List} of {@link RangingMeasurement}s
+         *
+         * @param rangingMeasurements {@link List} of {@link RangingMeasurement}s to add
+         */
+        public Builder addMeasurements(@NonNull List<RangingMeasurement> rangingMeasurements) {
+            mMeasurements.addAll(rangingMeasurements);
+            return this;
+        }
+
+        /**
+         * Build the {@link RangingReport} object
+         *
+         * @throws IllegalStateException if measurements are not in monotonically increasing order
+         */
+        public RangingReport build() {
+            // Verify that all measurement timestamps are monotonically increasing
+            RangingMeasurement prevMeasurement = null;
+            for (int curIndex = 0; curIndex < mMeasurements.size(); curIndex++) {
+                RangingMeasurement curMeasurement = mMeasurements.get(curIndex);
+                if (prevMeasurement != null
+                        && (prevMeasurement.getElapsedRealtimeNanos()
+                                > curMeasurement.getElapsedRealtimeNanos())) {
+                    throw new IllegalStateException(
+                            "Timestamp (" + curMeasurement.getElapsedRealtimeNanos()
+                            + ") at index " + curIndex + " is less than previous timestamp ("
+                            + prevMeasurement.getElapsedRealtimeNanos() + ")");
+                }
+                prevMeasurement = curMeasurement;
+            }
+            return new RangingReport(mMeasurements);
+        }
+    }
+}
+
diff --git a/core/java/android/uwb/RangingSession.java b/core/java/android/uwb/RangingSession.java
new file mode 100644
index 0000000..f4033fe
--- /dev/null
+++ b/core/java/android/uwb/RangingSession.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.uwb;
+
+import android.annotation.IntDef;
+import android.os.PersistableBundle;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+
+/**
+ * This class provides a way to control an active UWB ranging session.
+ * <p>It also defines the required {@link RangingSession.Callback} that must be implemented
+ * in order to be notified of UWB ranging results and status events related to the
+ * {@link RangingSession}.
+ *
+ * <p>To get an instance of {@link RangingSession}, first use
+ * {@link UwbManager#openRangingSession(RangingParams, Executor, Callback)} to request to open a
+ * session. Once the session is opened, a {@link RangingSession} object is provided through
+ * {@link RangingSession.Callback#onOpenSuccess(RangingSession, PersistableBundle)}. If opening a
+ * session fails, the failure is reported through {@link RangingSession.Callback#onClosed(int)} with
+ * the failure reason.
+ *
+ * @hide
+ */
+public final class RangingSession implements AutoCloseable {
+    /**
+     * Interface for receiving {@link RangingSession} events
+     */
+    public interface Callback {
+        /**
+         * Invoked when {@link UwbManager#openRangingSession(RangingParams, Executor, Callback)}
+         * is successful
+         *
+         * @param session the newly opened {@link RangingSession}
+         * @param sessionInfo session specific parameters from lower layers
+         */
+        void onOpenSuccess(RangingSession session, PersistableBundle sessionInfo);
+
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(value = {
+                CLOSE_REASON_UNKNOWN,
+                CLOSE_REASON_LOCAL_CLOSE_API,
+                CLOSE_REASON_LOCAL_BAD_PARAMETERS,
+                CLOSE_REASON_LOCAL_GENERIC_ERROR,
+                CLOSE_REASON_LOCAL_MAX_SESSIONS_REACHED,
+                CLOSE_REASON_LOCAL_SYSTEM_POLICY,
+                CLOSE_REASON_REMOTE_GENERIC_ERROR,
+                CLOSE_REASON_REMOTE_REQUEST})
+        @interface CloseReason {}
+
+        /**
+         * Indicates that the session was closed or failed to open due to an unknown reason
+         */
+        int CLOSE_REASON_UNKNOWN = 0;
+
+        /**
+         * Indicates that the session was closed or failed to open because
+         * {@link AutoCloseable#close()} or {@link RangingSession#close()} was called
+         */
+        int CLOSE_REASON_LOCAL_CLOSE_API = 1;
+
+        /**
+         * Indicates that the session failed to open due to erroneous parameters passed
+         * to {@link UwbManager#openRangingSession(RangingParams, Executor, Callback)}
+         */
+        int CLOSE_REASON_LOCAL_BAD_PARAMETERS = 2;
+
+        /**
+         * Indicates that the session was closed due to some local error on this device besides the
+         * error code already listed
+         */
+        int CLOSE_REASON_LOCAL_GENERIC_ERROR = 3;
+
+        /**
+         * Indicates that the session failed to open because the number of currently open sessions
+         * is equal to {@link UwbManager#getMaxSimultaneousSessions()}
+         */
+        int CLOSE_REASON_LOCAL_MAX_SESSIONS_REACHED = 4;
+
+        /**
+         * Indicates that the session was closed or failed to open due to local system policy, such
+         * as privacy policy, power management policy, permissions, and more.
+         */
+        int CLOSE_REASON_LOCAL_SYSTEM_POLICY = 5;
+
+        /**
+         * Indicates that the session was closed or failed to open due to an error with the remote
+         * device besides error codes already listed.
+         */
+        int CLOSE_REASON_REMOTE_GENERIC_ERROR = 6;
+
+        /**
+         * Indicates that the session was closed or failed to open due to an explicit request from
+         * the remote device.
+         */
+        int CLOSE_REASON_REMOTE_REQUEST = 7;
+
+        /**
+         * Invoked when session is either closed spontaneously, or per user request via
+         * {@link RangingSession#close()} or {@link AutoCloseable#close()}, or when session failed
+         * to open.
+         *
+         * @param reason reason for the session closure
+         */
+        void onClosed(@CloseReason int reason);
+
+        /**
+         * Called once per ranging interval even when a ranging measurement fails
+         *
+         * @param rangingReport ranging report for this interval's measurements
+         */
+        void onReportReceived(RangingReport rangingReport);
+    }
+
+    /**
+     * Close the ranging session
+     * <p>If this session is currently open, it will close and stop the session.
+     * <p>If the session is in the process of being opened, it will attempt to stop the session from
+     * being opened.
+     * <p>If the session is already closed, the registered {@link Callback#onClosed(int)} callback
+     * will still be invoked.
+     *
+     * <p>{@link Callback#onClosed(int)} will be invoked using the same callback
+     * object given to {@link UwbManager#openRangingSession(RangingParams, Executor, Callback)} when
+     * the {@link RangingSession} was opened. The callback will be invoked after each call to
+     * {@link #close()}, even if the {@link RangingSession} is already closed.
+     */
+    @Override
+    public void close() {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/core/java/android/uwb/UwbAddress.java b/core/java/android/uwb/UwbAddress.java
new file mode 100644
index 0000000..48fcb10e
--- /dev/null
+++ b/core/java/android/uwb/UwbAddress.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.uwb;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+/**
+ * A class representing a UWB address
+ *
+ * @hide
+ */
+public final class UwbAddress {
+    public static final int SHORT_ADDRESS_BYTE_LENGTH = 2;
+    public static final int EXTENDED_ADDRESS_BYTE_LENGTH = 8;
+
+    /**
+     * Create a {@link UwbAddress} from a byte array.
+     *
+     * <p>If the provided array is {@link #SHORT_ADDRESS_BYTE_LENGTH} bytes, a short address is
+     * created. If the provided array is {@link #EXTENDED_ADDRESS_BYTE_LENGTH} bytes, then an
+     * extended address is created.
+     *
+     * @param address a byte array to convert to a {@link UwbAddress}
+     * @return a {@link UwbAddress} created from the input byte array
+     * @throw IllegableArumentException when the length is not one of
+     *       {@link #SHORT_ADDRESS_BYTE_LENGTH} or {@link #EXTENDED_ADDRESS_BYTE_LENGTH} bytes
+     */
+    @NonNull
+    public static UwbAddress fromBytes(byte[] address) throws IllegalArgumentException {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Get the address as a byte array
+     *
+     * @return the byte representation of this {@link UwbAddress}
+     */
+    @NonNull
+    public byte[] toBytes() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * The length of the address in bytes
+     * <p>Possible values are {@link #SHORT_ADDRESS_BYTE_LENGTH} and
+     * {@link #EXTENDED_ADDRESS_BYTE_LENGTH}.
+     */
+    public int size() {
+        throw new UnsupportedOperationException();
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int hashCode() {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/core/java/android/uwb/UwbManager.java b/core/java/android/uwb/UwbManager.java
new file mode 100644
index 0000000..d58d5bf
--- /dev/null
+++ b/core/java/android/uwb/UwbManager.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.uwb;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.os.PersistableBundle;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Executor;
+
+/**
+ * This class provides a way to perform Ultra Wideband (UWB) operations such as querying the
+ * device's capabilities and determining the distance and angle between the local device and a
+ * remote device.
+ *
+ * <p>To get a {@link UwbManager}, call the <code>Context.getSystemService(UwbManager.class)</code>.
+ *
+ * @hide
+ */
+public final class UwbManager {
+    /**
+     * Interface for receiving UWB adapter state changes
+     */
+    public interface AdapterStateCallback {
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(value = {
+                STATE_CHANGED_REASON_SESSION_STARTED,
+                STATE_CHANGED_REASON_ALL_SESSIONS_CLOSED,
+                STATE_CHANGED_REASON_SYSTEM_POLICY,
+                STATE_CHANGED_REASON_SYSTEM_BOOT,
+                STATE_CHANGED_REASON_ERROR_UNKNOWN})
+        @interface StateChangedReason {}
+
+        /**
+         * Indicates that the state change was due to opening of first UWB session
+         */
+        int STATE_CHANGED_REASON_SESSION_STARTED = 0;
+
+        /**
+         * Indicates that the state change was due to closure of all UWB sessions
+         */
+        int STATE_CHANGED_REASON_ALL_SESSIONS_CLOSED = 1;
+
+        /**
+         * Indicates that the state change was due to changes in system policy
+         */
+        int STATE_CHANGED_REASON_SYSTEM_POLICY = 2;
+
+        /**
+         * Indicates that the current state is due to a system boot
+         */
+        int STATE_CHANGED_REASON_SYSTEM_BOOT = 3;
+
+        /**
+         * Indicates that the state change was due to some unknown error
+         */
+        int STATE_CHANGED_REASON_ERROR_UNKNOWN = 4;
+
+        /**
+         * Invoked when underlying UWB adapter's state is changed
+         * <p>Invoked with the adapter's current state after registering an
+         * {@link AdapterStateCallback} using
+         * {@link UwbManager#registerAdapterStateCallback(Executor, AdapterStateCallback)}.
+         *
+         * <p>Possible values for the state to change are
+         * {@link #STATE_CHANGED_REASON_SESSION_STARTED},
+         * {@link #STATE_CHANGED_REASON_ALL_SESSIONS_CLOSED},
+         * {@link #STATE_CHANGED_REASON_SYSTEM_POLICY},
+         * {@link #STATE_CHANGED_REASON_SYSTEM_BOOT},
+         * {@link #STATE_CHANGED_REASON_ERROR_UNKNOWN}.
+         *
+         * @param isEnabled true when UWB adapter is enabled, false when it is disabled
+         * @param reason the reason for the state change
+         */
+        void onStateChanged(boolean isEnabled, @StateChangedReason int reason);
+    }
+
+    /**
+     * Use <code>Context.getSystemService(UwbManager.class)</code> to get an instance.
+     */
+    private UwbManager() {
+        throw new UnsupportedOperationException();
+    }
+    /**
+     * Register an {@link AdapterStateCallback} to listen for UWB adapter state changes
+     * <p>The provided callback will be invoked by the given {@link Executor}.
+     *
+     * <p>When first registering a callback, the callbacks's
+     * {@link AdapterStateCallback#onStateChanged(boolean, int)} is immediately invoked to indicate
+     * the current state of the underlying UWB adapter with the most recent
+     * {@link AdapterStateCallback.StateChangedReason} that caused the change.
+     *
+     * @param executor an {@link Executor} to execute given callback
+     * @param callback user implementation of the {@link AdapterStateCallback}
+     */
+    public void registerAdapterStateCallback(Executor executor, AdapterStateCallback callback) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Unregister the specified {@link AdapterStateCallback}
+     * <p>The same {@link AdapterStateCallback} object used when calling
+     * {@link #registerAdapterStateCallback(Executor, AdapterStateCallback)} must be used.
+     *
+     * <p>Callbacks are automatically unregistered when application process goes away
+     *
+     * @param callback user implementation of the {@link AdapterStateCallback}
+     */
+    public void unregisterAdapterStateCallback(AdapterStateCallback callback) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Get a {@link PersistableBundle} with the supported UWB protocols and parameters.
+     * <p>The {@link PersistableBundle} should be parsed using a support library
+     *
+     * <p>Android reserves the '^android.*' namespace</p>
+     *
+     * @return {@link PersistableBundle} of the device's supported UWB protocols and parameters
+     */
+    @NonNull
+    public PersistableBundle getSpecificationInfo() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Check if ranging is supported, regardless of ranging method
+     *
+     * @return true if ranging is supported
+     */
+    public boolean isRangingSupported() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            ANGLE_OF_ARRIVAL_SUPPORT_TYPE_NONE,
+            ANGLE_OF_ARRIVAL_SUPPORT_TYPE_2D,
+            ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL,
+            ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL})
+    public @interface AngleOfArrivalSupportType {}
+
+    /**
+     * Indicate absence of support for angle of arrival measurement
+     */
+    public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_NONE = 1;
+
+    /**
+     * Indicate support for planar angle of arrival measurement, due to antenna
+     * limitation. Typically requires at least two antennas.
+     */
+    public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_2D = 2;
+
+    /**
+     * Indicate support for three dimensional angle of arrival measurement.
+     * Typically requires at least three antennas. However, due to antenna
+     * arrangement, a platform may only support hemi-spherical azimuth angles
+     * ranging from -pi/2 to pi/2
+     */
+    public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL = 2;
+
+    /**
+     * Indicate support for three dimensional angle of arrival measurement.
+     * Typically requires at least three antennas. This mode supports full
+     * azimuth angles ranging from -pi to pi.
+     */
+    public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL = 3;
+
+
+    /**
+     * Gets the {@link AngleOfArrivalSupportType} supported on this platform
+     * <p>Possible return values are
+     * {@link #ANGLE_OF_ARRIVAL_SUPPORT_TYPE_NONE},
+     * {@link #ANGLE_OF_ARRIVAL_SUPPORT_TYPE_2D},
+     * {@link #ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL},
+     * {@link #ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL}.
+     *
+     * @return angle of arrival type supported
+     */
+    @AngleOfArrivalSupportType
+    public int getAngleOfArrivalSupport() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Get a {@link List} of supported channel numbers based on the device's current location
+     * <p>The returned values are ordered by the system's desired ordered of use, with the first
+     * entry being the most preferred.
+     *
+     * <p>Channel numbers are defined based on the IEEE 802.15.4z standard for UWB.
+     *
+     * @return {@link List} of supported channel numbers ordered by preference
+     */
+    @NonNull
+    public List<Integer> getSupportedChannelNumbers() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Get a {@link List} of supported preamble code indices
+     * <p> Preamble code indices are defined based on the IEEE 802.15.4z standard for UWB.
+     *
+     * @return {@link List} of supported preamble code indices
+     */
+    @NonNull
+    public Set<Integer> getSupportedPreambleCodeIndices() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Get the timestamp resolution for events in nanoseconds
+     * <p>This value defines the maximum error of all timestamps for events reported to
+     * {@link RangingSession.Callback}.
+     *
+     * @return the timestamp resolution in nanoseconds
+     */
+    @SuppressLint("MethodNameUnits")
+    public long elapsedRealtimeResolutionNanos() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Get the number of simultaneous sessions allowed in the system
+     *
+     * @return the maximum allowed number of simultaneously open {@link RangingSession} instances.
+     */
+    public int getMaxSimultaneousSessions() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Get the maximum number of remote devices in a {@link RangingSession} when the local device
+     * is the initiator.
+     *
+     * @return the maximum number of remote devices per {@link RangingSession}
+     */
+    public int getMaxRemoteDevicesPerInitiatorSession() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Get the maximum number of remote devices in a {@link RangingSession} when the local device
+     * is a responder.
+     *
+     * @return the maximum number of remote devices per {@link RangingSession}
+     */
+    public int getMaxRemoteDevicesPerResponderSession() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Open a {@link RangingSession} with the given parameters
+     * <p>This function is asynchronous and will return before ranging begins. The
+     * {@link RangingSession.Callback#onOpenSuccess(RangingSession, PersistableBundle)} function is
+     * called with a {@link RangingSession} object used to control ranging when the session is
+     * successfully opened.
+     *
+     * <p>If a session cannot be opened, then {@link RangingSession.Callback#onClosed(int)} will be
+     * invoked with the appropriate {@link RangingSession.Callback.CloseReason}.
+     *
+     * <p>An open {@link RangingSession} will be automatically closed if client application process
+     * dies.
+     *
+     * @param params {@link RangingParams} used to initialize this {@link RangingSession}
+     * @param executor {@link Executor} to run callbacks
+     * @param callbacks {@link RangingSession.Callback} to associate with the
+     *                  {@link RangingSession} that is being opened.
+     *
+     * @return an {@link AutoCloseable} that is able to be used to close or cancel the opening of a
+     *         {@link RangingSession} that has been requested through {@link #openRangingSession}
+     *         but has not yet been made available by
+     *         {@link RangingSession.Callback#onOpenSuccess}.
+     */
+    @NonNull
+    public AutoCloseable openRangingSession(@NonNull RangingParams params,
+            @NonNull Executor executor, @NonNull RangingSession.Callback callbacks) {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index e520d7c..b5080cd 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -181,7 +181,8 @@
     private long mFrameIntervalNanos;
     private boolean mDebugPrintNextFrameTimeDelta;
     private int mFPSDivisor = 1;
-    private long mLastVsyncId = FrameInfo.INVALID_VSYNC_ID;
+    private DisplayEventReceiver.VsyncEventData mLastVsyncEventData =
+            new DisplayEventReceiver.VsyncEventData();
 
     /**
      * Contains information about the current frame for jank-tracking,
@@ -664,7 +665,18 @@
      * @hide
      */
     public long getVsyncId() {
-        return mLastVsyncId;
+        return mLastVsyncEventData.id;
+    }
+
+    /**
+     * Returns the frame deadline in {@link System#nanoTime()} timebase that it is allotted for the
+     * frame to be completed. Client are expected to call this function from their frame callback
+     * function. Calling this function from anywhere else will return an undefined value.
+     *
+     * @hide
+     */
+    public long getFrameDeadline() {
+        return mLastVsyncEventData.frameDeadline;
     }
 
     void setFPSDivisor(int divisor) {
@@ -673,7 +685,8 @@
         ThreadedRenderer.setFPSDivisor(divisor);
     }
 
-    void doFrame(long frameTimeNanos, int frame, long frameTimelineVsyncId) {
+    void doFrame(long frameTimeNanos, int frame,
+            DisplayEventReceiver.VsyncEventData vsyncEventData) {
         final long startNanos;
         synchronized (mLock) {
             if (!mFrameScheduled) {
@@ -723,10 +736,11 @@
                 }
             }
 
-            mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos, frameTimelineVsyncId);
+            mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos, vsyncEventData.id,
+                    vsyncEventData.frameDeadline);
             mFrameScheduled = false;
             mLastFrameTimeNanos = frameTimeNanos;
-            mLastVsyncId = frameTimelineVsyncId;
+            mLastVsyncEventData = vsyncEventData;
         }
 
         try {
@@ -910,7 +924,7 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case MSG_DO_FRAME:
-                    doFrame(System.nanoTime(), 0, FrameInfo.INVALID_VSYNC_ID);
+                    doFrame(System.nanoTime(), 0, new DisplayEventReceiver.VsyncEventData());
                     break;
                 case MSG_DO_SCHEDULE_VSYNC:
                     doScheduleVsync();
@@ -927,7 +941,7 @@
         private boolean mHavePendingVsync;
         private long mTimestampNanos;
         private int mFrame;
-        private long mFrameTimelineVsyncId;
+        private VsyncEventData mLastVsyncEventData = new VsyncEventData();
 
         public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
             super(looper, vsyncSource, CONFIG_CHANGED_EVENT_SUPPRESS);
@@ -938,7 +952,7 @@
         // for the internal display implicitly.
         @Override
         public void onVsync(long timestampNanos, long physicalDisplayId, int frame,
-                long frameTimelineVsyncId) {
+                VsyncEventData vsyncEventData) {
             // Post the vsync event to the Handler.
             // The idea is to prevent incoming vsync events from completely starving
             // the message queue.  If there are no messages in the queue with timestamps
@@ -961,7 +975,7 @@
 
             mTimestampNanos = timestampNanos;
             mFrame = frame;
-            mFrameTimelineVsyncId = frameTimelineVsyncId;
+            mLastVsyncEventData = vsyncEventData;
             Message msg = Message.obtain(mHandler, this);
             msg.setAsynchronous(true);
             mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
@@ -970,7 +984,7 @@
         @Override
         public void run() {
             mHavePendingVsync = false;
-            doFrame(mTimestampNanos, mFrame, mFrameTimelineVsyncId);
+            doFrame(mTimestampNanos, mFrame, mLastVsyncEventData);
         }
     }
 
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index c4048e5..34e8221 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -1466,7 +1466,7 @@
         }
 
         @Override
-        public boolean equals(Object other) {
+        public boolean equals(@Nullable Object other) {
             if (this == other) {
                 return true;
             }
@@ -1620,7 +1620,7 @@
         }
 
         @Override
-        public boolean equals(Object other) {
+        public boolean equals(@Nullable Object other) {
             if (this == other) {
                 return true;
             }
diff --git a/core/java/android/view/DisplayAddress.java b/core/java/android/view/DisplayAddress.java
index 92f1adc..91a24c6 100644
--- a/core/java/android/view/DisplayAddress.java
+++ b/core/java/android/view/DisplayAddress.java
@@ -110,7 +110,7 @@
         }
 
         @Override
-        public boolean equals(Object other) {
+        public boolean equals(@Nullable Object other) {
             return other instanceof Physical
                     && mPhysicalDisplayId == ((Physical) other).mPhysicalDisplayId;
         }
@@ -171,7 +171,7 @@
         private final String mMacAddress;
 
         @Override
-        public boolean equals(Object other) {
+        public boolean equals(@Nullable Object other) {
             return other instanceof Network && mMacAddress.equals(((Network) other).mMacAddress);
         }
 
diff --git a/core/java/android/view/DisplayAdjustments.java b/core/java/android/view/DisplayAdjustments.java
index 7c01f7a8..5d5771c 100644
--- a/core/java/android/view/DisplayAdjustments.java
+++ b/core/java/android/view/DisplayAdjustments.java
@@ -168,7 +168,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (!(o instanceof DisplayAdjustments)) {
             return false;
         }
@@ -220,7 +220,7 @@
         }
 
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (!(o instanceof FixedRotationAdjustments)) {
                 return false;
             }
diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java
index b4863f9..3f2dd4d 100644
--- a/core/java/android/view/DisplayCutout.java
+++ b/core/java/android/view/DisplayCutout.java
@@ -205,7 +205,7 @@
             return result;
         }
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (o == this) {
                 return true;
             }
@@ -535,7 +535,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (o == this) {
             return true;
         }
@@ -877,7 +877,7 @@
         }
 
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             return o instanceof ParcelableWrapper
                     && mInner.equals(((ParcelableWrapper) o).mInner);
         }
diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java
index 51474d3..467d93e 100644
--- a/core/java/android/view/DisplayEventReceiver.java
+++ b/core/java/android/view/DisplayEventReceiver.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import android.compat.annotation.UnsupportedAppUsage;
+import android.graphics.FrameInfo;
 import android.os.Looper;
 import android.os.MessageQueue;
 import android.util.Log;
@@ -145,6 +146,26 @@
         mMessageQueue = null;
     }
 
+    static final class VsyncEventData {
+        // The frame timeline vsync id, used to correlate a frame
+        // produced by HWUI with the timeline data stored in Surface Flinger.
+        public final long id;
+
+        // The frame deadline timestamp in {@link System#nanoTime()} timebase that it is
+        // allotted for the frame to be completed.
+        public final long frameDeadline;
+
+        VsyncEventData(long id, long frameDeadline) {
+            this.id = id;
+            this.frameDeadline = frameDeadline;
+        }
+
+        VsyncEventData() {
+            this.id = FrameInfo.INVALID_VSYNC_ID;
+            this.frameDeadline = Long.MAX_VALUE;
+        }
+    }
+
     /**
      * Called when a vertical sync pulse is received.
      * The recipient should render a frame and then call {@link #scheduleVsync}
@@ -154,11 +175,10 @@
      * timebase.
      * @param physicalDisplayId Stable display ID that uniquely describes a (display, port) pair.
      * @param frame The frame number.  Increases by one for each vertical sync interval.
-     * @param frameTimelineVsyncId The frame timeline vsync id, used to correlate a frame
-     * produced by HWUI with the timeline data stored in Surface Flinger.
+     * @param vsyncEventData The vsync event data.
      */
     public void onVsync(long timestampNanos, long physicalDisplayId, int frame,
-            long frameTimelineVsyncId) {
+            VsyncEventData vsyncEventData) {
     }
 
     /**
@@ -201,8 +221,9 @@
     // Called from native code.
     @SuppressWarnings("unused")
     private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame,
-            long frameTimelineVsyncId) {
-        onVsync(timestampNanos, physicalDisplayId, frame, frameTimelineVsyncId);
+            long frameTimelineVsyncId, long frameDeadline) {
+        onVsync(timestampNanos, physicalDisplayId, frame,
+                new VsyncEventData(frameTimelineVsyncId, frameDeadline));
     }
 
     // Called from native code.
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index b1ede41..fe9a1a7 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -295,7 +295,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         return o instanceof DisplayInfo && equals((DisplayInfo)o);
     }
 
diff --git a/core/java/android/view/FrameMetrics.java b/core/java/android/view/FrameMetrics.java
index 280a1c0..387787e 100644
--- a/core/java/android/view/FrameMetrics.java
+++ b/core/java/android/view/FrameMetrics.java
@@ -198,6 +198,7 @@
             Index.ANIMATION_START,
             Index.PERFORM_TRAVERSALS_START,
             Index.DRAW_START,
+            Index.FRAME_DEADLINE,
             Index.SYNC_QUEUED,
             Index.SYNC_START,
             Index.ISSUE_DRAW_COMMANDS_START,
@@ -216,13 +217,15 @@
         int ANIMATION_START = 7;
         int PERFORM_TRAVERSALS_START = 8;
         int DRAW_START = 9;
-        int SYNC_QUEUED = 10;
-        int SYNC_START = 11;
-        int ISSUE_DRAW_COMMANDS_START = 12;
-        int SWAP_BUFFERS = 13;
-        int FRAME_COMPLETED = 14;
+        int FRAME_DEADLINE = 10;
+        int SYNC_QUEUED = 11;
+        int SYNC_START = 12;
+        int ISSUE_DRAW_COMMANDS_START = 13;
+        int SWAP_BUFFERS = 14;
+        int FRAME_COMPLETED = 15;
 
-        int FRAME_STATS_COUNT = 18; // must always be last
+        int FRAME_STATS_COUNT = 19; // must always be last and in sync with
+                                    // FrameInfoIndex::NumIndexes in libs/hwui/FrameInfo.h
     }
 
     /*
diff --git a/core/java/android/view/IScrollCaptureController.aidl b/core/java/android/view/IScrollCaptureCallbacks.aidl
similarity index 67%
rename from core/java/android/view/IScrollCaptureController.aidl
rename to core/java/android/view/IScrollCaptureCallbacks.aidl
index 8474a00..d97e3c6 100644
--- a/core/java/android/view/IScrollCaptureController.aidl
+++ b/core/java/android/view/IScrollCaptureCallbacks.aidl
@@ -20,32 +20,31 @@
 import android.graphics.Rect;
 import android.view.Surface;
 
-import android.view.IScrollCaptureClient;
+import android.view.IScrollCaptureConnection;
 
 /**
- * Interface to a controller passed to the {@link IScrollCaptureClient} which provides the client an
- * asynchronous callback channel for responses.
+ * Asynchronous callback channel for responses to scroll capture requests.
  *
  * {@hide}
  */
-interface IScrollCaptureController {
+interface IScrollCaptureCallbacks {
     /**
-     * Scroll capture is available, and a client connect has been returned.
+     * Scroll capture is available, and a connection has been provided.
      *
-     * @param client interface to a ScrollCaptureCallback in the window process
+     * @param connection a connection to a window process and scrollable content
      * @param scrollAreaInWindow the location of scrolling in global (window) coordinate space
      */
-    oneway void onClientConnected(in IScrollCaptureClient client, in Rect scrollBounds,
+    oneway void onConnected(in IScrollCaptureConnection connection, in Rect scrollBounds,
             in Point positionInWindow);
 
     /**
-     * Nothing in the window can be scrolled, scroll capture not offered.
+     * The window does not support scroll capture.
      */
-    oneway void onClientUnavailable();
+    oneway void onUnavailable();
 
     /**
-     * Notifies the system that the client has confirmed the request and is ready to begin providing
-     * image requests.
+     * Called when the remote end has confirmed the request and is ready to begin providing image
+     * requests.
      */
     oneway void onCaptureStarted();
 
diff --git a/core/java/android/view/IScrollCaptureClient.aidl b/core/java/android/view/IScrollCaptureConnection.aidl
similarity index 97%
rename from core/java/android/view/IScrollCaptureClient.aidl
rename to core/java/android/view/IScrollCaptureConnection.aidl
index 5f135a37..63a4f48 100644
--- a/core/java/android/view/IScrollCaptureClient.aidl
+++ b/core/java/android/view/IScrollCaptureConnection.aidl
@@ -26,7 +26,7 @@
    *
    * {@hide}
    */
-interface IScrollCaptureClient {
+interface IScrollCaptureConnection {
 
     /**
      * Informs the client that it has been selected for scroll capture and should prepare to
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 193e674..e685b30 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -26,7 +26,7 @@
 import android.view.DragEvent;
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
-import android.view.IScrollCaptureController;
+import android.view.IScrollCaptureCallbacks;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.window.ClientWindowFrames;
@@ -137,7 +137,7 @@
     /**
      * Called when Scroll Capture support is requested for a window.
      *
-     * @param controller the controller to receive responses
+     * @param callbacks to receive responses
      */
-    void requestScrollCapture(in IScrollCaptureController controller);
+    void requestScrollCapture(in IScrollCaptureCallbacks callbacks);
 }
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index daab70a..f572eb9 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -42,7 +42,7 @@
 import android.view.IDisplayWindowRotationController;
 import android.view.IOnKeyguardExitResult;
 import android.view.IPinnedStackListener;
-import android.view.IScrollCaptureController;
+import android.view.IScrollCaptureCallbacks;
 import android.view.RemoteAnimationAdapter;
 import android.view.IRotationWatcher;
 import android.view.ISystemGestureExclusionListener;
@@ -337,11 +337,16 @@
      */
     boolean isDisplayRotationFrozen(int displayId);
 
-    /**
+   /**
     *  Sets if display rotation is fixed to user specified value for given displayId.
     */
     void setFixedToUserRotation(int displayId, int fixedToUserRotation);
 
+   /**
+    *  Sets if all requested fixed orientation should be ignored for given displayId.
+    */
+    void setIgnoreOrientationRequest(int displayId, boolean ignoreOrientationRequest);
+
     /**
      * Screenshot the current wallpaper layer, including the whole screen.
      */
@@ -724,7 +729,6 @@
      * @return {@code true} if system bars are always comsumed.
      */
     boolean getWindowInsets(in WindowManager.LayoutParams attrs, int displayId,
-            out Rect outContentInsets, out Rect outStableInsets,
             out DisplayCutout.ParcelableWrapper outDisplayCutout, out InsetsState outInsetsState);
 
     /**
@@ -746,12 +750,10 @@
      * @param behindClient token for a window, used to filter the search to windows behind it, or
      *                     {@code null} to accept a window at any zOrder
      * @param taskId specifies the id of a task the result must belong to, or -1 to ignore task ids
-     * @param controller the controller to receive results, a call to either
-     *      {@link IScrollCaptureController#onClientConnected} or
-     *      {@link IScrollCaptureController#onClientUnavailable}.
+     * @param callbacks the object to receive replies
      */
     void requestScrollCapture(int displayId, IBinder behindClient, int taskId,
-            IScrollCaptureController controller);
+            IScrollCaptureCallbacks callbacks);
 
     /**
      * Holds the WM lock for the specified amount of milliseconds.
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 69a5faf..9febc9f 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -45,18 +45,17 @@
  */
 interface IWindowSession {
     int addToDisplay(IWindow window, in WindowManager.LayoutParams attrs,
-            in int viewVisibility, in int layerStackId, out Rect outFrame,
-            out Rect outContentInsets, out Rect outStableInsets,
+            in int viewVisibility, in int layerStackId, in InsetsState requestedVisibility,
+            out Rect outFrame, out DisplayCutout.ParcelableWrapper displayCutout,
+            out InputChannel outInputChannel, out InsetsState insetsState,
+            out InsetsSourceControl[] activeControls);
+    int addToDisplayAsUser(IWindow window, in WindowManager.LayoutParams attrs,
+            in int viewVisibility, in int layerStackId, in int userId,
+            in InsetsState requestedVisibility, out Rect outFrame,
             out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel,
             out InsetsState insetsState, out InsetsSourceControl[] activeControls);
-    int addToDisplayAsUser(IWindow window, in WindowManager.LayoutParams attrs,
-                in int viewVisibility, in int layerStackId, in int userId,
-                out Rect outFrame, out Rect outContentInsets, out Rect outStableInsets,
-                out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel,
-                out InsetsState insetsState, out InsetsSourceControl[] activeControls);
     int addToDisplayWithoutInputChannel(IWindow window, in WindowManager.LayoutParams attrs,
-            in int viewVisibility, in int layerStackId, out Rect outContentInsets,
-            out Rect outStableInsets, out InsetsState insetsState);
+            in int viewVisibility, in int layerStackId, out InsetsState insetsState);
     @UnsupportedAppUsage
     void remove(IWindow window);
 
@@ -98,9 +97,6 @@
      * @param outSurface Object in which is placed the new display surface.
      * @param insetsState The current insets state in the system.
      * @param outSurfaceSize The width and height of the surface control
-     * @param outBlastSurfaceControl A BLAST SurfaceControl allocated by the WindowManager
-     * the SurfaceControl willl be managed by the client side, but the WindowManager
-     * may use it as a deferTransaction barrier.
      *
      * @return int Result flags: {@link WindowManagerGlobal#RELAYOUT_SHOW_FOCUS},
      * {@link WindowManagerGlobal#RELAYOUT_FIRST_TIME}.
@@ -110,7 +106,7 @@
             int flags, long frameNumber, out ClientWindowFrames outFrames,
             out MergedConfiguration outMergedConfiguration, out SurfaceControl outSurfaceControl,
             out InsetsState insetsState, out InsetsSourceControl[] activeControls,
-            out Point outSurfaceSize, out SurfaceControl outBlastSurfaceControl);
+            out Point outSurfaceSize);
 
     /*
      * Notify the window manager that an application is relaunching and
diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
index 1ef701f..d1a9a05 100644
--- a/core/java/android/view/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -21,6 +21,7 @@
 import android.annotation.Nullable;
 import android.graphics.Region;
 import android.os.IBinder;
+import android.os.TouchOcclusionMode;
 
 import java.lang.ref.WeakReference;
 
@@ -82,10 +83,18 @@
     // Window is trusted overlay.
     public boolean trustedOverlay;
 
+    // What effect this window has on touch occlusion if it lets touches pass through
+    // By default windows will block touches if they are untrusted and from a different UID due to
+    // security concerns
+    public int touchOcclusionMode = TouchOcclusionMode.BLOCK_UNTRUSTED;
+
     // Id of process and user that owns the window.
     public int ownerPid;
     public int ownerUid;
 
+    // Owner package of the window
+    public String packageName;
+
     // Window input features.
     public int inputFeatures;
 
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 71899fa..06ddf3c 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -38,6 +38,7 @@
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
 
 import android.annotation.Nullable;
+import android.content.res.CompatibilityInfo;
 import android.graphics.Insets;
 import android.graphics.Matrix;
 import android.graphics.Rect;
@@ -90,6 +91,7 @@
     private final WindowInsetsAnimation mAnimation;
     /** @see WindowInsetsAnimationController#hasZeroInsetsIme */
     private final boolean mHasZeroInsetsIme;
+    private final CompatibilityInfo.Translator mTranslator;
     private Insets mCurrentInsets;
     private Insets mPendingInsets;
     private float mPendingFraction;
@@ -107,7 +109,7 @@
             InsetsState state, WindowInsetsAnimationControlListener listener,
             @InsetsType int types,
             InsetsAnimationControlCallbacks controller, long durationMs, Interpolator interpolator,
-            @AnimationType int animationType) {
+            @AnimationType int animationType, CompatibilityInfo.Translator translator) {
         mControls = controls;
         mListener = listener;
         mTypes = types;
@@ -131,6 +133,7 @@
                 durationMs);
         mAnimation.setAlpha(getCurrentAlpha());
         mAnimationType = animationType;
+        mTranslator = translator;
         mController.startAnimation(this, listener, types, mAnimation,
                 new Bounds(mHiddenInsets, mShownInsets));
 
@@ -212,21 +215,21 @@
     /**
      * @return Whether the finish callback of this animation should be invoked.
      */
-    public boolean applyChangeInsets(InsetsState state) {
+    public boolean applyChangeInsets(@Nullable InsetsState outState) {
         if (mCancelled) {
             if (DEBUG) Log.d(TAG, "applyChangeInsets canceled");
             return false;
         }
         final Insets offset = Insets.subtract(mShownInsets, mPendingInsets);
         ArrayList<SurfaceParams> params = new ArrayList<>();
-        updateLeashesForSide(ISIDE_LEFT, offset.left, mShownInsets.left, mPendingInsets.left,
-                params, state, mPendingAlpha);
-        updateLeashesForSide(ISIDE_TOP, offset.top, mShownInsets.top, mPendingInsets.top, params,
-                state, mPendingAlpha);
-        updateLeashesForSide(ISIDE_RIGHT, offset.right, mShownInsets.right, mPendingInsets.right,
-                params, state, mPendingAlpha);
-        updateLeashesForSide(ISIDE_BOTTOM, offset.bottom, mShownInsets.bottom,
-                mPendingInsets.bottom, params, state, mPendingAlpha);
+        updateLeashesForSide(ISIDE_LEFT, offset.left, mPendingInsets.left, params, outState,
+                mPendingAlpha);
+        updateLeashesForSide(ISIDE_TOP, offset.top, mPendingInsets.top, params, outState,
+                mPendingAlpha);
+        updateLeashesForSide(ISIDE_RIGHT, offset.right, mPendingInsets.right, params, outState,
+                mPendingAlpha);
+        updateLeashesForSide(ISIDE_BOTTOM, offset.bottom, mPendingInsets.bottom, params, outState,
+                mPendingAlpha);
 
         mController.applySurfaceParams(params.toArray(new SurfaceParams[params.size()]));
         mCurrentInsets = mPendingInsets;
@@ -357,8 +360,8 @@
         return alpha >= 1 ? 1 : (alpha <= 0 ? 0 : alpha);
     }
 
-    private void updateLeashesForSide(@InternalInsetsSide int side, int offset, int maxInset,
-            int inset, ArrayList<SurfaceParams> surfaceParams, InsetsState state, Float alpha) {
+    private void updateLeashesForSide(@InternalInsetsSide int side, int offset, int inset,
+            ArrayList<SurfaceParams> surfaceParams, @Nullable InsetsState outState, float alpha) {
         ArraySet<InsetsSourceControl> items = mSideSourceMap.get(side);
         if (items == null) {
             return;
@@ -377,8 +380,10 @@
                     ? (mAnimationType == ANIMATION_TYPE_SHOW ? true : !mFinished)
                     : inset != 0;
 
-            state.getSource(source.getType()).setVisible(visible);
-            state.getSource(source.getType()).setFrame(mTmpFrame);
+            if (outState != null) {
+                outState.getSource(source.getType()).setVisible(visible);
+                outState.getSource(source.getType()).setFrame(mTmpFrame);
+            }
 
             // If the system is controlling the insets source, the leash can be null.
             if (leash != null) {
@@ -394,21 +399,23 @@
 
     private void addTranslationToMatrix(@InternalInsetsSide int side, int inset, Matrix m,
             Rect frame) {
+        final float surfaceOffset = mTranslator != null
+                ? mTranslator.translateLengthInAppWindowToScreen(inset) : inset;
         switch (side) {
             case ISIDE_LEFT:
-                m.postTranslate(-inset, 0);
+                m.postTranslate(-surfaceOffset, 0);
                 frame.offset(-inset, 0);
                 break;
             case ISIDE_TOP:
-                m.postTranslate(0, -inset);
+                m.postTranslate(0, -surfaceOffset);
                 frame.offset(0, -inset);
                 break;
             case ISIDE_RIGHT:
-                m.postTranslate(inset, 0);
+                m.postTranslate(surfaceOffset, 0);
                 frame.offset(inset, 0);
                 break;
             case ISIDE_BOTTOM:
-                m.postTranslate(0, inset);
+                m.postTranslate(0, surfaceOffset);
                 frame.offset(0, inset);
                 break;
         }
diff --git a/core/java/android/view/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java
index cc3cd27..4a5fa0f 100644
--- a/core/java/android/view/InsetsAnimationThreadControlRunner.java
+++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java
@@ -20,6 +20,7 @@
 import static android.view.SyncRtSurfaceTransactionApplier.applyParams;
 
 import android.annotation.UiThread;
+import android.content.res.CompatibilityInfo;
 import android.graphics.Rect;
 import android.os.Handler;
 import android.os.Trace;
@@ -44,7 +45,6 @@
     private final InsetsAnimationControlImpl mControl;
     private final InsetsAnimationControlCallbacks mOuterCallbacks;
     private final Handler mMainThreadHandler;
-    private final InsetsState mState = new InsetsState();
     private final InsetsAnimationControlCallbacks mCallbacks =
             new InsetsAnimationControlCallbacks() {
 
@@ -60,7 +60,7 @@
 
         @Override
         public void scheduleApplyChangeInsets(InsetsAnimationControlRunner runner) {
-            mControl.applyChangeInsets(mState);
+            mControl.applyChangeInsets(null /* outState */);
         }
 
         @Override
@@ -103,11 +103,12 @@
             InsetsState state, WindowInsetsAnimationControlListener listener,
             @InsetsType int types,
             InsetsAnimationControlCallbacks controller, long durationMs, Interpolator interpolator,
-            @AnimationType int animationType, Handler mainThreadHandler) {
+            @AnimationType int animationType, CompatibilityInfo.Translator translator,
+            Handler mainThreadHandler) {
         mMainThreadHandler = mainThreadHandler;
         mOuterCallbacks = controller;
         mControl = new InsetsAnimationControlImpl(controls, frame, state, listener,
-                types, mCallbacks, durationMs, interpolator, animationType);
+                types, mCallbacks, durationMs, interpolator, animationType, translator);
         InsetsAnimationThread.getHandler().post(() -> {
             Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW,
                     "InsetsAsyncAnimation: " + WindowInsets.Type.toString(types), types);
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 652781a..b5bf084 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -33,6 +33,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.res.CompatibilityInfo;
 import android.graphics.Insets;
 import android.graphics.Rect;
 import android.os.CancellationSignal;
@@ -176,6 +177,14 @@
          */
         @Nullable
         IBinder getWindowToken();
+
+        /**
+         * @return Translator associated with the host, if it has one.
+         */
+        @Nullable
+        default CompatibilityInfo.Translator getTranslator() {
+            return null;
+        }
     }
 
     private static final String TAG = "InsetsController";
@@ -484,7 +493,9 @@
     /** The state dispatched from server */
     private final InsetsState mLastDispatchedState = new InsetsState();
 
-    /** The state sent to server */
+    // TODO: Use other class to represent the requested visibility of each type, because the
+    //       display frame and the frame in each source are not used.
+    /** The requested visibilities sent to server */
     private final InsetsState mRequestedState = new InsetsState();
 
     private final Rect mFrame = new Rect();
@@ -499,6 +510,7 @@
     private final List<WindowInsetsAnimation> mUnmodifiableTmpRunningAnims =
             Collections.unmodifiableList(mTmpRunningAnims);
     private final ArrayList<InsetsAnimationControlImpl> mTmpFinishedControls = new ArrayList<>();
+    private final ArraySet<InsetsSourceConsumer> mRequestedVisibilityChanged = new ArraySet<>();
     private WindowInsets mLastInsets;
 
     private boolean mAnimCallbackScheduled;
@@ -640,11 +652,6 @@
             if (DEBUG) Log.d(TAG, "onStateChanged, notifyInsetsChanged");
             mHost.notifyInsetsChanged();
         }
-        if (!mState.equals(mLastDispatchedState, true /* excludingCaptionInsets */,
-                true /* excludeInvisibleIme */)) {
-            if (DEBUG) Log.d(TAG, "onStateChanged, send state to WM: " + mState);
-            updateRequestedState();
-        }
         return true;
     }
 
@@ -808,9 +815,9 @@
         if (hideTypes[0] != 0) {
             applyAnimation(hideTypes[0], false /* show */, false /* fromIme */);
         }
-        if (requestedStateStale) {
-            updateRequestedState();
-        }
+
+        // InsetsSourceConsumer#setControl might change the requested visibility.
+        updateRequestedVisibility();
     }
 
     @Override
@@ -945,6 +952,7 @@
         if (types == 0) {
             // nothing to animate.
             listener.onCancelled(null);
+            updateRequestedVisibility();
             if (DEBUG) Log.d(TAG, "no types to animate in controlAnimationUnchecked");
             return;
         }
@@ -980,12 +988,14 @@
                     }
                 });
             }
+            updateRequestedVisibility();
             return;
         }
 
         if (typesReady == 0) {
             if (DEBUG) Log.d(TAG, "No types ready. onCancelled()");
             listener.onCancelled(null);
+            updateRequestedVisibility();
             return;
         }
 
@@ -993,10 +1003,10 @@
         final InsetsAnimationControlRunner runner = useInsetsAnimationThread
                 ? new InsetsAnimationThreadControlRunner(controls,
                         frame, mState, listener, typesReady, this, durationMs, interpolator,
-                        animationType, mHost.getHandler())
+                        animationType, mHost.getTranslator(), mHost.getHandler())
                 : new InsetsAnimationControlImpl(controls,
                         frame, mState, listener, typesReady, this, durationMs, interpolator,
-                        animationType);
+                        animationType, mHost.getTranslator());
         mRunningAnimations.add(new RunningAnimation(runner, animationType));
         if (DEBUG) Log.d(TAG, "Animation added to runner. useInsetsAnimationThread: "
                 + useInsetsAnimationThread);
@@ -1010,6 +1020,7 @@
         } else {
             hideDirectly(types, false /* animationFinished */, animationType);
         }
+        updateRequestedVisibility();
     }
 
     /**
@@ -1177,7 +1188,6 @@
         }
         if (stateChanged) {
             mHost.notifyInsetsChanged();
-            updateRequestedState();
         }
     }
 
@@ -1202,7 +1212,6 @@
     @VisibleForTesting
     public void notifyVisibilityChanged() {
         mHost.notifyInsetsChanged();
-        updateRequestedState();
     }
 
     /**
@@ -1251,38 +1260,39 @@
         return ANIMATION_TYPE_NONE;
     }
 
+    @VisibleForTesting
+    public void onRequestedVisibilityChanged(InsetsSourceConsumer consumer) {
+        mRequestedVisibilityChanged.add(consumer);
+    }
+
     /**
-     * Sends the local visibility state back to window manager if it is changed.
+     * Sends the requested visibilities to window manager if any of them is changed.
      */
-    private void updateRequestedState() {
+    private void updateRequestedVisibility() {
         boolean changed = false;
-        for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
-            final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
+        for (int i = mRequestedVisibilityChanged.size() - 1; i >= 0; i--) {
+            final InsetsSourceConsumer consumer = mRequestedVisibilityChanged.valueAt(i);
             final @InternalInsetsType int type = consumer.getType();
             if (type == ITYPE_CAPTION_BAR) {
                 continue;
             }
-            if (consumer.getControl() != null) {
-                final InsetsSource localSource = mState.getSource(type);
-                if (!localSource.equals(mRequestedState.peekSource(type))) {
-                    // Our requested state is stale. Update it here and send it to window manager.
-                    mRequestedState.addSource(new InsetsSource(localSource));
-                    changed = true;
-                }
-                if (!localSource.equals(mLastDispatchedState.peekSource(type))) {
-                    // The server state is not what we expected. This can happen while we don't have
-                    // the control. Since we have the control now, we need to send our request again
-                    // to modify the server state.
-                    changed = true;
-                }
+            final boolean requestedVisible = consumer.isRequestedVisible();
+            if (requestedVisible != mRequestedState.getSourceOrDefaultVisibility(type)) {
+                mRequestedState.getSource(type).setVisible(requestedVisible);
+                changed = true;
             }
         }
+        mRequestedVisibilityChanged.clear();
         if (!changed) {
             return;
         }
         mHost.onInsetsModified(mRequestedState);
     }
 
+    InsetsState getRequestedVisibility() {
+        return mRequestedState;
+    }
+
     @VisibleForTesting
     public void applyAnimation(@InsetsType final int types, boolean show, boolean fromIme) {
         if (types == 0) {
@@ -1316,6 +1326,7 @@
         for (int i = internalTypes.size() - 1; i >= 0; i--) {
             getSourceConsumer(internalTypes.valueAt(i)).hide(animationFinished, animationType);
         }
+        updateRequestedVisibility();
     }
 
     private void showDirectly(@InsetsType int types) {
@@ -1326,6 +1337,7 @@
         for (int i = internalTypes.size() - 1; i >= 0; i--) {
             getSourceConsumer(internalTypes.valueAt(i)).show(false /* fromIme */);
         }
+        updateRequestedVisibility();
     }
 
     /**
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index 41cc8459..7a90bc0 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -188,7 +188,13 @@
         return false;
     }
 
-    void dumpDebug(ProtoOutputStream proto, long fieldId) {
+    /**
+     * Export the state of {@link InsetsSource} into a protocol buffer output stream.
+     *
+     * @param proto   Stream to write the state to
+     * @param fieldId FieldId of InsetsSource as defined in the parent message
+     */
+    public void dumpDebug(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
         proto.write(TYPE, InsetsState.typeToString(mType));
         mFrame.dumpDebug(proto, FRAME);
@@ -211,7 +217,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         return equals(o, false);
     }
 
@@ -219,7 +225,7 @@
      * @param excludeInvisibleImeFrames If {@link InsetsState#ITYPE_IME} frames should be ignored
      *                                  when IME is not visible.
      */
-    public boolean equals(Object o, boolean excludeInvisibleImeFrames) {
+    public boolean equals(@Nullable Object o, boolean excludeInvisibleImeFrames) {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
 
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index d7ceaf7..e4a24eb 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -350,6 +350,7 @@
         if (mRequestedVisible != requestedVisible) {
             mRequestedVisible = requestedVisible;
             mIsAnimationPending = false;
+            mController.onRequestedVisibilityChanged(this);
             if (DEBUG) Log.d(TAG, "setRequestedVisible: " + requestedVisible);
         }
         if (applyLocalVisibilityOverride()) {
diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java
index b45bd38..5ddbd02 100644
--- a/core/java/android/view/InsetsSourceControl.java
+++ b/core/java/android/view/InsetsSourceControl.java
@@ -128,7 +128,13 @@
         }
     };
 
-    void dumpDebug(ProtoOutputStream proto, long fieldId) {
+    /**
+     * Export the state of {@link InsetsSourceControl} into a protocol buffer output stream.
+     *
+     * @param proto   Stream to write the state to
+     * @param fieldId FieldId of InsetsSource as defined in the parent message
+     */
+    public void dumpDebug(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
         proto.write(TYPE, InsetsState.typeToString(mType));
 
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index eabb718..ac29f2e 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -63,8 +63,6 @@
  */
 public class InsetsState implements Parcelable {
 
-    public static final InsetsState EMPTY = new InsetsState();
-
     /**
      * Internal representation of inset source types. This is different from the public API in
      * {@link WindowInsets.Type} as one type from the public API might indicate multiple windows
@@ -429,6 +427,25 @@
         }
     }
 
+    /**
+     * Scales the frame and the visible frame (if there is one) of each source.
+     *
+     * @param scale the scale to be applied
+     */
+    public void scale(float scale) {
+        mDisplayFrame.scale(scale);
+        for (int i = 0; i < SIZE; i++) {
+            final InsetsSource source = mSources[i];
+            if (source != null) {
+                source.getFrame().scale(scale);
+                final Rect visibleFrame = source.getVisibleFrame();
+                if (visibleFrame != null) {
+                    visibleFrame.scale(scale);
+                }
+            }
+        }
+    }
+
     public void set(InsetsState other) {
         set(other, false /* copySources */);
     }
@@ -606,7 +623,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         return equals(o, false, false);
     }
 
@@ -621,7 +638,7 @@
      * @return {@code true} if the two InsetsState objects are equal, {@code false} otherwise.
      */
     @VisibleForTesting
-    public boolean equals(Object o, boolean excludingCaptionInsets,
+    public boolean equals(@Nullable Object o, boolean excludingCaptionInsets,
             boolean excludeInvisibleImeFrames) {
         if (this == o) { return true; }
         if (o == null || getClass() != o.getClass()) { return false; }
diff --git a/core/java/android/view/KeyEvent.aidl b/core/java/android/view/KeyEvent.aidl
index dc15ecf..1f6d16e 100644
--- a/core/java/android/view/KeyEvent.aidl
+++ b/core/java/android/view/KeyEvent.aidl
@@ -17,4 +17,4 @@
 
 package android.view;
 
-parcelable KeyEvent;
+@JavaOnlyStableParcelable parcelable KeyEvent;
diff --git a/core/java/android/view/MagnificationSpec.java b/core/java/android/view/MagnificationSpec.java
index aea337e..034598a 100644
--- a/core/java/android/view/MagnificationSpec.java
+++ b/core/java/android/view/MagnificationSpec.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Pools.SynchronizedPool;
@@ -106,7 +107,7 @@
     }
 
     @Override
-    public boolean equals(Object other) {
+    public boolean equals(@Nullable Object other) {
         if (this == other) {
             return true;
         }
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 51b0c6b..abb82bc 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -21,6 +21,7 @@
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
 import android.annotation.IntDef;
+import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.Matrix;
@@ -4160,7 +4161,7 @@
         }
 
         @Override
-        public boolean equals(Object other) {
+        public boolean equals(@Nullable Object other) {
             if (other instanceof PointerProperties) {
                 return equals((PointerProperties)other);
             }
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index 0c3d61f..355b314 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -17,7 +17,6 @@
 package android.view;
 
 import android.annotation.Nullable;
-import android.app.Notification;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Resources;
@@ -27,8 +26,6 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
 import android.widget.RemoteViews;
 
 import com.android.internal.R;
@@ -44,7 +41,6 @@
  */
 @RemoteViews.RemoteView
 public class NotificationHeaderView extends ViewGroup {
-    public static final int NO_COLOR = Notification.COLOR_INVALID;
     private final int mChildMinWidth;
     private final int mContentEndMargin;
     private final int mGravity;
@@ -54,12 +50,10 @@
     private OnClickListener mExpandClickListener;
     private OnClickListener mFeedbackListener;
     private HeaderTouchListener mTouchListener = new HeaderTouchListener();
-    private LinearLayout mTransferChip;
     private NotificationExpandButton mExpandButton;
     private CachingIconView mIcon;
     private View mProfileBadge;
     private View mFeedbackIcon;
-    private boolean mExpanded;
     private boolean mShowExpandButtonAtEnd;
     private boolean mShowWorkBadgeAtEnd;
     private int mHeaderTextMarginEnd;
@@ -111,7 +105,6 @@
         mAppName = findViewById(com.android.internal.R.id.app_name_text);
         mHeaderText = findViewById(com.android.internal.R.id.header_text);
         mSecondaryHeaderText = findViewById(com.android.internal.R.id.header_text_secondary);
-        mTransferChip = findViewById(com.android.internal.R.id.media_seamless);
         mExpandButton = findViewById(com.android.internal.R.id.expand_button);
         mIcon = findViewById(com.android.internal.R.id.icon);
         mProfileBadge = findViewById(com.android.internal.R.id.profile_badge);
@@ -143,8 +136,7 @@
             // Icons that should go at the end
             if ((child == mExpandButton && mShowExpandButtonAtEnd)
                     || child == mProfileBadge
-                    || child == mFeedbackIcon
-                    || child == mTransferChip) {
+                    || child == mFeedbackIcon) {
                 iconWidth += lp.leftMargin + lp.rightMargin + child.getMeasuredWidth();
             } else {
                 totalWidth += lp.leftMargin + lp.rightMargin + child.getMeasuredWidth();
@@ -208,8 +200,7 @@
             // Icons that should go at the end
             if ((child == mExpandButton && mShowExpandButtonAtEnd)
                     || child == mProfileBadge
-                    || child == mFeedbackIcon
-                    || child == mTransferChip) {
+                    || child == mFeedbackIcon) {
                 if (end == getMeasuredWidth()) {
                     layoutRight = end - mContentEndMargin;
                 } else {
@@ -301,35 +292,6 @@
         updateTouchListener();
     }
 
-    public int getOriginalIconColor() {
-        return mIcon.getOriginalIconColor();
-    }
-
-    public int getOriginalNotificationColor() {
-        return mExpandButton.getOriginalNotificationColor();
-    }
-
-    @RemotableViewMethod
-    public void setExpanded(boolean expanded) {
-        mExpanded = expanded;
-        updateExpandButton();
-    }
-
-    private void updateExpandButton() {
-        int drawableId;
-        int contentDescriptionId;
-        if (mExpanded) {
-            drawableId = R.drawable.ic_collapse_notification;
-            contentDescriptionId = R.string.expand_button_content_description_expanded;
-        } else {
-            drawableId = R.drawable.ic_expand_notification;
-            contentDescriptionId = R.string.expand_button_content_description_collapsed;
-        }
-        mExpandButton.setImageDrawable(getContext().getDrawable(drawableId));
-        mExpandButton.setColorFilter(getOriginalNotificationColor());
-        mExpandButton.setContentDescription(mContext.getText(contentDescriptionId));
-    }
-
     public void setShowWorkBadgeAtEnd(boolean showWorkBadgeAtEnd) {
         if (showWorkBadgeAtEnd != mShowWorkBadgeAtEnd) {
             setClipToPadding(!showWorkBadgeAtEnd);
@@ -349,14 +311,6 @@
         }
     }
 
-    public View getWorkProfileIcon() {
-        return mProfileBadge;
-    }
-
-    public CachingIconView getIcon() {
-        return mIcon;
-    }
-
     /**
      * Sets the margin end for the text portion of the header, excluding right-aligned elements
      * @param headerTextMarginEnd margin size
@@ -495,10 +449,6 @@
         return this;
     }
 
-    public ImageView getExpandButton() {
-        return mExpandButton;
-    }
-
     @Override
     public boolean hasOverlappingRendering() {
         return false;
diff --git a/core/java/android/view/OnReceiveContentCallback.java b/core/java/android/view/OnReceiveContentCallback.java
index 73bcb93..a217ff6 100644
--- a/core/java/android/view/OnReceiveContentCallback.java
+++ b/core/java/android/view/OnReceiveContentCallback.java
@@ -134,46 +134,52 @@
     final class Payload {
 
         /**
-         * Specifies the UI through which content is being inserted.
+         * Specifies the UI through which content is being inserted. Future versions of Android may
+         * support additional values.
          *
          * @hide
          */
-        @IntDef(prefix = {"SOURCE_"}, value = {SOURCE_CLIPBOARD, SOURCE_INPUT_METHOD,
+        @IntDef(prefix = {"SOURCE_"}, value = {SOURCE_APP, SOURCE_CLIPBOARD, SOURCE_INPUT_METHOD,
                 SOURCE_DRAG_AND_DROP, SOURCE_AUTOFILL, SOURCE_PROCESS_TEXT})
         @Retention(RetentionPolicy.SOURCE)
         public @interface Source {}
 
         /**
+         * Specifies that the operation was triggered by the app that contains the target view.
+         */
+        public static final int SOURCE_APP = 0;
+
+        /**
          * Specifies that the operation was triggered by a paste from the clipboard (e.g. "Paste" or
          * "Paste as plain text" action in the insertion/selection menu).
          */
-        public static final int SOURCE_CLIPBOARD = 0;
+        public static final int SOURCE_CLIPBOARD = 1;
 
         /**
          * Specifies that the operation was triggered from the soft keyboard (also known as input
          * method editor or IME). See https://developer.android.com/guide/topics/text/image-keyboard
          * for more info.
          */
-        public static final int SOURCE_INPUT_METHOD = 1;
+        public static final int SOURCE_INPUT_METHOD = 2;
 
         /**
          * Specifies that the operation was triggered by the drag/drop framework. See
          * https://developer.android.com/guide/topics/ui/drag-drop for more info.
          */
-        public static final int SOURCE_DRAG_AND_DROP = 2;
+        public static final int SOURCE_DRAG_AND_DROP = 3;
 
         /**
          * Specifies that the operation was triggered by the autofill framework. See
          * https://developer.android.com/guide/topics/text/autofill for more info.
          */
-        public static final int SOURCE_AUTOFILL = 3;
+        public static final int SOURCE_AUTOFILL = 4;
 
         /**
          * Specifies that the operation was triggered by a result from a
          * {@link android.content.Intent#ACTION_PROCESS_TEXT PROCESS_TEXT} action in the selection
          * menu.
          */
-        public static final int SOURCE_PROCESS_TEXT = 4;
+        public static final int SOURCE_PROCESS_TEXT = 5;
 
         /**
          * Returns the symbolic name of the given source.
@@ -182,6 +188,7 @@
          */
         static String sourceToString(@Source int source) {
             switch (source) {
+                case SOURCE_APP: return "SOURCE_APP";
                 case SOURCE_CLIPBOARD: return "SOURCE_CLIPBOARD";
                 case SOURCE_INPUT_METHOD: return "SOURCE_INPUT_METHOD";
                 case SOURCE_DRAG_AND_DROP: return "SOURCE_DRAG_AND_DROP";
@@ -217,37 +224,11 @@
             return String.valueOf(flags);
         }
 
-        /**
-         * The data to be inserted.
-         */
         @NonNull private final ClipData mClip;
-
-        /**
-         * The source of the operation. See {@code SOURCE_} constants.
-         */
         private final @Source int mSource;
-
-        /**
-         * Optional flags that control the insertion behavior. See {@code FLAG_} constants.
-         */
         private final @Flags int mFlags;
-
-        /**
-         * Optional http/https URI for the content that may be provided by the IME. This is only
-         * populated if the source is {@link #SOURCE_INPUT_METHOD} and if a non-empty
-         * {@link android.view.inputmethod.InputContentInfo#getLinkUri linkUri} was passed by the
-         * IME.
-         */
-        @Nullable
-        private final Uri mLinkUri;
-
-        /**
-         * Optional additional metadata. If the source is {@link #SOURCE_INPUT_METHOD}, this will
-         * include the {@link android.view.inputmethod.InputConnection#commitContent opts} passed by
-         * the IME.
-         */
-        @Nullable
-        private final Bundle mExtras;
+        @Nullable private final Uri mLinkUri;
+        @Nullable private final Bundle mExtras;
 
         private Payload(Builder b) {
             this.mClip = Objects.requireNonNull(b.mClip);
@@ -278,7 +259,8 @@
         }
 
         /**
-         * The source of the operation. See {@code SOURCE_} constants.
+         * The source of the operation. See {@code SOURCE_} constants. Future versions of Android
+         * may pass additional values.
          */
         public @Source int getSource() {
             return mSource;
diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java
index 18d0d7b..f60d98b 100644
--- a/core/java/android/view/PointerIcon.java
+++ b/core/java/android/view/PointerIcon.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.XmlRes;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
@@ -405,7 +406,7 @@
     }
 
     @Override
-    public boolean equals(Object other) {
+    public boolean equals(@Nullable Object other) {
         if (this == other) {
             return true;
         }
diff --git a/core/java/android/view/RemoteAccessibilityController.java b/core/java/android/view/RemoteAccessibilityController.java
new file mode 100644
index 0000000..bc0fab1b
--- /dev/null
+++ b/core/java/android/view/RemoteAccessibilityController.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.graphics.Matrix;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.accessibility.IAccessibilityEmbeddedConnection;
+
+class RemoteAccessibilityController {
+    private static final String TAG = "RemoteAccessibilityController";
+    private int mHostId;
+    private RemoteAccessibilityEmbeddedConnection mConnectionWrapper;
+    private Matrix mScreenMatrixForEmbeddedHierarchy = new Matrix();
+    private final float[] mMatrixValues = new float[9];
+    private View mHostView;
+
+    RemoteAccessibilityController(View v) {
+        mHostView = v;
+    }
+
+    private void runOnUiThread(Runnable runnable) {
+        final Handler h = mHostView.getHandler();
+        if (h != null && h.getLooper() != Looper.myLooper()) {
+            h.post(runnable);
+        } else {
+            runnable.run();
+        }
+    }
+
+    void assosciateHierarchy(IAccessibilityEmbeddedConnection connection,
+        IBinder leashToken, int hostId) {
+        mHostId = hostId;
+
+        try {
+            leashToken = connection.associateEmbeddedHierarchy(
+                leashToken, mHostId);
+            setRemoteAccessibilityEmbeddedConnection(connection, leashToken);
+        } catch (RemoteException e) {
+            Log.d(TAG, "Error in associateEmbeddedHierarchy " + e);
+        }
+    }
+
+    void disassosciateHierarchy() {
+        setRemoteAccessibilityEmbeddedConnection(null, null);
+    }
+
+    boolean alreadyAssociated(IAccessibilityEmbeddedConnection connection) {
+        if (mConnectionWrapper == null) {
+            return false;
+        }
+        return mConnectionWrapper.mConnection.equals(connection);
+    }
+
+    boolean connected() {
+      return mConnectionWrapper != null;
+    }
+
+    IBinder getLeashToken() {
+        return mConnectionWrapper.getLeashToken();
+    }
+
+    /**
+     * Wrapper of accessibility embedded connection for embedded view hierarchy.
+     */
+    private final class RemoteAccessibilityEmbeddedConnection implements IBinder.DeathRecipient {
+        private final IAccessibilityEmbeddedConnection mConnection;
+        private final IBinder mLeashToken;
+
+        RemoteAccessibilityEmbeddedConnection(IAccessibilityEmbeddedConnection connection,
+                IBinder leashToken) {
+            mConnection = connection;
+            mLeashToken = leashToken;
+        }
+
+        IAccessibilityEmbeddedConnection getConnection() {
+            return mConnection;
+        }
+
+        IBinder getLeashToken() {
+            return mLeashToken;
+        }
+
+        void linkToDeath() throws RemoteException {
+            mConnection.asBinder().linkToDeath(this, 0);
+        }
+
+        void unlinkToDeath() {
+            mConnection.asBinder().unlinkToDeath(this, 0);
+        }
+
+        @Override
+        public void binderDied() {
+            unlinkToDeath();
+            runOnUiThread(() -> {
+                if (mConnectionWrapper == this) {
+                    mConnectionWrapper = null;
+                }
+            });
+        }
+    }
+
+    private void setRemoteAccessibilityEmbeddedConnection(
+          IAccessibilityEmbeddedConnection connection, IBinder leashToken) {
+        try {
+            if (mConnectionWrapper != null) {
+                mConnectionWrapper.getConnection()
+                    .disassociateEmbeddedHierarchy();
+                mConnectionWrapper.unlinkToDeath();
+                mConnectionWrapper = null;
+            }
+            if (connection != null && leashToken != null) {
+                mConnectionWrapper =
+                    new RemoteAccessibilityEmbeddedConnection(connection, leashToken);
+                mConnectionWrapper.linkToDeath();
+            }
+        } catch (RemoteException e) {
+            Log.d(TAG, "Error while setRemoteEmbeddedConnection " + e);
+        }
+    }
+
+    private RemoteAccessibilityEmbeddedConnection getRemoteAccessibilityEmbeddedConnection() {
+        return mConnectionWrapper;
+    }
+
+    void setScreenMatrix(Matrix m) {
+        // If the screen matrix is identity or doesn't change, do nothing.
+        if (m.isIdentity() || m.equals(mScreenMatrixForEmbeddedHierarchy)) {
+            return;
+        }
+
+        try {
+            final RemoteAccessibilityEmbeddedConnection wrapper =
+                    getRemoteAccessibilityEmbeddedConnection();
+            if (wrapper == null) {
+                return;
+            }
+            m.getValues(mMatrixValues);
+            wrapper.getConnection().setScreenMatrix(mMatrixValues);
+            mScreenMatrixForEmbeddedHierarchy.set(m);
+        } catch (RemoteException e) {
+            Log.d(TAG, "Error while setScreenMatrix " + e);
+        }
+    }
+
+
+
+
+
+
+}
diff --git a/core/java/android/view/ScrollCaptureCallback.java b/core/java/android/view/ScrollCaptureCallback.java
index e1a4e74..d3aad2c 100644
--- a/core/java/android/view/ScrollCaptureCallback.java
+++ b/core/java/android/view/ScrollCaptureCallback.java
@@ -29,8 +29,8 @@
  * callbacks registered within the window.
  * <p>
  * A callback is assigned to a View using {@link View#setScrollCaptureCallback}, or to the window as
- * {@link Window#addScrollCaptureCallback}. The point where the callback is registered defines the
- * frame of reference for the bounds measurements used.
+ * {@link Window#registerScrollCaptureCallback}. The point where the callback is registered defines
+ * the frame of reference for the bounds measurements used.
  * <p>
  * <b>Terminology</b>
  * <dl>
@@ -39,9 +39,9 @@
  * is assigned  directly to a window.</dd>
  *
  * <dt>Scroll Bounds</dt>
- * <dd>A rectangle which describes an area within the containing view where scrolling content may
- * be positioned. This may be the Containing View bounds itself, or any rectangle within.
- * Requested by {@link #onScrollCaptureSearch}.</dd>
+ * <dd>A rectangle which describes an area within the containing view where scrolling content
+ * appears. This may be the entire view or any rectangle within. This defines a frame of reference
+ * for requests as well as the width and maximum height of a single request.</dd>
  *
  * <dt>Scroll Delta</dt>
  * <dd>The distance the scroll position has moved since capture started. Implementations are
@@ -57,7 +57,7 @@
  *
  * @see View#setScrollCaptureHint(int)
  * @see View#setScrollCaptureCallback(ScrollCaptureCallback)
- * @see Window#addScrollCaptureCallback(ScrollCaptureCallback)
+ * @see Window#registerScrollCaptureCallback(ScrollCaptureCallback)
  *
  * @hide
  */
diff --git a/core/java/android/view/ScrollCaptureClient.java b/core/java/android/view/ScrollCaptureConnection.java
similarity index 87%
rename from core/java/android/view/ScrollCaptureClient.java
rename to core/java/android/view/ScrollCaptureConnection.java
index f163124..0e6cdd1d 100644
--- a/core/java/android/view/ScrollCaptureClient.java
+++ b/core/java/android/view/ScrollCaptureConnection.java
@@ -19,7 +19,6 @@
 import static java.util.Objects.requireNonNull;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.annotation.UiThread;
 import android.annotation.WorkerThread;
 import android.graphics.Point;
@@ -33,15 +32,15 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
- * A client of the system providing Scroll Capture capability on behalf of a Window.
+ * Mediator between a selected scroll capture target view and a remote process.
  * <p>
  * An instance is created to wrap the selected {@link ScrollCaptureCallback}.
  *
  * @hide
  */
-public class ScrollCaptureClient extends IScrollCaptureClient.Stub {
+public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub {
 
-    private static final String TAG = "ScrollCaptureClient";
+    private static final String TAG = "ScrollCaptureConnection";
     private static final int DEFAULT_TIMEOUT = 1000;
 
     private final Handler mHandler;
@@ -49,7 +48,7 @@
     private int mTimeoutMillis = DEFAULT_TIMEOUT;
 
     protected Surface mSurface;
-    private IScrollCaptureController mController;
+    private IScrollCaptureCallbacks mCallbacks;
 
     private final Rect mScrollBounds;
     private final Point mPositionInWindow;
@@ -62,18 +61,18 @@
     private DelayedAction mTimeoutAction;
 
     /**
-     * Constructs a ScrollCaptureClient.
+     * Constructs a ScrollCaptureConnection.
      *
      * @param selectedTarget  the target the client is controlling
-     * @param controller the callbacks to reply to system requests
+     * @param callbacks the callbacks to reply to system requests
      *
      * @hide
      */
-    public ScrollCaptureClient(
+    public ScrollCaptureConnection(
             @NonNull ScrollCaptureTarget selectedTarget,
-            @NonNull IScrollCaptureController controller) {
+            @NonNull IScrollCaptureCallbacks callbacks) {
         requireNonNull(selectedTarget, "<selectedTarget> must non-null");
-        requireNonNull(controller, "<controller> must non-null");
+        requireNonNull(callbacks, "<callbacks> must non-null");
         final Rect scrollBounds = requireNonNull(selectedTarget.getScrollBounds(),
                 "target.getScrollBounds() must be non-null to construct a client");
 
@@ -82,7 +81,7 @@
         mScrollBounds = new Rect(scrollBounds);
         mPositionInWindow = new Point(selectedTarget.getPositionInWindow());
 
-        mController = controller;
+        mCallbacks = callbacks;
         mCloseGuard = new CloseGuard();
         mCloseGuard.open("close");
 
@@ -106,14 +105,13 @@
         mTimeoutMillis = timeoutMillis;
     }
 
-    @Nullable
     @VisibleForTesting
     public DelayedAction getTimeoutAction() {
         return mTimeoutAction;
     }
 
     private void checkConnected() {
-        if (mSelectedTarget == null || mController == null) {
+        if (mSelectedTarget == null || mCallbacks == null) {
             throw new IllegalStateException("This client has been disconnected.");
         }
     }
@@ -124,7 +122,7 @@
         }
     }
 
-    @WorkerThread // IScrollCaptureClient
+    @WorkerThread // IScrollCaptureConnection
     @Override
     public void startCapture(Surface surface) throws RemoteException {
         checkConnected();
@@ -140,7 +138,7 @@
         if (cancelTimeout()) {
             mHandler.post(() -> {
                 try {
-                    mController.onCaptureStarted();
+                    mCallbacks.onCaptureStarted();
                 } catch (RemoteException e) {
                     doShutdown();
                 }
@@ -153,7 +151,7 @@
         endCapture();
     }
 
-    @WorkerThread // IScrollCaptureClient
+    @WorkerThread // IScrollCaptureConnection
     @Override
     public void requestImage(Rect requestRect) {
         checkConnected();
@@ -170,7 +168,7 @@
         if (cancelTimeout()) {
             mHandler.post(() -> {
                 try {
-                    mController.onCaptureBufferSent(frameNumber, finalCapturedArea);
+                    mCallbacks.onCaptureBufferSent(frameNumber, finalCapturedArea);
                 } catch (RemoteException e) {
                     doShutdown();
                 }
@@ -183,7 +181,7 @@
         endCapture();
     }
 
-    @WorkerThread // IScrollCaptureClient
+    @WorkerThread // IScrollCaptureConnection
     @Override
     public void endCapture() {
         if (isStarted()) {
@@ -196,7 +194,7 @@
     }
 
     private boolean isStarted() {
-        return mController != null && mSelectedTarget != null;
+        return mCallbacks != null && mSelectedTarget != null;
     }
 
     @UiThread
@@ -214,8 +212,8 @@
 
     private void doShutdown() {
         try {
-            if (mController != null) {
-                mController.onConnectionClosed();
+            if (mCallbacks != null) {
+                mCallbacks.onConnectionClosed();
             }
         } catch (RemoteException e) {
             // Ignore
@@ -235,15 +233,15 @@
         }
 
         mSelectedTarget = null;
-        mController = null;
+        mCallbacks = null;
     }
 
     /** @return a string representation of the state of this client */
     public String toString() {
-        return "ScrollCaptureClient{"
+        return "ScrollCaptureConnection{"
                 + ", session=" + mSession
                 + ", selectedTarget=" + mSelectedTarget
-                + ", clientCallbacks=" + mController
+                + ", clientCallbacks=" + mCallbacks
                 + "}";
     }
 
diff --git a/core/java/android/view/ScrollCaptureSession.java b/core/java/android/view/ScrollCaptureSession.java
index 628e23f..92617a3 100644
--- a/core/java/android/view/ScrollCaptureSession.java
+++ b/core/java/android/view/ScrollCaptureSession.java
@@ -36,15 +36,15 @@
     private final Point mPositionInWindow;
 
     @Nullable
-    private ScrollCaptureClient mClient;
+    private ScrollCaptureConnection mConnection;
 
     /** @hide */
     public ScrollCaptureSession(Surface surface, Rect scrollBounds, Point positionInWindow,
-            ScrollCaptureClient client) {
+            ScrollCaptureConnection connection) {
         mSurface = surface;
         mScrollBounds = scrollBounds;
         mPositionInWindow = positionInWindow;
-        mClient = client;
+        mConnection = connection;
     }
 
     /**
@@ -88,8 +88,8 @@
      * @param capturedArea the area captured, relative to scroll bounds
      */
     public void notifyBufferSent(long frameNumber, @NonNull Rect capturedArea) {
-        if (mClient != null) {
-            mClient.onRequestImageCompleted(frameNumber, capturedArea);
+        if (mConnection != null) {
+            mConnection.onRequestImageCompleted(frameNumber, capturedArea);
         }
     }
 
@@ -97,7 +97,7 @@
      * @hide
      */
     public void disconnect() {
-        mClient = null;
+        mConnection = null;
         if (mSurface.isValid()) {
             mSurface.release();
         }
diff --git a/core/java/android/view/ScrollCaptureTargetResolver.java b/core/java/android/view/ScrollCaptureTargetResolver.java
index 71e82c5..5106534 100644
--- a/core/java/android/view/ScrollCaptureTargetResolver.java
+++ b/core/java/android/view/ScrollCaptureTargetResolver.java
@@ -57,7 +57,6 @@
 @UiThread
 public class ScrollCaptureTargetResolver {
     private static final String TAG = "ScrollCaptureTargetRes";
-    private static final boolean DEBUG = true;
 
     private final Object mLock = new Object();
 
@@ -86,18 +85,11 @@
      * Binary operator which selects the best {@link ScrollCaptureTarget}.
      */
     private static ScrollCaptureTarget chooseTarget(ScrollCaptureTarget a, ScrollCaptureTarget b) {
-        Log.d(TAG, "chooseTarget: " + a + " or " + b);
-        // Nothing plus nothing is still nothing.
         if (a == null && b == null) {
-            Log.d(TAG, "chooseTarget: (both null) return " + null);
             return null;
-        }
-        // Prefer non-null.
-        if (a == null || b == null) {
+        } else if (a == null || b == null) {
             ScrollCaptureTarget c = (a == null) ? b : a;
-            Log.d(TAG, "chooseTarget: (other is null) return " + c);
             return c;
-
         }
 
         boolean emptyScrollBoundsA = nullOrEmpty(a.getScrollBounds());
@@ -155,8 +147,7 @@
      *
      * @param targets   a list of {@link ScrollCaptureTarget} as collected by {@link
      *                  View#dispatchScrollCaptureSearch}.
-     * @param uiHandler the UI thread handler for the view tree
-     * @see #start(long, Consumer)
+     * @see #start(Handler, long, Consumer)
      */
     public ScrollCaptureTargetResolver(Queue<ScrollCaptureTarget> targets) {
         mTargets = targets;
@@ -184,7 +175,6 @@
         return mResult;
     }
 
-
     private void supplyResult(ScrollCaptureTarget target) {
         checkThread();
         if (mFinished) {
@@ -232,12 +222,11 @@
                 return;
             }
             mStarted = true;
-            uiHandler.post(() -> run(timeLimitMillis, resultConsumer));
+            uiHandler.post(this::run);
         }
     }
 
-
-    private void run(long timeLimitMillis, Consumer<ScrollCaptureTarget> resultConsumer) {
+    private void run() {
         checkThread();
 
         mPendingBoundsRequests = mTargets.size();
@@ -248,15 +237,11 @@
         mHandler.postAtTime(mTimeoutRunnable, mDeadlineMillis);
     }
 
-    private final Runnable mTimeoutRunnable = new Runnable() {
-        @Override
-        public void run() {
-            checkThread();
-            supplyResult(null);
-        }
+    private final Runnable mTimeoutRunnable = () -> {
+        checkThread();
+        supplyResult(null);
     };
 
-
     /**
      * Adds a target to the list and requests {@link ScrollCaptureCallback#onScrollCaptureSearch}
      * scrollBounds} from it. Results are returned by a call to {@link #onScrollBoundsProvided}.
@@ -274,7 +259,6 @@
                         // Queue and consume on the UI thread
                         ((scrollBounds) -> mHandler.post(
                                 () -> onScrollBoundsProvided(target, scrollBounds)))));
-
     }
 
     @UiThread
@@ -300,14 +284,8 @@
             supplyResult(target);
         }
 
-        System.err.println("mPendingBoundsRequests: " + mPendingBoundsRequests);
-        System.err.println("mDeadlineMillis: " + mDeadlineMillis);
-        System.err.println("SystemClock.elapsedRealtime(): " + SystemClock.elapsedRealtime());
-
         if (!mFinished) {
             // Reschedule the timeout.
-            System.err.println(
-                    "We think we're NOT done yet and will check back at " + mDeadlineMillis);
             mHandler.postAtTime(mTimeoutRunnable, mDeadlineMillis);
         }
     }
@@ -334,44 +312,17 @@
         return otherParent == view;
     }
 
-    private static int findRelation(@NonNull View a, @NonNull View b) {
-        if (a == b) {
-            return 0;
-        }
-
-        ViewParent parentA = a.getParent();
-        ViewParent parentB = b.getParent();
-
-        while (parentA != null || parentB != null) {
-            if (parentA == parentB) {
-                return 0;
-            }
-            if (parentA == b) {
-                return 1; // A is descendant of B
-            }
-            if (parentB == a) {
-                return -1; // B is descendant of A
-            }
-            if (parentA != null) {
-                parentA = parentA.getParent();
-            }
-            if (parentB != null) {
-                parentB = parentB.getParent();
-            }
-        }
-        return 0;
-    }
-
     /**
      * A safe wrapper for a consumer callbacks intended to accept a single value. It ensures
      * that the receiver of the consumer does not retain a reference to {@code target} after use nor
      * cause race conditions by invoking {@link Consumer#accept accept} more than once.
-     *
-     * @param target the target consumer
      */
     static class SingletonConsumer<T> implements Consumer<T> {
         final AtomicReference<Consumer<T>> mAtomicRef;
 
+        /**
+         * @param target the target consumer
+         **/
         SingletonConsumer(Consumer<T> target) {
             mAtomicRef = new AtomicReference<>(target);
         }
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 5b0d950..0847a17 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -739,7 +739,7 @@
      * Set the scaling mode to be used for this surfaces buffers
      * @hide
      */
-    void setScalingMode(@ScalingMode int scalingMode) {
+     public void setScalingMode(@ScalingMode int scalingMode) {
         synchronized (mLock) {
             checkNotReleasedLocked();
             int err = nativeSetScalingMode(mNativeObject, scalingMode);
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 0c64eea..6d6fabb 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -187,8 +187,6 @@
     private static native void nativeReparent(long transactionObj, long nativeObject,
             long newParentNativeObject);
     private static native void nativeSeverChildren(long transactionObj, long nativeObject);
-    private static native void nativeSetOverrideScalingMode(long transactionObj, long nativeObject,
-            int scalingMode);
 
     private static native Display.HdrCapabilities nativeGetHdrCapabilities(IBinder displayToken);
 
@@ -267,7 +265,7 @@
 
     private WeakReference<View> mLocalOwnerView;
 
-    static Transaction sGlobalTransaction;
+    static GlobalTransactionWrapper sGlobalTransaction;
     static long sTransactionNestCount = 0;
 
     /**
@@ -1453,7 +1451,7 @@
     public static void openTransaction() {
         synchronized (SurfaceControl.class) {
             if (sGlobalTransaction == null) {
-                sGlobalTransaction = new Transaction();
+                sGlobalTransaction = new GlobalTransactionWrapper();
             }
             synchronized(SurfaceControl.class) {
                 sTransactionNestCount++;
@@ -1487,7 +1485,7 @@
             } else if (--sTransactionNestCount > 0) {
                 return;
             }
-            sGlobalTransaction.apply();
+            sGlobalTransaction.applyGlobalTransaction(false);
         }
     }
 
@@ -1521,16 +1519,6 @@
     /**
      * @hide
      */
-    public void setOverrideScalingMode(int scalingMode) {
-        checkNotReleased();
-        synchronized(SurfaceControl.class) {
-            sGlobalTransaction.setOverrideScalingMode(this, scalingMode);
-        }
-    }
-
-    /**
-     * @hide
-     */
     @UnsupportedAppUsage
     public void setLayer(int zorder) {
         checkNotReleased();
@@ -1635,6 +1623,16 @@
     /**
      * @hide
      */
+    public void setBackgroundBlurRadius(int blur) {
+        checkNotReleased();
+        synchronized (SurfaceControl.class) {
+            sGlobalTransaction.setBackgroundBlurRadius(this, blur);
+        }
+    }
+
+    /**
+     * @hide
+     */
     public void setMatrix(float dsdx, float dtdx, float dtdy, float dsdy) {
         checkNotReleased();
         synchronized(SurfaceControl.class) {
@@ -1748,7 +1746,7 @@
         }
 
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (this == o) return true;
             if (o == null || getClass() != o.getClass()) return false;
             DisplayInfo that = (DisplayInfo) o;
@@ -1926,7 +1924,7 @@
         }
 
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             return o instanceof DesiredDisplayConfigSpecs && equals((DesiredDisplayConfigSpecs) o);
         }
 
@@ -2555,7 +2553,10 @@
             nativeApplyTransaction(mNativeObject, sync);
         }
 
-        private void applyResizedSurfaces() {
+        /**
+         * @hide
+         */
+        protected void applyResizedSurfaces() {
             for (int i = mResizedSurfaces.size() - 1; i >= 0; i--) {
                 final Point size = mResizedSurfaces.valueAt(i);
                 final SurfaceControl surfaceControl = mResizedSurfaces.keyAt(i);
@@ -2567,7 +2568,10 @@
             mResizedSurfaces.clear();
         }
 
-        private void notifyReparentedSurfaces() {
+        /**
+         * @hide
+         */
+        protected void notifyReparentedSurfaces() {
             final int reparentCount = mReparentedSurfaces.size();
             for (int i = reparentCount - 1; i >= 0; i--) {
                 final SurfaceControl child = mReparentedSurfaces.keyAt(i);
@@ -2983,16 +2987,6 @@
         }
 
         /**
-         * @hide
-         */
-        public Transaction setOverrideScalingMode(SurfaceControl sc, int overrideScalingMode) {
-            checkPreconditions(sc);
-            nativeSetOverrideScalingMode(mNativeObject, sc.mNativeObject,
-                    overrideScalingMode);
-            return this;
-        }
-
-        /**
          * Fills the surface with the specified color.
          * @param color A float array with three values to represent r, g, b in range [0..1]. An
          * invalid color will remove the color fill.
@@ -3403,6 +3397,26 @@
     }
 
     /**
+     * As part of eliminating usage of the global Transaction we expose
+     * a SurfaceControl.getGlobalTransaction function. However calling
+     * apply on this global transaction (rather than using closeTransaction)
+     * would be very dangerous. So for the global transaction we use this
+     * subclass of Transaction where the normal apply throws an exception.
+     */
+    private static class GlobalTransactionWrapper extends SurfaceControl.Transaction {
+        void applyGlobalTransaction(boolean sync) {
+            applyResizedSurfaces();
+            notifyReparentedSurfaces();
+            nativeApplyTransaction(mNativeObject, sync);
+        }
+
+        @Override
+        public void apply(boolean sync) {
+            throw new RuntimeException("Global transaction must be applied from closeTransaction");
+        }
+    }
+
+    /**
      * Acquire a frame rate flexibility token, which allows surface flinger to freely switch display
      * frame rates. This is used by CTS tests to put the device in a consistent state. See
      * ISurfaceComposer::acquireFrameRateFlexibilityToken(). The caller must have the
@@ -3422,4 +3436,17 @@
     public static void releaseFrameRateFlexibilityToken(long token) {
         nativeReleaseFrameRateFlexibilityToken(token);
     }
+
+    /**
+     * This is a refactoring utility function to enable lower levels of code to be refactored
+     * from using the global transaction (and instead use a passed in Transaction) without
+     * having to refactor the higher levels at the same time.
+     * The returned global transaction can't be applied, it must be applied from closeTransaction
+     * Unless you are working on removing Global Transaction usage in the WindowManager, this
+     * probably isn't a good function to use.
+     * @hide
+     */
+    public static Transaction getGlobalTransaction() {
+        return sGlobalTransaction;
+    }
 }
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 78c71b8..14748f0 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -41,7 +41,6 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
-import android.os.RemoteException;
 import android.os.SystemClock;
 import android.provider.Settings;
 import android.util.AttributeSet;
@@ -225,13 +224,12 @@
 
     private SurfaceControl.Transaction mRtTransaction = new SurfaceControl.Transaction();
     private SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
-    private int mParentSurfaceGenerationId;
+    private int mParentSurfaceSequenceId;
 
-    private RemoteAccessibilityEmbeddedConnection mRemoteAccessibilityEmbeddedConnection;
+    private RemoteAccessibilityController mRemoteAccessibilityController =
+        new RemoteAccessibilityController(this);
 
-    private final Matrix mScreenMatrixForEmbeddedHierarchy = new Matrix();
     private final Matrix mTmpMatrix = new Matrix();
-    private final float[] mMatrixValues = new float[9];
 
     SurfaceControlViewHost.SurfacePackage mSurfacePackage;
 
@@ -467,7 +465,7 @@
                                 Transaction t = new SurfaceControl.Transaction();
                                 t.setAlpha(mSurfaceControl, alpha);
                                 t.deferTransactionUntil(mSurfaceControl,
-                                        viewRoot.getRenderSurfaceControl(), frame);
+                                        viewRoot.getSurfaceControl(), frame);
                                 t.apply();
                             }
                         }
@@ -827,7 +825,7 @@
                         final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
                         updateRelativeZ(t);
                         t.deferTransactionUntil(mSurfaceControl,
-                                viewRoot.getRenderSurfaceControl(), frame);
+                                viewRoot.getSurfaceControl(), frame);
                         t.apply();
                     }
                 }
@@ -927,6 +925,103 @@
         }
     }
 
+    private boolean performSurfaceTransaction(ViewRootImpl viewRoot, Translator translator,
+        boolean creating, boolean sizeChanged, boolean needBLASTSync) {
+        boolean realSizeChanged = false;
+
+        mSurfaceLock.lock();
+        try {
+            mDrawingStopped = !mVisible;
+
+            if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+                    + "Cur surface: " + mSurface);
+
+            // If we are creating the surface control or the parent surface has not
+            // changed, then set relative z. Otherwise allow the parent
+            // SurfaceChangedCallback to update the relative z. This is needed so that
+            // we do not change the relative z before the server is ready to swap the
+            // parent surface.
+            if (creating || (mParentSurfaceSequenceId == viewRoot.getSurfaceSequenceId())) {
+                updateRelativeZ(mTmpTransaction);
+            }
+            mParentSurfaceSequenceId = viewRoot.getSurfaceSequenceId();
+
+            if (mViewVisibility) {
+                mTmpTransaction.show(mSurfaceControl);
+            } else {
+                mTmpTransaction.hide(mSurfaceControl);
+            }
+
+            if (mSurfacePackage != null) {
+                reparentSurfacePackage(mTmpTransaction, mSurfacePackage);
+            }
+
+            updateBackgroundVisibility(mTmpTransaction);
+            updateBackgroundColor(mTmpTransaction);
+            if (mUseAlpha) {
+                float alpha = getFixedAlpha();
+                mTmpTransaction.setAlpha(mSurfaceControl, alpha);
+                mSurfaceAlpha = alpha;
+            }
+
+            // While creating the surface, we will set it's initial
+            // geometry. Outside of that though, we should generally
+            // leave it to the RenderThread.
+            //
+            // There is one more case when the buffer size changes we aren't yet
+            // prepared to sync (as even following the transaction applying
+            // we still need to latch a buffer).
+            // b/28866173
+            if (sizeChanged || creating || !mRtHandlingPositionUpdates) {
+                onSetSurfacePositionAndScaleRT(mTmpTransaction, mSurfaceControl,
+                        mScreenRect.left, /*positionLeft*/
+                        mScreenRect.top /*positionTop*/ ,
+                        mScreenRect.width() / (float) mSurfaceWidth /*postScaleX*/,
+                        mScreenRect.height() / (float) mSurfaceHeight /*postScaleY*/);
+
+                // Set a window crop when creating the surface or changing its size to
+                // crop the buffer to the surface size since the buffer producer may
+                // use SCALING_MODE_SCALE and submit a larger size than the surface
+                // size.
+                if (mClipSurfaceToBounds && mClipBounds != null) {
+                    mTmpTransaction.setWindowCrop(mSurfaceControl, mClipBounds);
+                } else {
+                    mTmpTransaction.setWindowCrop(mSurfaceControl, mSurfaceWidth,
+                            mSurfaceHeight);
+                }
+            } else if (needBLASTSync) {
+                viewRoot.setUseBLASTSyncTransaction();
+            }
+            mTmpTransaction.setCornerRadius(mSurfaceControl, mCornerRadius);
+            if (sizeChanged && !creating) {
+                setBufferSize(mTmpTransaction);
+            }
+
+            mTmpTransaction.apply();
+            updateEmbeddedAccessibilityMatrix();
+
+            mSurfaceFrame.left = 0;
+            mSurfaceFrame.top = 0;
+            if (translator == null) {
+                mSurfaceFrame.right = mSurfaceWidth;
+                mSurfaceFrame.bottom = mSurfaceHeight;
+            } else {
+                float appInvertedScale = translator.applicationInvertedScale;
+                mSurfaceFrame.right = (int) (mSurfaceWidth * appInvertedScale + 0.5f);
+                mSurfaceFrame.bottom = (int) (mSurfaceHeight * appInvertedScale + 0.5f);
+            }
+            final int surfaceWidth = mSurfaceFrame.right;
+            final int surfaceHeight = mSurfaceFrame.bottom;
+            realSizeChanged = mLastSurfaceWidth != surfaceWidth
+                    || mLastSurfaceHeight != surfaceHeight;
+            mLastSurfaceWidth = surfaceWidth;
+            mLastSurfaceHeight = surfaceHeight;
+        } finally {
+            mSurfaceLock.unlock();
+        }
+        return realSizeChanged;
+    }
+
     /** @hide */
     protected void updateSurface() {
         if (!mHaveFrame) {
@@ -965,7 +1060,6 @@
                 && mRequestedVisible;
         final boolean sizeChanged = mSurfaceWidth != myWidth || mSurfaceHeight != myHeight;
         final boolean windowVisibleChanged = mWindowVisibility != mLastWindowVisibility;
-        boolean redrawNeeded = false;
         getLocationInSurface(mLocation);
         final boolean positionChanged = mWindowSpaceLeft != mLocation[0]
             || mWindowSpaceTop != mLocation[1];
@@ -988,7 +1082,7 @@
                     + " top=" + (mWindowSpaceTop != mLocation[1]));
 
             try {
-                final boolean visible = mVisible = mRequestedVisible;
+                mVisible = mRequestedVisible;
                 mWindowSpaceLeft = mLocation[0];
                 mWindowSpaceTop = mLocation[1];
                 mSurfaceWidth = myWidth;
@@ -1014,119 +1108,26 @@
                     return;
                 }
 
-                boolean realSizeChanged = false;
-
-                mSurfaceLock.lock();
-                try {
-                    mDrawingStopped = !visible;
-
-                    if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
-                            + "Cur surface: " + mSurface);
-
-                    // If we are creating the surface control or the parent surface has not
-                    // changed, then set relative z. Otherwise allow the parent
-                    // SurfaceChangedCallback to update the relative z. This is needed so that
-                    // we do not change the relative z before the server is ready to swap the
-                    // parent surface.
-                    if (creating || (mParentSurfaceGenerationId
-                            == viewRoot.mSurface.getGenerationId())) {
-                        updateRelativeZ(mTmpTransaction);
-                    }
-                    mParentSurfaceGenerationId = viewRoot.mSurface.getGenerationId();
-
-                    if (mViewVisibility) {
-                        mTmpTransaction.show(mSurfaceControl);
-                    } else {
-                        mTmpTransaction.hide(mSurfaceControl);
-                    }
-
-                    if (mSurfacePackage != null) {
-                        reparentSurfacePackage(mTmpTransaction, mSurfacePackage);
-                    }
-
-                    updateBackgroundVisibility(mTmpTransaction);
-                    updateBackgroundColor(mTmpTransaction);
-                    if (mUseAlpha) {
-                        mTmpTransaction.setAlpha(mSurfaceControl, alpha);
-                        mSurfaceAlpha = alpha;
-                    }
-
-                    // While creating the surface, we will set it's initial
-                    // geometry. Outside of that though, we should generally
-                    // leave it to the RenderThread.
-                    //
-                    // There is one more case when the buffer size changes we aren't yet
-                    // prepared to sync (as even following the transaction applying
-                    // we still need to latch a buffer).
-                    // b/28866173
-                    if (sizeChanged || creating || !mRtHandlingPositionUpdates) {
-                        onSetSurfacePositionAndScaleRT(mTmpTransaction, mSurfaceControl,
-                                mScreenRect.left, /*positionLeft*/
-                                mScreenRect.top /*positionTop*/ ,
-                                mScreenRect.width() / (float) mSurfaceWidth /*postScaleX*/,
-                                mScreenRect.height() / (float) mSurfaceHeight /*postScaleY*/);
-
-                        // Set a window crop when creating the surface or changing its size to
-                        // crop the buffer to the surface size since the buffer producer may
-                        // use SCALING_MODE_SCALE and submit a larger size than the surface
-                        // size.
-                        if (mClipSurfaceToBounds && mClipBounds != null) {
-                            mTmpTransaction.setWindowCrop(mSurfaceControl, mClipBounds);
-                        } else {
-                            mTmpTransaction.setWindowCrop(mSurfaceControl, mSurfaceWidth,
-                                    mSurfaceHeight);
-                        }
-                    } else if ((layoutSizeChanged || positionChanged || visibleChanged) &&
-                            viewRoot.useBLAST()) {
-                        viewRoot.setUseBLASTSyncTransaction();
-                    }
-                    mTmpTransaction.setCornerRadius(mSurfaceControl, mCornerRadius);
-                    if (sizeChanged && !creating) {
-                        setBufferSize(mTmpTransaction);
-                    }
-
-                    mTmpTransaction.apply();
-                    updateScreenMatrixForEmbeddedHierarchy();
-
-                    if (sizeChanged || creating) {
-                        redrawNeeded = true;
-                    }
-
-                    mSurfaceFrame.left = 0;
-                    mSurfaceFrame.top = 0;
-                    if (translator == null) {
-                        mSurfaceFrame.right = mSurfaceWidth;
-                        mSurfaceFrame.bottom = mSurfaceHeight;
-                    } else {
-                        float appInvertedScale = translator.applicationInvertedScale;
-                        mSurfaceFrame.right = (int) (mSurfaceWidth * appInvertedScale + 0.5f);
-                        mSurfaceFrame.bottom = (int) (mSurfaceHeight * appInvertedScale + 0.5f);
-                    }
-
-                    final int surfaceWidth = mSurfaceFrame.right;
-                    final int surfaceHeight = mSurfaceFrame.bottom;
-                    realSizeChanged = mLastSurfaceWidth != surfaceWidth
-                            || mLastSurfaceHeight != surfaceHeight;
-                    mLastSurfaceWidth = surfaceWidth;
-                    mLastSurfaceHeight = surfaceHeight;
-                } finally {
-                    mSurfaceLock.unlock();
-                }
+                final boolean needBLASTSync =
+                    (layoutSizeChanged || positionChanged || visibleChanged) &&
+                        viewRoot.useBLAST();
+                final boolean realSizeChanged = performSurfaceTransaction(viewRoot,
+                    translator, creating, sizeChanged, needBLASTSync);
+                final boolean redrawNeeded = sizeChanged || creating ||
+                    (mVisible && !mDrawFinished);
 
                 try {
-                    redrawNeeded |= visible && !mDrawFinished;
-
                     SurfaceHolder.Callback[] callbacks = null;
 
                     final boolean surfaceChanged = creating;
-                    if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) {
+                    if (mSurfaceCreated && (surfaceChanged || (!mVisible && visibleChanged))) {
                         mSurfaceCreated = false;
                         notifySurfaceDestroyed();
                     }
 
                     copySurface(creating /* surfaceControlCreated */, sizeChanged);
 
-                    if (visible && mSurface.isValid()) {
+                    if (mVisible && mSurface.isValid()) {
                         if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
                             mSurfaceCreated = true;
                             mIsCreating = true;
@@ -1206,7 +1207,7 @@
             // Therefore, we must explicitly recreate the {@link Surface} in these
             // cases.
             if (mUseBlastAdapter) {
-                mSurface.transferFrom(mBlastBufferQueue.createSurface());
+                mSurface.transferFrom(mBlastBufferQueue.createSurfaceWithHandle());
             } else {
                 mSurface.createFrom(mSurfaceControl);
             }
@@ -1352,7 +1353,7 @@
             Rect position, long frameNumber) {
         final ViewRootImpl viewRoot = getViewRootImpl();
         if (frameNumber > 0 && viewRoot != null && !viewRoot.useBLAST()) {
-            t.deferTransactionUntil(surface, viewRoot.getRenderSurfaceControl(),
+            t.deferTransactionUntil(surface, viewRoot.getSurfaceControl(),
                     frameNumber);
         }
 
@@ -1470,7 +1471,7 @@
                 } else {
                     if (frameNumber > 0 && viewRoot != null && viewRoot.mSurface.isValid()) {
                         mRtTransaction.deferTransactionUntil(mSurfaceControl,
-                                viewRoot.getRenderSurfaceControl(), frameNumber);
+                                viewRoot.getSurfaceControl(), frameNumber);
                     }
                     mRtTransaction.hide(mSurfaceControl);
                     if (mRtReleaseSurfaces) {
@@ -1754,7 +1755,7 @@
     @Override
     public void surfaceDestroyed() {
         setWindowStopped(true);
-        setRemoteAccessibilityEmbeddedConnection(null, null);
+        mRemoteAccessibilityController.disassosciateHierarchy();
     }
 
     /**
@@ -1834,14 +1835,12 @@
     @Override
     public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfoInternal(info);
-        final RemoteAccessibilityEmbeddedConnection wrapper =
-                getRemoteAccessibilityEmbeddedConnection();
-        if (wrapper == null) {
+        if (!mRemoteAccessibilityController.connected()) {
             return;
         }
         // Add a leashed child when this SurfaceView embeds another view hierarchy. Getting this
         // leashed child would return the root node in the embedded hierarchy
-        info.addChild(wrapper.getLeashToken());
+        info.addChild(mRemoteAccessibilityController.getLeashToken());
     }
 
     @Override
@@ -1850,7 +1849,7 @@
         // If developers explicitly set the important mode for it, don't change the mode.
         // Only change the mode to important when this SurfaceView isn't explicitly set and has
         // an embedded hierarchy.
-        if (mRemoteAccessibilityEmbeddedConnection == null
+        if ((mRemoteAccessibilityController!= null && !mRemoteAccessibilityController.connected())
                 || mode != IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
             return mode;
         }
@@ -1859,74 +1858,13 @@
 
     private void initEmbeddedHierarchyForAccessibility(SurfaceControlViewHost.SurfacePackage p) {
         final IAccessibilityEmbeddedConnection connection = p.getAccessibilityEmbeddedConnection();
-        final RemoteAccessibilityEmbeddedConnection wrapper =
-                getRemoteAccessibilityEmbeddedConnection();
-
-        // Do nothing if package is embedding the same view hierarchy.
-        if (wrapper != null && wrapper.getConnection().equals(connection)) {
+        if (mRemoteAccessibilityController.alreadyAssociated(connection)) {
             return;
         }
+        mRemoteAccessibilityController.assosciateHierarchy(connection,
+            getViewRootImpl().mLeashToken, getAccessibilityViewId());
 
-        // If this SurfaceView embeds a different view hierarchy, unlink the previous one first.
-        setRemoteAccessibilityEmbeddedConnection(null, null);
-
-        try {
-            final IBinder leashToken = connection.associateEmbeddedHierarchy(
-                    getViewRootImpl().mLeashToken, getAccessibilityViewId());
-            setRemoteAccessibilityEmbeddedConnection(connection, leashToken);
-        } catch (RemoteException e) {
-            Log.d(TAG, "Error while associateEmbeddedHierarchy " + e);
-        }
-        updateScreenMatrixForEmbeddedHierarchy();
-    }
-
-    private void setRemoteAccessibilityEmbeddedConnection(
-            IAccessibilityEmbeddedConnection connection, IBinder leashToken) {
-        try {
-            if (mRemoteAccessibilityEmbeddedConnection != null) {
-                mRemoteAccessibilityEmbeddedConnection.getConnection()
-                        .disassociateEmbeddedHierarchy();
-                mRemoteAccessibilityEmbeddedConnection.unlinkToDeath();
-                mRemoteAccessibilityEmbeddedConnection = null;
-            }
-            if (connection != null && leashToken != null) {
-                mRemoteAccessibilityEmbeddedConnection =
-                        new RemoteAccessibilityEmbeddedConnection(connection, leashToken);
-                mRemoteAccessibilityEmbeddedConnection.linkToDeath();
-            }
-        } catch (RemoteException e) {
-            Log.d(TAG, "Error while setRemoteEmbeddedConnection " + e);
-        }
-    }
-
-    private RemoteAccessibilityEmbeddedConnection getRemoteAccessibilityEmbeddedConnection() {
-        return mRemoteAccessibilityEmbeddedConnection;
-    }
-
-    private void updateScreenMatrixForEmbeddedHierarchy() {
-        getBoundsOnScreen(mTmpRect);
-        mTmpMatrix.reset();
-        mTmpMatrix.setTranslate(mTmpRect.left, mTmpRect.top);
-        mTmpMatrix.postScale(mScreenRect.width() / (float) mSurfaceWidth,
-                mScreenRect.height() / (float) mSurfaceHeight);
-
-        // If the screen matrix is identity or doesn't change, do nothing.
-        if (mTmpMatrix.isIdentity() || mTmpMatrix.equals(mScreenMatrixForEmbeddedHierarchy)) {
-            return;
-        }
-
-        try {
-            final RemoteAccessibilityEmbeddedConnection wrapper =
-                    getRemoteAccessibilityEmbeddedConnection();
-            if (wrapper == null) {
-                return;
-            }
-            mTmpMatrix.getValues(mMatrixValues);
-            wrapper.getConnection().setScreenMatrix(mMatrixValues);
-            mScreenMatrixForEmbeddedHierarchy.set(mTmpMatrix);
-        } catch (RemoteException e) {
-            Log.d(TAG, "Error while setScreenMatrix " + e);
-        }
+        updateEmbeddedAccessibilityMatrix();
     }
 
     private void notifySurfaceDestroyed() {
@@ -1954,6 +1892,18 @@
         }
     }
 
+    void updateEmbeddedAccessibilityMatrix() {
+        if (!mRemoteAccessibilityController.connected()) {
+            return;
+        }
+        getBoundsOnScreen(mTmpRect);
+        mTmpMatrix.reset();
+        mTmpMatrix.setTranslate(mTmpRect.left, mTmpRect.top);
+        mTmpMatrix.postScale(mScreenRect.width() / (float) mSurfaceWidth,
+                mScreenRect.height() / (float) mSurfaceHeight);
+        mRemoteAccessibilityController.setScreenMatrix(mTmpMatrix);
+    }
+
     @Override
     protected void onFocusChanged(boolean gainFocus, @FocusDirection int direction,
                                   @Nullable Rect previouslyFocusedRect) {
@@ -1970,44 +1920,4 @@
                     + "Exception requesting focus on embedded window", e);
         }
     }
-
-    /**
-     * Wrapper of accessibility embedded connection for embedded view hierarchy.
-     */
-    private final class RemoteAccessibilityEmbeddedConnection implements IBinder.DeathRecipient {
-        private final IAccessibilityEmbeddedConnection mConnection;
-        private final IBinder mLeashToken;
-
-        RemoteAccessibilityEmbeddedConnection(IAccessibilityEmbeddedConnection connection,
-                IBinder leashToken) {
-            mConnection = connection;
-            mLeashToken = leashToken;
-        }
-
-        IAccessibilityEmbeddedConnection getConnection() {
-            return mConnection;
-        }
-
-        IBinder getLeashToken() {
-            return mLeashToken;
-        }
-
-        void linkToDeath() throws RemoteException {
-            mConnection.asBinder().linkToDeath(this, 0);
-        }
-
-        void unlinkToDeath() {
-            mConnection.asBinder().unlinkToDeath(this, 0);
-        }
-
-        @Override
-        public void binderDied() {
-            unlinkToDeath();
-            runOnUiThread(() -> {
-                if (mRemoteAccessibilityEmbeddedConnection == this) {
-                    mRemoteAccessibilityEmbeddedConnection = null;
-                }
-            });
-        }
-    }
 }
diff --git a/core/java/android/view/SyncRtSurfaceTransactionApplier.java b/core/java/android/view/SyncRtSurfaceTransactionApplier.java
index 062285f..bce78b5 100644
--- a/core/java/android/view/SyncRtSurfaceTransactionApplier.java
+++ b/core/java/android/view/SyncRtSurfaceTransactionApplier.java
@@ -60,7 +60,7 @@
         if (mTargetViewRootImpl == null) {
             return;
         }
-        mTargetSc = mTargetViewRootImpl.getRenderSurfaceControl();
+        mTargetSc = mTargetViewRootImpl.getSurfaceControl();
         mTargetViewRootImpl.registerRtFrameCallback(frame -> {
             if (mTargetSc == null || !mTargetSc.isValid()) {
                 return;
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 14a324d..57ca71a 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityManager;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.HardwareRenderer;
@@ -26,7 +27,6 @@
 import android.graphics.RecordingCanvas;
 import android.graphics.Rect;
 import android.graphics.RenderNode;
-import android.os.SystemProperties;
 import android.os.Trace;
 import android.util.Log;
 import android.view.Surface.OutOfResourcesException;
@@ -186,37 +186,12 @@
     public static int EGL_CONTEXT_PRIORITY_MEDIUM_IMG = 0x3102;
     public static int EGL_CONTEXT_PRIORITY_LOW_IMG = 0x3103;
 
-    static {
-        // Try to check OpenGL support early if possible.
-        isAvailable();
-    }
-
-    /**
-     * A process can set this flag to false to prevent the use of threaded
-     * rendering.
-     *
-     * @hide
-     */
-    public static boolean sRendererDisabled = false;
-
     /**
      * Further threaded renderer disabling for the system process.
      *
      * @hide
      */
-    public static boolean sSystemRendererDisabled = false;
-
-    /**
-     * Invoke this method to disable threaded rendering in the current process.
-     *
-     * @hide
-     */
-    public static void disable(boolean system) {
-        sRendererDisabled = true;
-        if (system) {
-            sSystemRendererDisabled = true;
-        }
-    }
+    public static boolean sRendererEnabled = true;
 
     public static boolean sTrimForeground = false;
 
@@ -230,16 +205,19 @@
         sTrimForeground = true;
     }
 
-
     /**
-     * Indicates whether threaded rendering is available under any form for
-     * the view hierarchy.
-     *
-     * @return True if the view hierarchy can potentially be defer rendered,
-     *         false otherwise
+     * Initialize HWUI for being in a system process like system_server
+     * Should not be called in non-system processes
      */
-    public static boolean isAvailable() {
-        return true;
+    public static void initForSystemProcess() {
+        // The system process on low-memory devices do not get to use hardware
+        // accelerated drawing, since this can add too much overhead to the
+        // process.
+        if (!ActivityManager.isHighEndGfx()) {
+            sRendererEnabled = false;
+        } else {
+            enableForegroundTrimming();
+        }
     }
 
     /**
@@ -250,11 +228,7 @@
      * @return A threaded renderer backed by OpenGL.
      */
     public static ThreadedRenderer create(Context context, boolean translucent, String name) {
-        ThreadedRenderer renderer = null;
-        if (isAvailable()) {
-            renderer = new ThreadedRenderer(context, translucent, name);
-        }
-        return renderer;
+        return new ThreadedRenderer(context, translucent, name);
     }
 
     private static final String[] VISUALIZERS = {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index c430a4d..667f0b9 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6150,6 +6150,7 @@
      * was set.
      */
     @NonNull
+    @SuppressWarnings("AndroidFrameworkEfficientCollections")
     public Map<Integer, Integer> getAttributeSourceResourceMap() {
         HashMap<Integer, Integer> map = new HashMap<>();
         if (!sDebugViewAttributes || mAttributeSourceResId == null) {
@@ -14861,15 +14862,7 @@
      */
     public void getWindowVisibleDisplayFrame(Rect outRect) {
         if (mAttachInfo != null) {
-            mAttachInfo.mViewRootImpl.getDisplayFrame(outRect);
-            // XXX This is really broken, and probably all needs to be done
-            // in the window manager, and we need to know more about whether
-            // we want the area behind or in front of the IME.
-            final Rect insets = mAttachInfo.mVisibleInsets;
-            outRect.left += insets.left;
-            outRect.top += insets.top;
-            outRect.right -= insets.right;
-            outRect.bottom -= insets.bottom;
+            mAttachInfo.mViewRootImpl.getWindowVisibleDisplayFrame(outRect);
             return;
         }
         // The view is not attached to a display so we don't have a context.
@@ -15165,6 +15158,42 @@
     }
 
     /**
+     * Called by the {@link android.view.inputmethod.InputMethodManager} to notify the application
+     * that the system has successfully initialized an {@link InputConnection} and it is ready for
+     * use.
+     *
+     * <p>The default implementation does nothing, since a view doesn't support input methods by
+     * default (see {@link #onCreateInputConnection}).
+     *
+     * @param inputConnection The {@link InputConnection} from {@link #onCreateInputConnection},
+     * after it's been fully initialized by the system.
+     * @param editorInfo The {@link EditorInfo} that was used to create the {@link InputConnection}.
+     * @param handler The dedicated {@link Handler} on which IPC method calls from input methods
+     * will be dispatched. This is the handler returned by {@link InputConnection#getHandler()}. If
+     * that method returns null, this parameter will be null also.
+     *
+     * @hide
+     */
+    public void onInputConnectionOpenedInternal(@NonNull InputConnection inputConnection,
+            @NonNull EditorInfo editorInfo, @Nullable Handler handler) {}
+
+    /**
+     * Called by the {@link android.view.inputmethod.InputMethodManager} to notify the application
+     * that the {@link InputConnection} has been closed.
+     *
+     * <p>The default implementation does nothing, since a view doesn't support input methods by
+     * default (see {@link #onCreateInputConnection}).
+     *
+     * <p><strong>Note:</strong> This callback is not invoked if the view is already detached when
+     * the {@link InputConnection} is closed or the connection is not valid and managed by
+     * {@link com.android.server.inputmethod.InputMethodManagerService}.
+     * TODO(b/170645312): Before un-hiding this API, handle the detached view scenario.
+     *
+     * @hide
+     */
+    public void onInputConnectionClosedInternal() {}
+
+    /**
      * Called by the {@link android.view.inputmethod.InputMethodManager}
      * when a view who is not the current
      * input connection target is trying to make a call on the manager.  The
@@ -23537,8 +23566,7 @@
         if ((privateFlags & PFLAG_SELECTED) != 0) viewStateIndex |= StateSet.VIEW_STATE_SELECTED;
         if (hasWindowFocus()) viewStateIndex |= StateSet.VIEW_STATE_WINDOW_FOCUSED;
         if ((privateFlags & PFLAG_ACTIVATED) != 0) viewStateIndex |= StateSet.VIEW_STATE_ACTIVATED;
-        if (mAttachInfo != null && mAttachInfo.mHardwareAccelerationRequested &&
-                ThreadedRenderer.isAvailable()) {
+        if (mAttachInfo != null && mAttachInfo.mHardwareAccelerationRequested) {
             // This is set if HW acceleration is requested, even if the current
             // process doesn't allow it.  This is just to allow app preview
             // windows to better match their app.
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index fd7c2d8..eb6c495 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -50,6 +50,7 @@
 import android.os.Parcelable;
 import android.os.SystemClock;
 import android.util.AttributeSet;
+import android.util.IntArray;
 import android.util.Log;
 import android.util.Pools;
 import android.util.Pools.SynchronizedPool;
@@ -611,7 +612,7 @@
     private int mNestedScrollAxes;
 
     // Used to manage the list of transient views, added by addTransientView()
-    private List<Integer> mTransientIndices = null;
+    private IntArray mTransientIndices = null;
     private List<View> mTransientViews = null;
 
     /**
@@ -4853,7 +4854,7 @@
         }
 
         if (mTransientIndices == null) {
-            mTransientIndices = new ArrayList<Integer>();
+            mTransientIndices = new IntArray();
             mTransientViews = new ArrayList<View>();
         }
         final int oldSize = mTransientIndices.size();
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e00ff7e..9bc0770 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -105,6 +105,7 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.FrameInfo;
+import android.graphics.HardwareRenderer;
 import android.graphics.HardwareRenderer.FrameDrawingCallback;
 import android.graphics.Insets;
 import android.graphics.Matrix;
@@ -519,7 +520,6 @@
     @UnsupportedAppUsage
     public final Surface mSurface = new Surface();
     private final SurfaceControl mSurfaceControl = new SurfaceControl();
-    private SurfaceControl mBlastSurfaceControl = new SurfaceControl();
 
     private BLASTBufferQueue mBlastBufferQueue;
 
@@ -562,6 +562,9 @@
             = new ViewTreeObserver.InternalInsetsInfo();
 
     private WindowInsets mLastWindowInsets;
+    private final Rect mSystemInsetsCache = new Rect();
+    private final Rect mVisibleInsetsCache = new Rect();
+    private final Rect mStableInsetsCache = new Rect();
 
     // Insets types hidden by legacy window flags or system UI flags.
     private @InsetsType int mTypesHiddenByFlags = 0;
@@ -649,7 +652,7 @@
     private final InsetsController mInsetsController;
     private final ImeFocusController mImeFocusController;
 
-    private ScrollCaptureClient mScrollCaptureClient;
+    private ScrollCaptureConnection mScrollCaptureConnection;
 
     /**
      * @return {@link ImeFocusController} for this instance.
@@ -659,10 +662,10 @@
         return mImeFocusController;
     }
 
-    /** @return The current {@link ScrollCaptureClient} for this instance, if any is active. */
+    /** @return The current {@link ScrollCaptureConnection} for this instance, if any is active. */
     @Nullable
-    public ScrollCaptureClient getScrollCaptureClient() {
-        return mScrollCaptureClient;
+    public ScrollCaptureConnection getScrollCaptureConnection() {
+        return mScrollCaptureConnection;
     }
 
     private final GestureExclusionTracker mGestureExclusionTracker = new GestureExclusionTracker();
@@ -702,6 +705,11 @@
 
     private HashSet<ScrollCaptureCallback> mRootScrollCaptureCallbacks;
 
+    /**
+     * Increment this value when the surface has been replaced.
+     */
+    private int mSurfaceSequenceId = 0;
+
     private String mTag = TAG;
 
     public ViewRootImpl(Context context, Display display) {
@@ -1016,11 +1024,16 @@
                     mAttachInfo.mRecomputeGlobalAttributes = true;
                     collectViewAttributes();
                     adjustLayoutParamsForCompatibility(mWindowAttributes);
+                    controlInsetsForCompatibility(mWindowAttributes);
                     res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
-                            getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrames.frame,
-                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
+                            getHostVisibility(), mDisplay.getDisplayId(), userId,
+                            mInsetsController.getRequestedVisibility(), mTmpFrames.frame,
                             mAttachInfo.mDisplayCutout, inputChannel,
                             mTempInsets, mTempControls);
+                    if (mTranslator != null) {
+                        mTranslator.translateRectInScreenToAppWindow(mTmpFrames.frame);
+                        mTranslator.translateInsetsStateInScreenToAppWindow(mTempInsets);
+                    }
                     setFrame(mTmpFrames.frame);
                 } catch (RemoteException e) {
                     mAdded = false;
@@ -1037,9 +1050,6 @@
                     }
                 }
 
-                if (mTranslator != null) {
-                    mTranslator.translateRectInScreenToAppWindow(mAttachInfo.mContentInsets);
-                }
                 mPendingDisplayCutout.set(mAttachInfo.mDisplayCutout);
                 mAttachInfo.mAlwaysConsumeSystemBars =
                         (res & WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS) != 0;
@@ -1286,10 +1296,6 @@
                 (attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0;
 
         if (hardwareAccelerated) {
-            if (!ThreadedRenderer.isAvailable()) {
-                return;
-            }
-
             // Persistent processes (including the system) should not do
             // accelerated rendering on low-end devices.  In that case,
             // sRendererDisabled will be set.  In addition, the system process
@@ -1309,8 +1315,7 @@
                 // shows for launching applications, so they will look more like
                 // the app being launched.
                 mAttachInfo.mHardwareAccelerationRequested = true;
-            } else if (!ThreadedRenderer.sRendererDisabled
-                    || (ThreadedRenderer.sSystemRendererDisabled && forceHwAccelerated)) {
+            } else if (ThreadedRenderer.sRendererEnabled || forceHwAccelerated) {
                 if (mAttachInfo.mThreadedRenderer != null) {
                     mAttachInfo.mThreadedRenderer.destroy();
                 }
@@ -1808,7 +1813,7 @@
             mBoundsLayer = new SurfaceControl.Builder(mSurfaceSession)
                     .setContainerLayer()
                     .setName("Bounds for - " + getTitle().toString())
-                    .setParent(getRenderSurfaceControl())
+                    .setParent(getSurfaceControl())
                     .setCallsite("ViewRootImpl.getBoundsLayer")
                     .build();
             setBoundsLayerCrop(mTransaction);
@@ -1818,22 +1823,19 @@
     }
 
     Surface getOrCreateBLASTSurface(int width, int height) {
-        if (mSurfaceControl == null
-                || !mSurfaceControl.isValid()
-                || mBlastSurfaceControl == null
-                || !mBlastSurfaceControl.isValid()) {
+        if (!mSurfaceControl.isValid()) {
             return null;
         }
 
         Surface ret = null;
         if (mBlastBufferQueue == null) {
             mBlastBufferQueue = new BLASTBufferQueue(mTag,
-                mBlastSurfaceControl, width, height, mEnableTripleBuffering);
+                    mSurfaceControl, width, height, mEnableTripleBuffering);
             // We only return the Surface the first time, as otherwise
             // it hasn't changed and there is no need to update.
             ret = mBlastBufferQueue.createSurface();
         } else {
-            mBlastBufferQueue.update(mBlastSurfaceControl, width, height);
+            mBlastBufferQueue.update(mSurfaceControl, width, height);
         }
 
         return ret;
@@ -1855,7 +1857,7 @@
     private boolean updateBoundsLayer(SurfaceControl.Transaction t) {
         if (mBoundsLayer != null) {
             setBoundsLayerCrop(t);
-            t.deferTransactionUntil(mBoundsLayer, getRenderSurfaceControl(),
+            t.deferTransactionUntil(mBoundsLayer, getSurfaceControl(),
                 mSurface.getNextFrameNumber());
             return true;
         }
@@ -1864,7 +1866,7 @@
 
     private void prepareSurfaces(boolean sizeChanged) {
         final SurfaceControl.Transaction t = mTransaction;
-        final SurfaceControl sc = getRenderSurfaceControl();
+        final SurfaceControl sc = getSurfaceControl();
         if (!sc.isValid()) return;
 
         boolean applyTransaction = updateBoundsLayer(t);
@@ -1885,7 +1887,6 @@
         mSurface.release();
         mSurfaceControl.release();
 
-        mBlastSurfaceControl.release();
         // We should probably add an explicit dispose.
         mBlastBufferQueue = null;
     }
@@ -2315,12 +2316,10 @@
                     (mWindowAttributes.systemUiVisibility
                             | mWindowAttributes.subtreeSystemUiVisibility));
 
-            Rect visibleInsets = mInsetsController.calculateVisibleInsets(
-                    mWindowAttributes.softInputMode);
-
-            mAttachInfo.mVisibleInsets.set(visibleInsets);
-            mAttachInfo.mContentInsets.set(mLastWindowInsets.getSystemWindowInsets().toRect());
-            mAttachInfo.mStableInsets.set(mLastWindowInsets.getStableInsets().toRect());
+            mSystemInsetsCache.set(mLastWindowInsets.getSystemWindowInsets().toRect());
+            mStableInsetsCache.set(mLastWindowInsets.getStableInsets().toRect());
+            mVisibleInsetsCache.set(mInsetsController.calculateVisibleInsets(
+                    mWindowAttributes.softInputMode));
         }
         return mLastWindowInsets;
     }
@@ -2613,7 +2612,7 @@
         boolean surfaceSizeChanged = false;
         boolean surfaceCreated = false;
         boolean surfaceDestroyed = false;
-        /* True if surface generation id changes. */
+        // True if surface generation id changes or relayout result is RELAYOUT_RES_SURFACE_CHANGED.
         boolean surfaceReplaced = false;
 
         final boolean windowAttributesChanged = mWindowAttributesChanged;
@@ -2708,6 +2707,7 @@
                 updateColorModeIfNeeded(lp.getColorMode());
                 surfaceCreated = !hadSurface && mSurface.isValid();
                 surfaceDestroyed = hadSurface && !mSurface.isValid();
+
                 // When using Blast, the surface generation id may not change when there's a new
                 // SurfaceControl. In that case, we also check relayout flag
                 // RELAYOUT_RES_SURFACE_CHANGED since it should indicate that WMS created a new
@@ -2716,6 +2716,9 @@
                         || (relayoutResult & RELAYOUT_RES_SURFACE_CHANGED)
                         == RELAYOUT_RES_SURFACE_CHANGED)
                         && mSurface.isValid();
+                if (surfaceReplaced) {
+                    mSurfaceSequenceId++;
+                }
 
                 if (cutoutChanged) {
                     mAttachInfo.mDisplayCutout.set(mPendingDisplayCutout);
@@ -2824,8 +2827,7 @@
                                         && mWinFrame.height() == mPendingBackDropFrame.height();
                         // TODO: Need cutout?
                         startDragResizing(mPendingBackDropFrame, !backdropSizeMatchesFrame,
-                                mLastWindowInsets.getSystemWindowInsets().toRect(),
-                                mLastWindowInsets.getStableInsets().toRect(), mResizeMode);
+                                mSystemInsetsCache, mStableInsetsCache, mResizeMode);
                     } else {
                         // We shouldn't come here, but if we come we should end the resize.
                         endDragResizing();
@@ -3232,9 +3234,6 @@
         final boolean windowMoved = mAttachInfo.mWindowLeft != frame.left
                 || mAttachInfo.mWindowTop != frame.top;
         if (windowMoved) {
-            if (mTranslator != null) {
-                mTranslator.translateRectInScreenToAppWinFrame(frame);
-            }
             mAttachInfo.mWindowLeft = frame.left;
             mAttachInfo.mWindowTop = frame.top;
         }
@@ -3815,6 +3814,89 @@
         }
     }
 
+    /**
+     * The callback will run on the render thread.
+     */
+    private HardwareRenderer.FrameCompleteCallback createFrameCompleteCallback(Handler handler,
+            boolean reportNextDraw, ArrayList<Runnable> commitCallbacks) {
+        return frameNr -> {
+            // Use a new transaction here since mRtBLASTSyncTransaction can only be accessed by
+            // the render thread and mSurfaceChangedTransaction can only be accessed by the UI
+            // thread. The temporary transaction is used so mRtBLASTSyncTransaction can be merged
+            // with mSurfaceChangedTransaction without synchronization issues.
+            final Transaction t = new Transaction();
+            finishBLASTSyncOnRT(!mSendNextFrameToWm, t);
+            handler.postAtFrontOfQueue(() -> {
+                mSurfaceChangedTransaction.merge(t);
+                if (reportNextDraw) {
+                    // TODO: Use the frame number
+                    pendingDrawFinished();
+                }
+                if (commitCallbacks != null) {
+                    for (int i = 0; i < commitCallbacks.size(); i++) {
+                        commitCallbacks.get(i).run();
+                    }
+                }
+            });
+        };
+    }
+
+    private boolean addFrameCompleteCallbackIfNeeded() {
+        if (mAttachInfo.mThreadedRenderer == null || !mAttachInfo.mThreadedRenderer.isEnabled()) {
+            return false;
+        }
+
+        ArrayList<Runnable> commitCallbacks = mAttachInfo.mTreeObserver
+                .captureFrameCommitCallbacks();
+        final boolean needFrameCompleteCallback =
+                mNextDrawUseBLASTSyncTransaction || mReportNextDraw
+                        || (commitCallbacks != null && commitCallbacks.size() > 0);
+        if (needFrameCompleteCallback) {
+            mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(
+                    createFrameCompleteCallback(mAttachInfo.mHandler, mReportNextDraw,
+                            commitCallbacks));
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * The callback will run on a worker thread pool from the render thread.
+     */
+    private HardwareRenderer.FrameDrawingCallback createFrameDrawingCallback() {
+        return frame -> {
+            mRtNextFrameReportedConsumeWithBlast = true;
+            if (mBlastBufferQueue != null) {
+                // We don't need to synchronize mRtBLASTSyncTransaction here since it's not
+                // being modified and only sent to BlastBufferQueue.
+                mBlastBufferQueue.setNextTransaction(mRtBLASTSyncTransaction);
+            }
+        };
+    }
+
+    private void addFrameCallbackIfNeeded() {
+        // Frame callbacks will always occur after submitting draw requests and before
+        // the draw actually occurs. This will ensure that we set the next transaction
+        // for the frame that's about to get drawn and not on a previous frame that.
+        //
+        // This is thread safe since mRtNextFrameReportConsumeWithBlast will only be
+        // modified in onFrameDraw and then again in onFrameComplete. This is to ensure the
+        // next frame completed should be reported with the blast sync transaction.
+        if (mNextDrawUseBLASTSyncTransaction) {
+            registerRtFrameCallback(createFrameDrawingCallback());
+            mNextDrawUseBLASTSyncTransaction = false;
+        } else if (mReportNextDraw) {
+            registerRtFrameCallback(frame -> {
+                if (mBlastBufferQueue != null) {
+                    // If we need to report next draw, wait for adapter to flush its shadow queue
+                    // by processing previously queued buffers so that we can submit the
+                    // transaction a timely manner.
+                    mBlastBufferQueue.flushShadowQueue();
+                }
+            });
+        }
+    }
+
     private void performDraw() {
         if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
             return;
@@ -3828,58 +3910,14 @@
         mIsDrawing = true;
         Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
 
-        boolean usingAsyncReport = false;
-        boolean reportNextDraw = mReportNextDraw; // Capture the original value
-        if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) {
-            ArrayList<Runnable> commitCallbacks = mAttachInfo.mTreeObserver
-                    .captureFrameCommitCallbacks();
-            final boolean needFrameCompleteCallback = mNextDrawUseBLASTSyncTransaction ||
-                (commitCallbacks != null && commitCallbacks.size() > 0) ||
-                mReportNextDraw;
-            usingAsyncReport = mReportNextDraw;
-            if (needFrameCompleteCallback) {
-                final Handler handler = mAttachInfo.mHandler;
-                mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) -> {
-                        finishBLASTSync(!mSendNextFrameToWm);
-                        handler.postAtFrontOfQueue(() -> {
-                            if (reportNextDraw) {
-                                // TODO: Use the frame number
-                                pendingDrawFinished();
-                            }
-                            if (commitCallbacks != null) {
-                                for (int i = 0; i < commitCallbacks.size(); i++) {
-                                    commitCallbacks.get(i).run();
-                                }
-                            }
-                        });
-                });
-            }
-        }
+        boolean usingAsyncReport = addFrameCompleteCallbackIfNeeded();
+        addFrameCallbackIfNeeded();
 
         try {
-            if (mNextDrawUseBLASTSyncTransaction) {
-                // Frame callbacks will always occur after submitting draw requests and before
-                // the draw actually occurs. This will ensure that we set the next transaction
-                // for the frame that's about to get drawn and not on a previous frame that.
-                //
-                // This is thread safe since mRtNextFrameReportConsumeWithBlast will only be
-                // modified in onFrameDraw and then again in onFrameComplete. This is to ensure the
-                // next frame completed should be reported with the blast sync transaction.
-                registerRtFrameCallback(frame -> {
-                    mRtNextFrameReportedConsumeWithBlast = true;
-                    if (mBlastBufferQueue != null) {
-                        // We don't need to synchronize mRtBLASTSyncTransaction here since it's not
-                        // being modified and only sent to BlastBufferQueue.
-                        mBlastBufferQueue.setNextTransaction(mRtBLASTSyncTransaction);
-                    }
-                });
-                mNextDrawUseBLASTSyncTransaction = false;
-            }
             boolean canUseAsync = draw(fullRedrawNeeded);
             if (usingAsyncReport && !canUseAsync) {
                 mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(null);
                 usingAsyncReport = false;
-                finishBLASTSync(true /* apply */);
             }
         } finally {
             mIsDrawing = false;
@@ -4409,8 +4447,8 @@
     }
 
     boolean scrollToRectOrFocus(Rect rectangle, boolean immediate) {
-        final Rect ci = getWindowInsets(false).getSystemWindowInsetsAsRect();
-        final Rect vi = mAttachInfo.mVisibleInsets;
+        final Rect ci = mSystemInsetsCache;
+        final Rect vi = mVisibleInsetsCache;
         int scrollY = 0;
         boolean handled = false;
 
@@ -5151,7 +5189,7 @@
                     updateLocationInParentDisplay(msg.arg1, msg.arg2);
                 } break;
                 case MSG_REQUEST_SCROLL_CAPTURE:
-                    handleScrollCaptureRequest((IScrollCaptureController) msg.obj);
+                    handleScrollCaptureRequest((IScrollCaptureCallbacks) msg.obj);
                     break;
             }
         }
@@ -7447,7 +7485,7 @@
                 (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
                 insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
                 mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
-                mTempControls, mSurfaceSize, mBlastSurfaceControl);
+                mTempControls, mSurfaceSize);
         mPendingDisplayCutout.set(mTmpFrames.displayCutout);
         mPendingBackDropFrame.set(mTmpFrames.backdropFrame);
         if (mSurfaceControl.isValid()) {
@@ -7475,7 +7513,8 @@
         }
 
         if (mTranslator != null) {
-            mTranslator.translateRectInScreenToAppWinFrame(mTmpFrames.frame);
+            mTranslator.translateRectInScreenToAppWindow(mTmpFrames.frame);
+            mTranslator.translateInsetsStateInScreenToAppWindow(mTempInsets);
         }
         setFrame(mTmpFrames.frame);
         mInsetsController.onStateChanged(mTempInsets);
@@ -7497,6 +7536,22 @@
     }
 
     /**
+     * Gets the current display size in which the window is being laid out, accounting for screen
+     * decorations around it.
+     */
+    void getWindowVisibleDisplayFrame(Rect outFrame) {
+        outFrame.set(mTmpFrames.displayFrame);
+        // XXX This is really broken, and probably all needs to be done
+        // in the window manager, and we need to know more about whether
+        // we want the area behind or in front of the IME.
+        final Rect insets = mVisibleInsetsCache;
+        outFrame.left += insets.left;
+        outFrame.top += insets.top;
+        outFrame.right -= insets.right;
+        outFrame.bottom -= insets.bottom;
+    }
+
+    /**
      * {@inheritDoc}
      */
     @Override
@@ -7815,13 +7870,8 @@
             MergedConfiguration mergedConfiguration, boolean forceLayout,
             boolean alwaysConsumeSystemBars, int displayId) {
         final Rect frame = frames.frame;
-        final Rect contentInsets = frames.contentInsets;
-        final Rect visibleInsets = frames.visibleInsets;
-        final Rect stableInsets = frames.stableInsets;
         final Rect backDropFrame = frames.backdropFrame;
         if (DEBUG_LAYOUT) Log.v(mTag, "Resizing " + this + ": frame=" + frame.toShortString()
-                + " contentInsets=" + contentInsets.toShortString()
-                + " visibleInsets=" + visibleInsets.toShortString()
                 + " reportDraw=" + reportDraw
                 + " backDropFrame=" + backDropFrame);
 
@@ -7832,7 +7882,7 @@
             synchronized (mWindowCallbacks) {
                 for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) {
                     mWindowCallbacks.get(i).onWindowSizeIsChanging(backDropFrame, fullscreen,
-                            visibleInsets, stableInsets);
+                            mVisibleInsetsCache, mStableInsetsCache);
                 }
             }
         }
@@ -7840,8 +7890,6 @@
         Message msg = mHandler.obtainMessage(reportDraw ? MSG_RESIZED_REPORT : MSG_RESIZED);
         if (mTranslator != null) {
             mTranslator.translateRectInScreenToAppWindow(frame);
-            mTranslator.translateRectInScreenToAppWindow(contentInsets);
-            mTranslator.translateRectInScreenToAppWindow(visibleInsets);
         }
         SomeArgs args = SomeArgs.obtain();
         final boolean sameProcessCall = (Binder.getCallingPid() == android.os.Process.myPid());
@@ -7859,6 +7907,9 @@
         if (Binder.getCallingPid() == android.os.Process.myPid()) {
             insetsState = new InsetsState(insetsState, true /* copySource */);
         }
+        if (mTranslator != null) {
+            mTranslator.translateInsetsStateInScreenToAppWindow(insetsState);
+        }
         mHandler.obtainMessage(MSG_INSETS_CHANGED, insetsState).sendToTarget();
     }
 
@@ -7872,6 +7923,9 @@
                 }
             }
         }
+        if (mTranslator != null) {
+            mTranslator.translateInsetsStateInScreenToAppWindow(insetsState);
+        }
         SomeArgs args = SomeArgs.obtain();
         args.arg1 = insetsState;
         args.arg2 = activeControls;
@@ -8938,10 +8992,10 @@
     /**
      * Dispatches a scroll capture request to the view hierarchy on the ui thread.
      *
-     * @param controller the controller to receive replies
+     * @param callbacks for replies
      */
-    public void dispatchScrollCaptureRequest(@NonNull IScrollCaptureController controller) {
-        mHandler.obtainMessage(MSG_REQUEST_SCROLL_CAPTURE, controller).sendToTarget();
+    public void dispatchScrollCaptureRequest(@NonNull IScrollCaptureCallbacks callbacks) {
+        mHandler.obtainMessage(MSG_REQUEST_SCROLL_CAPTURE, callbacks).sendToTarget();
     }
 
     /**
@@ -8966,14 +9020,14 @@
      * Handles an inbound request for scroll capture from the system. If a client is not already
      * active, a search will be dispatched through the view tree to locate scrolling content.
      * <p>
-     * Either {@link IScrollCaptureController#onClientConnected(IScrollCaptureClient, Rect,
-     * Point)} or {@link IScrollCaptureController#onClientUnavailable()} will be returned
+     * Either {@link IScrollCaptureCallbacks#onClientConnected(IScrollCaptureConnection, Rect,
+     * Point)} or {@link IScrollCaptureCallbacks#onUnavailable()} will be returned
      * depending on the results of the search.
      *
-     * @param controller the interface to the system controller
+     * @param callbacks to receive responses
      * @see ScrollCaptureTargetResolver
      */
-    private void handleScrollCaptureRequest(@NonNull IScrollCaptureController controller) {
+    private void handleScrollCaptureRequest(@NonNull IScrollCaptureCallbacks callbacks) {
         LinkedList<ScrollCaptureTarget> targetList = new LinkedList<>();
 
         // Window (root) level callbacks
@@ -8988,7 +9042,7 @@
 
         // No-op path. Scroll capture not offered for this window.
         if (targetList.isEmpty()) {
-            dispatchScrollCaptureSearchResult(controller, null);
+            dispatchScrollCaptureSearchResult(callbacks, null);
             return;
         }
 
@@ -8996,12 +9050,12 @@
         // Continues with the consumer once all responses are consumed, or the timeout expires.
         ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(targetList);
         resolver.start(mHandler, 1000,
-                (selected) -> dispatchScrollCaptureSearchResult(controller, selected));
+                (selected) -> dispatchScrollCaptureSearchResult(callbacks, selected));
     }
 
     /** Called by {@link #handleScrollCaptureRequest} when a result is returned */
     private void dispatchScrollCaptureSearchResult(
-            @NonNull IScrollCaptureController controller,
+            @NonNull IScrollCaptureCallbacks callbacks,
             @Nullable ScrollCaptureTarget selectedTarget) {
 
         // If timeout or no eligible targets found.
@@ -9010,31 +9064,31 @@
                 if (DEBUG_SCROLL_CAPTURE) {
                     Log.d(TAG, "scrollCaptureSearch returned no targets available.");
                 }
-                controller.onClientUnavailable();
+                callbacks.onUnavailable();
             } catch (RemoteException e) {
                 if (DEBUG_SCROLL_CAPTURE) {
-                    Log.w(TAG, "Failed to notify controller of scroll capture search result.", e);
+                    Log.w(TAG, "Failed to send scroll capture search result.", e);
                 }
             }
             return;
         }
 
         // Create a client instance and return it to the caller
-        mScrollCaptureClient = new ScrollCaptureClient(selectedTarget, controller);
+        mScrollCaptureConnection = new ScrollCaptureConnection(selectedTarget, callbacks);
         try {
             if (DEBUG_SCROLL_CAPTURE) {
-                Log.d(TAG, "scrollCaptureSearch returning client: " + getScrollCaptureClient());
+                Log.d(TAG, "scrollCaptureSearch returning client: " + getScrollCaptureConnection());
             }
-            controller.onClientConnected(
-                    mScrollCaptureClient,
+            callbacks.onConnected(
+                    mScrollCaptureConnection,
                     selectedTarget.getScrollBounds(),
                     selectedTarget.getPositionInWindow());
         } catch (RemoteException e) {
             if (DEBUG_SCROLL_CAPTURE) {
-                Log.w(TAG, "Failed to notify controller of scroll capture search result.", e);
+                Log.w(TAG, "Failed to send scroll capture search result.", e);
             }
-            mScrollCaptureClient.disconnect();
-            mScrollCaptureClient = null;
+            mScrollCaptureConnection.disconnect();
+            mScrollCaptureConnection = null;
         }
     }
 
@@ -9332,10 +9386,10 @@
         }
 
         @Override
-        public void requestScrollCapture(IScrollCaptureController controller) {
+        public void requestScrollCapture(IScrollCaptureCallbacks callbacks) {
             final ViewRootImpl viewAncestor = mViewAncestor.get();
             if (viewAncestor != null) {
-                viewAncestor.dispatchScrollCaptureRequest(controller);
+                viewAncestor.dispatchScrollCaptureRequest(callbacks);
             }
         }
     }
@@ -9844,7 +9898,12 @@
         mNextDrawUseBLASTSyncTransaction = true;
     }
 
-    private void finishBLASTSync(boolean apply) {
+    /**
+     * This should only be called from the render thread.
+     */
+    private void finishBLASTSyncOnRT(boolean apply, Transaction t) {
+        // This is safe to modify on the render thread since the only other place it's modified
+        // is on the UI thread when the render thread is paused.
         mSendNextFrameToWm = false;
         if (mRtNextFrameReportedConsumeWithBlast) {
             mRtNextFrameReportedConsumeWithBlast = false;
@@ -9855,7 +9914,7 @@
             if (apply) {
                 mRtBLASTSyncTransaction.apply();
             } else {
-                mSurfaceChangedTransaction.merge(mRtBLASTSyncTransaction);
+                t.merge(mRtBLASTSyncTransaction);
             }
         }
     }
@@ -9868,17 +9927,6 @@
         return mRtBLASTSyncTransaction;
     }
 
-    /**
-     * @hide
-     */
-    public SurfaceControl getRenderSurfaceControl() {
-        if (useBLAST()) {
-            return mBlastSurfaceControl;
-        } else {
-            return mSurfaceControl;
-        }
-    }
-
     @Override
     public void onDescendantUnbufferedRequested() {
         mUnbufferedInputSource = mView.mUnbufferedInputSource;
@@ -9895,4 +9943,8 @@
     boolean useBLAST() {
         return mUseBLASTAdapter && !mForceDisableBLAST;
     }
+
+    int getSurfaceSequenceId() {
+        return mSurfaceSequenceId;
+    }
 }
diff --git a/core/java/android/view/ViewRootInsetsControllerHost.java b/core/java/android/view/ViewRootInsetsControllerHost.java
index 90a80ce..514fb29 100644
--- a/core/java/android/view/ViewRootInsetsControllerHost.java
+++ b/core/java/android/view/ViewRootInsetsControllerHost.java
@@ -21,6 +21,7 @@
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_BEHAVIOR_CONTROLLED;
 
 import android.annotation.NonNull;
+import android.content.res.CompatibilityInfo;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -144,7 +145,9 @@
     @Override
     public void onInsetsModified(InsetsState insetsState) {
         try {
-            mViewRoot.mWindowSession.insetsModified(mViewRoot.mWindow, insetsState);
+            if (mViewRoot.mAdded) {
+                mViewRoot.mWindowSession.insetsModified(mViewRoot.mWindow, insetsState);
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to call insetsModified", e);
         }
@@ -249,4 +252,12 @@
         }
         return view.getWindowToken();
     }
+
+    @Override
+    public CompatibilityInfo.Translator getTranslator() {
+        if (mViewRoot != null) {
+            return mViewRoot.mTranslator;
+        }
+        return null;
+    }
 }
diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java
index d7b0afc..d9b55e4 100644
--- a/core/java/android/view/ViewTreeObserver.java
+++ b/core/java/android/view/ViewTreeObserver.java
@@ -307,7 +307,7 @@
         }
 
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (this == o) return true;
             if (o == null || getClass() != o.getClass()) return false;
 
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 1dbf37a..5331a1b 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -2560,19 +2560,20 @@
     /**
      * System request to begin scroll capture.
      *
-     * @param controller the controller to receive responses
+     * @param callbacks to receive responses
      * @hide
      */
-    public void requestScrollCapture(IScrollCaptureController controller) {
+    public void requestScrollCapture(IScrollCaptureCallbacks callbacks) {
     }
 
     /**
-     * Registers a {@link ScrollCaptureCallback} with the root of this window.
+     * Used to provide scroll capture support for an arbitrary window. This registeres the given
+     * callback with the root view of the window.
      *
      * @param callback the callback to add
      * @hide
      */
-    public void addScrollCaptureCallback(@NonNull ScrollCaptureCallback callback) {
+    public void registerScrollCaptureCallback(@NonNull ScrollCaptureCallback callback) {
     }
 
     /**
@@ -2581,7 +2582,7 @@
      * @param callback the callback to remove
      * @hide
      */
-    public void removeScrollCaptureCallback(@NonNull ScrollCaptureCallback callback) {
+    public void unregisterScrollCaptureCallback(@NonNull ScrollCaptureCallback callback) {
     }
 
     /** @hide */
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 5e94758..8b0cf3b 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -924,6 +924,15 @@
         Preconditions.checkArgumentNonnegative(right);
         Preconditions.checkArgumentNonnegative(bottom);
 
+        return insetUnchecked(left, top, right, bottom);
+    }
+
+    /**
+     * @see #inset(int, int, int, int)
+     * @hide
+     */
+    @NonNull
+    public WindowInsets insetUnchecked(int left, int top, int right, int bottom) {
         return new WindowInsets(
                 mSystemWindowInsetsConsumed
                         ? null
@@ -942,7 +951,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) return true;
         if (o == null || !(o instanceof WindowInsets)) return false;
         WindowInsets that = (WindowInsets) o;
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index e96e98b..9336872 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -224,12 +224,6 @@
     int TRANSIT_ACTIVITY_RELAUNCH = 18;
 
     /**
-     * A task is being docked from recents.
-     * @hide
-     */
-    int TRANSIT_DOCK_TASK_FROM_RECENTS = 19;
-
-    /**
      * Keyguard is going away.
      * @hide
      */
@@ -278,13 +272,6 @@
     int TRANSIT_TASK_CHANGE_WINDOWING_MODE = 27;
 
     /**
-     * A display which can only contain one task is being shown because the first activity is
-     * started or it's being turned on.
-     * @hide
-     */
-    int TRANSIT_SHOW_SINGLE_TASK_DISPLAY = 28;
-
-    /**
      * @hide
      */
     @IntDef(prefix = { "TRANSIT_" }, value = {
@@ -302,7 +289,6 @@
             TRANSIT_WALLPAPER_INTRA_CLOSE,
             TRANSIT_TASK_OPEN_BEHIND,
             TRANSIT_ACTIVITY_RELAUNCH,
-            TRANSIT_DOCK_TASK_FROM_RECENTS,
             TRANSIT_KEYGUARD_GOING_AWAY,
             TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
             TRANSIT_KEYGUARD_OCCLUDE,
@@ -310,8 +296,7 @@
             TRANSIT_TRANSLUCENT_ACTIVITY_OPEN,
             TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE,
             TRANSIT_CRASHING_ACTIVITY_CLOSE,
-            TRANSIT_TASK_CHANGE_WINDOWING_MODE,
-            TRANSIT_SHOW_SINGLE_TASK_DISPLAY
+            TRANSIT_TASK_CHANGE_WINDOWING_MODE
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface TransitionType {}
@@ -2939,6 +2924,13 @@
         public boolean preferMinimalPostProcessing = false;
 
         /**
+         * Indicates that this window wants to have blurred content behind it.
+         *
+         * @hide
+         */
+        public int backgroundBlurRadius = 0;
+
+        /**
          * The color mode requested by this window. The target display may
          * not be able to honor the request. When the color mode is not set
          * to {@link ActivityInfo#COLOR_MODE_DEFAULT}, it might override the
@@ -3262,6 +3254,7 @@
             out.writeInt(mFitInsetsSides);
             out.writeBoolean(mFitInsetsIgnoringVisibility);
             out.writeBoolean(preferMinimalPostProcessing);
+            out.writeInt(backgroundBlurRadius);
             if (providesInsetsTypes != null) {
                 out.writeInt(providesInsetsTypes.length);
                 out.writeIntArray(providesInsetsTypes);
@@ -3329,6 +3322,7 @@
             mFitInsetsSides = in.readInt();
             mFitInsetsIgnoringVisibility = in.readBoolean();
             preferMinimalPostProcessing = in.readBoolean();
+            backgroundBlurRadius = in.readInt();
             int insetsTypesLength = in.readInt();
             if (insetsTypesLength > 0) {
                 providesInsetsTypes = new int[insetsTypesLength];
@@ -3381,6 +3375,8 @@
         public static final int INSET_FLAGS_CHANGED = 1 << 27;
         /** {@hide} */
         public static final int MINIMAL_POST_PROCESSING_PREFERENCE_CHANGED = 1 << 28;
+        /** {@hide} */
+        public static final int BACKGROUND_BLUR_RADIUS_CHANGED = 1 << 29;
 
         // internal buffer to backup/restore parameters under compatibility mode.
         private int[] mCompatibilityParamsBackup = null;
@@ -3566,6 +3562,11 @@
                 changes |= MINIMAL_POST_PROCESSING_PREFERENCE_CHANGED;
             }
 
+            if (backgroundBlurRadius != o.backgroundBlurRadius) {
+                backgroundBlurRadius = o.backgroundBlurRadius;
+                changes |= BACKGROUND_BLUR_RADIUS_CHANGED;
+            }
+
             // This can't change, it's only set at window creation time.
             hideTimeoutMilliseconds = o.hideTimeoutMilliseconds;
 
@@ -3729,6 +3730,10 @@
                 sb.append(" preferMinimalPostProcessing=");
                 sb.append(preferMinimalPostProcessing);
             }
+            if (backgroundBlurRadius != 0) {
+                sb.append(" backgroundBlurRadius=");
+                sb.append(backgroundBlurRadius);
+            }
             sb.append(System.lineSeparator());
             sb.append(prefix).append("  fl=").append(
                     ViewDebug.flagsToString(LayoutParams.class, "flags", flags));
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 8490f2a..f01cbcc 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -527,7 +527,7 @@
             }
             allViewsRemoved = mRoots.isEmpty();
         }
-        if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) {
+        if (ThreadedRenderer.sTrimForeground) {
             doTrimForeground();
         }
 
@@ -561,29 +561,28 @@
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public void trimMemory(int level) {
-        if (ThreadedRenderer.isAvailable()) {
-            if (shouldDestroyEglContext(level)) {
-                // Destroy all hardware surfaces and resources associated to
-                // known windows
-                synchronized (mLock) {
-                    for (int i = mRoots.size() - 1; i >= 0; --i) {
-                        mRoots.get(i).destroyHardwareResources();
-                    }
+
+        if (shouldDestroyEglContext(level)) {
+            // Destroy all hardware surfaces and resources associated to
+            // known windows
+            synchronized (mLock) {
+                for (int i = mRoots.size() - 1; i >= 0; --i) {
+                    mRoots.get(i).destroyHardwareResources();
                 }
-                // Force a full memory flush
-                level = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
             }
+            // Force a full memory flush
+            level = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
+        }
 
-            ThreadedRenderer.trimMemory(level);
+        ThreadedRenderer.trimMemory(level);
 
-            if (ThreadedRenderer.sTrimForeground) {
-                doTrimForeground();
-            }
+        if (ThreadedRenderer.sTrimForeground) {
+            doTrimForeground();
         }
     }
 
     public static void trimForeground() {
-        if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) {
+        if (ThreadedRenderer.sTrimForeground) {
             WindowManagerGlobal wm = WindowManagerGlobal.getInstance();
             wm.doTrimForeground();
         }
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 59e0226..7dfae00 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -262,14 +262,11 @@
 
     private WindowInsets getWindowInsetsFromServer(WindowManager.LayoutParams attrs, Rect bounds) {
         try {
-            final Rect systemWindowInsets = new Rect();
-            final Rect stableInsets = new Rect();
             final DisplayCutout.ParcelableWrapper displayCutout =
                     new DisplayCutout.ParcelableWrapper();
             final InsetsState insetsState = new InsetsState();
             final boolean alwaysConsumeSystemBars = WindowManagerGlobal.getWindowManagerService()
-                    .getWindowInsets(attrs, mContext.getDisplayId(), systemWindowInsets,
-                    stableInsets, displayCutout, insetsState);
+                    .getWindowInsets(attrs, mContext.getDisplayId(), displayCutout, insetsState);
             final Configuration config = mContext.getResources().getConfiguration();
             final boolean isScreenRound = config.isScreenRound();
             final int windowingMode = config.windowConfiguration.getWindowingMode();
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index dbd8184..5e5d14f 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -130,8 +130,7 @@
      */
     @Override
     public int addToDisplay(IWindow window, WindowManager.LayoutParams attrs,
-            int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
-            Rect outStableInsets,
+            int viewVisibility, int displayId, InsetsState requestedVisibility, Rect outFrame,
             DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
             InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {
         final SurfaceControl.Builder b = new SurfaceControl.Builder(mSurfaceSession)
@@ -166,19 +165,17 @@
      */
     @Override
     public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
-            int viewVisibility, int displayId, int userId, Rect outFrame,
-            Rect outContentInsets, Rect outStableInsets,
-            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
-            InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {
-        return addToDisplay(window, attrs, viewVisibility, displayId,
-                outFrame, outContentInsets, outStableInsets, outDisplayCutout, outInputChannel,
-                outInsetsState, outActiveControls);
+            int viewVisibility, int displayId, int userId, InsetsState requestedVisibility,
+            Rect outFrame, DisplayCutout.ParcelableWrapper outDisplayCutout,
+            InputChannel outInputChannel, InsetsState outInsetsState,
+            InsetsSourceControl[] outActiveControls) {
+        return addToDisplay(window, attrs, viewVisibility, displayId, requestedVisibility,
+                outFrame, outDisplayCutout, outInputChannel, outInsetsState, outActiveControls);
     }
 
     @Override
     public int addToDisplayWithoutInputChannel(android.view.IWindow window,
             android.view.WindowManager.LayoutParams attrs, int viewVisibility, int layerStackId,
-            android.graphics.Rect outContentInsets, android.graphics.Rect outStableInsets,
             android.view.InsetsState insetsState) {
         return 0;
     }
@@ -227,8 +224,7 @@
             int requestedWidth, int requestedHeight, int viewFlags, int flags, long frameNumber,
             ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
             SurfaceControl outSurfaceControl, InsetsState outInsetsState,
-            InsetsSourceControl[] outActiveControls, Point outSurfaceSize,
-            SurfaceControl outBLASTSurfaceControl) {
+            InsetsSourceControl[] outActiveControls, Point outSurfaceSize) {
         final State state;
         synchronized (this) {
             state = mStateForWindow.get(window.asBinder());
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index d80d230..f6d6fde 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -199,7 +199,7 @@
  * <b>Window state changed</b> - represents the event of a change to a section of
  * the user interface that is visually distinct. Should be sent from either the
  * root view of a window or from a view that is marked as a pane
- * {@link android.view.View#setAccessibilityPaneTitle(CharSequence)}. Not that changes
+ * {@link android.view.View#setAccessibilityPaneTitle(CharSequence)}. Note that changes
  * to true windows are represented by {@link #TYPE_WINDOWS_CHANGED}.</br>
  * <em>Type:</em> {@link #TYPE_WINDOW_STATE_CHANGED}</br>
  * <em>Properties:</em></br>
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index 299ae2f0..d803f8b 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -441,8 +441,8 @@
                     prefetchFlags &= ~AccessibilityNodeInfo.FLAG_PREFETCH_MASK;
                 }
                 final int interactionId = mInteractionIdCounter.getAndIncrement();
-                final long identityToken = Binder.clearCallingIdentity();
                 final String[] packageNames;
+                final long identityToken = Binder.clearCallingIdentity();
                 try {
                     packageNames = connection.findAccessibilityNodeInfoByAccessibilityId(
                             accessibilityWindowId, accessibilityNodeId, interactionId, this,
@@ -501,8 +501,8 @@
             IAccessibilityServiceConnection connection = getConnection(connectionId);
             if (connection != null) {
                 final int interactionId = mInteractionIdCounter.getAndIncrement();
-                final long identityToken = Binder.clearCallingIdentity();
                 final String[] packageNames;
+                final long identityToken = Binder.clearCallingIdentity();
                 try {
                     packageNames = connection.findAccessibilityNodeInfosByViewId(
                             accessibilityWindowId, accessibilityNodeId, viewId, interactionId, this,
@@ -555,8 +555,8 @@
             IAccessibilityServiceConnection connection = getConnection(connectionId);
             if (connection != null) {
                 final int interactionId = mInteractionIdCounter.getAndIncrement();
-                final long identityToken = Binder.clearCallingIdentity();
                 final String[] packageNames;
+                final long identityToken = Binder.clearCallingIdentity();
                 try {
                     packageNames = connection.findAccessibilityNodeInfosByText(
                             accessibilityWindowId, accessibilityNodeId, text, interactionId, this,
@@ -608,8 +608,8 @@
             IAccessibilityServiceConnection connection = getConnection(connectionId);
             if (connection != null) {
                 final int interactionId = mInteractionIdCounter.getAndIncrement();
-                final long identityToken = Binder.clearCallingIdentity();
                 final String[] packageNames;
+                final long identityToken = Binder.clearCallingIdentity();
                 try {
                     packageNames = connection.findFocus(accessibilityWindowId,
                             accessibilityNodeId, focusType, interactionId, this,
@@ -657,8 +657,8 @@
             IAccessibilityServiceConnection connection = getConnection(connectionId);
             if (connection != null) {
                 final int interactionId = mInteractionIdCounter.getAndIncrement();
-                final long identityToken = Binder.clearCallingIdentity();
                 final String[] packageNames;
+                final long identityToken = Binder.clearCallingIdentity();
                 try {
                     packageNames = connection.focusSearch(accessibilityWindowId,
                             accessibilityNodeId, direction, interactionId, this,
@@ -705,8 +705,8 @@
             IAccessibilityServiceConnection connection = getConnection(connectionId);
             if (connection != null) {
                 final int interactionId = mInteractionIdCounter.getAndIncrement();
-                final long identityToken = Binder.clearCallingIdentity();
                 final boolean success;
+                final long identityToken = Binder.clearCallingIdentity();
                 try {
                     success = connection.performAccessibilityAction(
                             accessibilityWindowId, accessibilityNodeId, action, arguments,
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 16f35ad..a9e8d54 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -606,7 +606,7 @@
             // it is possible that this manager is in the same process as the service but
             // client using it is called through Binder from another process. Example: MMS
             // app adds a SMS notification and the NotificationManagerService calls this method
-            long identityToken = Binder.clearCallingIdentity();
+            final long identityToken = Binder.clearCallingIdentity();
             try {
                 service.sendAccessibilityEvent(dispatchedEvent, userId);
             } finally {
@@ -1249,7 +1249,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
     public void performAccessibilityShortcut() {
         performAccessibilityShortcut(null);
@@ -1294,7 +1293,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
     public void registerSystemAction(@NonNull RemoteAction action, int actionId) {
         final IAccessibilityManager service;
@@ -1322,7 +1320,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
     public void unregisterSystemAction(int actionId) {
         final IAccessibilityManager service;
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 2d0f05e..303ba9e 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -4369,7 +4369,7 @@
     }
 
     @Override
-    public boolean equals(Object object) {
+    public boolean equals(@Nullable Object object) {
         if (this == object) {
             return true;
         }
@@ -5039,7 +5039,7 @@
         }
 
         @Override
-        public boolean equals(Object other) {
+        public boolean equals(@Nullable Object other) {
             if (other == null) {
                 return false;
             }
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
index 813234f..ccc7a36 100644
--- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
@@ -655,7 +655,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (this == obj) {
             return true;
         }
diff --git a/core/java/android/view/autofill/AutofillId.java b/core/java/android/view/autofill/AutofillId.java
index 32b9cf7..82d52b6 100644
--- a/core/java/android/view/autofill/AutofillId.java
+++ b/core/java/android/view/autofill/AutofillId.java
@@ -185,7 +185,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (this == obj) return true;
         if (obj == null) return false;
         if (getClass() != obj.getClass()) return false;
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 9ba886a..fb66b52 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -2076,7 +2076,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public void setAugmentedAutofillWhitelist(@Nullable Set<String> packages,
             @Nullable Set<ComponentName> activities) {
         if (!hasAutofillFeature()) {
diff --git a/core/java/android/view/autofill/AutofillValue.java b/core/java/android/view/autofill/AutofillValue.java
index 2967041..e92c30f 100644
--- a/core/java/android/view/autofill/AutofillValue.java
+++ b/core/java/android/view/autofill/AutofillValue.java
@@ -65,7 +65,7 @@
      * @throws IllegalStateException if the value is not a text value
      */
     @NonNull public CharSequence getTextValue() {
-        Preconditions.checkState(isText(), "value must be a text value, not type=" + mType);
+        Preconditions.checkState(isText(), "value must be a text value, not type=%d", mType);
         return (CharSequence) mValue;
     }
 
@@ -86,7 +86,7 @@
      * @throws IllegalStateException if the value is not a toggle value
      */
     public boolean getToggleValue() {
-        Preconditions.checkState(isToggle(), "value must be a toggle value, not type=" + mType);
+        Preconditions.checkState(isToggle(), "value must be a toggle value, not type=%d", mType);
         return (Boolean) mValue;
     }
 
@@ -107,7 +107,7 @@
      * @throws IllegalStateException if the value is not a list value
      */
     public int getListValue() {
-        Preconditions.checkState(isList(), "value must be a list value, not type=" + mType);
+        Preconditions.checkState(isList(), "value must be a list value, not type=%d", mType);
         return (Integer) mValue;
     }
 
@@ -128,7 +128,7 @@
      * @throws IllegalStateException if the value is not a date value
      */
     public long getDateValue() {
-        Preconditions.checkState(isDate(), "value must be a date value, not type=" + mType);
+        Preconditions.checkState(isDate(), "value must be a date value, not type=%d", mType);
         return (Long) mValue;
     }
 
@@ -150,7 +150,7 @@
      */
     public @NonNull ClipData getRichContentValue() {
         Preconditions.checkState(isRichContent(),
-                "value must be a rich content value, not type=" + mType);
+                "value must be a rich content value, not type=%d", mType);
         return (ClipData) mValue;
     }
 
@@ -184,7 +184,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (this == obj) return true;
         if (obj == null) return false;
         if (getClass() != obj.getClass()) return false;
diff --git a/core/java/android/view/contentcapture/ContentCaptureCondition.java b/core/java/android/view/contentcapture/ContentCaptureCondition.java
index 54ebf55..1adef94 100644
--- a/core/java/android/view/contentcapture/ContentCaptureCondition.java
+++ b/core/java/android/view/contentcapture/ContentCaptureCondition.java
@@ -17,6 +17,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.LocusId;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -91,7 +92,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (this == obj) return true;
         if (obj == null) return false;
         if (getClass() != obj.getClass()) return false;
diff --git a/core/java/android/view/contentcapture/ContentCaptureContext.java b/core/java/android/view/contentcapture/ContentCaptureContext.java
index b84cb88..9bf3626 100644
--- a/core/java/android/view/contentcapture/ContentCaptureContext.java
+++ b/core/java/android/view/contentcapture/ContentCaptureContext.java
@@ -21,7 +21,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.app.TaskInfo;
 import android.content.ComponentName;
 import android.content.Context;
@@ -58,7 +57,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final int FLAG_DISABLED_BY_APP = 0x1;
 
     /**
@@ -69,7 +67,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final int FLAG_DISABLED_BY_FLAG_SECURE = 0x2;
 
     /**
@@ -79,7 +76,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final int FLAG_RECONNECTED = 0x4;
 
     /** @hide */
@@ -173,7 +169,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public int getTaskId() {
         return mTaskId;
     }
@@ -184,7 +179,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public @Nullable ComponentName getActivityComponent() {
         return mComponentName;
     }
@@ -197,7 +191,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public @Nullable ContentCaptureSessionId getParentSessionId() {
         return mParentSessionId == NO_SESSION_ID ? null
                 : new ContentCaptureSessionId(mParentSessionId);
@@ -215,7 +208,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public int getDisplayId() {
         return mDisplayId;
     }
@@ -229,7 +221,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public @ContextCreationFlags int getFlags() {
         return mFlags;
     }
diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java
index f49b1be..2b12230 100644
--- a/core/java/android/view/contentcapture/ContentCaptureEvent.java
+++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java
@@ -22,7 +22,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.graphics.Insets;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -39,7 +38,6 @@
 
 /** @hide */
 @SystemApi
-@TestApi
 public final class ContentCaptureEvent implements Parcelable {
 
     private static final String TAG = ContentCaptureEvent.class.getSimpleName();
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index 029552d..10f6c61 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -644,7 +644,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public boolean isContentCaptureFeatureEnabled() {
         final SyncResultReceiver resultReceiver = syncRun(
                 (r) -> mService.isContentCaptureFeatureEnabled(r));
diff --git a/core/java/android/view/contentcapture/ContentCaptureSessionId.java b/core/java/android/view/contentcapture/ContentCaptureSessionId.java
index 2d350b2..413a2f3 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSessionId.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSessionId.java
@@ -17,6 +17,7 @@
 package android.view.contentcapture;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -56,7 +57,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (this == obj) return true;
         if (obj == null) return false;
         if (getClass() != obj.getClass()) return false;
diff --git a/core/java/android/view/contentcapture/ViewNode.java b/core/java/android/view/contentcapture/ViewNode.java
index e035c62..e731d4b 100644
--- a/core/java/android/view/contentcapture/ViewNode.java
+++ b/core/java/android/view/contentcapture/ViewNode.java
@@ -42,7 +42,6 @@
 // instead
 /** @hide */
 @SystemApi
-@TestApi
 public final class ViewNode extends AssistStructure.ViewNode {
 
     private static final String TAG = ViewNode.class.getSimpleName();
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index 73636f8..e071113 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -19,6 +19,8 @@
 import static android.view.OnReceiveContentCallback.Payload.SOURCE_INPUT_METHOD;
 
 import android.annotation.CallSuper;
+import android.annotation.IntRange;
+import android.annotation.Nullable;
 import android.content.ClipData;
 import android.content.Context;
 import android.content.res.TypedArray;
@@ -585,6 +587,48 @@
     }
 
     /**
+     * The default implementation returns the given amount of text around the current cursor
+     * position in the buffer.
+     */
+    @Nullable
+    public SurroundingText getSurroundingText(
+            @IntRange(from = 0) int beforeLength, @IntRange(from = 0)  int afterLength, int flags) {
+        final Editable content = getEditable();
+        if (content == null) return null;
+
+        int selStart = Selection.getSelectionStart(content);
+        int selEnd = Selection.getSelectionEnd(content);
+
+        // Guard against the case where the cursor has not been positioned yet.
+        if (selStart < 0 || selEnd < 0) {
+            return null;
+        }
+
+        if (selStart > selEnd) {
+            int tmp = selStart;
+            selStart = selEnd;
+            selEnd = tmp;
+        }
+
+        int contentLength = content.length();
+        int startPos = selStart - beforeLength;
+        int endPos = selEnd + afterLength;
+
+        // Guards the start and end pos within range [0, contentLength].
+        startPos = Math.max(0, startPos);
+        endPos = Math.min(contentLength, endPos);
+
+        CharSequence surroundingText;
+        if ((flags & GET_TEXT_WITH_STYLES) != 0) {
+            surroundingText = content.subSequence(startPos, endPos);
+        } else {
+            surroundingText = TextUtils.substring(content, startPos, endPos);
+        }
+        return new SurroundingText(
+                surroundingText, selStart - startPos, selEnd - startPos, startPos);
+    }
+
+    /**
      * The default implementation turns this into the enter key.
      */
     public boolean performEditorAction(int actionCode) {
diff --git a/core/java/android/view/inputmethod/CursorAnchorInfo.java b/core/java/android/view/inputmethod/CursorAnchorInfo.java
index 21ead4c..c8c1d87 100644
--- a/core/java/android/view/inputmethod/CursorAnchorInfo.java
+++ b/core/java/android/view/inputmethod/CursorAnchorInfo.java
@@ -183,7 +183,7 @@
     }
 
     @Override
-    public boolean equals(Object obj){
+    public boolean equals(@Nullable Object obj){
         if (obj == null) {
             return false;
         }
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index 4337ed5..c7acd29 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -16,14 +16,20 @@
 
 package android.view.inputmethod;
 
+import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.inputmethodservice.InputMethodService;
 import android.os.Bundle;
 import android.os.Handler;
+import android.text.TextUtils;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * The InputConnection interface is the communication channel from an
  * {@link InputMethod} back to the application that is receiving its
@@ -122,14 +128,20 @@
  * of each other, and the IME may use them however they see fit.</p>
  */
 public interface InputConnection {
+    /** @hide */
+    @IntDef(flag = true, prefix = { "GET_TEXT_" }, value = {
+            GET_TEXT_WITH_STYLES,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface GetTextType {}
+
     /**
-     * Flag for use with {@link #getTextAfterCursor} and
-     * {@link #getTextBeforeCursor} to have style information returned
-     * along with the text. If not set, {@link #getTextAfterCursor}
-     * sends only the raw text, without style or other spans. If set,
-     * it may return a complex CharSequence of both text and style
-     * spans. <strong>Editor authors</strong>: you should strive to
-     * send text with styles if possible, but it is not required.
+     * Flag for use with {@link #getTextAfterCursor}, {@link #getTextBeforeCursor} and
+     * {@link #getSurroundingText} to have style information returned along with the text. If not
+     * set, {@link #getTextAfterCursor} sends only the raw text, without style or other spans. If
+     * set, it may return a complex CharSequence of both text and style spans.
+     * <strong>Editor authors</strong>: you should strive to send text with styles if possible, but
+     * it is not required.
      */
     int GET_TEXT_WITH_STYLES = 0x0001;
 
@@ -264,6 +276,61 @@
     CharSequence getSelectedText(int flags);
 
     /**
+     * Gets the surrounding text around the current cursor, with <var>beforeLength</var> characters
+     * of text before the cursor (start of the selection), <var>afterLength</var> characters of text
+     * after the cursor (end of the selection), and all of the selected text.
+     *
+     * <p>This method may fail either if the input connection has become invalid (such as its
+     * process crashing), or the client is taking too long to respond with the text (it is given a
+     * couple seconds to return), or the protocol is not supported. In any of these cases, null is
+     * returned.
+     *
+     * <p>This method does not affect the text in the editor in any way, nor does it affect the
+     * selection or composing spans.</p>
+     *
+     * <p>If {@link #GET_TEXT_WITH_STYLES} is supplied as flags, the editor should return a
+     * {@link android.text.Spanned} with all the spans set on the text.</p>
+     *
+     * <p><strong>IME authors:</strong> please consider this will trigger an IPC round-trip that
+     * will take some time. Assume this method consumes a lot of time. If you are using this to get
+     * the initial surrounding text around the cursor, you may consider using
+     * {@link EditorInfo#getInitialTextBeforeCursor(int, int)},
+     * {@link EditorInfo#getInitialSelectedText(int)}, and
+     * {@link EditorInfo#getInitialTextAfterCursor(int, int)} to prevent IPC costs.</p>
+     *
+     * @param beforeLength The expected length of the text before the cursor.
+     * @param afterLength The expected length of the text after the cursor.
+     * @param flags Supplies additional options controlling how the text is returned. Defined by the
+     *              constants.
+     * @return an {@link android.view.inputmethod.SurroundingText} object describing the surrounding
+     * text and state of selection, or null if the input connection is no longer valid, or the
+     * editor can't comply with the request for some reason, or the application does not implement
+     * this method. The length of the returned text might be less than the sum of
+     * <var>beforeLength</var> and <var>afterLength</var> .
+     */
+    @Nullable
+    default SurroundingText getSurroundingText(
+            @IntRange(from = 0) int beforeLength, @IntRange(from = 0) int afterLength,
+            @GetTextType int flags) {
+        CharSequence textBeforeCursor = getTextBeforeCursor(beforeLength, flags);
+        if (textBeforeCursor == null) {
+            textBeforeCursor = "";
+        }
+        CharSequence selectedText = getSelectedText(flags);
+        if (selectedText == null) {
+            selectedText = "";
+        }
+        CharSequence textAfterCursor = getTextAfterCursor(afterLength, flags);
+        if (textAfterCursor == null) {
+            textAfterCursor = "";
+        }
+        CharSequence surroundingText =
+                TextUtils.concat(textBeforeCursor, selectedText, textAfterCursor);
+        return new SurroundingText(surroundingText, textBeforeCursor.length(),
+                textBeforeCursor.length() + selectedText.length(), -1);
+    }
+
+    /**
      * Retrieve the current capitalization mode in effect at the
      * current cursor position in the text. See
      * {@link android.text.TextUtils#getCapsMode TextUtils.getCapsMode}
diff --git a/core/java/android/view/inputmethod/InputConnectionInspector.java b/core/java/android/view/inputmethod/InputConnectionInspector.java
index 5f25bf5..7621da7 100644
--- a/core/java/android/view/inputmethod/InputConnectionInspector.java
+++ b/core/java/android/view/inputmethod/InputConnectionInspector.java
@@ -44,6 +44,7 @@
             MissingMethodFlags.GET_HANDLER,
             MissingMethodFlags.CLOSE_CONNECTION,
             MissingMethodFlags.COMMIT_CONTENT,
+            MissingMethodFlags.GET_SURROUNDING_TEXT
     })
     public @interface MissingMethodFlags {
         /**
@@ -86,6 +87,11 @@
          * {@link android.os.Build.VERSION_CODES#N} MR-1 and later.
          */
         int COMMIT_CONTENT = 1 << 7;
+        /**
+         * {@link InputConnection#getSurroundingText(int, int, int)} is available in
+         * {@link android.os.Build.VERSION_CODES#S} and later.
+         */
+        int GET_SURROUNDING_TEXT = 1 << 8;
     }
 
     private static final Map<Class, Integer> sMissingMethodsMap = Collections.synchronizedMap(
@@ -138,6 +144,9 @@
         if (!hasCommitContent(clazz)) {
             flags |= MissingMethodFlags.COMMIT_CONTENT;
         }
+        if (!hasGetSurroundingText(clazz)) {
+            flags |= MissingMethodFlags.GET_SURROUNDING_TEXT;
+        }
         sMissingMethodsMap.put(clazz, flags);
         return flags;
     }
@@ -216,6 +225,16 @@
         }
     }
 
+    private static boolean hasGetSurroundingText(@NonNull final Class clazz) {
+        try {
+            final Method method = clazz.getMethod("getSurroundingText", int.class, int.class,
+                    int.class);
+            return !Modifier.isAbstract(method.getModifiers());
+        } catch (NoSuchMethodException e) {
+            return false;
+        }
+    }
+
     public static String getMissingMethodFlagsAsString(@MissingMethodFlags final int flags) {
         final StringBuilder sb = new StringBuilder();
         boolean isEmpty = true;
diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java
index f671e22..ec7fa60 100644
--- a/core/java/android/view/inputmethod/InputConnectionWrapper.java
+++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java
@@ -16,6 +16,7 @@
 
 package android.view.inputmethod;
 
+import android.annotation.Nullable;
 import android.os.Bundle;
 import android.os.Handler;
 import android.view.KeyEvent;
@@ -101,6 +102,16 @@
      * {@inheritDoc}
      * @throws NullPointerException if the target is {@code null}.
      */
+    @Nullable
+    @Override
+    public SurroundingText getSurroundingText(int beforeLength, int afterLength, int flags) {
+        return mTarget.getSurroundingText(beforeLength, afterLength, flags);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws NullPointerException if the target is {@code null}.
+     */
     @Override
     public int getCursorCapsMode(int reqModes) {
         return mTarget.getCursorCapsMode(reqModes);
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 7cc347d..5d876a6 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -17,6 +17,7 @@
 package android.view.inputmethod;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
@@ -515,7 +516,7 @@
      *         {@link InputMethodInfo} and its Id is the same to this one.
      */
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (o == this) return true;
         if (o == null) return false;
 
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index b8f04159..5785999 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1006,6 +1006,24 @@
                 return;
             }
             closeConnection();
+
+            // Notify the app that the InputConnection was closed.
+            final View servedView = mServedView.get();
+            if (servedView != null) {
+                final Handler handler = servedView.getHandler();
+                // The handler is null if the view is already detached. When that's the case, for
+                // now, we simply don't dispatch this callback.
+                if (handler != null) {
+                    if (DEBUG) {
+                        Log.v(TAG, "Calling View.onInputConnectionClosed: view=" + servedView);
+                    }
+                    if (handler.getLooper().isCurrentThread()) {
+                        servedView.onInputConnectionClosedInternal();
+                    } else {
+                        handler.post(servedView::onInputConnectionClosedInternal);
+                    }
+                }
+            }
         }
 
         @Override
@@ -1940,6 +1958,8 @@
         InputConnection ic = view.onCreateInputConnection(tba);
         if (DEBUG) Log.v(TAG, "Starting input: tba=" + tba + " ic=" + ic);
 
+        final Handler icHandler;
+        InputBindResult res = null;
         synchronized (mH) {
             // Now that we are locked again, validate that our state hasn't
             // changed.
@@ -1976,7 +1996,6 @@
                 mCursorCandEnd = -1;
                 mCursorRect.setEmpty();
                 mCursorAnchorInfo = null;
-                final Handler icHandler;
                 missingMethodFlags = InputConnectionInspector.getMissingMethodFlags(ic);
                 if ((missingMethodFlags & InputConnectionInspector.MissingMethodFlags.GET_HANDLER)
                         != 0) {
@@ -1990,6 +2009,7 @@
             } else {
                 servedContext = null;
                 missingMethodFlags = 0;
+                icHandler = null;
             }
             mServedInputConnectionWrapper = servedContext;
 
@@ -1997,7 +2017,7 @@
                 if (DEBUG) Log.v(TAG, "START INPUT: view=" + dumpViewInfo(view) + " ic="
                         + ic + " tba=" + tba + " startInputFlags="
                         + InputMethodDebug.startInputFlagsToString(startInputFlags));
-                final InputBindResult res = mService.startInputOrWindowGainedFocus(
+                res = mService.startInputOrWindowGainedFocus(
                         startInputReason, mClient, windowGainingFocus, startInputFlags,
                         softInputMode, windowFlags, tba, servedContext, missingMethodFlags,
                         view.getContext().getApplicationInfo().targetSdkVersion);
@@ -2036,6 +2056,15 @@
             }
         }
 
+        // Notify the app that the InputConnection is initialized and ready for use.
+        if (ic != null && res != null && res.method != null) {
+            if (DEBUG) {
+                Log.v(TAG, "Calling View.onInputConnectionOpened: view= " + view
+                        + ", ic=" + ic + ", tba=" + tba + ", handler=" + icHandler);
+            }
+            view.onInputConnectionOpenedInternal(ic, tba, icHandler);
+        }
+
         return true;
     }
 
diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.java b/core/java/android/view/inputmethod/InputMethodSubtype.java
index 5989847..14abbdb 100644
--- a/core/java/android/view/inputmethod/InputMethodSubtype.java
+++ b/core/java/android/view/inputmethod/InputMethodSubtype.java
@@ -597,7 +597,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (o instanceof InputMethodSubtype) {
             InputMethodSubtype subtype = (InputMethodSubtype) o;
             if (subtype.mSubtypeId != 0 || mSubtypeId != 0) {
diff --git a/core/java/android/view/inputmethod/SparseRectFArray.java b/core/java/android/view/inputmethod/SparseRectFArray.java
index 596ea86..07d0b06 100644
--- a/core/java/android/view/inputmethod/SparseRectFArray.java
+++ b/core/java/android/view/inputmethod/SparseRectFArray.java
@@ -16,6 +16,7 @@
 
 package android.view.inputmethod;
 
+import android.annotation.Nullable;
 import android.graphics.RectF;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -92,7 +93,7 @@
     }
 
     @Override
-    public boolean equals(Object obj){
+    public boolean equals(@Nullable Object obj){
         if (obj == null) {
             return false;
         }
diff --git a/media/java/android/media/tv/TvChannelInfo.aidl b/core/java/android/view/inputmethod/SurroundingText.aidl
similarity index 82%
copy from media/java/android/media/tv/TvChannelInfo.aidl
copy to core/java/android/view/inputmethod/SurroundingText.aidl
index 71cd0a7..7a9898e 100644
--- a/media/java/android/media/tv/TvChannelInfo.aidl
+++ b/core/java/android/view/inputmethod/SurroundingText.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open 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,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.media.tv;
+package android.view.inputmethod;
 
-parcelable TvChannelInfo;
+parcelable SurroundingText;
\ No newline at end of file
diff --git a/core/java/android/view/inputmethod/SurroundingText.java b/core/java/android/view/inputmethod/SurroundingText.java
new file mode 100644
index 0000000..506f95a
--- /dev/null
+++ b/core/java/android/view/inputmethod/SurroundingText.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.inputmethod;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+/**
+ * Information about the surrounding text around the cursor for use by an input method.
+ *
+ * <p>This contains information about the text and the selection relative to the text. </p>
+ */
+public final class SurroundingText implements Parcelable {
+    /**
+     * The surrounding text around the cursor.
+     */
+    @NonNull
+    private final CharSequence mText;
+
+    /**
+     * The text offset of the start of the selection in the surrounding text.
+     *
+     * <p>This needs to be the position relative to the {@link #mText} instead of the real position
+     * in the editor.</p>
+     */
+    @IntRange(from = 0)
+    private final int mSelectionStart;
+
+    /**
+     * The text offset of the end of the selection in the surrounding text.
+     *
+     * <p>This needs to be the position relative to the {@link #mText} instead of the real position
+     * in the editor.</p>
+     */
+    @IntRange(from = 0)
+    private final int mSelectionEnd;
+
+    /**
+     * The text offset between the start of the editor's text and the start of the surrounding text.
+     *
+     * <p>-1 indicates the offset information is unknown.</p>
+     */
+    @IntRange(from = -1)
+    private final int mOffset;
+
+    /**
+     * Constructor.
+     *
+     * @param text The surrounding text.
+     * @param selectionStart The text offset of the start of the selection in the surrounding text.
+     *                       Reversed selection is allowed.
+     * @param selectionEnd The text offset of the end of the selection in the surrounding text.
+     *                     Reversed selection is allowed.
+     * @param offset The text offset between the start of the editor's text and the start of the
+     *               surrounding text. -1 indicates the offset is unknown.
+     */
+    public SurroundingText(@NonNull final CharSequence text,
+            @IntRange(from = 0) int selectionStart, @IntRange(from = 0) int selectionEnd,
+            @IntRange(from = -1) int offset) {
+        mText = text;
+        mSelectionStart = selectionStart;
+        mSelectionEnd = selectionEnd;
+        mOffset = offset;
+    }
+
+    /**
+     * Returns the surrounding text around the cursor.
+     */
+    @NonNull
+    public CharSequence getText() {
+        return mText;
+    }
+
+    /**
+     * Returns the text offset of the start of the selection in the surrounding text.
+     */
+    @IntRange(from = 0)
+    public int getSelectionStart() {
+        return mSelectionStart;
+    }
+
+    /**
+     * Returns the text offset of the end of the selection in the surrounding text.
+     */
+    @IntRange(from = 0)
+    public int getSelectionEnd() {
+        return mSelectionEnd;
+    }
+
+    /**
+     * Returns text offset between the start of the editor's text and the start of the surrounding
+     * text.
+     *
+     * <p>-1 indicates the offset information is unknown.</p>
+     */
+    @IntRange(from = -1)
+    public int getOffset() {
+        return mOffset;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        TextUtils.writeToParcel(mText, out, flags);
+        out.writeInt(mSelectionStart);
+        out.writeInt(mSelectionEnd);
+        out.writeInt(mOffset);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<SurroundingText> CREATOR =
+            new Parcelable.Creator<SurroundingText>() {
+                @NonNull
+                public SurroundingText createFromParcel(Parcel in) {
+                    final CharSequence text =
+                            TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+                    final int selectionHead = in.readInt();
+                    final int selectionEnd = in.readInt();
+                    final int offset = in.readInt();
+                    return new SurroundingText(
+                            text == null ? "" : text,  selectionHead, selectionEnd, offset);
+                }
+
+                @NonNull
+                public SurroundingText[] newArray(int size) {
+                    return new SurroundingText[size];
+                }
+            };
+}
diff --git a/core/java/android/view/textclassifier/SelectionEvent.java b/core/java/android/view/textclassifier/SelectionEvent.java
index 6f9556b..858825b 100644
--- a/core/java/android/view/textclassifier/SelectionEvent.java
+++ b/core/java/android/view/textclassifier/SelectionEvent.java
@@ -645,7 +645,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (this == obj) {
             return true;
         }
diff --git a/core/java/android/view/textclassifier/TextClassificationSessionId.java b/core/java/android/view/textclassifier/TextClassificationSessionId.java
index aa680c9..5fdcc31 100644
--- a/core/java/android/view/textclassifier/TextClassificationSessionId.java
+++ b/core/java/android/view/textclassifier/TextClassificationSessionId.java
@@ -17,6 +17,7 @@
 package android.view.textclassifier;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -63,7 +64,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
         TextClassificationSessionId that = (TextClassificationSessionId) o;
diff --git a/core/java/android/view/textservice/SpellCheckerSubtype.java b/core/java/android/view/textservice/SpellCheckerSubtype.java
index 12fe626..38a140f 100644
--- a/core/java/android/view/textservice/SpellCheckerSubtype.java
+++ b/core/java/android/view/textservice/SpellCheckerSubtype.java
@@ -203,7 +203,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (o instanceof SpellCheckerSubtype) {
             SpellCheckerSubtype subtype = (SpellCheckerSubtype) o;
             if (subtype.mSubtypeId != SUBTYPE_ID_NONE || mSubtypeId != SUBTYPE_ID_NONE) {
diff --git a/core/java/android/view/textservice/SuggestionsInfo.java b/core/java/android/view/textservice/SuggestionsInfo.java
index e69b6e7..ca529f6 100644
--- a/core/java/android/view/textservice/SuggestionsInfo.java
+++ b/core/java/android/view/textservice/SuggestionsInfo.java
@@ -45,6 +45,14 @@
      * the result suggestions include highly recommended ones.
      */
     public static final int RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS = 0x0004;
+
+    /**
+     * Flag of the attributes of the suggestions that can be obtained by
+     * {@link #getSuggestionsAttributes}: this tells that the text service thinks the requested
+     * sentence contains a grammar error.
+     */
+    public static final int RESULT_ATTR_LOOKS_LIKE_GRAMMAR_ERROR = 0x0008;
+
     private final int mSuggestionsAttributes;
     private final String[] mSuggestions;
     private final boolean mSuggestionsAvailable;
diff --git a/core/java/android/webkit/PacProcessor.java b/core/java/android/webkit/PacProcessor.java
index 28496c6..d7b4e8b 100644
--- a/core/java/android/webkit/PacProcessor.java
+++ b/core/java/android/webkit/PacProcessor.java
@@ -34,7 +34,7 @@
      *
      * <p> There can only be one default {@link PacProcessor} instance.
      * This method will create a new instance if one did not already exist, or
-     * if the previous instance was released with {@link #releasePacProcessor}.
+     * if the previous instance was released with {@link #release}.
      *
      * @return the default PacProcessor instance.
      */
@@ -47,7 +47,7 @@
      * Create a new PacProcessor instance.
      *
      * <p> The created instance needs to be released manually once it is no longer needed
-     * by calling {@link #releasePacProcessor} to prevent memory leaks.
+     * by calling {@link #release} to prevent memory leaks.
      *
      * <p> The created instance is not tied to any particular {@link Network}.
      * To associate {@link PacProcessor} with a {@link Network} use {@link #setNetwork} method.
@@ -82,7 +82,7 @@
      * {@link #getInstance} and {@link #getInstanceForNetwork}
      * for the same network will create a new instance.
      */
-    default void releasePacProcessor() {
+    default void release() {
         throw new UnsupportedOperationException("Not implemented");
     }
 
diff --git a/core/java/android/webkit/UserPackage.java b/core/java/android/webkit/UserPackage.java
index 2e5ee04..5bcfa8b 100644
--- a/core/java/android/webkit/UserPackage.java
+++ b/core/java/android/webkit/UserPackage.java
@@ -34,7 +34,7 @@
     private final UserInfo mUserInfo;
     private final PackageInfo mPackageInfo;
 
-    public static final int MINIMUM_SUPPORTED_SDK = Build.VERSION_CODES.R;
+    public static final int MINIMUM_SUPPORTED_SDK = Build.VERSION_CODES.S;
 
     public UserPackage(UserInfo user, PackageInfo packageInfo) {
         this.mUserInfo = user;
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 8790bbd..5fc9344 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -47,7 +47,7 @@
     // visible for WebViewZygoteInit to look up the class by reflection and call preloadInZygote.
     /** @hide */
     private static final String CHROMIUM_WEBVIEW_FACTORY =
-            "com.android.webview.chromium.WebViewChromiumFactoryProviderForR";
+            "com.android.webview.chromium.WebViewChromiumFactoryProviderForS";
 
     private static final String CHROMIUM_WEBVIEW_FACTORY_METHOD = "create";
 
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 97e0689..45b21c5 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -82,6 +82,7 @@
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputContentInfo;
 import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.SurroundingText;
 import android.view.inspector.InspectableProperty;
 import android.view.inspector.InspectableProperty.EnumEntry;
 import android.widget.RemoteViews.OnClickHandler;
@@ -5997,6 +5998,12 @@
         }
 
         @Override
+        public SurroundingText getSurroundingText(int beforeLength, int afterLength, int flags) {
+            if (mTarget == null) return null;
+            return mTarget.getSurroundingText(beforeLength, afterLength, flags);
+        }
+
+        @Override
         public int getCursorCapsMode(int reqModes) {
             if (mTarget == null) return InputType.TYPE_TEXT_FLAG_CAP_SENTENCES;
             return mTarget.getCursorCapsMode(reqModes);
diff --git a/core/java/android/widget/ActivityChooserModel.java b/core/java/android/widget/ActivityChooserModel.java
index d87bdf4..9da337a 100644
--- a/core/java/android/widget/ActivityChooserModel.java
+++ b/core/java/android/widget/ActivityChooserModel.java
@@ -16,6 +16,7 @@
 
 package android.widget;
 
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
@@ -839,7 +840,7 @@
         }
 
         @Override
-        public boolean equals(Object obj) {
+        public boolean equals(@Nullable Object obj) {
             if (this == obj) {
                 return true;
             }
@@ -908,7 +909,7 @@
         }
 
         @Override
-        public boolean equals(Object obj) {
+        public boolean equals(@Nullable Object obj) {
             if (this == obj) {
                 return true;
             }
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index fa19521..f2955ac 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -186,6 +186,9 @@
     private static final int MENU_ITEM_ORDER_SECONDARY_ASSIST_ACTIONS_START = 50;
     private static final int MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START = 100;
 
+    private static final int FLAG_MISSPELLED_OR_GRAMMAR_ERROR =
+            SuggestionSpan.FLAG_MISSPELLED | SuggestionSpan.FLAG_GRAMMAR_ERROR;
+
     @IntDef({MagnifierHandleTrigger.SELECTION_START,
             MagnifierHandleTrigger.SELECTION_END,
             MagnifierHandleTrigger.INSERTION})
@@ -203,6 +206,10 @@
         int TEXT_LINK = 2;
     }
 
+    // Default content insertion handler.
+    private final TextViewOnReceiveContentCallback mDefaultOnReceiveContentCallback =
+            new TextViewOnReceiveContentCallback();
+
     // Each Editor manages its own undo stack.
     private final UndoManager mUndoManager = new UndoManager();
     private UndoOwner mUndoOwner = mUndoManager.getOwner(UNDO_OWNER_TAG, this);
@@ -581,6 +588,11 @@
         mUndoOwner = mUndoManager.getOwner(UNDO_OWNER_TAG, this);
     }
 
+    @VisibleForTesting
+    public @NonNull TextViewOnReceiveContentCallback getDefaultOnReceiveContentCallback() {
+        return mDefaultOnReceiveContentCallback;
+    }
+
     /**
      * Forgets all undo and redo operations for this Editor.
      */
@@ -706,6 +718,8 @@
 
         hideCursorAndSpanControllers();
         stopTextActionModeWithPreservingSelection();
+
+        mDefaultOnReceiveContentCallback.clearInputConnectionInfo();
     }
 
     private void discardTextDisplayLists() {
@@ -1552,7 +1566,7 @@
             for (int i = 0; i < suggestionSpans.length; i++) {
                 int flags = suggestionSpans[i].getFlags();
                 if ((flags & SuggestionSpan.FLAG_EASY_CORRECT) != 0
-                        && (flags & SuggestionSpan.FLAG_MISSPELLED) == 0) {
+                        && (flags & FLAG_MISSPELLED_OR_GRAMMAR_ERROR) == 0) {
                     flags &= ~SuggestionSpan.FLAG_EASY_CORRECT;
                     suggestionSpans[i].setFlags(flags);
                 }
@@ -3064,8 +3078,9 @@
 
             // Remove potential misspelled flags
             int suggestionSpanFlags = suggestionSpan.getFlags();
-            if ((suggestionSpanFlags & SuggestionSpan.FLAG_MISSPELLED) != 0) {
+            if ((suggestionSpanFlags & FLAG_MISSPELLED_OR_GRAMMAR_ERROR) != 0) {
                 suggestionSpanFlags &= ~SuggestionSpan.FLAG_MISSPELLED;
+                suggestionSpanFlags &= ~SuggestionSpan.FLAG_GRAMMAR_ERROR;
                 suggestionSpanFlags &= ~SuggestionSpan.FLAG_EASY_CORRECT;
                 suggestionSpan.setFlags(suggestionSpanFlags);
             }
@@ -3601,19 +3616,29 @@
                 final int flag1 = span1.getFlags();
                 final int flag2 = span2.getFlags();
                 if (flag1 != flag2) {
-                    // The order here should match what is used in updateDrawState
-                    final boolean easy1 = (flag1 & SuggestionSpan.FLAG_EASY_CORRECT) != 0;
-                    final boolean easy2 = (flag2 & SuggestionSpan.FLAG_EASY_CORRECT) != 0;
-                    final boolean misspelled1 = (flag1 & SuggestionSpan.FLAG_MISSPELLED) != 0;
-                    final boolean misspelled2 = (flag2 & SuggestionSpan.FLAG_MISSPELLED) != 0;
-                    if (easy1 && !misspelled1) return -1;
-                    if (easy2 && !misspelled2) return 1;
-                    if (misspelled1) return -1;
-                    if (misspelled2) return 1;
+                    // Compare so that the order will be: easy -> misspelled -> grammarError
+                    int easy = compareFlag(SuggestionSpan.FLAG_EASY_CORRECT, flag1, flag2);
+                    if (easy != 0) return easy;
+                    int misspelled = compareFlag(SuggestionSpan.FLAG_MISSPELLED, flag1, flag2);
+                    if (misspelled != 0) return misspelled;
+                    int grammarError = compareFlag(SuggestionSpan.FLAG_GRAMMAR_ERROR, flag1, flag2);
+                    if (grammarError != 0) return grammarError;
                 }
 
                 return mSpansLengths.get(span1).intValue() - mSpansLengths.get(span2).intValue();
             }
+
+            /*
+             * Returns -1 if flags1 has flagToCompare but flags2 does not.
+             * Returns 1 if flags2 has flagToCompare but flags1 does not.
+             * Otherwise, returns 0.
+             */
+            private int compareFlag(int flagToCompare, int flags1, int flags2) {
+                boolean hasFlag1 = (flags1 & flagToCompare) != 0;
+                boolean hasFlag2 = (flags2 & flagToCompare) != 0;
+                if (hasFlag1 == hasFlag2) return 0;
+                return hasFlag1 ? -1 : 1;
+            }
         }
 
         /**
@@ -3632,8 +3657,9 @@
                 mSpansLengths.put(suggestionSpan, Integer.valueOf(end - start));
             }
 
-            // The suggestions are sorted according to their types (easy correction first, then
-            // misspelled) and to the length of the text that they cover (shorter first).
+            // The suggestions are sorted according to their types (easy correction first,
+            // misspelled second, then grammar error) and to the length of the text that they cover
+            // (shorter first).
             Arrays.sort(suggestionSpans, mSuggestionSpanComparator);
             mSpansLengths.clear();
 
@@ -3661,7 +3687,7 @@
                 final int spanEnd = spannable.getSpanEnd(suggestionSpan);
 
                 if (misspelledSpanInfo != null
-                        && (suggestionSpan.getFlags() & SuggestionSpan.FLAG_MISSPELLED) != 0) {
+                        && (suggestionSpan.getFlags() & FLAG_MISSPELLED_OR_GRAMMAR_ERROR) != 0) {
                     misspelledSpanInfo.mSuggestionSpan = suggestionSpan;
                     misspelledSpanInfo.mSpanStart = spanStart;
                     misspelledSpanInfo.mSpanEnd = spanEnd;
diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java
index f132197..a6dce7f 100644
--- a/core/java/android/widget/GridLayout.java
+++ b/core/java/android/widget/GridLayout.java
@@ -31,6 +31,7 @@
 import static java.lang.Math.min;
 
 import android.annotation.IntDef;
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.TypedArray;
@@ -2221,7 +2222,7 @@
         }
 
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (this == o) return true;
             if (o == null || getClass() != o.getClass()) return false;
 
@@ -2496,7 +2497,7 @@
          *         {@code Interval}, {@code false} otherwise.
          */
         @Override
-        public boolean equals(Object that) {
+        public boolean equals(@Nullable Object that) {
             if (this == that) {
                 return true;
             }
@@ -2609,7 +2610,7 @@
          *         {@code Spec}; {@code false} otherwise
          */
         @Override
-        public boolean equals(Object that) {
+        public boolean equals(@Nullable Object that) {
             if (this == that) {
                 return true;
             }
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 7016c5c..dcfb387 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -21,6 +21,7 @@
 import android.annotation.IntDef;
 import android.annotation.LayoutRes;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.StyleRes;
 import android.app.Activity;
 import android.app.ActivityOptions;
@@ -348,7 +349,7 @@
         public String methodName;
 
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (!(o instanceof MethodKey)) {
                 return false;
             }
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index b4379ec..b884936 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -19,6 +19,7 @@
 import static android.widget.RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID;
 import static android.widget.RemoteViews.EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND;
 
+import android.annotation.Nullable;
 import android.annotation.WorkerThread;
 import android.app.IServiceConnection;
 import android.appwidget.AppWidgetHostView;
@@ -814,7 +815,7 @@
         }
 
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (!(o instanceof RemoteViewsCacheKey)) {
                 return false;
             }
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index 2eadb56..b8a3249 100755
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -1695,7 +1695,7 @@
         Intent queryIntent = new Intent(Intent.ACTION_SEARCH);
         queryIntent.setComponent(searchActivity);
         PendingIntent pending = PendingIntent.getActivity(getContext(), 0, queryIntent,
-                PendingIntent.FLAG_ONE_SHOT);
+                PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_MUTABLE);
 
         // Now set up the bundle that will be inserted into the pending intent
         // when it's time to do the search.  We always build it here (even if empty)
diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java
index d4aad752..0611bea 100644
--- a/core/java/android/widget/SpellChecker.java
+++ b/core/java/android/widget/SpellChecker.java
@@ -338,11 +338,13 @@
                         ((attributes & SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY) > 0);
                 final boolean looksLikeTypo =
                         ((attributes & SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO) > 0);
+                final boolean looksLikeGrammarError =
+                        ((attributes & SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_GRAMMAR_ERROR) > 0);
 
                 final SpellCheckSpan spellCheckSpan = mSpellCheckSpans[k];
                 //TODO: we need to change that rule for results from a sentence-level spell
                 // checker that will probably be in dictionary.
-                if (!isInDictionary && looksLikeTypo) {
+                if (!isInDictionary && (looksLikeTypo || looksLikeGrammarError)) {
                     createMisspelledSuggestionSpan(
                             editable, suggestionsInfo, spellCheckSpan, offset, length);
                 } else {
@@ -482,8 +484,16 @@
             suggestions = ArrayUtils.emptyArray(String.class);
         }
 
-        SuggestionSpan suggestionSpan = new SuggestionSpan(mTextView.getContext(), suggestions,
-                SuggestionSpan.FLAG_EASY_CORRECT | SuggestionSpan.FLAG_MISSPELLED);
+        final int suggestionsAttrs = suggestionsInfo.getSuggestionsAttributes();
+        int flags = SuggestionSpan.FLAG_EASY_CORRECT;
+        if ((suggestionsAttrs & SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO) != 0) {
+            flags |= SuggestionSpan.FLAG_MISSPELLED;
+        }
+        if ((suggestionsAttrs & SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_GRAMMAR_ERROR) != 0) {
+            flags |= SuggestionSpan.FLAG_GRAMMAR_ERROR;
+        }
+        SuggestionSpan suggestionSpan =
+                new SuggestionSpan(mTextView.getContext(), suggestions, flags);
         // TODO: Remove mIsSentenceSpellCheckSupported by extracting an interface
         // to share the logic of word level spell checker and sentence level spell checker
         if (mIsSentenceSpellCheckSupported) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 52a3f41..3fc0f4e 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -80,6 +80,7 @@
 import android.os.Build;
 import android.os.Build.VERSION_CODES;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.LocaleList;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -890,13 +891,6 @@
     @UnsupportedAppUsage
     private Editor mEditor;
 
-    /**
-     * The default content insertion callback used by {@link TextView}. See
-     * {@link #setOnReceiveContentCallback} for more info.
-     */
-    private static final TextViewOnReceiveContentCallback DEFAULT_ON_RECEIVE_CONTENT_CALLBACK =
-            new TextViewOnReceiveContentCallback();
-
     private static final int DEVICE_PROVISIONED_UNKNOWN = 0;
     private static final int DEVICE_PROVISIONED_NO = 1;
     private static final int DEVICE_PROVISIONED_YES = 2;
@@ -11057,12 +11051,12 @@
                     MotionEvent.actionToString(event.getActionMasked()),
                     event.getX(), event.getY());
         }
-        if (!isFromPrimePointer(event, false)) {
-            return true;
-        }
-
         final int action = event.getActionMasked();
         if (mEditor != null) {
+            if (!isFromPrimePointer(event, false)) {
+                return true;
+            }
+
             mEditor.onTouchEvent(event);
 
             if (mEditor.mInsertionPointCursorController != null
@@ -13723,6 +13717,23 @@
         }
     }
 
+    /** @hide */
+    @Override
+    public void onInputConnectionOpenedInternal(@NonNull InputConnection ic,
+            @NonNull EditorInfo editorInfo, @Nullable Handler handler) {
+        if (mEditor != null) {
+            mEditor.getDefaultOnReceiveContentCallback().setInputConnectionInfo(ic, editorInfo);
+        }
+    }
+
+    /** @hide */
+    @Override
+    public void onInputConnectionClosedInternal() {
+        if (mEditor != null) {
+            mEditor.getDefaultOnReceiveContentCallback().clearInputConnectionInfo();
+        }
+    }
+
     /**
      * Returns the callback used for handling insertion of content into this view. See
      * {@link #setOnReceiveContentCallback} for more info.
@@ -13773,8 +13784,8 @@
         ClipDescription description = payload.getClip().getDescription();
         if (receiver != null && receiver.supports(this, description)) {
             receiver.onReceiveContent(this, payload);
-        } else {
-            DEFAULT_ON_RECEIVE_CONTENT_CALLBACK.onReceiveContent(this, payload);
+        } else if (mEditor != null) {
+            mEditor.getDefaultOnReceiveContentCallback().onReceiveContent(this, payload);
         }
     }
 
diff --git a/core/java/android/widget/TextViewOnReceiveContentCallback.java b/core/java/android/widget/TextViewOnReceiveContentCallback.java
index 35618cb..d7c95b7 100644
--- a/core/java/android/widget/TextViewOnReceiveContentCallback.java
+++ b/core/java/android/widget/TextViewOnReceiveContentCallback.java
@@ -16,24 +16,44 @@
 
 package android.widget;
 
+import static android.content.ContentResolver.SCHEME_CONTENT;
 import static android.view.OnReceiveContentCallback.Payload.FLAG_CONVERT_TO_PLAIN_TEXT;
 import static android.view.OnReceiveContentCallback.Payload.SOURCE_AUTOFILL;
 import static android.view.OnReceiveContentCallback.Payload.SOURCE_DRAG_AND_DROP;
 
+import static java.util.Collections.singleton;
+
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SuppressLint;
+import android.compat.Compatibility;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
 import android.content.ClipData;
+import android.content.ClipDescription;
 import android.content.Context;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
 import android.text.Editable;
 import android.text.Selection;
 import android.text.SpannableStringBuilder;
 import android.text.Spanned;
+import android.util.ArraySet;
 import android.util.Log;
 import android.view.OnReceiveContentCallback;
 import android.view.OnReceiveContentCallback.Payload.Flags;
 import android.view.OnReceiveContentCallback.Payload.Source;
+import android.view.View;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputContentInfo;
 
-import java.util.Collections;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Set;
 
 /**
@@ -46,19 +66,26 @@
 public class TextViewOnReceiveContentCallback implements OnReceiveContentCallback<TextView> {
     private static final String LOG_TAG = "OnReceiveContent";
 
-    private static final Set<String> MIME_TYPES_ALL_TEXT = Collections.singleton("text/*");
+    private static final String MIME_TYPE_ALL_TEXT = "text/*";
+    private static final Set<String> MIME_TYPES_ALL_TEXT = singleton(MIME_TYPE_ALL_TEXT);
+
+    @Nullable private InputConnectionInfo mInputConnectionInfo;
+    @Nullable private ArraySet<String> mCachedSupportedMimeTypes;
 
     @SuppressLint("CallbackMethodName")
     @NonNull
     @Override
     public Set<String> getSupportedMimeTypes(@NonNull TextView view) {
-        return MIME_TYPES_ALL_TEXT;
+        if (!isUsageOfImeCommitContentEnabled(view)) {
+            return MIME_TYPES_ALL_TEXT;
+        }
+        return getSupportedMimeTypesAugmentedWithImeCommitContentMimeTypes();
     }
 
     @Override
     public boolean onReceiveContent(@NonNull TextView view, @NonNull Payload payload) {
         if (Log.isLoggable(LOG_TAG, Log.DEBUG)) {
-            Log.d(LOG_TAG, "onReceive:" + payload);
+            Log.d(LOG_TAG, "onReceive: " + payload);
         }
         ClipData clip = payload.getClip();
         @Source int source = payload.getSource();
@@ -109,13 +136,22 @@
         editable.replace(start, end, replacement);
     }
 
-    private static boolean onReceiveForAutofill(@NonNull TextView textView, @NonNull ClipData clip,
+    private boolean onReceiveForAutofill(@NonNull TextView view, @NonNull ClipData clip,
             @Flags int flags) {
-        final CharSequence text = coerceToText(clip, textView.getContext(), flags);
+        if (isUsageOfImeCommitContentEnabled(view)) {
+            clip = handleNonTextViaImeCommitContent(clip);
+            if (clip == null) {
+                if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
+                    Log.v(LOG_TAG, "onReceive: Handled via IME");
+                }
+                return true;
+            }
+        }
+        final CharSequence text = coerceToText(clip, view.getContext(), flags);
         // First autofill it...
-        textView.setText(text);
+        view.setText(text);
         // ...then move cursor to the end.
-        final Editable editable = (Editable) textView.getText();
+        final Editable editable = (Editable) view.getText();
         Selection.setSelection(editable, editable.length());
         return true;
     }
@@ -146,4 +182,250 @@
         }
         return ssb;
     }
+
+    /**
+     * On Android S and above, the platform can provide non-text suggestions (e.g. images) via the
+     * augmented autofill framework (see
+     * <a href="/guide/topics/text/autofill-services">autofill services</a>). In order for an app to
+     * be able to handle these suggestions, it must normally implement the
+     * {@link android.view.OnReceiveContentCallback} API. To make the adoption of this smoother for
+     * apps that have previously implemented the
+     * {@link android.view.inputmethod.InputConnection#commitContent(InputContentInfo, int, Bundle)}
+     * API, we reuse that API as a fallback if {@link android.view.OnReceiveContentCallback} is not
+     * yet implemented by the app. This fallback is only enabled on Android S. This change ID
+     * disables the fallback, such that apps targeting Android T and above must implement the
+     * {@link android.view.OnReceiveContentCallback} API in order to accept non-text suggestions.
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S) // Enabled on Android T and higher
+    private static final long AUTOFILL_NON_TEXT_REQUIRES_ON_RECEIVE_CONTENT_CALLBACK = 163400105L;
+
+    /**
+     * Returns true if we can use the IME {@link InputConnection#commitContent} API in order handle
+     * non-text content.
+     */
+    private static boolean isUsageOfImeCommitContentEnabled(@NonNull View view) {
+        if (view.getOnReceiveContentCallback() != null) {
+            if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
+                Log.v(LOG_TAG, "Fallback to commitContent disabled (custom callback is set)");
+            }
+            return false;
+        }
+        if (Compatibility.isChangeEnabled(AUTOFILL_NON_TEXT_REQUIRES_ON_RECEIVE_CONTENT_CALLBACK)) {
+            if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
+                Log.v(LOG_TAG, "Fallback to commitContent disabled (target SDK is above S)");
+            }
+            return false;
+        }
+        return true;
+    }
+
+    private static final class InputConnectionInfo {
+        @NonNull private final WeakReference<InputConnection> mInputConnection;
+        @NonNull private final String[] mEditorInfoContentMimeTypes;
+
+        private InputConnectionInfo(@NonNull InputConnection inputConnection,
+                @NonNull String[] editorInfoContentMimeTypes) {
+            mInputConnection = new WeakReference<>(inputConnection);
+            mEditorInfoContentMimeTypes = editorInfoContentMimeTypes;
+        }
+
+        @Override
+        public String toString() {
+            return "InputConnectionInfo{"
+                    + "mimeTypes=" + Arrays.toString(mEditorInfoContentMimeTypes)
+                    + ", ic=" + mInputConnection
+                    + '}';
+        }
+    }
+
+    /**
+     * Invoked by the platform when an {@link InputConnection} is successfully created for the view
+     * that owns this callback instance.
+     */
+    void setInputConnectionInfo(@NonNull InputConnection ic, @NonNull EditorInfo editorInfo) {
+        if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
+            Log.v(LOG_TAG, "setInputConnectionInfo: "
+                    + Arrays.toString(editorInfo.contentMimeTypes));
+        }
+        String[] contentMimeTypes = editorInfo.contentMimeTypes;
+        if (contentMimeTypes == null || contentMimeTypes.length == 0) {
+            mInputConnectionInfo = null;
+        } else {
+            mInputConnectionInfo = new InputConnectionInfo(ic, contentMimeTypes);
+        }
+    }
+
+    /**
+     * Invoked by the platform when an {@link InputConnection} is closed for the view that owns this
+     * callback instance.
+     */
+    void clearInputConnectionInfo() {
+        if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
+            Log.v(LOG_TAG, "clearInputConnectionInfo: " + mInputConnectionInfo);
+        }
+        mInputConnectionInfo = null;
+    }
+
+    private Set<String> getSupportedMimeTypesAugmentedWithImeCommitContentMimeTypes() {
+        InputConnectionInfo icInfo = mInputConnectionInfo;
+        if (icInfo == null) {
+            if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
+                Log.v(LOG_TAG, "getSupportedMimeTypes: No usable EditorInfo/InputConnection");
+            }
+            return MIME_TYPES_ALL_TEXT;
+        }
+        String[] editorInfoContentMimeTypes = icInfo.mEditorInfoContentMimeTypes;
+        if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
+            Log.v(LOG_TAG, "getSupportedMimeTypes: Augmenting with EditorInfo.contentMimeTypes: "
+                    + Arrays.toString(editorInfoContentMimeTypes));
+        }
+        ArraySet<String> supportedMimeTypes = mCachedSupportedMimeTypes;
+        if (canReuse(supportedMimeTypes, editorInfoContentMimeTypes)) {
+            return supportedMimeTypes;
+        }
+        supportedMimeTypes = new ArraySet<>(editorInfoContentMimeTypes);
+        supportedMimeTypes.add(MIME_TYPE_ALL_TEXT);
+        mCachedSupportedMimeTypes = supportedMimeTypes;
+        return supportedMimeTypes;
+    }
+
+    /**
+     * We want to avoid creating a new set on every invocation of {@link #getSupportedMimeTypes}.
+     * This method will check if the cached set of MIME types matches the data in the given array
+     * from {@link EditorInfo} or if a new set should be created. The custom logic is needed for
+     * comparing the data because the set contains the additional "text/*" MIME type.
+     *
+     * @param cachedMimeTypes Previously cached set of MIME types.
+     * @param newEditorInfoMimeTypes MIME types from {@link EditorInfo}.
+     *
+     * @return Returns true if the data in the given cached set matches the data in the array.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public static boolean canReuse(@Nullable ArraySet<String> cachedMimeTypes,
+            @NonNull String[] newEditorInfoMimeTypes) {
+        if (cachedMimeTypes == null) {
+            return false;
+        }
+        if (newEditorInfoMimeTypes.length != cachedMimeTypes.size()
+                && newEditorInfoMimeTypes.length != (cachedMimeTypes.size() - 1)) {
+            return false;
+        }
+        final boolean ignoreAllTextMimeType =
+                newEditorInfoMimeTypes.length == (cachedMimeTypes.size() - 1);
+        for (String mimeType : cachedMimeTypes) {
+            if (ignoreAllTextMimeType && mimeType.equals(MIME_TYPE_ALL_TEXT)) {
+                continue;
+            }
+            boolean present = false;
+            for (String editorInfoContentMimeType : newEditorInfoMimeTypes) {
+                if (editorInfoContentMimeType.equals(mimeType)) {
+                    present = true;
+                    break;
+                }
+            }
+            if (!present) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Tries to insert the content in the clip into the app via the image keyboard API. If all the
+     * items in the clip are successfully inserted, returns null. If one or more of the items in the
+     * clip cannot be inserted, returns a non-null clip that contains the items that were not
+     * inserted.
+     */
+    @Nullable
+    private ClipData handleNonTextViaImeCommitContent(@NonNull ClipData clip) {
+        ClipDescription description = clip.getDescription();
+        if (!containsUri(clip) || containsOnlyText(clip)) {
+            if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
+                Log.v(LOG_TAG, "onReceive: Clip doesn't contain any non-text URIs: "
+                        + description);
+            }
+            return clip;
+        }
+
+        InputConnectionInfo icInfo = mInputConnectionInfo;
+        InputConnection inputConnection = (icInfo != null) ? icInfo.mInputConnection.get() : null;
+        if (inputConnection == null) {
+            if (Log.isLoggable(LOG_TAG, Log.DEBUG)) {
+                Log.d(LOG_TAG, "onReceive: No usable EditorInfo/InputConnection");
+            }
+            return clip;
+        }
+        String[] editorInfoContentMimeTypes = icInfo.mEditorInfoContentMimeTypes;
+        if (!isClipMimeTypeSupported(editorInfoContentMimeTypes, clip.getDescription())) {
+            if (Log.isLoggable(LOG_TAG, Log.DEBUG)) {
+                Log.d(LOG_TAG,
+                        "onReceive: MIME type is not supported by the app's commitContent impl");
+            }
+            return clip;
+        }
+
+        if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
+            Log.v(LOG_TAG, "onReceive: Trying to insert via IME: " + description);
+        }
+        ArrayList<ClipData.Item> remainingItems = new ArrayList<>(0);
+        for (int i = 0; i < clip.getItemCount(); i++) {
+            ClipData.Item item = clip.getItemAt(i);
+            Uri uri = item.getUri();
+            if (uri == null || !SCHEME_CONTENT.equals(uri.getScheme())) {
+                if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
+                    Log.v(LOG_TAG, "onReceive: No content URI in item: uri=" + uri);
+                }
+                remainingItems.add(item);
+                continue;
+            }
+            if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
+                Log.v(LOG_TAG, "onReceive: Calling commitContent: uri=" + uri);
+            }
+            InputContentInfo contentInfo = new InputContentInfo(uri, description);
+            if (!inputConnection.commitContent(contentInfo, 0, null)) {
+                if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
+                    Log.v(LOG_TAG, "onReceive: Call to commitContent returned false: uri=" + uri);
+                }
+                remainingItems.add(item);
+            }
+        }
+        if (remainingItems.isEmpty()) {
+            return null;
+        }
+        return new ClipData(description, remainingItems);
+    }
+
+    private static boolean isClipMimeTypeSupported(@NonNull String[] supportedMimeTypes,
+            @NonNull ClipDescription description) {
+        for (String imeSupportedMimeType : supportedMimeTypes) {
+            if (description.hasMimeType(imeSupportedMimeType)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static boolean containsUri(@NonNull ClipData clip) {
+        for (int i = 0; i < clip.getItemCount(); i++) {
+            ClipData.Item item = clip.getItemAt(i);
+            if (item.getUri() != null) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static boolean containsOnlyText(@NonNull ClipData clip) {
+        ClipDescription description = clip.getDescription();
+        for (int i = 0; i < description.getMimeTypeCount(); i++) {
+            String mimeType = description.getMimeType(i);
+            if (!mimeType.startsWith("text/")) {
+                return false;
+            }
+        }
+        return true;
+    }
 }
diff --git a/core/java/android/widget/ToastPresenter.java b/core/java/android/widget/ToastPresenter.java
index fb5d55d..b484dfa 100644
--- a/core/java/android/widget/ToastPresenter.java
+++ b/core/java/android/widget/ToastPresenter.java
@@ -48,6 +48,8 @@
 public class ToastPresenter {
     private static final String TAG = "ToastPresenter";
     private static final String WINDOW_TITLE = "Toast";
+
+    // exclusively used to guarantee window timeouts
     private static final long SHORT_DURATION_TIMEOUT = 4000;
     private static final long LONG_DURATION_TIMEOUT = 7000;
 
@@ -145,7 +147,7 @@
      */
     private void adjustLayoutParams(WindowManager.LayoutParams params, IBinder windowToken,
             int duration, int gravity, int xOffset, int yOffset, float horizontalMargin,
-            float verticalMargin) {
+            float verticalMargin, boolean removeWindowAnimations) {
         Configuration config = mResources.getConfiguration();
         int absGravity = Gravity.getAbsoluteGravity(gravity, config.getLayoutDirection());
         params.gravity = absGravity;
@@ -163,6 +165,10 @@
         params.hideTimeoutMilliseconds =
                 (duration == Toast.LENGTH_LONG) ? LONG_DURATION_TIMEOUT : SHORT_DURATION_TIMEOUT;
         params.token = windowToken;
+
+        if (removeWindowAnimations && params.windowAnimations == R.style.Animation_Toast) {
+            params.windowAnimations = 0;
+        }
     }
 
     /**
@@ -193,16 +199,28 @@
 
     /**
      * Shows the toast in {@code view} with the parameters passed and callback {@code callback}.
+     * Uses window animations to animate the toast.
      */
     public void show(View view, IBinder token, IBinder windowToken, int duration, int gravity,
             int xOffset, int yOffset, float horizontalMargin, float verticalMargin,
             @Nullable ITransientNotificationCallback callback) {
+        show(view, token, windowToken, duration, gravity, xOffset, yOffset, horizontalMargin,
+                verticalMargin, callback, false /* removeWindowAnimations */);
+    }
+
+    /**
+     * Shows the toast in {@code view} with the parameters passed and callback {@code callback}.
+     * Can optionally remove window animations from the toast window.
+     */
+    public void show(View view, IBinder token, IBinder windowToken, int duration, int gravity,
+            int xOffset, int yOffset, float horizontalMargin, float verticalMargin,
+            @Nullable ITransientNotificationCallback callback, boolean removeWindowAnimations) {
         checkState(mView == null, "Only one toast at a time is allowed, call hide() first.");
         mView = view;
         mToken = token;
 
         adjustLayoutParams(mParams, windowToken, duration, gravity, xOffset, yOffset,
-                horizontalMargin, verticalMargin);
+                horizontalMargin, verticalMargin, removeWindowAnimations);
         if (mView.getParent() != null) {
             mWindowManager.removeView(mView);
         }
@@ -247,7 +265,8 @@
             try {
                 callback.onToastHidden();
             } catch (RemoteException e) {
-                Log.w(TAG, "Error calling back " + mPackageName + " to notify onToastHide()", e);
+                Log.w(TAG, "Error calling back " + mPackageName + " to notify onToastHide()",
+                        e);
             }
         }
         mView = null;
diff --git a/core/java/android/window/ClientWindowFrames.java b/core/java/android/window/ClientWindowFrames.java
index 0523e64..5d7025b 100644
--- a/core/java/android/window/ClientWindowFrames.java
+++ b/core/java/android/window/ClientWindowFrames.java
@@ -42,19 +42,11 @@
     /** The area cut from the display. */
     public final @NonNull DisplayCutout.ParcelableWrapper displayCutout;
 
-    // TODO(b/149813814): Remove legacy insets.
-    public final Rect contentInsets;
-    public final Rect visibleInsets;
-    public final Rect stableInsets;
-
     public ClientWindowFrames() {
         frame = new Rect();
         displayFrame = new Rect();
         backdropFrame = new Rect();
         displayCutout = new DisplayCutout.ParcelableWrapper();
-        contentInsets = new Rect();
-        visibleInsets = new Rect();
-        stableInsets = new Rect();
     }
 
     public ClientWindowFrames(ClientWindowFrames other) {
@@ -62,9 +54,6 @@
         displayFrame = new Rect(other.displayFrame);
         backdropFrame = new Rect(other.backdropFrame);
         displayCutout = new DisplayCutout.ParcelableWrapper(other.displayCutout.get());
-        contentInsets = new Rect(other.contentInsets);
-        visibleInsets = new Rect(other.visibleInsets);
-        stableInsets = new Rect(other.stableInsets);
     }
 
     private ClientWindowFrames(Parcel in) {
@@ -72,9 +61,6 @@
         displayFrame = Rect.CREATOR.createFromParcel(in);
         backdropFrame = Rect.CREATOR.createFromParcel(in);
         displayCutout = DisplayCutout.ParcelableWrapper.CREATOR.createFromParcel(in);
-        contentInsets = Rect.CREATOR.createFromParcel(in);
-        visibleInsets = Rect.CREATOR.createFromParcel(in);
-        stableInsets = Rect.CREATOR.createFromParcel(in);
     }
 
     /** Needed for AIDL out parameters. */
@@ -83,9 +69,6 @@
         displayFrame.set(Rect.CREATOR.createFromParcel(in));
         backdropFrame.set(Rect.CREATOR.createFromParcel(in));
         displayCutout.set(DisplayCutout.ParcelableWrapper.CREATOR.createFromParcel(in));
-        contentInsets.set(Rect.CREATOR.createFromParcel(in));
-        visibleInsets.set(Rect.CREATOR.createFromParcel(in));
-        stableInsets.set(Rect.CREATOR.createFromParcel(in));
     }
 
     @Override
@@ -94,9 +77,6 @@
         displayFrame.writeToParcel(dest, flags);
         backdropFrame.writeToParcel(dest, flags);
         displayCutout.writeToParcel(dest, flags);
-        contentInsets.writeToParcel(dest, flags);
-        visibleInsets.writeToParcel(dest, flags);
-        stableInsets.writeToParcel(dest, flags);
     }
 
     @Override
diff --git a/core/java/android/window/ITaskOrganizerController.aidl b/core/java/android/window/ITaskOrganizerController.aidl
index 12b16ff..3a84c1f 100644
--- a/core/java/android/window/ITaskOrganizerController.aidl
+++ b/core/java/android/window/ITaskOrganizerController.aidl
@@ -17,7 +17,9 @@
 package android.window;
 
 import android.app.ActivityManager;
+import android.content.pm.ParceledListSlice;
 import android.window.ITaskOrganizer;
+import android.window.TaskAppearedInfo;
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
@@ -26,8 +28,11 @@
 
     /**
      * Register a TaskOrganizer to manage all the tasks with supported windowing modes.
+     *
+     * @return a list of the tasks that should be managed by the organizer, not including tasks
+     *         created via {@link #createRootTask}.
      */
-    void registerTaskOrganizer(ITaskOrganizer organizer);
+    ParceledListSlice<TaskAppearedInfo> registerTaskOrganizer(ITaskOrganizer organizer);
 
     /**
      * Unregisters a previously registered task organizer.
diff --git a/media/java/android/media/tv/TvChannelInfo.aidl b/core/java/android/window/TaskAppearedInfo.aidl
similarity index 72%
copy from media/java/android/media/tv/TvChannelInfo.aidl
copy to core/java/android/window/TaskAppearedInfo.aidl
index 71cd0a7..13eba25f 100644
--- a/media/java/android/media/tv/TvChannelInfo.aidl
+++ b/core/java/android/window/TaskAppearedInfo.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open 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,6 +14,11 @@
  * limitations under the License.
  */
 
-package android.media.tv;
+package android.window;
 
-parcelable TvChannelInfo;
+/**
+ * Data object for the task info provided when a task is presented to an organizer.
+ * @hide
+ */
+parcelable TaskAppearedInfo;
+
diff --git a/core/java/android/window/TaskAppearedInfo.java b/core/java/android/window/TaskAppearedInfo.java
new file mode 100644
index 0000000..2ff331e
--- /dev/null
+++ b/core/java/android/window/TaskAppearedInfo.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.window;
+
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.SurfaceControl;
+
+/**
+ * Data object for the task info provided when a task is presented to an organizer.
+ * @hide
+ */
+@TestApi
+public final class TaskAppearedInfo implements Parcelable {
+
+    @NonNull
+    private final RunningTaskInfo mTaskInfo;
+
+    @NonNull
+    private final SurfaceControl mLeash;
+
+    @NonNull
+    public static final Creator<TaskAppearedInfo> CREATOR = new Creator<TaskAppearedInfo>() {
+        @Override
+        public TaskAppearedInfo createFromParcel(Parcel source) {
+            final RunningTaskInfo taskInfo = source.readTypedObject(RunningTaskInfo.CREATOR);
+            final SurfaceControl leash = source.readTypedObject(SurfaceControl.CREATOR);
+            return new TaskAppearedInfo(taskInfo, leash);
+        }
+
+        @Override
+        public TaskAppearedInfo[] newArray(int size) {
+            return new TaskAppearedInfo[size];
+        }
+
+    };
+
+    public TaskAppearedInfo(@NonNull RunningTaskInfo taskInfo, @NonNull SurfaceControl leash) {
+        mTaskInfo = taskInfo;
+        mLeash = leash;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeTypedObject(mTaskInfo, flags);
+        dest.writeTypedObject(mLeash, flags);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * @return the task info.
+     */
+    @NonNull
+    public RunningTaskInfo getTaskInfo() {
+        return mTaskInfo;
+    }
+
+    /**
+     * @return the leash for the task.
+     */
+    @NonNull
+    public SurfaceControl getLeash() {
+        return mLeash;
+    }
+}
diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java
index d8f2bb2..6c739be 100644
--- a/core/java/android/window/TaskOrganizer.java
+++ b/core/java/android/window/TaskOrganizer.java
@@ -17,6 +17,7 @@
 package android.window;
 
 import android.annotation.BinderThread;
+import android.annotation.CallSuper;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -28,6 +29,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.util.List;
+import java.util.concurrent.Executor;
 
 /**
  * Interface for ActivityTaskManager/WindowManager to delegate control of tasks.
@@ -36,25 +38,35 @@
 @TestApi
 public class TaskOrganizer extends WindowOrganizer {
 
-    private ITaskOrganizerController mTaskOrganizerController;
+    private final ITaskOrganizerController mTaskOrganizerController;
+    // Callbacks WM Core are posted on this executor if it isn't null, otherwise direct calls are
+    // made on the incoming binder call.
+    private final Executor mExecutor;
 
     public TaskOrganizer() {
-        mTaskOrganizerController = getController();
+        this(null /*taskOrganizerController*/, null /*executor*/);
     }
 
     /** @hide */
     @VisibleForTesting
-    public TaskOrganizer(ITaskOrganizerController taskOrganizerController) {
-        mTaskOrganizerController = taskOrganizerController;
+    public TaskOrganizer(ITaskOrganizerController taskOrganizerController, Executor executor) {
+        mExecutor = executor != null ? executor : command -> command.run();
+        mTaskOrganizerController = taskOrganizerController != null
+                ? taskOrganizerController : getController();
     }
 
     /**
      * Register a TaskOrganizer to manage tasks as they enter a supported windowing mode.
+     *
+     * @return a list of the tasks that should be managed by the organizer, not including tasks
+     *         created via {@link #createRootTask}.
      */
     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
-    public final void registerOrganizer() {
+    @CallSuper
+    @NonNull
+    public List<TaskAppearedInfo> registerOrganizer() {
         try {
-            mTaskOrganizerController.registerTaskOrganizer(mInterface);
+            return mTaskOrganizerController.registerTaskOrganizer(mInterface).getList();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -62,7 +74,8 @@
 
     /** Unregisters a previously registered task organizer. */
     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
-    public final void unregisterOrganizer() {
+    @CallSuper
+    public void unregisterOrganizer() {
         try {
             mTaskOrganizerController.unregisterTaskOrganizer(mInterface);
         } catch (RemoteException e) {
@@ -175,22 +188,22 @@
 
         @Override
         public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
-            TaskOrganizer.this.onTaskAppeared(taskInfo, leash);
+            mExecutor.execute(() -> TaskOrganizer.this.onTaskAppeared(taskInfo, leash));
         }
 
         @Override
         public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
-            TaskOrganizer.this.onTaskVanished(taskInfo);
+            mExecutor.execute(() -> TaskOrganizer.this.onTaskVanished(taskInfo));
         }
 
         @Override
         public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
-            TaskOrganizer.this.onTaskInfoChanged(info);
+            mExecutor.execute(() -> TaskOrganizer.this.onTaskInfoChanged(info));
         }
 
         @Override
         public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo info) {
-            TaskOrganizer.this.onBackPressedOnTaskRoot(info);
+            mExecutor.execute(() -> TaskOrganizer.this.onBackPressedOnTaskRoot(info));
         }
     };
 
diff --git a/core/java/android/window/VirtualDisplayTaskEmbedder.java b/core/java/android/window/VirtualDisplayTaskEmbedder.java
index db27d62..be5d55a 100644
--- a/core/java/android/window/VirtualDisplayTaskEmbedder.java
+++ b/core/java/android/window/VirtualDisplayTaskEmbedder.java
@@ -63,12 +63,10 @@
 
     // For Virtual Displays
     private int mDisplayDensityDpi;
-    private final boolean mSingleTaskInstance;
     private final boolean mUsePublicVirtualDisplay;
     private final boolean mUseTrustedDisplay;
     private VirtualDisplay mVirtualDisplay;
     private Insets mForwardedInsets;
-    private DisplayMetrics mTmpDisplayMetrics;
     private TaskStackListener mTaskStackListener;
 
     /**
@@ -76,14 +74,10 @@
      *
      * @param context the context
      * @param host the host for this embedded task
-     * @param singleTaskInstance whether to apply a single-task constraint to this container,
-     *                           only applicable if virtual displays are used
      */
     public VirtualDisplayTaskEmbedder(Context context, VirtualDisplayTaskEmbedder.Host host,
-            boolean singleTaskInstance, boolean usePublicVirtualDisplay,
-            boolean useTrustedDisplay) {
+            boolean usePublicVirtualDisplay, boolean useTrustedDisplay) {
         super(context, host);
-        mSingleTaskInstance = singleTaskInstance;
         mUsePublicVirtualDisplay = usePublicVirtualDisplay;
         mUseTrustedDisplay = useTrustedDisplay;
     }
@@ -128,10 +122,6 @@
             WindowManagerGlobal.getWindowSession().reparentDisplayContent(
                     mHost.getWindow(), mSurfaceControl, displayId);
             wm.dontOverrideDisplayInfo(displayId);
-            if (mSingleTaskInstance) {
-                mContext.getSystemService(ActivityTaskManager.class)
-                        .setDisplayToSingleTaskInstance(displayId);
-            }
             setForwardedInsets(mForwardedInsets);
 
             mTaskStackListener = new TaskStackListenerImpl();
diff --git a/core/java/android/window/WindowContainerToken.java b/core/java/android/window/WindowContainerToken.java
index 96e8b44..22b90b2 100644
--- a/core/java/android/window/WindowContainerToken.java
+++ b/core/java/android/window/WindowContainerToken.java
@@ -17,6 +17,7 @@
 package android.window;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -83,7 +84,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (!(obj instanceof WindowContainerToken)) {
             return false;
         }
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index ba90154..eba4fd2 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -238,10 +238,10 @@
     }
 
     /**
-     * Sets whether a container should ignore the orientation request from apps below it. It
-     * currently only applies to {@link com.android.server.wm.TaskDisplayArea}. When {@code false},
-     * it may rotate based on the orientation request; When {@code true}, it can never specify
-     * orientation, but shows the fixed-orientation apps in the letterbox.
+     * Sets whether a container should ignore the orientation request from apps and windows below
+     * it. It currently only applies to {@link com.android.server.wm.DisplayArea}. When
+     * {@code false}, it may rotate based on the orientation request; When {@code true}, it can
+     * never specify orientation, but shows the fixed-orientation apps below it in the letterbox.
      * @hide
      */
     @NonNull
diff --git a/core/java/com/android/internal/BrightnessSynchronizer.java b/core/java/com/android/internal/BrightnessSynchronizer.java
index 6b8cf63..c98477e 100644
--- a/core/java/com/android/internal/BrightnessSynchronizer.java
+++ b/core/java/com/android/internal/BrightnessSynchronizer.java
@@ -246,10 +246,12 @@
             }
             if (BRIGHTNESS_URI.equals(uri)) {
                 int currentBrightness = getScreenBrightnessInt(mContext);
+                mHandler.removeMessages(MSG_UPDATE_FLOAT);
                 mHandler.obtainMessage(MSG_UPDATE_FLOAT, currentBrightness, 0).sendToTarget();
             } else if (BRIGHTNESS_FLOAT_URI.equals(uri)) {
                 float currentFloat = getScreenBrightnessFloat(mContext);
                 int toSend = Float.floatToIntBits(currentFloat);
+                mHandler.removeMessages(MSG_UPDATE_INT);
                 mHandler.obtainMessage(MSG_UPDATE_INT, toSend, 0).sendToTarget();
             }
         }
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index 491ddba..2b4e09d 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -80,6 +80,8 @@
             "com.android.server.accessibility.MagnificationController";
     public static final ComponentName MAGNIFICATION_COMPONENT_NAME =
             new ComponentName("com.android.server.accessibility", "Magnification");
+    public static final ComponentName REDUCE_BRIGHT_COLORS_COMPONENT_NAME =
+            new ComponentName("com.android.server.accessibility", "ReduceBrightColors");
 
     private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
             .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
@@ -126,6 +128,11 @@
                             Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
                             "1" /* Value to enable */, "0" /* Value to disable */,
                             R.string.color_correction_feature_name));
+            featuresMap.put(REDUCE_BRIGHT_COLORS_COMPONENT_NAME,
+                    new ToggleableFrameworkFeatureInfo(
+                            Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED,
+                            "1" /* Value to enable */, "0" /* Value to disable */,
+                            R.string.reduce_bright_colors_feature_name));
             sFrameworkShortcutFeaturesMap = Collections.unmodifiableMap(featuresMap);
         }
         return sFrameworkShortcutFeaturesMap;
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
index a7c5f6d..9d06bb9 100644
--- a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
@@ -21,6 +21,7 @@
 import static com.android.internal.accessibility.AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME;
 import static com.android.internal.accessibility.AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME;
 import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
+import static com.android.internal.accessibility.AccessibilityShortcutController.REDUCE_BRIGHT_COLORS_COMPONENT_NAME;
 import static com.android.internal.accessibility.util.AccessibilityUtils.getAccessibilityServiceFragmentType;
 import static com.android.internal.accessibility.util.ShortcutUtils.isShortcutContained;
 
@@ -112,7 +113,7 @@
             @ShortcutType int shortcutType) {
         final List<AccessibilityTarget> targets = new ArrayList<>();
         targets.addAll(getAccessibilityFilteredTargets(context, shortcutType));
-        targets.addAll(getWhiteListingFeatureTargets(context, shortcutType));
+        targets.addAll(getAllowListingFeatureTargets(context, shortcutType));
 
         return targets;
     }
@@ -196,12 +197,12 @@
         return targets;
     }
 
-    private static List<AccessibilityTarget> getWhiteListingFeatureTargets(Context context,
+    private static List<AccessibilityTarget> getAllowListingFeatureTargets(Context context,
             @ShortcutType int shortcutType) {
         final List<AccessibilityTarget> targets = new ArrayList<>();
 
-        final InvisibleToggleWhiteListingFeatureTarget magnification =
-                new InvisibleToggleWhiteListingFeatureTarget(context,
+        final InvisibleToggleAllowListingFeatureTarget magnification =
+                new InvisibleToggleAllowListingFeatureTarget(context,
                 shortcutType,
                 isShortcutContained(context, shortcutType, MAGNIFICATION_CONTROLLER_NAME),
                 MAGNIFICATION_CONTROLLER_NAME,
@@ -209,8 +210,8 @@
                 context.getDrawable(R.drawable.ic_accessibility_magnification),
                 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED);
 
-        final ToggleWhiteListingFeatureTarget daltonizer =
-                new ToggleWhiteListingFeatureTarget(context,
+        final ToggleAllowListingFeatureTarget daltonizer =
+                new ToggleAllowListingFeatureTarget(context,
                 shortcutType,
                 isShortcutContained(context, shortcutType,
                         DALTONIZER_COMPONENT_NAME.flattenToString()),
@@ -219,8 +220,8 @@
                 context.getDrawable(R.drawable.ic_accessibility_color_correction),
                 Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED);
 
-        final ToggleWhiteListingFeatureTarget colorInversion =
-                new ToggleWhiteListingFeatureTarget(context,
+        final ToggleAllowListingFeatureTarget colorInversion =
+                new ToggleAllowListingFeatureTarget(context,
                 shortcutType,
                 isShortcutContained(context, shortcutType,
                         COLOR_INVERSION_COMPONENT_NAME.flattenToString()),
@@ -229,9 +230,21 @@
                 context.getDrawable(R.drawable.ic_accessibility_color_inversion),
                 Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
 
+        // TODO: Update with shortcut icon
+        final ToggleAllowListingFeatureTarget reduceBrightColors =
+                new ToggleAllowListingFeatureTarget(context,
+                        shortcutType,
+                        isShortcutContained(context, shortcutType,
+                                REDUCE_BRIGHT_COLORS_COMPONENT_NAME.flattenToString()),
+                        REDUCE_BRIGHT_COLORS_COMPONENT_NAME.flattenToString(),
+                        context.getString(R.string.reduce_bright_colors_feature_name),
+                        null,
+                        Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED);
+
         targets.add(magnification);
         targets.add(daltonizer);
         targets.add(colorInversion);
+        targets.add(reduceBrightColors);
 
         return targets;
     }
diff --git a/core/java/com/android/internal/accessibility/dialog/InvisibleToggleWhiteListingFeatureTarget.java b/core/java/com/android/internal/accessibility/dialog/InvisibleToggleAllowListingFeatureTarget.java
similarity index 91%
rename from core/java/com/android/internal/accessibility/dialog/InvisibleToggleWhiteListingFeatureTarget.java
rename to core/java/com/android/internal/accessibility/dialog/InvisibleToggleAllowListingFeatureTarget.java
index acd101b..e78036d 100644
--- a/core/java/com/android/internal/accessibility/dialog/InvisibleToggleWhiteListingFeatureTarget.java
+++ b/core/java/com/android/internal/accessibility/dialog/InvisibleToggleAllowListingFeatureTarget.java
@@ -26,9 +26,9 @@
  * Extension for {@link AccessibilityTarget} with {@link AccessibilityFragmentType#INVISIBLE_TOGGLE}
  * type.
  */
-class InvisibleToggleWhiteListingFeatureTarget extends AccessibilityTarget {
+class InvisibleToggleAllowListingFeatureTarget extends AccessibilityTarget {
 
-    InvisibleToggleWhiteListingFeatureTarget(Context context, @ShortcutType int shortcutType,
+    InvisibleToggleAllowListingFeatureTarget(Context context, @ShortcutType int shortcutType,
             boolean isShortcutSwitched, String id, CharSequence label, Drawable icon, String key) {
         super(context, shortcutType, AccessibilityFragmentType.INVISIBLE_TOGGLE,
                 isShortcutSwitched, id, label, icon, key);
diff --git a/core/java/com/android/internal/accessibility/dialog/ToggleWhiteListingFeatureTarget.java b/core/java/com/android/internal/accessibility/dialog/ToggleAllowListingFeatureTarget.java
similarity index 94%
rename from core/java/com/android/internal/accessibility/dialog/ToggleWhiteListingFeatureTarget.java
rename to core/java/com/android/internal/accessibility/dialog/ToggleAllowListingFeatureTarget.java
index 5ab9eb8..38aac70 100644
--- a/core/java/com/android/internal/accessibility/dialog/ToggleWhiteListingFeatureTarget.java
+++ b/core/java/com/android/internal/accessibility/dialog/ToggleAllowListingFeatureTarget.java
@@ -32,9 +32,9 @@
  * Extension for {@link AccessibilityTarget} with {@link AccessibilityFragmentType#TOGGLE}
  * type.
  */
-class ToggleWhiteListingFeatureTarget extends AccessibilityTarget {
+class ToggleAllowListingFeatureTarget extends AccessibilityTarget {
 
-    ToggleWhiteListingFeatureTarget(Context context, @ShortcutType int shortcutType,
+    ToggleAllowListingFeatureTarget(Context context, @ShortcutType int shortcutType,
             boolean isShortcutSwitched, String id, CharSequence label, Drawable icon, String key) {
         super(context, shortcutType, AccessibilityFragmentType.TOGGLE,
                 isShortcutSwitched, id, label, icon, key);
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 61a625e..cb4f285 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -1655,18 +1655,24 @@
     private void showTargetDetails(DisplayResolveInfo ti) {
         if (ti == null) return;
 
-        List<DisplayResolveInfo> targetList;
+        ArrayList<DisplayResolveInfo> targetList;
 
         // For multiple targets, include info on all targets
         if (ti instanceof MultiDisplayResolveInfo) {
             MultiDisplayResolveInfo mti = (MultiDisplayResolveInfo) ti;
             targetList = mti.getTargets();
         } else {
-            targetList = Collections.singletonList(ti);
+            targetList = new ArrayList<DisplayResolveInfo>();
+            targetList.add(ti);
         }
 
-        ChooserTargetActionsDialogFragment f = new ChooserTargetActionsDialogFragment(
-                targetList, mChooserMultiProfilePagerAdapter.getCurrentUserHandle());
+        ChooserTargetActionsDialogFragment f = new ChooserTargetActionsDialogFragment();
+        Bundle b = new Bundle();
+        b.putParcelable(ChooserTargetActionsDialogFragment.USER_HANDLE_KEY,
+                mChooserMultiProfilePagerAdapter.getCurrentUserHandle());
+        b.putParcelableArrayList(ChooserTargetActionsDialogFragment.TARGET_INFOS_KEY,
+                targetList);
+        f.setArguments(b);
 
         f.show(getFragmentManager(), TARGET_DETAILS_FRAGMENT_TAG);
     }
@@ -1725,9 +1731,14 @@
         if (targetInfo instanceof MultiDisplayResolveInfo) {
             MultiDisplayResolveInfo mti = (MultiDisplayResolveInfo) targetInfo;
             if (!mti.hasSelected()) {
-                ChooserStackedAppDialogFragment f = new ChooserStackedAppDialogFragment(
-                        mti, which,
+                ChooserStackedAppDialogFragment f = new ChooserStackedAppDialogFragment();
+                Bundle b = new Bundle();
+                b.putParcelable(ChooserTargetActionsDialogFragment.USER_HANDLE_KEY,
                         mChooserMultiProfilePagerAdapter.getCurrentUserHandle());
+                b.putObject(ChooserStackedAppDialogFragment.MULTI_DRI_KEY,
+                        mti);
+                b.putInt(ChooserStackedAppDialogFragment.WHICH_KEY, which);
+                f.setArguments(b);
 
                 f.show(getFragmentManager(), TARGET_DETAILS_FRAGMENT_TAG);
                 return;
@@ -1855,6 +1866,7 @@
 
     @VisibleForTesting
     protected void queryTargetServices(ChooserListAdapter adapter) {
+
         mQueriedTargetServicesTimeMs = System.currentTimeMillis();
 
         Context selectedProfileContext = createContextAsUser(
@@ -1871,15 +1883,14 @@
                 continue;
             }
             final ActivityInfo ai = dri.getResolveInfo().activityInfo;
-            if (ChooserFlags.USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS
-                    && sm.hasShareTargets(ai.packageName)) {
+            if (sm.hasShareTargets(ai.packageName)) {
                 // Share targets will be queried from ShortcutManager
                 continue;
             }
             final Bundle md = ai.metaData;
             final String serviceName = md != null ? convertServiceName(ai.packageName,
                     md.getString(ChooserTargetService.META_DATA_NAME)) : null;
-            if (serviceName != null) {
+            if (serviceName != null && ChooserFlags.USE_SERVICE_TARGETS_FOR_DIRECT_TARGETS) {
                 final ComponentName serviceComponent = new ComponentName(
                         ai.packageName, serviceName);
 
@@ -2883,8 +2894,7 @@
             return;
         }
 
-        if (ChooserFlags.USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS
-                || ChooserFlags.USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS) {
+        if (ChooserFlags.USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS) {
             if (DEBUG) {
                 Log.d(TAG, "querying direct share targets from ShortcutManager");
             }
diff --git a/core/java/com/android/internal/app/ChooserFlags.java b/core/java/com/android/internal/app/ChooserFlags.java
index f1f1dbf..3e26679 100644
--- a/core/java/com/android/internal/app/ChooserFlags.java
+++ b/core/java/com/android/internal/app/ChooserFlags.java
@@ -17,22 +17,29 @@
 package com.android.internal.app;
 
 import android.app.prediction.AppPredictionManager;
+import android.provider.DeviceConfig;
+
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
 
 /**
  * Common flags for {@link ChooserListAdapter} and {@link ChooserActivity}.
  */
 public class ChooserFlags {
-    /**
-     * If set to true, use ShortcutManager to retrieve the matching direct share targets, instead of
-     * binding to every ChooserTargetService implementation.
-     */
-    // TODO(b/121287573): Replace with a system flag (setprop?)
-    public static final boolean USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS = true;
 
     /**
-     * If {@link ChooserFlags#USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS} and this is set to true,
-     * {@link AppPredictionManager} will be queried for direct share targets.
+     * Whether to use the deprecated {@link android.service.chooser.ChooserTargetService} API for
+     * direct share targets. If true, both CTS and Shortcuts will be used to find Direct Share
+     * targets. If false, only Shortcuts will be used.
+     */
+    public static final boolean USE_SERVICE_TARGETS_FOR_DIRECT_TARGETS =
+            DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
+                SystemUiDeviceConfigFlags.SHARE_USE_SERVICE_TARGETS, true);
+
+    /**
+     * Whether to use {@link AppPredictionManager} to query for direct share targets (as opposed to
+     * talking directly to {@link android.content.pm.ShortcutManager}.
      */
     // TODO(b/123089490): Replace with system flag
     static final boolean USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS = true;
 }
+
diff --git a/core/java/com/android/internal/app/ChooserStackedAppDialogFragment.java b/core/java/com/android/internal/app/ChooserStackedAppDialogFragment.java
index fdeba8f..726622b 100644
--- a/core/java/com/android/internal/app/ChooserStackedAppDialogFragment.java
+++ b/core/java/com/android/internal/app/ChooserStackedAppDialogFragment.java
@@ -20,6 +20,7 @@
 import android.content.DialogInterface;
 import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
+import android.os.Bundle;
 import android.os.UserHandle;
 
 import com.android.internal.app.chooser.DisplayResolveInfo;
@@ -32,17 +33,26 @@
 public class ChooserStackedAppDialogFragment extends ChooserTargetActionsDialogFragment
         implements DialogInterface.OnClickListener {
 
+    static final String WHICH_KEY = "which_key";
+    static final String MULTI_DRI_KEY = "multi_dri_key";
+
     private MultiDisplayResolveInfo mMultiDisplayResolveInfo;
     private int mParentWhich;
 
-    public ChooserStackedAppDialogFragment() {
+    public ChooserStackedAppDialogFragment() {}
+
+    void setStateFromBundle(Bundle b) {
+        mMultiDisplayResolveInfo = (MultiDisplayResolveInfo) b.get(MULTI_DRI_KEY);
+        mTargetInfos = mMultiDisplayResolveInfo.getTargets();
+        mUserHandle = (UserHandle) b.get(USER_HANDLE_KEY);
+        mParentWhich = b.getInt(WHICH_KEY);
     }
 
-    public ChooserStackedAppDialogFragment(MultiDisplayResolveInfo targets,
-            int parentWhich, UserHandle userHandle) {
-        super(targets.getTargets(), userHandle);
-        mMultiDisplayResolveInfo = targets;
-        mParentWhich = parentWhich;
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putInt(WHICH_KEY, mParentWhich);
+        outState.putParcelable(MULTI_DRI_KEY, mMultiDisplayResolveInfo);
     }
 
     @Override
@@ -53,6 +63,7 @@
 
     @Override
     protected Drawable getItemIcon(DisplayResolveInfo dri) {
+
         // Show no icon for the group disambig dialog, null hides the imageview
         return null;
     }
diff --git a/core/java/com/android/internal/app/ChooserTargetActionsDialogFragment.java b/core/java/com/android/internal/app/ChooserTargetActionsDialogFragment.java
index 21063d5..9afc0e9 100644
--- a/core/java/com/android/internal/app/ChooserTargetActionsDialogFragment.java
+++ b/core/java/com/android/internal/app/ChooserTargetActionsDialogFragment.java
@@ -32,7 +32,6 @@
 import android.content.DialogInterface;
 import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
-import android.content.res.Configuration;
 import android.graphics.Color;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
@@ -59,25 +58,51 @@
 public class ChooserTargetActionsDialogFragment extends DialogFragment
         implements DialogInterface.OnClickListener {
 
-    protected List<DisplayResolveInfo> mTargetInfos = new ArrayList<>();
+    protected ArrayList<DisplayResolveInfo> mTargetInfos = new ArrayList<>();
     protected UserHandle mUserHandle;
 
-    public ChooserTargetActionsDialogFragment() {
+    public static final String USER_HANDLE_KEY = "user_handle";
+    public static final String TARGET_INFOS_KEY = "target_infos";
+
+    public ChooserTargetActionsDialogFragment() {}
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        if (savedInstanceState != null) {
+            setStateFromBundle(savedInstanceState);
+        } else {
+            setStateFromBundle(getArguments());
+        }
     }
 
-    public ChooserTargetActionsDialogFragment(List<DisplayResolveInfo> targets,
-            UserHandle userHandle) {
-        mUserHandle = userHandle;
-        mTargetInfos = targets;
+    void setStateFromBundle(Bundle b) {
+        mTargetInfos = (ArrayList<DisplayResolveInfo>) b.get(TARGET_INFOS_KEY);
+        mUserHandle = (UserHandle) b.get(USER_HANDLE_KEY);
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+
+        outState.putParcelable(ChooserTargetActionsDialogFragment.USER_HANDLE_KEY,
+                mUserHandle);
+        outState.putParcelableArrayList(ChooserTargetActionsDialogFragment.TARGET_INFOS_KEY,
+                mTargetInfos);
     }
 
     /**
      * Recreate the layout from scratch to match new Sharesheet redlines
      */
+    @Override
     public View onCreateView(LayoutInflater inflater,
             @Nullable ViewGroup container,
             Bundle savedInstanceState) {
-
+        if (savedInstanceState != null) {
+            setStateFromBundle(savedInstanceState);
+        } else {
+            setStateFromBundle(getArguments());
+        }
         // Make the background transparent to show dialog rounding
         Optional.of(getDialog()).map(Dialog::getWindow)
                 .ifPresent(window -> {
@@ -204,12 +229,4 @@
                 mTargetInfos.get(0).getResolveInfo());
     }
 
-    @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        // Dismiss on config changed (eg: rotation)
-        // TODO: Maintain state on config change
-        super.onConfigurationChanged(newConfig);
-        dismiss();
-    }
-
 }
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index 51e56b7..04146bc 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -56,6 +56,13 @@
             String proxiedAttributionTag, int proxyUid, String proxyPackageName,
             String proxyAttributionTag, boolean shouldCollectAsyncNotedOp, String message,
             boolean shouldCollectMessage);
+    int startProxyOperation(IBinder clientId, int code, int proxiedUid, String proxiedPackageName,
+            @nullable String proxiedAttributionTag, int proxyUid, String proxyPackageName,
+            @nullable String proxyAttributionTag, boolean startIfModeDefault,
+            boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage);
+    void finishProxyOperation(IBinder clientId, int code, int proxiedUid, String proxiedPackageName,
+            @nullable String proxiedAttributionTag, int proxyUid, String proxyPackageName,
+            @nullable String proxyAttributionTag);
 
     // Remaining methods are only used in Java.
     int checkPackage(int uid, String packageName);
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index c3b5703..83cbe38 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.INTERACT_ACROSS_PROFILES;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.PermissionChecker.PID_UNKNOWN;
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
 
 import android.annotation.Nullable;
 import android.annotation.StringRes;
@@ -69,7 +70,9 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
+import android.view.Window;
 import android.view.WindowInsets;
+import android.view.WindowManager;
 import android.widget.AbsListView;
 import android.widget.AdapterView;
 import android.widget.Button;
@@ -101,7 +104,6 @@
 import java.util.Objects;
 import java.util.Set;
 
-
 /**
  * This activity is displayed when the system attempts to start an Intent for
  * which there is more than one matching activity, allowing the user to decide
@@ -822,6 +824,8 @@
     @Override
     protected void onStart() {
         super.onStart();
+
+        this.getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
         if (shouldShowTabs()) {
             mWorkProfileStateReceiver = createWorkProfileStateReceiver();
             registerWorkProfileStateReceiver();
@@ -849,6 +853,12 @@
     @Override
     protected void onStop() {
         super.onStop();
+
+        final Window window = this.getWindow();
+        final WindowManager.LayoutParams attrs = window.getAttributes();
+        attrs.privateFlags &= ~SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+        window.setAttributes(attrs);
+
         if (mRegistered) {
             mPersonalPackageMonitor.unregister();
             if (mWorkPackageMonitor != null) {
@@ -1281,7 +1291,7 @@
     private void safelyStartActivityInternal(TargetInfo cti) {
         // If the target is suspended, the activity will not be successfully launched.
         // Do not unregister from package manager updates in this case
-        if (!cti.isSuspended()) {
+        if (!cti.isSuspended() && mRegistered) {
             if (mPersonalPackageMonitor != null) {
                 mPersonalPackageMonitor.unregister();
             }
diff --git a/core/java/com/android/internal/app/SuspendedAppActivity.java b/core/java/com/android/internal/app/SuspendedAppActivity.java
index d8eaeda..52dc7e6 100644
--- a/core/java/com/android/internal/app/SuspendedAppActivity.java
+++ b/core/java/com/android/internal/app/SuspendedAppActivity.java
@@ -191,8 +191,9 @@
         mOnUnsuspend = intent.getParcelableExtra(EXTRA_UNSUSPEND_INTENT);
         if (mSuppliedDialogInfo != null) {
             try {
-                mSuspendingAppResources = mPm.getResourcesForApplicationAsUser(mSuspendingPackage,
-                        mUserId);
+                mSuspendingAppResources = createContextAsUser(
+                        UserHandle.of(mUserId), /* flags */ 0).getPackageManager()
+                        .getResourcesForApplication(mSuspendedPackage);
             } catch (PackageManager.NameNotFoundException ne) {
                 Slog.e(TAG, "Could not find resources for " + mSuspendingPackage, ne);
             }
diff --git a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
index fe0e7d0..b00148a 100644
--- a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
+++ b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
@@ -27,19 +27,22 @@
 import android.content.pm.ResolveInfo;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.os.UserHandle;
 
 import com.android.internal.app.ResolverActivity;
 import com.android.internal.app.ResolverListAdapter.ResolveInfoPresentationGetter;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 /**
  * A TargetInfo plus additional information needed to render it (such as icon and label) and
  * resolve it to an activity.
  */
-public class DisplayResolveInfo implements TargetInfo {
+public class DisplayResolveInfo implements TargetInfo, Parcelable {
     // Temporary flag for new chooser delegate behavior. There are occassional token
     // permission errors from bouncing through the delegate. Watch out before reenabling:
     // b/157272342 is one example but this issue has been reported many times
@@ -202,4 +205,41 @@
         mPinned = pinned;
     }
 
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeCharSequence(mDisplayLabel);
+        dest.writeCharSequence(mExtendedInfo);
+        dest.writeParcelable(mResolvedIntent, 0);
+        dest.writeParcelableArray((Intent[]) mSourceIntents.toArray(), 0);
+        dest.writeBoolean(mIsSuspended);
+        dest.writeBoolean(mPinned);
+        dest.writeParcelable(mResolveInfo, 0);
+    }
+
+    public static final Parcelable.Creator<DisplayResolveInfo> CREATOR =
+            new Parcelable.Creator<DisplayResolveInfo>() {
+        public DisplayResolveInfo createFromParcel(Parcel in) {
+            return new DisplayResolveInfo(in);
+        }
+
+        public DisplayResolveInfo[] newArray(int size) {
+            return new DisplayResolveInfo[size];
+        }
+    };
+
+    private DisplayResolveInfo(Parcel in) {
+        mDisplayLabel = in.readCharSequence();
+        mExtendedInfo = in.readCharSequence();
+        mResolvedIntent = in.readParcelable(null /* ClassLoader */);
+        mSourceIntents.addAll(
+                Arrays.asList((Intent[]) in.readParcelableArray(null /* ClassLoader */)));
+        mIsSuspended = in.readBoolean();
+        mPinned = in.readBoolean();
+        mResolveInfo = in.readParcelable(null /* ClassLoader */);
+    }
 }
diff --git a/core/java/com/android/internal/app/chooser/MultiDisplayResolveInfo.java b/core/java/com/android/internal/app/chooser/MultiDisplayResolveInfo.java
index cf921d7..2828e25 100644
--- a/core/java/com/android/internal/app/chooser/MultiDisplayResolveInfo.java
+++ b/core/java/com/android/internal/app/chooser/MultiDisplayResolveInfo.java
@@ -23,14 +23,13 @@
 import com.android.internal.app.ResolverActivity;
 
 import java.util.ArrayList;
-import java.util.List;
 
 /**
  * Represents a "stack" of chooser targets for various activities within the same component.
  */
 public class MultiDisplayResolveInfo extends DisplayResolveInfo {
 
-    List<DisplayResolveInfo> mTargetInfos = new ArrayList<>();
+    ArrayList<DisplayResolveInfo> mTargetInfos = new ArrayList<>();
     // We'll use this DRI for basic presentation info - eg icon, name.
     final DisplayResolveInfo mBaseInfo;
     // Index of selected target
@@ -61,7 +60,7 @@
     /**
      * List of all DisplayResolveInfos included in this target.
      */
-    public List<DisplayResolveInfo> getTargets() {
+    public ArrayList<DisplayResolveInfo> getTargets() {
         return mTargetInfos;
     }
 
diff --git a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
index 28a9601..ce75f45 100644
--- a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
+++ b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
@@ -38,7 +38,6 @@
 import android.util.Log;
 
 import com.android.internal.app.ChooserActivity;
-import com.android.internal.app.ChooserFlags;
 import com.android.internal.app.ResolverActivity;
 import com.android.internal.app.ResolverListAdapter.ActivityInfoPresentationGetter;
 import com.android.internal.app.SimpleIconFactory;
@@ -147,7 +146,7 @@
         final Icon icon = target.getIcon();
         if (icon != null) {
             directShareIcon = icon.loadDrawable(mContext);
-        } else if (ChooserFlags.USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS && shortcutInfo != null) {
+        } else if (shortcutInfo != null) {
             LauncherApps launcherApps = (LauncherApps) mContext.getSystemService(
                     Context.LAUNCHER_APPS_SERVICE);
             directShareIcon = launcherApps.getShortcutIconDrawable(shortcutInfo, 0);
diff --git a/core/java/com/android/internal/compat/ChangeReporter.java b/core/java/com/android/internal/compat/ChangeReporter.java
index 5e886a6..7a87be3 100644
--- a/core/java/com/android/internal/compat/ChangeReporter.java
+++ b/core/java/com/android/internal/compat/ChangeReporter.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.compat;
 
+import static android.text.TextUtils.formatSimple;
+
 import android.annotation.IntDef;
 import android.util.Log;
 import android.util.Slog;
@@ -175,7 +177,7 @@
     }
 
     private void debugLog(int uid, long changeId, int state) {
-        String message = String.format("Compat change id reported: %d; UID %d; state: %s", changeId,
+        String message = formatSimple("Compat change id reported: %d; UID %d; state: %s", changeId,
                 uid, stateToString(state));
         if (mSource == SOURCE_SYSTEM_SERVER) {
             Slog.d(TAG, message);
diff --git a/core/java/com/android/internal/compat/CompatibilityChangeInfo.java b/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
index 9ba0259..670ca9f 100644
--- a/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
+++ b/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
@@ -28,7 +28,7 @@
 public class CompatibilityChangeInfo implements Parcelable {
     private final long mChangeId;
     private final @Nullable String mName;
-    private final int mEnableAfterTargetSdk;
+    private final int mEnableSinceTargetSdk;
     private final boolean mDisabled;
     private final boolean mLoggingOnly;
     private final @Nullable String mDescription;
@@ -42,8 +42,8 @@
         return mName;
     }
 
-    public int getEnableAfterTargetSdk() {
-        return mEnableAfterTargetSdk;
+    public int getEnableSinceTargetSdk() {
+        return mEnableSinceTargetSdk;
     }
 
     public boolean getDisabled() {
@@ -59,20 +59,37 @@
     }
 
     public CompatibilityChangeInfo(
-            Long changeId, String name, int enableAfterTargetSdk, boolean disabled,
-            boolean loggingOnly, String description) {
+            Long changeId, String name, int enableAfterTargetSdk, int enableSinceTargetSdk,
+            boolean disabled, boolean loggingOnly, String description) {
         this.mChangeId = changeId;
         this.mName = name;
-        this.mEnableAfterTargetSdk = enableAfterTargetSdk;
+        if (enableAfterTargetSdk > 0) {
+            // Need to maintain support for @EnabledAfter(X), but make it equivalent to
+            // @EnabledSince(X+1)
+            this.mEnableSinceTargetSdk = enableAfterTargetSdk + 1;
+        } else if (enableSinceTargetSdk > 0) {
+            this.mEnableSinceTargetSdk = enableSinceTargetSdk;
+        } else {
+            this.mEnableSinceTargetSdk = -1;
+        }
         this.mDisabled = disabled;
         this.mLoggingOnly = loggingOnly;
         this.mDescription = description;
     }
 
+    public CompatibilityChangeInfo(CompatibilityChangeInfo other) {
+        this.mChangeId = other.mChangeId;
+        this.mName = other.mName;
+        this.mEnableSinceTargetSdk = other.mEnableSinceTargetSdk;
+        this.mDisabled = other.mDisabled;
+        this.mLoggingOnly = other.mLoggingOnly;
+        this.mDescription = other.mDescription;
+    }
+
     private CompatibilityChangeInfo(Parcel in) {
         mChangeId = in.readLong();
         mName = in.readString();
-        mEnableAfterTargetSdk = in.readInt();
+        mEnableSinceTargetSdk = in.readInt();
         mDisabled = in.readBoolean();
         mLoggingOnly = in.readBoolean();
         mDescription = in.readString();
@@ -87,7 +104,7 @@
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeLong(mChangeId);
         dest.writeString(mName);
-        dest.writeInt(mEnableAfterTargetSdk);
+        dest.writeInt(mEnableSinceTargetSdk);
         dest.writeBoolean(mDisabled);
         dest.writeBoolean(mLoggingOnly);
         dest.writeString(mDescription);
@@ -100,8 +117,8 @@
         if (getName() != null) {
             sb.append("; name=").append(getName());
         }
-        if (getEnableAfterTargetSdk() != -1) {
-            sb.append("; enableAfterTargetSdk=").append(getEnableAfterTargetSdk());
+        if (getEnableSinceTargetSdk() != -1) {
+            sb.append("; enableSinceTargetSdk=").append(getEnableSinceTargetSdk());
         }
         if (getDisabled()) {
             sb.append("; disabled");
@@ -123,7 +140,7 @@
         CompatibilityChangeInfo that = (CompatibilityChangeInfo) o;
         return this.mChangeId == that.mChangeId
                 && this.mName.equals(that.mName)
-                && this.mEnableAfterTargetSdk == that.mEnableAfterTargetSdk
+                && this.mEnableSinceTargetSdk == that.mEnableSinceTargetSdk
                 && this.mDisabled == that.mDisabled
                 && this.mLoggingOnly == that.mLoggingOnly
                 && this.mDescription.equals(that.mDescription);
diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl
index 6408def..cc266d6 100644
--- a/core/java/com/android/internal/compat/IPlatformCompat.aidl
+++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl
@@ -164,7 +164,7 @@
     boolean clearOverride(long changeId, String packageName);
 
     /**
-     * Enable all compatibility changes which have enabledAfterTargetSdk ==
+     * Enable all compatibility changes which have enabledSinceTargetSdk ==
      * {@param targetSdkVersion} for an app, subject to the policy. Kills the app to allow the
      * changes to take effect.
      *
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 1ce246a..a2af4d6 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -132,6 +132,11 @@
      */
     public static final String PROPERTY_MIC_CAMERA_ENABLED = "camera_mic_icons_enabled";
 
+    /**
+     * Whether to show app ops chip for location.
+     */
+    public static final String PROPERTY_LOCATION_INDICATORS_ENABLED = "location_indicators_enabled";
+
     // Flags related to Assistant
 
     /**
@@ -429,6 +434,14 @@
      */
     public static final String BACK_GESTURE_ML_MODEL_THRESHOLD = "back_gesture_ml_model_threshold";
 
+    /**
+     * (boolean) Sharesheet - Whether to use the deprecated
+     * {@link android.service.chooser.ChooserTargetService} API for
+     *  direct share targets. If true, both CTS and Shortcuts will be used to find Direct
+     *  Share targets. If false, only Shortcuts will be used.
+     */
+    public static final String SHARE_USE_SERVICE_TARGETS = "share_use_service_targets";
+
 
     private SystemUiDeviceConfigFlags() {
     }
diff --git a/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java b/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java
index f1398bf..70505bc 100644
--- a/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java
+++ b/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java
@@ -25,6 +25,7 @@
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Base class representing a remote service that can queue multiple pending requests while not
@@ -39,7 +40,7 @@
 
     private final int mInitialCapacity;
 
-    protected ArrayList<BasePendingRequest<S, I>> mPendingRequests;
+    protected @NonNull List<BasePendingRequest<S, I>> mPendingRequests;
 
     public AbstractMultiplePendingRequestsRemoteService(@NonNull Context context,
             @NonNull String serviceInterface, @NonNull ComponentName componentName, int userId,
@@ -48,35 +49,36 @@
         super(context, serviceInterface, componentName, userId, callback, handler, bindingFlags,
                 verbose);
         mInitialCapacity = initialCapacity;
+        mPendingRequests = new ArrayList<>(mInitialCapacity);
     }
 
     @Override // from AbstractRemoteService
     void handlePendingRequests() {
-        if (mPendingRequests != null) {
+        synchronized (mPendingRequests) {
             final int size = mPendingRequests.size();
             if (mVerbose) Slog.v(mTag, "Sending " + size + " pending requests");
             for (int i = 0; i < size; i++) {
                 mPendingRequests.get(i).run();
             }
-            mPendingRequests = null;
+            mPendingRequests.clear();
         }
     }
 
     @Override // from AbstractRemoteService
     protected void handleOnDestroy() {
-        if (mPendingRequests != null) {
+        synchronized (mPendingRequests) {
             final int size = mPendingRequests.size();
             if (mVerbose) Slog.v(mTag, "Canceling " + size + " pending requests");
             for (int i = 0; i < size; i++) {
                 mPendingRequests.get(i).cancel();
             }
-            mPendingRequests = null;
+            mPendingRequests.clear();
         }
     }
 
     @Override // from AbstractRemoteService
     final void handleBindFailure() {
-        if (mPendingRequests != null) {
+        synchronized (mPendingRequests) {
             final int size = mPendingRequests.size();
             if (mVerbose) Slog.v(mTag, "Sending failure to " + size + " pending requests");
             for (int i = 0; i < size; i++) {
@@ -84,7 +86,7 @@
                 request.onFailed();
                 request.finish();
             }
-            mPendingRequests = null;
+            mPendingRequests.clear();
         }
     }
 
@@ -94,18 +96,21 @@
 
         pw.append(prefix).append("initialCapacity=").append(String.valueOf(mInitialCapacity))
                 .println();
-        final int size = mPendingRequests == null ? 0 : mPendingRequests.size();
+        int size;
+        synchronized (mPendingRequests) {
+            size = mPendingRequests.size();
+        }
         pw.append(prefix).append("pendingRequests=").append(String.valueOf(size)).println();
     }
 
     @Override // from AbstractRemoteService
     void handlePendingRequestWhileUnBound(@NonNull BasePendingRequest<S, I> pendingRequest) {
-        if (mPendingRequests == null) {
-            mPendingRequests = new ArrayList<>(mInitialCapacity);
-        }
-        mPendingRequests.add(pendingRequest);
-        if (mVerbose) {
-            Slog.v(mTag, "queued " + mPendingRequests.size() + " requests; last=" + pendingRequest);
+        synchronized (mPendingRequests) {
+            mPendingRequests.add(pendingRequest);
+            if (mVerbose) {
+                Slog.v(mTag,
+                        "queued " + mPendingRequests.size() + " requests; last=" + pendingRequest);
+            }
         }
     }
 }
diff --git a/core/java/com/android/internal/inputmethod/CancellationGroup.java b/core/java/com/android/internal/inputmethod/CancellationGroup.java
index 09c9d12..a4a2208 100644
--- a/core/java/com/android/internal/inputmethod/CancellationGroup.java
+++ b/core/java/com/android/internal/inputmethod/CancellationGroup.java
@@ -263,6 +263,16 @@
                 super(factory);
             }
         }
+
+        /**
+         * Completable object of {@link android.view.inputmethod.SurroundingText}.
+         */
+        public static final class SurroundingText
+                extends Values<android.view.inputmethod.SurroundingText> {
+            private SurroundingText(@NonNull CancellationGroup factory) {
+                super(factory);
+            }
+        }
     }
 
     /**
@@ -292,6 +302,16 @@
         return new Completable.ExtractedText(this);
     }
 
+    /**
+     * @return an instance of {@link Completable.SurroundingText} that is associated with this
+     *         {@link CancellationGroup}.
+     */
+    @AnyThread
+    public Completable.SurroundingText createCompletableSurroundingText() {
+        return new Completable.SurroundingText(this);
+    }
+
+
     @AnyThread
     private boolean registerLatch(@NonNull CountDownLatch latch) {
         synchronized (mLock) {
diff --git a/media/java/android/media/tv/TvChannelInfo.aidl b/core/java/com/android/internal/inputmethod/ISurroundingTextResultCallback.aidl
similarity index 69%
copy from media/java/android/media/tv/TvChannelInfo.aidl
copy to core/java/com/android/internal/inputmethod/ISurroundingTextResultCallback.aidl
index 71cd0a7..6c4f3d5 100644
--- a/media/java/android/media/tv/TvChannelInfo.aidl
+++ b/core/java/com/android/internal/inputmethod/ISurroundingTextResultCallback.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open 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,6 +14,10 @@
  * limitations under the License.
  */
 
-package android.media.tv;
+package com.android.internal.inputmethod;
 
-parcelable TvChannelInfo;
+import android.view.inputmethod.SurroundingText;
+
+oneway interface ISurroundingTextResultCallback {
+    void onResult(in SurroundingText result);
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/inputmethod/ResultCallbacks.java b/core/java/com/android/internal/inputmethod/ResultCallbacks.java
index 44a8a83..5eba898 100644
--- a/core/java/com/android/internal/inputmethod/ResultCallbacks.java
+++ b/core/java/com/android/internal/inputmethod/ResultCallbacks.java
@@ -129,4 +129,32 @@
             }
         };
     }
+
+    /**
+     * Creates {@link ISurroundingTextResultCallback.Stub} that is to set
+     * {@link CancellationGroup.Completable.SurroundingText} when receiving the result.
+     *
+     * @param value {@link CancellationGroup.Completable.SurroundingText} to be set when receiving
+     *              the result.
+     * @return {@link ISurroundingTextResultCallback.Stub} that can be passed as a binder IPC
+     *         parameter.
+     */
+    @AnyThread
+    public static ISurroundingTextResultCallback.Stub of(
+            @NonNull CancellationGroup.Completable.SurroundingText value) {
+        final AtomicReference<WeakReference<CancellationGroup.Completable.SurroundingText>>
+                atomicRef = new AtomicReference<>(new WeakReference<>(value));
+
+        return new ISurroundingTextResultCallback.Stub() {
+            @BinderThread
+            @Override
+            public void onResult(android.view.inputmethod.SurroundingText result) {
+                final CancellationGroup.Completable.SurroundingText value = unwrap(atomicRef);
+                if (value == null) {
+                    return;
+                }
+                value.onComplete(result);
+            }
+        };
+    }
 }
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index dd4409c..3624f0d 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -16,7 +16,18 @@
 
 package com.android.internal.jank;
 
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_PIP;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_ICON;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_RECENTS;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_QUICK_SWITCH;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATION_SHADE_SWIPE;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_EXPAND_COLLAPSE_LOCK;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_QS_EXPAND_COLLAPSE;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_QS_SCROLL_SWIPE;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_EXPAND;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_SWIPE;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_SCROLL_FLING;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -63,18 +74,19 @@
     // Use NO_STATSD_LOGGING in case the measurement for a given CUJ should not be logged to statsd.
     @VisibleForTesting
     public static final int[] CUJ_TO_STATSD_INTERACTION_TYPE = {
+            // This should be mapping to CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE.
             UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATION_SHADE_SWIPE,
-            NO_STATSD_LOGGING,
-            NO_STATSD_LOGGING,
-            NO_STATSD_LOGGING,
-            NO_STATSD_LOGGING,
-            NO_STATSD_LOGGING,
-            NO_STATSD_LOGGING,
-            NO_STATSD_LOGGING,
-            NO_STATSD_LOGGING,
-            NO_STATSD_LOGGING,
-            NO_STATSD_LOGGING,
-            NO_STATSD_LOGGING,
+            UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_EXPAND_COLLAPSE_LOCK,
+            UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_SCROLL_FLING,
+            UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_EXPAND,
+            UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_SWIPE,
+            UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_QS_EXPAND_COLLAPSE,
+            UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_QS_SCROLL_SWIPE,
+            UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_RECENTS,
+            UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_ICON,
+            UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME,
+            UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_PIP,
+            UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_QUICK_SWITCH,
     };
 
     private static volatile InteractionJankMonitor sInstance;
diff --git a/core/java/com/android/internal/net/VpnConfig.java b/core/java/com/android/internal/net/VpnConfig.java
index 7dc3871..c0648ab 100644
--- a/core/java/com/android/internal/net/VpnConfig.java
+++ b/core/java/com/android/internal/net/VpnConfig.java
@@ -70,7 +70,8 @@
         intent.setClassName(DIALOGS_PACKAGE, DIALOGS_PACKAGE + ".ManageDialog");
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_HISTORY |
                 Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-        return PendingIntent.getActivityAsUser(context, 0, intent, 0, null, UserHandle.CURRENT);
+        return PendingIntent.getActivityAsUser(context, 0 /* requestCode */, intent,
+                PendingIntent.FLAG_IMMUTABLE, null /* options */, UserHandle.CURRENT);
     }
 
     public static CharSequence getVpnLabel(Context context, String packageName)
diff --git a/core/java/com/android/internal/os/AppIdToPackageMap.java b/core/java/com/android/internal/os/AppIdToPackageMap.java
index 65aa989..98cced8 100644
--- a/core/java/com/android/internal/os/AppIdToPackageMap.java
+++ b/core/java/com/android/internal/os/AppIdToPackageMap.java
@@ -16,25 +16,23 @@
 
 package com.android.internal.os;
 
-
 import android.app.AppGlobals;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
 
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 /** Maps AppIds to their package names. */
 public final class AppIdToPackageMap {
-    private final Map<Integer, String> mAppIdToPackageMap;
+    private final SparseArray<String> mAppIdToPackageMap;
 
     @VisibleForTesting
-    public AppIdToPackageMap(Map<Integer, String> appIdToPackageMap) {
+    public AppIdToPackageMap(SparseArray<String> appIdToPackageMap) {
         mAppIdToPackageMap = appIdToPackageMap;
     }
 
@@ -50,10 +48,10 @@
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
-        final Map<Integer, String> map = new HashMap<>();
+        final SparseArray<String> map = new SparseArray<>();
         for (PackageInfo pkg : packages) {
             final int uid = pkg.applicationInfo.uid;
-            if (pkg.sharedUserId != null && map.containsKey(uid)) {
+            if (pkg.sharedUserId != null && map.indexOfKey(uid) >= 0) {
                 // Use sharedUserId string as package name if there are collisions
                 map.put(uid, "shared:" + pkg.sharedUserId);
             } else {
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 308af99..b986463 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -65,7 +65,6 @@
 import android.telephony.CellSignalStrength;
 import android.telephony.DataConnectionRealTimeInfo;
 import android.telephony.ModemActivityInfo;
-import android.telephony.ModemActivityInfo.TransmitPower;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.TelephonyManager;
@@ -196,7 +195,7 @@
     static final int MSG_REPORT_POWER_CHANGE = 2;
     static final int MSG_REPORT_CHARGING = 3;
     static final int MSG_REPORT_RESET_STATS = 4;
-    static final long DELAY_UPDATE_WAKELOCKS = 5*1000;
+    static final long DELAY_UPDATE_WAKELOCKS = 60 * 1000;
 
     private static final double MILLISECONDS_IN_HOUR = 3600 * 1000;
     private static final long MILLISECONDS_IN_YEAR = 365 * 24 * 3600 * 1000L;
@@ -1019,8 +1018,6 @@
      */
     private LongSamplingCounterArray mSystemServerCpuTimesUs;
 
-    private long[] mTmpSystemServerCpuTimesUs;
-
     /**
      * Times spent by the system server threads grouped by cluster and CPU speed.
      */
@@ -7793,7 +7790,7 @@
         public ControllerActivityCounterImpl getOrCreateModemControllerActivityLocked() {
             if (mModemControllerActivity == null) {
                 mModemControllerActivity = new ControllerActivityCounterImpl(mBsi.mOnBatteryTimeBase,
-                        ModemActivityInfo.TX_POWER_LEVELS);
+                        ModemActivityInfo.getNumTxPowerLevels());
             }
             return mModemControllerActivity;
         }
@@ -9259,7 +9256,7 @@
 
             if (in.readInt() != 0) {
                 mModemControllerActivity = new ControllerActivityCounterImpl(mBsi.mOnBatteryTimeBase,
-                        ModemActivityInfo.TX_POWER_LEVELS, in);
+                        ModemActivityInfo.getNumTxPowerLevels(), in);
             } else {
                 mModemControllerActivity = null;
             }
@@ -10522,7 +10519,7 @@
         mBluetoothActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
                 NUM_BT_TX_LEVELS);
         mModemActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
-                ModemActivityInfo.TX_POWER_LEVELS);
+                ModemActivityInfo.getNumTxPowerLevels());
         mMobileRadioActiveTimer = new StopwatchTimer(mClocks, null, -400, null, mOnBatteryTimeBase);
         mMobileRadioActivePerAppTimer = new StopwatchTimer(mClocks, null, -401, null,
                 mOnBatteryTimeBase);
@@ -11712,26 +11709,7 @@
         }
     }
 
-    private ModemActivityInfo mLastModemActivityInfo =
-            new ModemActivityInfo(0, 0, 0, new int[0], 0);
-
-    private ModemActivityInfo getDeltaModemActivityInfo(ModemActivityInfo activityInfo) {
-        if (activityInfo == null) {
-            return null;
-        }
-        int[] txTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS];
-        for (int i = 0; i < ModemActivityInfo.TX_POWER_LEVELS; i++) {
-            txTimeMs[i] = activityInfo.getTransmitPowerInfo().get(i).getTimeInMillis()
-                    - mLastModemActivityInfo.getTransmitPowerInfo().get(i).getTimeInMillis();
-        }
-        ModemActivityInfo deltaInfo = new ModemActivityInfo(activityInfo.getTimestamp(),
-                activityInfo.getSleepTimeMillis() - mLastModemActivityInfo.getSleepTimeMillis(),
-                activityInfo.getIdleTimeMillis() - mLastModemActivityInfo.getIdleTimeMillis(),
-                txTimeMs,
-                activityInfo.getReceiveTimeMillis() - mLastModemActivityInfo.getReceiveTimeMillis());
-        mLastModemActivityInfo = activityInfo;
-        return deltaInfo;
-    }
+    private ModemActivityInfo mLastModemActivityInfo = null;
 
     /**
      * Distribute Cell radio energy info and network traffic to apps.
@@ -11748,7 +11726,9 @@
         if (DEBUG_ENERGY) {
             Slog.d(TAG, "Updating mobile radio stats with " + activityInfo);
         }
-        ModemActivityInfo deltaInfo = getDeltaModemActivityInfo(activityInfo);
+        ModemActivityInfo deltaInfo = mLastModemActivityInfo == null ? activityInfo
+                : mLastModemActivityInfo.getDelta(activityInfo);
+        mLastModemActivityInfo = activityInfo;
 
         // Add modem tx power to history.
         addModemTxPowerToHistory(deltaInfo, elapsedRealtimeMs, uptimeMs);
@@ -11780,10 +11760,9 @@
                 mModemActivity.getSleepTimeCounter().addCountLocked(
                         deltaInfo.getSleepTimeMillis());
                 mModemActivity.getRxTimeCounter().addCountLocked(deltaInfo.getReceiveTimeMillis());
-                for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) {
+                for (int lvl = 0; lvl < ModemActivityInfo.getNumTxPowerLevels(); lvl++) {
                     mModemActivity.getTxTimeCounters()[lvl]
-                        .addCountLocked(deltaInfo.getTransmitPowerInfo()
-                            .get(lvl).getTimeInMillis());
+                        .addCountLocked(deltaInfo.getTransmitDurationMillisAtPowerLevel(lvl));
                 }
 
                 // POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V.
@@ -11797,11 +11776,11 @@
                             mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_IDLE)
                             + deltaInfo.getReceiveTimeMillis() *
                             mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX);
-                    List<TransmitPower> txPowerInfo = deltaInfo.getTransmitPowerInfo();
-                    for (int i = 0; i < Math.min(txPowerInfo.size(),
+                    for (int i = 0; i < Math.min(ModemActivityInfo.getNumTxPowerLevels(),
                             CellSignalStrength.getNumSignalStrengthLevels()); i++) {
-                        energyUsed += txPowerInfo.get(i).getTimeInMillis() * mPowerProfile
-                            .getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_TX, i);
+                        energyUsed += deltaInfo.getTransmitDurationMillisAtPowerLevel(i)
+                                * mPowerProfile.getAveragePower(
+                                        PowerProfile.POWER_MODEM_CONTROLLER_TX, i);
                     }
 
                     // We store the power drain as mAms.
@@ -11896,10 +11875,10 @@
                             }
 
                             if (totalTxPackets > 0 && entry.txPackets > 0) {
-                                for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) {
-                                    long txMs =
-                                            entry.txPackets * deltaInfo.getTransmitPowerInfo()
-                                                .get(lvl).getTimeInMillis();
+                                for (int lvl = 0; lvl < ModemActivityInfo.getNumTxPowerLevels();
+                                        lvl++) {
+                                    long txMs = entry.txPackets
+                                            * deltaInfo.getTransmitDurationMillisAtPowerLevel(lvl);
                                     txMs /= totalTxPackets;
                                     activityCounter.getTxTimeCounters()[lvl].addCountLocked(txMs);
                                 }
@@ -11931,18 +11910,14 @@
         if (activityInfo == null) {
             return;
         }
-        List<TransmitPower> txPowerInfo = activityInfo.getTransmitPowerInfo();
-        if (txPowerInfo == null || txPowerInfo.size() != ModemActivityInfo.TX_POWER_LEVELS) {
-            return;
-        }
         int levelMaxTimeSpent = 0;
-        for (int i = 1; i < txPowerInfo.size(); i++) {
-            if (txPowerInfo.get(i).getTimeInMillis() > txPowerInfo.get(levelMaxTimeSpent)
-                .getTimeInMillis()) {
+        for (int i = 1; i < ModemActivityInfo.getNumTxPowerLevels(); i++) {
+            if (activityInfo.getTransmitDurationMillisAtPowerLevel(i)
+                    > activityInfo.getTransmitDurationMillisAtPowerLevel(levelMaxTimeSpent)) {
                 levelMaxTimeSpent = i;
             }
         }
-        if (levelMaxTimeSpent == ModemActivityInfo.TX_POWER_LEVELS - 1) {
+        if (levelMaxTimeSpent == ModemActivityInfo.getNumTxPowerLevels() - 1) {
             mHistoryCur.states2 |= HistoryItem.STATE2_CELLULAR_HIGH_TX_POWER_FLAG;
             addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
         }
@@ -12432,41 +12407,15 @@
             return;
         }
 
-        int numCpuClusters = mPowerProfile.getNumCpuClusters();
         if (mSystemServerCpuTimesUs == null) {
             mSystemServerCpuTimesUs = new LongSamplingCounterArray(mOnBatteryTimeBase);
             mSystemServerThreadCpuTimesUs = new LongSamplingCounterArray(mOnBatteryTimeBase);
             mBinderThreadCpuTimesUs = new LongSamplingCounterArray(mOnBatteryTimeBase);
         }
+        mSystemServerCpuTimesUs.addCountLocked(systemServiceCpuThreadTimes.processCpuTimesUs);
         mSystemServerThreadCpuTimesUs.addCountLocked(systemServiceCpuThreadTimes.threadCpuTimesUs);
         mBinderThreadCpuTimesUs.addCountLocked(systemServiceCpuThreadTimes.binderThreadCpuTimesUs);
 
-        long totalCpuTimeAllThreads = 0;
-        for (int index = systemServiceCpuThreadTimes.threadCpuTimesUs.length - 1; index >= 0;
-                index--) {
-            totalCpuTimeAllThreads += systemServiceCpuThreadTimes.threadCpuTimesUs[index];
-        }
-
-        // Estimate per cluster per frequency CPU time for the entire process
-        // by distributing the total process CPU time proportionately to how much
-        // CPU time its threads took on those clusters/frequencies.  This algorithm
-        // works more accurately when when we have equally distributed concurrency.
-        // TODO(b/169279846): obtain actual process CPU times from the kernel
-        long processCpuTime = systemServiceCpuThreadTimes.processCpuTimeUs;
-        if (mTmpSystemServerCpuTimesUs == null) {
-            mTmpSystemServerCpuTimesUs =
-                    new long[systemServiceCpuThreadTimes.threadCpuTimesUs.length];
-        }
-        for (int index = systemServiceCpuThreadTimes.threadCpuTimesUs.length - 1; index >= 0;
-                index--) {
-            mTmpSystemServerCpuTimesUs[index] =
-                    processCpuTime * systemServiceCpuThreadTimes.threadCpuTimesUs[index]
-                            / totalCpuTimeAllThreads;
-
-        }
-
-        mSystemServerCpuTimesUs.addCountLocked(mTmpSystemServerCpuTimesUs);
-
         if (DEBUG_BINDER_STATS) {
             Slog.d(TAG, "System server threads per CPU cluster (binder threads/total threads/%)");
             long totalCpuTimeMs = 0;
@@ -12480,6 +12429,7 @@
             final long[] binderThreadCpuTimesUs =
                     mBinderThreadCpuTimesUs.getCountsLocked(0);
             int index = 0;
+            int numCpuClusters = mPowerProfile.getNumCpuClusters();
             for (int cluster = 0; cluster < numCpuClusters; cluster++) {
                 StringBuilder sb = new StringBuilder();
                 sb.append("cpu").append(cpuIndex).append(": [");
@@ -12624,11 +12574,11 @@
                 // This could happen if the isolated uid mapping was removed before that process
                 // was actually killed.
                 mCpuUidUserSysTimeReader.removeUid(uid);
-                Slog.d(TAG, "Got readings for an isolated uid with no mapping: " + uid);
+                if (DEBUG) Slog.d(TAG, "Got readings for an isolated uid: " + uid);
                 return;
             }
             if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
-                Slog.d(TAG, "Got readings for an invalid user's uid " + uid);
+                if (DEBUG) Slog.d(TAG, "Got readings for an invalid user's uid " + uid);
                 mCpuUidUserSysTimeReader.removeUid(uid);
                 return;
             }
@@ -12733,11 +12683,11 @@
             uid = mapUid(uid);
             if (Process.isIsolated(uid)) {
                 mCpuUidFreqTimeReader.removeUid(uid);
-                Slog.d(TAG, "Got freq readings for an isolated uid with no mapping: " + uid);
+                if (DEBUG) Slog.d(TAG, "Got freq readings for an isolated uid: " + uid);
                 return;
             }
             if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
-                Slog.d(TAG, "Got freq readings for an invalid user's uid " + uid);
+                if (DEBUG) Slog.d(TAG, "Got freq readings for an invalid user's uid " + uid);
                 mCpuUidFreqTimeReader.removeUid(uid);
                 return;
             }
@@ -12847,11 +12797,11 @@
             uid = mapUid(uid);
             if (Process.isIsolated(uid)) {
                 mCpuUidActiveTimeReader.removeUid(uid);
-                Slog.w(TAG, "Got active times for an isolated uid with no mapping: " + uid);
+                if (DEBUG) Slog.w(TAG, "Got active times for an isolated uid: " + uid);
                 return;
             }
             if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
-                Slog.w(TAG, "Got active times for an invalid user's uid " + uid);
+                if (DEBUG) Slog.w(TAG, "Got active times for an invalid user's uid " + uid);
                 mCpuUidActiveTimeReader.removeUid(uid);
                 return;
             }
@@ -12877,11 +12827,11 @@
             uid = mapUid(uid);
             if (Process.isIsolated(uid)) {
                 mCpuUidClusterTimeReader.removeUid(uid);
-                Slog.w(TAG, "Got cluster times for an isolated uid with no mapping: " + uid);
+                if (DEBUG) Slog.w(TAG, "Got cluster times for an isolated uid: " + uid);
                 return;
             }
             if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
-                Slog.w(TAG, "Got cluster times for an invalid user's uid " + uid);
+                if (DEBUG) Slog.w(TAG, "Got cluster times for an invalid user's uid " + uid);
                 mCpuUidClusterTimeReader.removeUid(uid);
                 return;
             }
@@ -13489,7 +13439,7 @@
             timeInRxSignalStrengthLevelMs[i] =
                 getPhoneSignalStrengthTime(i, rawRealTimeUs, which) / 1000;
         }
-        long[] txTimeMs = new long[Math.min(ModemActivityInfo.TX_POWER_LEVELS,
+        long[] txTimeMs = new long[Math.min(ModemActivityInfo.getNumTxPowerLevels(),
             counter.getTxTimeCounters().length)];
         long totalTxTimeMs = 0;
         for (int i = 0; i < txTimeMs.length; i++) {
@@ -15628,7 +15578,7 @@
         mBluetoothActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
                 NUM_BT_TX_LEVELS, in);
         mModemActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
-                ModemActivityInfo.TX_POWER_LEVELS, in);
+                ModemActivityInfo.getNumTxPowerLevels(), in);
         mHasWifiReporting = in.readInt() != 0;
         mHasBluetoothReporting = in.readInt() != 0;
         mHasModemReporting = in.readInt() != 0;
diff --git a/core/java/com/android/internal/os/KernelCpuThreadReader.java b/core/java/com/android/internal/os/KernelCpuThreadReader.java
index 2ba372a..0843741 100644
--- a/core/java/com/android/internal/os/KernelCpuThreadReader.java
+++ b/core/java/com/android/internal/os/KernelCpuThreadReader.java
@@ -18,10 +18,10 @@
 
 import android.annotation.Nullable;
 import android.os.Process;
+import android.util.IntArray;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Preconditions;
 
 import java.io.IOException;
@@ -456,14 +456,14 @@
          * cluster has started.
          */
         private static int[] getClusterStartIndices(long[] frequencies) {
-            ArrayList<Integer> indices = new ArrayList<>();
+            IntArray indices = new IntArray();
             indices.add(0);
             for (int i = 0; i < frequencies.length - 1; i++) {
                 if (frequencies[i] >= frequencies[i + 1]) {
                     indices.add(i + 1);
                 }
             }
-            return ArrayUtils.convertToIntArray(indices);
+            return indices.toArray();
         }
 
         /** Get the index in frequencies where each bucket starts */
@@ -477,7 +477,7 @@
                 return Arrays.copyOfRange(clusterStartIndices, 0, targetNumBuckets);
             }
 
-            ArrayList<Integer> bucketStartIndices = new ArrayList<>();
+            IntArray bucketStartIndices = new IntArray();
             for (int clusterIdx = 0; clusterIdx < numClusters; clusterIdx++) {
                 final int clusterStartIdx = getLowerBound(clusterIdx, clusterStartIndices);
                 final int clusterEndIdx =
@@ -509,7 +509,7 @@
                     bucketStartIndices.add(bucketStartIdx);
                 }
             }
-            return ArrayUtils.convertToIntArray(bucketStartIndices);
+            return bucketStartIndices.toArray();
         }
 
         private static int getLowerBound(int index, int[] startIndices) {
diff --git a/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java b/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java
index 0578b89..e6a9623 100644
--- a/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java
+++ b/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java
@@ -33,8 +33,7 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Arrays;
 
 /**
  * Iterates over all threads owned by a given process, and return the CPU usage for
@@ -46,6 +45,7 @@
     private static final String TAG = "KernelSingleProcCpuThreadRdr";
 
     private static final boolean DEBUG = false;
+    private static final boolean NATIVE_ENABLED = true;
 
     /**
      * The name of the file to read CPU statistics from, must be found in {@code
@@ -65,7 +65,7 @@
     private static final Path INITIAL_TIME_IN_STATE_PATH = Paths.get("self/time_in_state");
 
     /** See https://man7.org/linux/man-pages/man5/proc.5.html */
-    private static final int[] PROCESS_FULL_STATS_FORMAT = new int[]{
+    private static final int[] PROCESS_FULL_STATS_FORMAT = new int[] {
             PROC_SPACE_TERM,
             PROC_SPACE_TERM,
             PROC_SPACE_TERM,
@@ -151,7 +151,7 @@
 
     /**
      * Get the CPU frequencies that correspond to the times reported in {@link
-     * ThreadCpuUsage#usageTimesMillis}
+     * ProcessCpuUsage#processCpuTimesMillis} etc.
      */
     public int getCpuFrequencyCount() {
         if (mFrequencyCount == 0) {
@@ -163,14 +163,37 @@
     /**
      * Get the total and per-thread CPU usage of the process with the PID specified in the
      * constructor.
+     *
+     * @param selectedThreadIds a SORTED array of native Thread IDs whose CPU times should
+     *                          be aggregated as a group.  This is expected to be a subset
+     *                          of all thread IDs owned by the process.
      */
     @Nullable
-    public ProcessCpuUsage getProcessCpuUsage() {
+    public ProcessCpuUsage getProcessCpuUsage(int[] selectedThreadIds) {
         if (DEBUG) {
             Slog.d(TAG, "Reading CPU thread usages with directory " + mProcPath + " process ID "
                     + mPid);
         }
 
+        int cpuFrequencyCount = getCpuFrequencyCount();
+        ProcessCpuUsage processCpuUsage = new ProcessCpuUsage(cpuFrequencyCount);
+
+        if (NATIVE_ENABLED) {
+            boolean result = readProcessCpuUsage(mProcPath.toString(), mPid,
+                    selectedThreadIds, processCpuUsage.processCpuTimesMillis,
+                    processCpuUsage.threadCpuTimesMillis,
+                    processCpuUsage.selectedThreadCpuTimesMillis);
+            if (!result) {
+                return null;
+            }
+            return processCpuUsage;
+        }
+
+        if (!isSorted(selectedThreadIds)) {
+            throw new IllegalArgumentException("selectedThreadIds is not sorted: "
+                    + Arrays.toString(selectedThreadIds));
+        }
+
         if (!Process.readProcFile(mProcessStatFilePath, PROCESS_FULL_STATS_FORMAT, null,
                 mProcessFullStatsData, null)) {
             Slog.e(TAG, "Failed to read process stat file " + mProcessStatFilePath);
@@ -182,39 +205,41 @@
 
         long processCpuTimeMillis = (utime + stime) * mJiffyMillis;
 
-        final ArrayList<ThreadCpuUsage> threadCpuUsages = new ArrayList<>();
         try (DirectoryStream<Path> threadPaths = Files.newDirectoryStream(mThreadsDirectoryPath)) {
             for (Path threadDirectory : threadPaths) {
-                ThreadCpuUsage threadCpuUsage = getThreadCpuUsage(threadDirectory);
-                if (threadCpuUsage == null) {
-                    continue;
-                }
-                threadCpuUsages.add(threadCpuUsage);
+                readThreadCpuUsage(processCpuUsage, selectedThreadIds, threadDirectory);
             }
         } catch (IOException | DirectoryIteratorException e) {
             // Expected when a process finishes
             return null;
         }
 
-        // If we found no threads, then the process has exited while we were reading from it
-        if (threadCpuUsages.isEmpty()) {
-            return null;
+        // Estimate per cluster per frequency CPU time for the entire process
+        // by distributing the total process CPU time proportionately to how much
+        // CPU time its threads took on those clusters/frequencies.  This algorithm
+        // works more accurately when when we have equally distributed concurrency.
+        // TODO(b/169279846): obtain actual process CPU times from the kernel
+        long totalCpuTimeAllThreads = 0;
+        for (int i = cpuFrequencyCount - 1; i >= 0; i--) {
+            totalCpuTimeAllThreads += processCpuUsage.threadCpuTimesMillis[i];
         }
-        if (DEBUG) {
-            Slog.d(TAG, "Read CPU usage of " + threadCpuUsages.size() + " threads");
+
+        for (int i = cpuFrequencyCount - 1; i >= 0; i--) {
+            processCpuUsage.processCpuTimesMillis[i] =
+                    processCpuTimeMillis * processCpuUsage.threadCpuTimesMillis[i]
+                            / totalCpuTimeAllThreads;
         }
-        return new ProcessCpuUsage(processCpuTimeMillis, threadCpuUsages);
+
+        return processCpuUsage;
     }
 
     /**
-     * Get a thread's CPU usage
+     * Reads a thread's CPU usage and aggregates the per-cluster per-frequency CPU times.
      *
      * @param threadDirectory the {@code /proc} directory of the thread
-     * @return thread CPU usage. Null if the thread exited and its {@code proc} directory was
-     * removed while collecting information
      */
-    @Nullable
-    private ThreadCpuUsage getThreadCpuUsage(Path threadDirectory) {
+    private void readThreadCpuUsage(ProcessCpuUsage processCpuUsage, int[] selectedThreadIds,
+            Path threadDirectory) {
         // Get the thread ID from the directory name
         final int threadId;
         try {
@@ -222,38 +247,49 @@
             threadId = Integer.parseInt(directoryName);
         } catch (NumberFormatException e) {
             Slog.w(TAG, "Failed to parse thread ID when iterating over /proc/*/task", e);
-            return null;
+            return;
         }
 
         // Get the CPU statistics from the directory
         final Path threadCpuStatPath = threadDirectory.resolve(CPU_STATISTICS_FILENAME);
         final long[] cpuUsages = mProcTimeInStateReader.getUsageTimesMillis(threadCpuStatPath);
         if (cpuUsages == null) {
-            return null;
+            return;
         }
 
-        return new ThreadCpuUsage(threadId, cpuUsages);
+        final int cpuFrequencyCount = getCpuFrequencyCount();
+        final boolean isSelectedThread = Arrays.binarySearch(selectedThreadIds, threadId) >= 0;
+        for (int i = cpuFrequencyCount - 1; i >= 0; i--) {
+            processCpuUsage.threadCpuTimesMillis[i] += cpuUsages[i];
+            if (isSelectedThread) {
+                processCpuUsage.selectedThreadCpuTimesMillis[i] += cpuUsages[i];
+            }
+        }
     }
 
-    /** CPU usage of a process and all of its threads */
+    /** CPU usage of a process, all of its threads and a selected subset of its threads */
     public static class ProcessCpuUsage {
-        public final long cpuTimeMillis;
-        public final List<ThreadCpuUsage> threadCpuUsages;
+        public long[] processCpuTimesMillis;
+        public long[] threadCpuTimesMillis;
+        public long[] selectedThreadCpuTimesMillis;
 
-        ProcessCpuUsage(long cpuTimeMillis, List<ThreadCpuUsage> threadCpuUsages) {
-            this.cpuTimeMillis = cpuTimeMillis;
-            this.threadCpuUsages = threadCpuUsages;
+        public ProcessCpuUsage(int cpuFrequencyCount) {
+            processCpuTimesMillis = new long[cpuFrequencyCount];
+            threadCpuTimesMillis = new long[cpuFrequencyCount];
+            selectedThreadCpuTimesMillis = new long[cpuFrequencyCount];
         }
     }
 
-    /** CPU usage of a thread */
-    public static class ThreadCpuUsage {
-        public final int threadId;
-        public final long[] usageTimesMillis;
-
-        ThreadCpuUsage(int threadId, long[] usageTimesMillis) {
-            this.threadId = threadId;
-            this.usageTimesMillis = usageTimesMillis;
+    private static boolean isSorted(int[] array) {
+        for (int i = 0; i < array.length - 1; i++) {
+            if (array[i] > array[i + 1]) {
+                return false;
+            }
         }
+        return true;
     }
+
+    private native boolean readProcessCpuUsage(String procPath, int pid, int[] selectedThreadIds,
+            long[] processCpuTimesMillis, long[] threadCpuTimesMillis,
+            long[] selectedThreadCpuTimesMillis);
 }
diff --git a/core/java/com/android/internal/os/ProcTimeInStateReader.java b/core/java/com/android/internal/os/ProcTimeInStateReader.java
index 2318473..d54a9c7 100644
--- a/core/java/com/android/internal/os/ProcTimeInStateReader.java
+++ b/core/java/com/android/internal/os/ProcTimeInStateReader.java
@@ -18,16 +18,11 @@
 
 import android.annotation.Nullable;
 import android.os.Process;
-
-import com.android.internal.util.ArrayUtils;
+import android.util.IntArray;
 
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
 
 /**
  * Reads and parses {@code time_in_state} files in the {@code proc} filesystem.
@@ -68,24 +63,25 @@
      * The format of a single line of the {@code time_in_state} file that exports the frequency
      * values
      */
-    private static final List<Integer> TIME_IN_STATE_LINE_FREQUENCY_FORMAT = Arrays.asList(
+    private static final int[] TIME_IN_STATE_LINE_FREQUENCY_FORMAT = new int[] {
             Process.PROC_OUT_LONG | Process.PROC_SPACE_TERM,
             Process.PROC_NEWLINE_TERM
-    );
+    };
 
     /**
      * The format of a single line of the {@code time_in_state} file that exports the time values
      */
-    private static final List<Integer> TIME_IN_STATE_LINE_TIME_FORMAT = Arrays.asList(
+    private static final int[] TIME_IN_STATE_LINE_TIME_FORMAT = new int[] {
             Process.PROC_SPACE_TERM,
             Process.PROC_OUT_LONG | Process.PROC_NEWLINE_TERM
-    );
+    };
 
     /**
      * The format of a header line of the {@code time_in_state} file
      */
-    private static final List<Integer> TIME_IN_STATE_HEADER_LINE_FORMAT =
-            Collections.singletonList(Process.PROC_NEWLINE_TERM);
+    private static final int[] TIME_IN_STATE_HEADER_LINE_FORMAT = new int[] {
+            Process.PROC_NEWLINE_TERM
+    };
 
     /**
      * The format of the {@code time_in_state} file to extract times, defined using {@link
@@ -166,8 +162,8 @@
         // formats. These formats are used to extract either the frequencies or the times from a
         // time_in_state file
         // Also check if each line is a header, and handle this in the created format arrays
-        ArrayList<Integer> timeInStateFrequencyFormat = new ArrayList<>();
-        ArrayList<Integer> timeInStateTimeFormat = new ArrayList<>();
+        IntArray timeInStateFrequencyFormat = new IntArray();
+        IntArray timeInStateTimeFormat = new IntArray();
         int numFrequencies = 0;
         for (int i = 0; i < timeInStateBytes.length; i++) {
             // If the first character of the line is not a digit, we treat it as a header
@@ -194,12 +190,12 @@
         final long[] readLongs = new long[numFrequencies];
         final boolean readSuccess = Process.parseProcLine(
                 timeInStateBytes, 0, timeInStateBytes.length,
-                ArrayUtils.convertToIntArray(timeInStateFrequencyFormat), null, readLongs, null);
+                timeInStateFrequencyFormat.toArray(), null, readLongs, null);
         if (!readSuccess) {
             throw new IOException("Failed to parse time_in_state file");
         }
 
-        mTimeInStateTimeFormat = ArrayUtils.convertToIntArray(timeInStateTimeFormat);
+        mTimeInStateTimeFormat = timeInStateTimeFormat.toArray();
         mFrequenciesKhz = readLongs;
     }
 }
diff --git a/core/java/com/android/internal/os/SystemServerCpuThreadReader.java b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java
index d9f0dc0..fbbee94 100644
--- a/core/java/com/android/internal/os/SystemServerCpuThreadReader.java
+++ b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java
@@ -24,7 +24,6 @@
 import java.io.IOException;
 import java.nio.file.Path;
 import java.util.Arrays;
-import java.util.List;
 
 /**
  * Reads /proc/UID/task/TID/time_in_state files to obtain statistics on CPU usage
@@ -34,10 +33,7 @@
     private final KernelSingleProcessCpuThreadReader mKernelCpuThreadReader;
     private int[] mBinderThreadNativeTids = new int[0];  // Sorted
 
-    private long mProcessCpuTimeUs;
-    private long[] mThreadCpuTimesUs;
-    private long[] mBinderThreadCpuTimesUs;
-    private long mLastProcessCpuTimeUs;
+    private long[] mLastProcessCpuTimeUs;
     private long[] mLastThreadCpuTimesUs;
     private long[] mLastBinderThreadCpuTimesUs;
 
@@ -46,14 +42,15 @@
      */
     public static class SystemServiceCpuThreadTimes {
         // The entire process
-        public long processCpuTimeUs;
+        public long[] processCpuTimesUs;
         // All threads
         public long[] threadCpuTimesUs;
         // Just the threads handling incoming binder calls
         public long[] binderThreadCpuTimesUs;
     }
 
-    private SystemServiceCpuThreadTimes mDeltaCpuThreadTimes = new SystemServiceCpuThreadTimes();
+    private final SystemServiceCpuThreadTimes mDeltaCpuThreadTimes =
+            new SystemServiceCpuThreadTimes();
 
     /**
      * Creates a configured instance of SystemServerCpuThreadReader.
@@ -83,59 +80,38 @@
      */
     @Nullable
     public SystemServiceCpuThreadTimes readDelta() {
-        int numCpuFrequencies = mKernelCpuThreadReader.getCpuFrequencyCount();
-        if (mBinderThreadCpuTimesUs == null) {
-            mThreadCpuTimesUs = new long[numCpuFrequencies];
-            mBinderThreadCpuTimesUs = new long[numCpuFrequencies];
-
+        final int numCpuFrequencies = mKernelCpuThreadReader.getCpuFrequencyCount();
+        if (mLastProcessCpuTimeUs == null) {
+            mLastProcessCpuTimeUs = new long[numCpuFrequencies];
             mLastThreadCpuTimesUs = new long[numCpuFrequencies];
             mLastBinderThreadCpuTimesUs = new long[numCpuFrequencies];
 
+            mDeltaCpuThreadTimes.processCpuTimesUs = new long[numCpuFrequencies];
             mDeltaCpuThreadTimes.threadCpuTimesUs = new long[numCpuFrequencies];
             mDeltaCpuThreadTimes.binderThreadCpuTimesUs = new long[numCpuFrequencies];
         }
 
-        mProcessCpuTimeUs = 0;
-        Arrays.fill(mThreadCpuTimesUs, 0);
-        Arrays.fill(mBinderThreadCpuTimesUs, 0);
-
-        KernelSingleProcessCpuThreadReader.ProcessCpuUsage processCpuUsage =
-                mKernelCpuThreadReader.getProcessCpuUsage();
+        final KernelSingleProcessCpuThreadReader.ProcessCpuUsage processCpuUsage =
+                mKernelCpuThreadReader.getProcessCpuUsage(mBinderThreadNativeTids);
         if (processCpuUsage == null) {
             return null;
         }
 
-        mProcessCpuTimeUs = processCpuUsage.cpuTimeMillis * 1000;
-
-        List<KernelSingleProcessCpuThreadReader.ThreadCpuUsage> threadCpuUsages =
-                processCpuUsage.threadCpuUsages;
-        int threadCpuUsagesSize = threadCpuUsages.size();
-        for (int i = 0; i < threadCpuUsagesSize; i++) {
-            KernelSingleProcessCpuThreadReader.ThreadCpuUsage tcu = threadCpuUsages.get(i);
-            boolean isBinderThread =
-                    Arrays.binarySearch(mBinderThreadNativeTids, tcu.threadId) >= 0;
-            for (int k = 0; k < numCpuFrequencies; k++) {
-                long usageTimeUs = tcu.usageTimesMillis[k] * 1000;
-                mThreadCpuTimesUs[k] += usageTimeUs;
-                if (isBinderThread) {
-                    mBinderThreadCpuTimesUs[k] += usageTimeUs;
-                }
-            }
-        }
-
-        for (int i = 0; i < mThreadCpuTimesUs.length; i++) {
-            mDeltaCpuThreadTimes.processCpuTimeUs =
-                    Math.max(0, mProcessCpuTimeUs - mLastProcessCpuTimeUs);
+        for (int i = numCpuFrequencies - 1; i >= 0; i--) {
+            long processCpuTimesUs = processCpuUsage.processCpuTimesMillis[i] * 1000;
+            long threadCpuTimesUs = processCpuUsage.threadCpuTimesMillis[i] * 1000;
+            long binderThreadCpuTimesUs = processCpuUsage.selectedThreadCpuTimesMillis[i] * 1000;
+            mDeltaCpuThreadTimes.processCpuTimesUs[i] =
+                    Math.max(0, processCpuTimesUs - mLastProcessCpuTimeUs[i]);
             mDeltaCpuThreadTimes.threadCpuTimesUs[i] =
-                    Math.max(0, mThreadCpuTimesUs[i] - mLastThreadCpuTimesUs[i]);
+                    Math.max(0, threadCpuTimesUs - mLastThreadCpuTimesUs[i]);
             mDeltaCpuThreadTimes.binderThreadCpuTimesUs[i] =
-                    Math.max(0, mBinderThreadCpuTimesUs[i] - mLastBinderThreadCpuTimesUs[i]);
-            mLastThreadCpuTimesUs[i] = mThreadCpuTimesUs[i];
-            mLastBinderThreadCpuTimesUs[i] = mBinderThreadCpuTimesUs[i];
+                    Math.max(0, binderThreadCpuTimesUs - mLastBinderThreadCpuTimesUs[i]);
+            mLastProcessCpuTimeUs[i] = processCpuTimesUs;
+            mLastThreadCpuTimesUs[i] = threadCpuTimesUs;
+            mLastBinderThreadCpuTimesUs[i] = binderThreadCpuTimesUs;
         }
 
-        mLastProcessCpuTimeUs = mProcessCpuTimeUs;
-
         return mDeltaCpuThreadTimes;
     }
 }
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 5948e7e..c762939 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -385,7 +385,6 @@
                 "/system/framework/android.hidl.manager-V1.0-java.jar", null /*packageName*/,
                 null /*codePaths*/, null /*name*/, 0 /*version*/, SharedLibraryInfo.TYPE_BUILTIN,
                 null /*declaringPackage*/, null /*dependentPackages*/, null /*dependencies*/);
-        hidlManager.addDependency(hidlBase);
 
         SharedLibraryInfo androidTestBase = new SharedLibraryInfo(
                 "/system/framework/android.test.base.jar", null /*packageName*/,
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 2f8c457..e848da9 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -77,7 +77,7 @@
 import android.view.ContextThemeWrapper;
 import android.view.Gravity;
 import android.view.IRotationWatcher.Stub;
-import android.view.IScrollCaptureController;
+import android.view.IScrollCaptureCallbacks;
 import android.view.IWindowManager;
 import android.view.InputDevice;
 import android.view.InputEvent;
@@ -1511,11 +1511,13 @@
         if (drawable != mBackgroundDrawable) {
             mBackgroundDrawable = drawable;
             if (mDecor != null) {
+                mDecor.startChanging();
                 mDecor.setWindowBackground(drawable);
                 if (mBackgroundFallbackDrawable != null) {
                     mDecor.setBackgroundFallback(drawable != null ? null :
                             mBackgroundFallbackDrawable);
                 }
+                mDecor.finishChanging();
             }
         }
     }
@@ -1916,8 +1918,8 @@
                 // If we have a session send it the volume command, otherwise
                 // use the suggested stream.
                 if (mMediaController != null) {
-                    getMediaSessionManager().dispatchVolumeKeyEventAsSystemService(
-                            mMediaController.getSessionToken(), event);
+                    getMediaSessionManager().dispatchVolumeKeyEventToSessionAsSystemService(event,
+                            mMediaController.getSessionToken());
                 } else {
                     getMediaSessionManager().dispatchVolumeKeyEventAsSystemService(event,
                             mVolumeControlStreamType);
@@ -1938,8 +1940,8 @@
             case KeyEvent.KEYCODE_MEDIA_RECORD:
             case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
                 if (mMediaController != null) {
-                    if (getMediaSessionManager().dispatchMediaKeyEventAsSystemService(
-                            mMediaController.getSessionToken(), event)) {
+                    if (getMediaSessionManager().dispatchMediaKeyEventToSessionAsSystemService(
+                            event, mMediaController.getSessionToken())) {
                         return true;
                     }
                 }
@@ -2010,8 +2012,8 @@
                 // If we have a session send it the volume command, otherwise
                 // use the suggested stream.
                 if (mMediaController != null) {
-                    getMediaSessionManager().dispatchVolumeKeyEventAsSystemService(
-                            mMediaController.getSessionToken(), event);
+                    getMediaSessionManager().dispatchVolumeKeyEventToSessionAsSystemService(event,
+                            mMediaController.getSessionToken());
                 } else {
                     getMediaSessionManager().dispatchVolumeKeyEventAsSystemService(
                             event, mVolumeControlStreamType);
@@ -2041,8 +2043,8 @@
             case KeyEvent.KEYCODE_MEDIA_RECORD:
             case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
                 if (mMediaController != null) {
-                    if (getMediaSessionManager().dispatchMediaKeyEventAsSystemService(
-                            mMediaController.getSessionToken(), event)) {
+                    if (getMediaSessionManager().dispatchMediaKeyEventToSessionAsSystemService(
+                            event, mMediaController.getSessionToken())) {
                         return true;
                     }
                 }
@@ -3910,12 +3912,12 @@
     /**
      * System request to begin scroll capture.
      *
-     * @param controller the controller to receive responses
+     * @param callbacks to receive responses
      * @hide
      */
     @Override
-    public void requestScrollCapture(IScrollCaptureController controller) {
-        getViewRootImpl().dispatchScrollCaptureRequest(controller);
+    public void requestScrollCapture(IScrollCaptureCallbacks callbacks) {
+        getViewRootImpl().dispatchScrollCaptureRequest(callbacks);
     }
 
     /**
@@ -3924,7 +3926,7 @@
      * @param callback the callback to add
      */
     @Override
-    public void addScrollCaptureCallback(@NonNull ScrollCaptureCallback callback) {
+    public void registerScrollCaptureCallback(@NonNull ScrollCaptureCallback callback) {
         getViewRootImpl().addScrollCaptureCallback(callback);
     }
 
@@ -3934,7 +3936,7 @@
      * @param callback the callback to remove
      */
     @Override
-    public void removeScrollCaptureCallback(@NonNull ScrollCaptureCallback callback) {
+    public void unregisterScrollCaptureCallback(@NonNull ScrollCaptureCallback callback) {
         getViewRootImpl().removeScrollCaptureCallback(callback);
     }
 
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index caae518..77c7ce8 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -102,6 +102,13 @@
     void onCameraLaunchGestureDetected(int source);
 
     /**
+     * Notifies the status bar that the Emergency Action launch gesture has been detected.
+     *
+     * TODO(b/169175022) Update method name and docs when feature name is locked.
+     */
+    void onEmergencyActionLaunchGestureDetected();
+
+    /**
      * Shows the picture-in-picture menu if an activity is in picture-in-picture mode.
      */
     void showPictureInPictureMenu();
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index f317f3f..c6dea18 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -310,6 +310,10 @@
         return total;
     }
 
+    /**
+     * @deprecated use {@code IntArray} instead
+     */
+    @Deprecated
     public static int[] convertToIntArray(List<Integer> list) {
         int[] array = new int[list.size()];
         for (int i = 0; i < list.size(); i++) {
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index 555f62c..c7ac189 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -15,14 +15,22 @@
 package com.android.internal.util;
 
 import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
 import android.os.Build;
 import android.os.SystemClock;
 import android.os.Trace;
+import android.os.UserHandle;
+import android.provider.Settings;
 import android.util.EventLog;
+import android.util.KeyValueListParser;
 import android.util.Log;
 import android.util.SparseLongArray;
 
 import com.android.internal.logging.EventLogTags;
+import com.android.internal.os.BackgroundThread;
+
+import java.util.concurrent.ThreadLocalRandom;
 
 /**
  * Class to track various latencies in SystemUI. It then writes the latency to statsd and also
@@ -34,6 +42,12 @@
  */
 public class LatencyTracker {
     private static final String TAG = "LatencyTracker";
+    private static final String SETTINGS_ENABLED_KEY = "enabled";
+    private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval";
+    /** Default to being enabled on debug builds. */
+    private static final boolean DEFAULT_ENABLED = Build.IS_DEBUGGABLE;
+    /** Default to collecting data for 1/5 of all actions (randomly sampled). */
+    private static final int DEFAULT_SAMPLING_INTERVAL = 5;
 
     /**
      * Time it takes until the first frame of the notification panel to be displayed while expanding
@@ -76,7 +90,7 @@
      */
     public static final int ACTION_FACE_WAKE_AND_UNLOCK = 7;
 
-    private static final String[] NAMES = new String[] {
+    private static final String[] NAMES = new String[]{
             "expand panel",
             "toggle recents",
             "fingerprint wake-and-unlock",
@@ -84,9 +98,9 @@
             "check credential unlocked",
             "turn on screen",
             "rotate the screen",
-            "face wake-and-unlock" };
+            "face wake-and-unlock"};
 
-    private static final int[] STATSD_ACTION = new int[] {
+    private static final int[] STATSD_ACTION = new int[]{
             FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_EXPAND_PANEL,
             FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_TOGGLE_RECENTS,
             FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FINGERPRINT_WAKE_AND_UNLOCK,
@@ -100,20 +114,59 @@
     private static LatencyTracker sLatencyTracker;
 
     private final SparseLongArray mStartRtc = new SparseLongArray();
+    private final Context mContext;
+    private volatile int mSamplingInterval;
+    private volatile boolean mEnabled;
 
     public static LatencyTracker getInstance(Context context) {
         if (sLatencyTracker == null) {
-            sLatencyTracker = new LatencyTracker();
+            synchronized (LatencyTracker.class) {
+                if (sLatencyTracker == null) {
+                    sLatencyTracker = new LatencyTracker(context);
+                }
+            }
         }
         return sLatencyTracker;
     }
 
+    public LatencyTracker(Context context) {
+        mContext = context;
+        mEnabled = DEFAULT_ENABLED;
+        mSamplingInterval = DEFAULT_SAMPLING_INTERVAL;
+
+        // Post initialization to the background in case we're running on the main thread.
+        BackgroundThread.getHandler().post(this::registerSettingsObserver);
+        BackgroundThread.getHandler().post(this::readSettings);
+    }
+
+    private void registerSettingsObserver() {
+        Uri settingsUri = Settings.Global.getUriFor(Settings.Global.LATENCY_TRACKER);
+        mContext.getContentResolver().registerContentObserver(
+                settingsUri, false, new SettingsObserver(this), UserHandle.myUserId());
+    }
+
+    private void readSettings() {
+        KeyValueListParser parser = new KeyValueListParser(',');
+        String settingsValue = Settings.Global.getString(mContext.getContentResolver(),
+                Settings.Global.LATENCY_TRACKER);
+
+        try {
+            parser.setString(settingsValue);
+            mSamplingInterval = parser.getInt(SETTINGS_SAMPLING_INTERVAL_KEY,
+                    DEFAULT_SAMPLING_INTERVAL);
+            mEnabled = parser.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED);
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG, "Incorrect settings format", e);
+            mEnabled = false;
+        }
+    }
+
     public static boolean isEnabled(Context ctx) {
         return getInstance(ctx).isEnabled();
     }
 
     public boolean isEnabled() {
-        return Build.IS_DEBUGGABLE;
+        return mEnabled;
     }
 
     /**
@@ -145,19 +198,48 @@
         }
         mStartRtc.delete(action);
         Trace.asyncTraceEnd(Trace.TRACE_TAG_APP, NAMES[action], 0);
-        logAction(action, (int)(endRtc - startRtc));
+        logAction(action, (int) (endRtc - startRtc));
     }
 
     /**
      * Logs an action that has started and ended. This needs to be called from the main thread.
      *
-     * @param action The action to end. One of the ACTION_* values.
-     * @param duration The duration of the action in ms.
+     * @param action          The action to end. One of the ACTION_* values.
+     * @param duration        The duration of the action in ms.
      */
-    public static void logAction(int action, int duration) {
+    public void logAction(int action, int duration) {
+        boolean shouldSample = ThreadLocalRandom.current().nextInt() % mSamplingInterval == 0;
+        logActionDeprecated(action, duration, shouldSample);
+    }
+
+    /**
+     * Logs an action that has started and ended. This needs to be called from the main thread.
+     *
+     * @param action          The action to end. One of the ACTION_* values.
+     * @param duration        The duration of the action in ms.
+     * @param writeToStatsLog Whether to write the measured latency to FrameworkStatsLog.
+     */
+    public static void logActionDeprecated(int action, int duration, boolean writeToStatsLog) {
         Log.i(TAG, "action=" + action + " latency=" + duration);
         EventLog.writeEvent(EventLogTags.SYSUI_LATENCY, action, duration);
-        FrameworkStatsLog.write(
-                FrameworkStatsLog.UI_ACTION_LATENCY_REPORTED, STATSD_ACTION[action], duration);
+
+        if (writeToStatsLog) {
+            FrameworkStatsLog.write(
+                    FrameworkStatsLog.UI_ACTION_LATENCY_REPORTED, STATSD_ACTION[action], duration);
+        }
+    }
+
+    private static class SettingsObserver extends ContentObserver {
+        private final LatencyTracker mThisTracker;
+
+        SettingsObserver(LatencyTracker thisTracker) {
+            super(BackgroundThread.getHandler());
+            mThisTracker = thisTracker;
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri, int userId) {
+            mThisTracker.readSettings();
+        }
     }
 }
diff --git a/core/java/com/android/internal/util/LocalLog.java b/core/java/com/android/internal/util/LocalLog.java
index 3916691..057dc8f 100644
--- a/core/java/com/android/internal/util/LocalLog.java
+++ b/core/java/com/android/internal/util/LocalLog.java
@@ -16,12 +16,12 @@
 
 package com.android.internal.util;
 
-import java.io.PrintWriter;
-import java.util.ArrayList;
-
+import android.util.IndentingPrintWriter;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 
+import java.util.ArrayList;
+
 /**
  * Helper class for logging serious issues, which also keeps a small
  * snapshot of the logged events that can be printed later, such as part
@@ -47,20 +47,21 @@
         }
     }
 
-    public boolean dump(PrintWriter pw, String header, String prefix) {
+    public boolean dump(IndentingPrintWriter pw, String header) {
         synchronized (mLines) {
             if (mLines.size() <= 0) {
                 return false;
             }
             if (header != null) {
                 pw.println(header);
+                pw.increaseIndent();
             }
             for (int i=0; i<mLines.size(); i++) {
-                if (prefix != null) {
-                    pw.print(prefix);
-                }
                 pw.println(mLines.get(i));
             }
+            if (header != null) {
+                pw.decreaseIndent();
+            }
             return true;
         }
     }
diff --git a/core/java/com/android/internal/util/LocationPermissionChecker.java b/core/java/com/android/internal/util/LocationPermissionChecker.java
index cd8fc35..59c0c00 100644
--- a/core/java/com/android/internal/util/LocationPermissionChecker.java
+++ b/core/java/com/android/internal/util/LocationPermissionChecker.java
@@ -218,7 +218,7 @@
     }
 
     private boolean isTargetSdkLessThan(String packageName, int versionCode, int callingUid) {
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             if (mContext.getPackageManager().getApplicationInfoAsUser(
                     packageName, 0,
diff --git a/core/java/com/android/internal/util/NotificationMessagingUtil.java b/core/java/com/android/internal/util/NotificationMessagingUtil.java
index 28994fd..c59647d 100644
--- a/core/java/com/android/internal/util/NotificationMessagingUtil.java
+++ b/core/java/com/android/internal/util/NotificationMessagingUtil.java
@@ -26,7 +26,7 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.service.notification.StatusBarNotification;
-import android.util.ArrayMap;
+import android.util.SparseArray;
 
 import java.util.Collection;
 import java.util.Objects;
@@ -39,7 +39,7 @@
 
     private static final String DEFAULT_SMS_APP_SETTING = Settings.Secure.SMS_DEFAULT_APPLICATION;
     private final Context mContext;
-    private ArrayMap<Integer, String> mDefaultSmsApp = new ArrayMap<>();
+    private SparseArray<String> mDefaultSmsApp = new SparseArray<>();
 
     public NotificationMessagingUtil(Context context) {
         mContext = context;
diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java
index dae649a..4d441cd 100644
--- a/core/java/com/android/internal/util/Preconditions.java
+++ b/core/java/com/android/internal/util/Preconditions.java
@@ -31,6 +31,12 @@
  */
 public class Preconditions {
 
+    /**
+     * Ensures that an expression checking an argument is true.
+     *
+     * @param expression the expression to check
+     * @throws IllegalArgumentException if {@code expression} is false
+     */
     @UnsupportedAppUsage
     public static void checkArgument(boolean expression) {
         if (!expression) {
@@ -62,8 +68,9 @@
      * @param messageArgs arguments for {@code messageTemplate}
      * @throws IllegalArgumentException if {@code expression} is false
      */
-    public static void checkArgument(boolean expression,
-            final String messageTemplate,
+    public static void checkArgument(
+            final boolean expression,
+            final @NonNull String messageTemplate,
             final Object... messageArgs) {
         if (!expression) {
             throw new IllegalArgumentException(String.format(messageTemplate, messageArgs));
@@ -114,7 +121,9 @@
      * @throws IllegalArgumentException if {@code string} is empty
      */
     public static @NonNull <T extends CharSequence> T checkStringNotEmpty(
-            final T string, final String messageTemplate, final Object... messageArgs) {
+            final T string,
+            final @NonNull String messageTemplate,
+            final Object... messageArgs) {
         if (TextUtils.isEmpty(string)) {
             throw new IllegalArgumentException(String.format(messageTemplate, messageArgs));
         }
@@ -160,18 +169,22 @@
     }
 
     /**
-     * Ensures the truth of an expression involving the state of the calling
-     * instance, but not involving any parameters to the calling method.
+     * Ensures that an object reference passed as a parameter to the calling
+     * method is not null.
      *
-     * @param expression a boolean expression
-     * @param message exception message
-     * @throws IllegalStateException if {@code expression} is false
+     * @param messageTemplate a printf-style message template to use if the check fails; will
+     *     be converted to a string using {@link String#format(String, Object...)}
+     * @param messageArgs arguments for {@code messageTemplate}
+     * @throws NullPointerException if {@code reference} is null
      */
-    @UnsupportedAppUsage
-    public static void checkState(final boolean expression, String message) {
-        if (!expression) {
-            throw new IllegalStateException(message);
+    public static @NonNull <T> T checkNotNull(
+            final T reference,
+            final @NonNull String messageTemplate,
+            final Object... messageArgs) {
+        if (reference == null) {
+            throw new NullPointerException(String.format(messageTemplate, messageArgs));
         }
+        return reference;
     }
 
     /**
@@ -187,6 +200,41 @@
     }
 
     /**
+     * Ensures the truth of an expression involving the state of the calling
+     * instance, but not involving any parameters to the calling method.
+     *
+     * @param expression a boolean expression
+     * @param errorMessage the exception message to use if the check fails; will
+     *     be converted to a string using {@link String#valueOf(Object)}
+     * @throws IllegalStateException if {@code expression} is false
+     */
+    @UnsupportedAppUsage
+    public static void checkState(final boolean expression, String errorMessage) {
+        if (!expression) {
+            throw new IllegalStateException(errorMessage);
+        }
+    }
+
+    /**
+     * Ensures the truth of an expression involving the state of the calling
+     * instance, but not involving any parameters to the calling method.
+     *
+     * @param expression a boolean expression
+     * @param messageTemplate a printf-style message template to use if the check fails; will
+     *     be converted to a string using {@link String#format(String, Object...)}
+     * @param messageArgs arguments for {@code messageTemplate}
+     * @throws IllegalStateException if {@code expression} is false
+     */
+    public static void checkState(
+            final boolean expression,
+            final @NonNull String messageTemplate,
+            final Object... messageArgs) {
+        if (!expression) {
+            throw new IllegalStateException(String.format(messageTemplate, messageArgs));
+        }
+    }
+
+    /**
      * Ensures the truth of an expression involving whether the calling identity is authorized to
      * call the calling method.
      *
@@ -214,6 +262,25 @@
     }
 
     /**
+     * Ensures the truth of an expression involving whether the calling identity is authorized to
+     * call the calling method.
+     *
+     * @param expression a boolean expression
+     * @param messageTemplate a printf-style message template to use if the check fails; will
+     *     be converted to a string using {@link String#format(String, Object...)}
+     * @param messageArgs arguments for {@code messageTemplate}
+     * @throws SecurityException if {@code expression} is false
+     */
+    public static void checkCallAuthorization(
+            final boolean expression,
+            final @NonNull String messageTemplate,
+            final Object... messageArgs) {
+        if (!expression) {
+            throw new SecurityException(String.format(messageTemplate, messageArgs));
+        }
+    }
+
+    /**
      * Ensures the truth of an expression involving whether the calling user is authorized to
      * call the calling method.
      *
@@ -646,7 +713,7 @@
      */
     public static float[] checkArrayElementsInRange(float[] value, float lower, float upper,
             String valueName) {
-        checkNotNull(value, valueName + " must not be null");
+        checkNotNull(value, "%s must not be null", valueName);
 
         for (int i = 0; i < value.length; ++i) {
             float v = value[i];
@@ -682,7 +749,7 @@
      */
     public static int[] checkArrayElementsInRange(int[] value, int lower, int upper,
             String valueName) {
-        checkNotNull(value, valueName + " must not be null");
+        checkNotNull(value, "%s must not be null", valueName);
 
         for (int i = 0; i < value.length; ++i) {
             int v = value[i];
diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java
index a23fc4b..0bafb2f 100644
--- a/core/java/com/android/internal/util/ScreenshotHelper.java
+++ b/core/java/com/android/internal/util/ScreenshotHelper.java
@@ -279,6 +279,7 @@
             final Runnable mScreenshotTimeout = () -> {
                 synchronized (mScreenshotLock) {
                     if (mScreenshotConnection != null) {
+                        Log.e(TAG, "Timed out before getting screenshot capture response");
                         mContext.unbindService(mScreenshotConnection);
                         mScreenshotConnection = null;
                         mScreenshotService = null;
@@ -353,6 +354,7 @@
                                 mScreenshotService = null;
                                 // only log an error if we're still within the timeout period
                                 if (handler.hasCallbacks(mScreenshotTimeout)) {
+                                    Log.e(TAG, "Screenshot service disconnected");
                                     handler.removeCallbacks(mScreenshotTimeout);
                                     notifyScreenshotError();
                                 }
diff --git a/core/java/com/android/internal/util/XmlUtils.java b/core/java/com/android/internal/util/XmlUtils.java
index c1be33a..bd6b950 100644
--- a/core/java/com/android/internal/util/XmlUtils.java
+++ b/core/java/com/android/internal/util/XmlUtils.java
@@ -1416,19 +1416,19 @@
         if (tagName.equals("null")) {
             res = null;
         } else if (tagName.equals("string")) {
-            String value = "";
+            final StringBuilder value = new StringBuilder();
             int eventType;
             while ((eventType = parser.next()) != parser.END_DOCUMENT) {
                 if (eventType == parser.END_TAG) {
                     if (parser.getName().equals("string")) {
                         name[0] = valueName;
                         //System.out.println("Returning value for " + valueName + ": " + value);
-                        return value;
+                        return value.toString();
                     }
                     throw new XmlPullParserException(
                         "Unexpected end tag in <string>: " + parser.getName());
                 } else if (eventType == parser.TEXT) {
-                    value += parser.getText();
+                    value.append(parser.getText());
                 } else if (eventType == parser.START_TAG) {
                     throw new XmlPullParserException(
                         "Unexpected start tag in <string>: " + parser.getName());
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index fff9ac9..8962dc3 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -24,7 +24,7 @@
 import android.os.RemoteException;
 import android.util.MergedConfiguration;
 import android.view.DragEvent;
-import android.view.IScrollCaptureController;
+import android.view.IScrollCaptureCallbacks;
 import android.view.IWindow;
 import android.view.IWindowSession;
 import android.view.InsetsSourceControl;
@@ -162,9 +162,9 @@
     }
 
     @Override
-    public void requestScrollCapture(IScrollCaptureController controller) {
+    public void requestScrollCapture(IScrollCaptureCallbacks callbacks) {
         try {
-            controller.onClientUnavailable();
+            callbacks.onUnavailable();
         } catch (RemoteException ex) {
             // ignore
         }
diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
index 9257c6d..cd5502c 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -35,11 +35,13 @@
 import android.view.inputmethod.InputConnectionInspector;
 import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags;
 import android.view.inputmethod.InputContentInfo;
+import android.view.inputmethod.SurroundingText;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.inputmethod.ICharSequenceResultCallback;
 import com.android.internal.inputmethod.IExtractedTextResultCallback;
 import com.android.internal.inputmethod.IIntResultCallback;
+import com.android.internal.inputmethod.ISurroundingTextResultCallback;
 import com.android.internal.os.SomeArgs;
 
 public abstract class IInputConnectionWrapper extends IInputContext.Stub {
@@ -70,6 +72,8 @@
     private static final int DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO = 140;
     private static final int DO_CLOSE_CONNECTION = 150;
     private static final int DO_COMMIT_CONTENT = 160;
+    private static final int DO_GET_SURROUNDING_TEXT = 41;
+
 
     @GuardedBy("mLock")
     @Nullable
@@ -127,6 +131,21 @@
         dispatchMessage(mH.obtainMessage(DO_GET_SELECTED_TEXT, flags, 0 /* unused */, callback));
     }
 
+    /**
+     * Dispatches the request for retrieving surrounding text.
+     *
+     * <p>See {@link InputConnection#getSurroundingText(int, int, int)}.
+     */
+    public void getSurroundingText(int beforeLength, int afterLength, int flags,
+            ISurroundingTextResultCallback callback) {
+        final SomeArgs args = SomeArgs.obtain();
+        args.arg1 = beforeLength;
+        args.arg2 = afterLength;
+        args.arg3 = flags;
+        args.arg4 = callback;
+        dispatchMessage(mH.obtainMessage(DO_GET_SURROUNDING_TEXT, flags, 0 /* unused */, args));
+    }
+
     public void getCursorCapsMode(int reqModes, IIntResultCallback callback) {
         dispatchMessage(
                 mH.obtainMessage(DO_GET_CURSOR_CAPS_MODE, reqModes, 0 /* unused */, callback));
@@ -293,6 +312,33 @@
                 }
                 return;
             }
+            case DO_GET_SURROUNDING_TEXT: {
+                final SomeArgs args = (SomeArgs) msg.obj;
+                try {
+                    int beforeLength = (int) args.arg1;
+                    int afterLength  = (int) args.arg2;
+                    int flags = (int) args.arg3;
+                    final ISurroundingTextResultCallback callback =
+                            (ISurroundingTextResultCallback) args.arg4;
+                    final InputConnection ic = getInputConnection();
+                    final SurroundingText result;
+                    if (ic == null || !isActive()) {
+                        Log.w(TAG, "getSurroundingText on inactive InputConnection");
+                        result = null;
+                    } else {
+                        result = ic.getSurroundingText(beforeLength, afterLength, flags);
+                    }
+                    try {
+                        callback.onResult(result);
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "Failed to return the result to getSurroundingText()."
+                                + " result=" + result, e);
+                    }
+                } finally {
+                    args.recycle();
+                }
+                return;
+            }
             case DO_GET_CURSOR_CAPS_MODE: {
                 final IIntResultCallback callback = (IIntResultCallback) msg.obj;
                 final InputConnection ic = getInputConnection();
diff --git a/core/java/com/android/internal/view/IInputContext.aidl b/core/java/com/android/internal/view/IInputContext.aidl
index 86f1293..074908a 100644
--- a/core/java/com/android/internal/view/IInputContext.aidl
+++ b/core/java/com/android/internal/view/IInputContext.aidl
@@ -26,6 +26,7 @@
 import com.android.internal.inputmethod.ICharSequenceResultCallback;
 import com.android.internal.inputmethod.IExtractedTextResultCallback;
 import com.android.internal.inputmethod.IIntResultCallback;
+import com.android.internal.inputmethod.ISurroundingTextResultCallback;
 
 /**
  * Interface from an input method to the application, allowing it to perform
@@ -79,4 +80,7 @@
 
     void commitContent(in InputContentInfo inputContentInfo, int flags, in Bundle opts,
             IIntResultCallback callback);
+
+    void getSurroundingText(int beforeLength, int afterLength, int flags,
+            ISurroundingTextResultCallback callback);
 }
diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java
index 0bf5234..f086dd7 100644
--- a/core/java/com/android/internal/view/InputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/InputConnectionWrapper.java
@@ -33,6 +33,7 @@
 import android.view.inputmethod.InputConnectionInspector;
 import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags;
 import android.view.inputmethod.InputContentInfo;
+import android.view.inputmethod.SurroundingText;
 
 import com.android.internal.inputmethod.CancellationGroup;
 import com.android.internal.inputmethod.ResultCallbacks;
@@ -157,6 +158,38 @@
         return getResultOrNull(value, "getSelectedText()");
     }
 
+    /**
+     * Get {@link SurroundingText} around the current cursor, with <var>beforeLength</var>
+     * characters of text before the cursor, <var>afterLength</var> characters of text after the
+     * cursor, and all of the selected text.
+     * @param beforeLength The expected length of the text before the cursor
+     * @param afterLength The expected length of the text after the cursor
+     * @param flags Supplies additional options controlling how the text is returned. May be either
+     *              0 or {@link #GET_TEXT_WITH_STYLES}.
+     * @return the surrounding text around the cursor position; the length of the returned text
+     * might be less than requested.  It could also be {@code null} when the editor or system could
+     * not support this protocol.
+     */
+    @AnyThread
+    public SurroundingText getSurroundingText(int beforeLength, int afterLength, int flags) {
+        if (mCancellationGroup.isCanceled()) {
+            return null;
+        }
+        if (isMethodMissing(MissingMethodFlags.GET_SURROUNDING_TEXT)) {
+            // This method is not implemented.
+            return null;
+        }
+        final CancellationGroup.Completable.SurroundingText value =
+                mCancellationGroup.createCompletableSurroundingText();
+        try {
+            mIInputContext.getSurroundingText(beforeLength, afterLength, flags,
+                    ResultCallbacks.of(value));
+        } catch (RemoteException e) {
+            return null;
+        }
+        return getResultOrNull(value, "getSurroundingText()");
+    }
+
     @AnyThread
     public int getCursorCapsMode(int reqModes) {
         if (mCancellationGroup.isCanceled()) {
diff --git a/core/java/com/android/internal/view/RecyclerViewCaptureHelper.java b/core/java/com/android/internal/view/RecyclerViewCaptureHelper.java
new file mode 100644
index 0000000..461e116
--- /dev/null
+++ b/core/java/com/android/internal/view/RecyclerViewCaptureHelper.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.view;
+
+import android.animation.ValueAnimator;
+import android.annotation.NonNull;
+import android.graphics.Rect;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+
+/**
+ * ScrollCapture for RecyclerView and <i>RecyclerView-like</i> ViewGroups.
+ * <p>
+ * Requirements for proper operation:
+ * <ul>
+ * <li>at least one visible child view</li>
+ * <li>scrolls by pixels in response to {@link View#scrollBy(int, int)}.
+ * <li>reports ability to scroll with {@link View#canScrollVertically(int)}
+ * <li>properly implements {@link ViewParent#requestChildRectangleOnScreen(View, Rect, boolean)}
+ * </ul>
+ *
+ * @see ScrollCaptureViewSupport
+ */
+public class RecyclerViewCaptureHelper implements ScrollCaptureViewHelper<ViewGroup> {
+
+    // Experiment
+    private static final boolean DISABLE_ANIMATORS = false;
+    // Experiment
+    private static final boolean STOP_RENDER_THREAD = false;
+
+    private static final String TAG = "RVCaptureHelper";
+    private int mScrollDelta;
+    private boolean mScrollBarWasEnabled;
+    private int mOverScrollMode;
+    private float mDurationScale;
+
+    @Override
+    public void onPrepareForStart(@NonNull ViewGroup view, Rect scrollBounds) {
+        mScrollDelta = 0;
+
+        mOverScrollMode = view.getOverScrollMode();
+        view.setOverScrollMode(View.OVER_SCROLL_NEVER);
+
+        mScrollBarWasEnabled = view.isVerticalScrollBarEnabled();
+        view.setVerticalScrollBarEnabled(false);
+        if (DISABLE_ANIMATORS) {
+            mDurationScale = ValueAnimator.getDurationScale();
+            ValueAnimator.setDurationScale(0);
+        }
+        if (STOP_RENDER_THREAD) {
+            view.getThreadedRenderer().stop();
+        }
+    }
+
+    @Override
+    public ScrollResult onScrollRequested(@NonNull ViewGroup recyclerView, Rect scrollBounds,
+            Rect requestRect) {
+        ScrollResult result = new ScrollResult();
+        result.requestedArea = new Rect(requestRect);
+        result.scrollDelta = mScrollDelta;
+        result.availableArea = new Rect(); // empty
+
+        Log.d(TAG, "current scrollDelta: " + mScrollDelta);
+        if (!recyclerView.isVisibleToUser() || recyclerView.getChildCount() == 0) {
+            Log.w(TAG, "recyclerView is empty or not visible, cannot continue");
+            return result; // result.availableArea == empty Rect
+        }
+
+        // move from scrollBounds-relative to parent-local coordinates
+        Rect requestedContainerBounds = new Rect(requestRect);
+        requestedContainerBounds.offset(0, -mScrollDelta);
+        requestedContainerBounds.offset(scrollBounds.left, scrollBounds.top);
+
+        // requestedContainerBounds is now in recyclerview-local coordinates
+        Log.d(TAG, "requestedContainerBounds: " + requestedContainerBounds);
+
+        // Save a copy for later
+        View anchor = findChildNearestTarget(recyclerView, requestedContainerBounds);
+        if (anchor == null) {
+            Log.d(TAG, "Failed to locate anchor view");
+            return result; // result.availableArea == null
+        }
+
+        Log.d(TAG, "Anchor view:" + anchor);
+        Rect requestedContentBounds = new Rect(requestedContainerBounds);
+        recyclerView.offsetRectIntoDescendantCoords(anchor, requestedContentBounds);
+
+        Log.d(TAG, "requestedContentBounds = " + requestedContentBounds);
+        int prevAnchorTop = anchor.getTop();
+        // Note: requestChildRectangleOnScreen may modify rectangle, must pass pass in a copy here
+        Rect input = new Rect(requestedContentBounds);
+        if (recyclerView.requestChildRectangleOnScreen(anchor, input, true)) {
+            int scrolled = prevAnchorTop - anchor.getTop(); // inverse of movement
+            Log.d(TAG, "RecyclerView scrolled by " + scrolled + " px");
+            mScrollDelta += scrolled; // view.top-- is equivalent to parent.scrollY++
+            result.scrollDelta = mScrollDelta;
+            Log.d(TAG, "requestedContentBounds, (post-request-rect) = " + requestedContentBounds);
+        }
+
+        requestedContainerBounds.set(requestedContentBounds);
+        recyclerView.offsetDescendantRectToMyCoords(anchor, requestedContainerBounds);
+        Log.d(TAG, "requestedContainerBounds, (post-scroll): " + requestedContainerBounds);
+
+        Rect recyclerLocalVisible = new Rect(scrollBounds);
+        recyclerView.getLocalVisibleRect(recyclerLocalVisible);
+        Log.d(TAG, "recyclerLocalVisible: " + recyclerLocalVisible);
+
+        if (!requestedContainerBounds.intersect(recyclerLocalVisible)) {
+            // Requested area is still not visible
+            Log.d(TAG, "requested bounds not visible!");
+            return result;
+        }
+        Rect available = new Rect(requestedContainerBounds);
+        available.offset(-scrollBounds.left, -scrollBounds.top);
+        available.offset(0, mScrollDelta);
+        result.availableArea = available;
+        Log.d(TAG, "availableArea: " + result.availableArea);
+        return result;
+    }
+
+    /**
+     * Find a view that is located "closest" to targetRect. Returns the first view to fully
+     * vertically overlap the target targetRect. If none found, returns the view with an edge
+     * nearest the target targetRect.
+     *
+     * @param parent the parent vertical layout
+     * @param targetRect a rectangle in local coordinates of <code>parent</code>
+     * @return a child view within parent matching the criteria or null
+     */
+    static View findChildNearestTarget(ViewGroup parent, Rect targetRect) {
+        View selected = null;
+        int minCenterDistance = Integer.MAX_VALUE;
+        int maxOverlap = 0;
+
+        // allowable center-center distance, relative to targetRect.
+        // if within this range, taller views are preferred
+        final float preferredRangeFromCenterPercent = 0.25f;
+        final int preferredDistance =
+                (int) (preferredRangeFromCenterPercent * targetRect.height());
+
+        Rect parentLocalVis = new Rect();
+        parent.getLocalVisibleRect(parentLocalVis);
+        Log.d(TAG, "findChildNearestTarget: parentVis=" + parentLocalVis
+                + " targetRect=" + targetRect);
+
+        Rect frame = new Rect();
+        for (int i = 0; i < parent.getChildCount(); i++) {
+            final View child = parent.getChildAt(i);
+            child.getHitRect(frame);
+            Log.d(TAG, "child #" + i + " hitRect=" + frame);
+
+            if (child.getVisibility() != View.VISIBLE) {
+                Log.d(TAG, "child #" + i + " is not visible");
+                continue;
+            }
+
+            int centerDistance = Math.abs(targetRect.centerY() - frame.centerY());
+            Log.d(TAG, "child #" + i + " : center to center: " + centerDistance + "px");
+
+            if (centerDistance < minCenterDistance) {
+                // closer to center
+                minCenterDistance = centerDistance;
+                selected = child;
+            } else if (frame.intersect(targetRect) && (frame.height() > preferredDistance)) {
+                // within X% pixels of center, but taller
+                selected = child;
+            }
+        }
+        return selected;
+    }
+
+
+    @Override
+    public void onPrepareForEnd(@NonNull ViewGroup view) {
+        // Restore original position and state
+        view.scrollBy(0, mScrollDelta);
+        view.setOverScrollMode(mOverScrollMode);
+        view.setVerticalScrollBarEnabled(mScrollBarWasEnabled);
+        if (DISABLE_ANIMATORS) {
+            ValueAnimator.setDurationScale(mDurationScale);
+        }
+        if (STOP_RENDER_THREAD) {
+            view.getThreadedRenderer().start();
+        }
+    }
+}
diff --git a/core/java/com/android/internal/view/ScrollCaptureInternal.java b/core/java/com/android/internal/view/ScrollCaptureInternal.java
index c589afde..ae1a815 100644
--- a/core/java/com/android/internal/view/ScrollCaptureInternal.java
+++ b/core/java/com/android/internal/view/ScrollCaptureInternal.java
@@ -17,8 +17,11 @@
 package com.android.internal.view;
 
 import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.util.Log;
 import android.view.ScrollCaptureCallback;
 import android.view.View;
 import android.view.ViewGroup;
@@ -29,6 +32,12 @@
 public class ScrollCaptureInternal {
     private static final String TAG = "ScrollCaptureInternal";
 
+    // Log found scrolling views
+    private static final boolean DEBUG = true;
+
+    // Log all investigated views, as well as heuristic checks
+    private static final boolean DEBUG_VERBOSE = false;
+
     private static final int UP = -1;
     private static final int DOWN = 1;
 
@@ -57,38 +66,72 @@
      * This needs to be fast and not alloc memory. It's called on everything in the tree not marked
      * as excluded during scroll capture search.
      */
-    public static int detectScrollingType(View view) {
+    private static int detectScrollingType(View view) {
         // Must be a ViewGroup
         if (!(view instanceof ViewGroup)) {
+            if (DEBUG_VERBOSE) {
+                Log.v(TAG, "hint: not a subclass of ViewGroup");
+            }
             return TYPE_FIXED;
         }
+        if (DEBUG_VERBOSE) {
+            Log.v(TAG, "hint: is a subclass of ViewGroup");
+        }
         // Confirm that it can scroll.
         if (!(view.canScrollVertically(DOWN) || view.canScrollVertically(UP))) {
             // Nothing to scroll here, move along.
+            if (DEBUG_VERBOSE) {
+                Log.v(TAG, "hint: cannot be scrolled");
+            }
             return TYPE_FIXED;
         }
+        if (DEBUG_VERBOSE) {
+            Log.v(TAG, "hint: can be scrolled up or down");
+        }
         // ScrollViews accept only a single child.
         if (((ViewGroup) view).getChildCount() > 1) {
+            if (DEBUG_VERBOSE) {
+                Log.v(TAG, "hint: scrollable with multiple children");
+            }
             return TYPE_RECYCLING;
         }
+        if (DEBUG_VERBOSE) {
+            Log.v(TAG, "hint: less than two child views");
+        }
         //Because recycling containers don't use scrollY, a non-zero value means Scroll view.
         if (view.getScrollY() != 0) {
+            if (DEBUG_VERBOSE) {
+                Log.v(TAG, "hint: scrollY != 0");
+            }
             return TYPE_SCROLLING;
         }
+        Log.v(TAG, "hint: scrollY == 0");
         // Since scrollY cannot be negative, this means a Recycling view.
         if (view.canScrollVertically(UP)) {
+            if (DEBUG_VERBOSE) {
+                Log.v(TAG, "hint: able to scroll up");
+            }
             return TYPE_RECYCLING;
         }
-        // canScrollVertically(UP) == false, getScrollY() == 0, getChildCount() == 1.
+        if (DEBUG_VERBOSE) {
+            Log.v(TAG, "hint: cannot be scrolled up");
+        }
 
+        // canScrollVertically(UP) == false, getScrollY() == 0, getChildCount() == 1.
         // For Recycling containers, this should be a no-op (RecyclerView logs a warning)
         view.scrollTo(view.getScrollX(), 1);
 
         // A scrolling container would have moved by 1px.
         if (view.getScrollY() == 1) {
             view.scrollTo(view.getScrollX(), 0);
+            if (DEBUG_VERBOSE) {
+                Log.v(TAG, "hint: scrollTo caused scrollY to change");
+            }
             return TYPE_SCROLLING;
         }
+        if (DEBUG_VERBOSE) {
+            Log.v(TAG, "hint: scrollTo did not cause scrollY to change");
+        }
         return TYPE_RECYCLING;
     }
 
@@ -99,19 +142,61 @@
      * @param localVisibleRect the visible area of the given view in local coordinates, as supplied
      *                         by the view parent
      * @param positionInWindow the offset of localVisibleRect within the window
-     *
      * @return a new callback or null if the View isn't supported
      */
     @Nullable
     public ScrollCaptureCallback requestCallback(View view, Rect localVisibleRect,
             Point positionInWindow) {
         // Nothing to see here yet.
+        if (DEBUG_VERBOSE) {
+            Log.v(TAG, "scroll capture: checking " + view.getClass().getName()
+                    + "[" + resolveId(view.getContext(), view.getId()) + "]");
+        }
         int i = detectScrollingType(view);
         switch (i) {
             case TYPE_SCROLLING:
+                if (DEBUG) {
+                    Log.d(TAG, "scroll capture: FOUND " + view.getClass().getName()
+                            + "[" + resolveId(view.getContext(), view.getId()) + "]"
+                            + " -> TYPE_SCROLLING");
+                }
                 return new ScrollCaptureViewSupport<>((ViewGroup) view,
                         new ScrollViewCaptureHelper());
+            case TYPE_RECYCLING:
+                if (DEBUG) {
+                    Log.d(TAG, "scroll capture: FOUND " + view.getClass().getName()
+                            + "[" + resolveId(view.getContext(), view.getId()) + "]"
+                            + " -> TYPE_RECYCLING");
+                }
+                return new ScrollCaptureViewSupport<>((ViewGroup) view,
+                        new RecyclerViewCaptureHelper());
+            case TYPE_FIXED:
+                // ignore
+                break;
+
         }
         return null;
     }
+
+    // Lifted from ViewDebug (package protected)
+
+    private static String formatIntToHexString(int value) {
+        return "0x" + Integer.toHexString(value).toUpperCase();
+    }
+
+    static String resolveId(Context context, int id) {
+        String fieldValue;
+        final Resources resources = context.getResources();
+        if (id >= 0) {
+            try {
+                fieldValue = resources.getResourceTypeName(id) + '/'
+                        + resources.getResourceEntryName(id);
+            } catch (Resources.NotFoundException e) {
+                fieldValue = "id/" + formatIntToHexString(id);
+            }
+        } else {
+            fieldValue = "NO_ID";
+        }
+        return fieldValue;
+    }
 }
diff --git a/core/java/com/android/internal/view/ScrollCaptureViewHelper.java b/core/java/com/android/internal/view/ScrollCaptureViewHelper.java
index a92e978..9829d0b 100644
--- a/core/java/com/android/internal/view/ScrollCaptureViewHelper.java
+++ b/core/java/com/android/internal/view/ScrollCaptureViewHelper.java
@@ -17,7 +17,6 @@
 package com.android.internal.view;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.graphics.Rect;
 import android.view.View;
 
@@ -62,8 +61,8 @@
      * @param view the view being captured
      * @return true if the callback should respond to a request with scroll bounds
      */
-    default boolean onAcceptSession(@Nullable V view) {
-        return view != null && view.isVisibleToUser()
+    default boolean onAcceptSession(@NonNull V view) {
+        return view.isVisibleToUser()
                 && (view.canScrollVertically(UP) || view.canScrollVertically(DOWN));
     }
 
@@ -73,7 +72,7 @@
      *
      * @param view the view being captured
      */
-    default Rect onComputeScrollBounds(@Nullable V view) {
+    @NonNull default Rect onComputeScrollBounds(@NonNull V view) {
         return new Rect(view.getPaddingLeft(), view.getPaddingTop(),
                 view.getWidth() - view.getPaddingRight(),
                 view.getHeight() - view.getPaddingBottom());
@@ -88,7 +87,7 @@
      * @param view         the view being captured
      * @param scrollBounds the bounds within {@code view} where content scrolls
      */
-    void onPrepareForStart(@NonNull V view, Rect scrollBounds);
+    void onPrepareForStart(@NonNull V view, @NonNull Rect scrollBounds);
 
     /**
      * Map the request onto the screen.
@@ -105,7 +104,9 @@
      *                     content to capture for the request
      * @return the result of the request as a {@link ScrollResult}
      */
-    ScrollResult onScrollRequested(@NonNull V view, Rect scrollBounds, Rect requestRect);
+    @NonNull
+    ScrollResult onScrollRequested(@NonNull V view, @NonNull Rect scrollBounds,
+            @NonNull Rect requestRect);
 
     /**
      * Restore the target after capture.
diff --git a/core/java/com/android/internal/view/ScrollCaptureViewSupport.java b/core/java/com/android/internal/view/ScrollCaptureViewSupport.java
index 7b4f73f..85fa791 100644
--- a/core/java/com/android/internal/view/ScrollCaptureViewSupport.java
+++ b/core/java/com/android/internal/view/ScrollCaptureViewSupport.java
@@ -23,8 +23,8 @@
 import android.graphics.RectF;
 import android.graphics.RenderNode;
 import android.os.Handler;
-import android.os.SystemClock;
 import android.util.DisplayMetrics;
+import android.util.Log;
 import android.view.ScrollCaptureCallback;
 import android.view.ScrollCaptureSession;
 import android.view.Surface;
@@ -46,8 +46,12 @@
  */
 public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCallback {
 
+    public static final long NO_FRAME_PRODUCED = -1;
+
     private static final String TAG = "ScrollCaptureViewSupport";
 
+    private static final boolean WAIT_FOR_ANIMATION = true;
+
     private final WeakReference<V> mWeakView;
     private final ScrollCaptureViewHelper<V> mViewHelper;
     private ViewRenderer mRenderer;
@@ -99,12 +103,16 @@
         V view = mWeakView.get();
         if (view == null || !view.isVisibleToUser()) {
             // Signal to the controller that we have a problem and can't continue.
-            session.notifyBufferSent(0, null);
+            session.notifyBufferSent(NO_FRAME_PRODUCED, new Rect());
             return;
         }
         // Ask the view to scroll as needed to bring this area into view.
         ScrollResult scrollResult = mViewHelper.onScrollRequested(view, session.getScrollBounds(),
                 requestRect);
+        if (scrollResult.availableArea.isEmpty()) {
+            session.notifyBufferSent(NO_FRAME_PRODUCED, scrollResult.availableArea);
+            return;
+        }
         view.invalidate(); // don't wait for vsync
 
         // For image capture, shift back by scrollDelta to arrive at the location within the view
@@ -112,8 +120,19 @@
         Rect viewCaptureArea = new Rect(scrollResult.availableArea);
         viewCaptureArea.offset(0, -scrollResult.scrollDelta);
 
-        mRenderer.renderView(view, viewCaptureArea, mUiHandler,
-                (frameNumber) -> session.notifyBufferSent(frameNumber, scrollResult.availableArea));
+        if (WAIT_FOR_ANIMATION) {
+            Log.d(TAG, "render: delaying until animation");
+            view.postOnAnimation(() ->  {
+                Log.d(TAG, "postOnAnimation(): rendering now");
+                long resultFrame = mRenderer.renderView(view, viewCaptureArea);
+                Log.d(TAG, "notifyBufferSent: " + scrollResult.availableArea);
+
+                session.notifyBufferSent(resultFrame, new Rect(scrollResult.availableArea));
+            });
+        } else {
+            long resultFrame = mRenderer.renderView(view, viewCaptureArea);
+            session.notifyBufferSent(resultFrame, new Rect(scrollResult.availableArea));
+        }
     }
 
     @Override
@@ -132,8 +151,7 @@
 
     /**
      * Internal helper class which assists in rendering sections of the view hierarchy relative to a
-     * given view. Used by framework implementations of ScrollCaptureHandler to render and dispatch
-     * image requests.
+     * given view.
      */
     static final class ViewRenderer {
         // alpha, "reasonable default" from Javadoc
@@ -157,14 +175,11 @@
         private final Matrix mTempMatrix = new Matrix();
         private final int[] mTempLocation = new int[2];
         private long mLastRenderedSourceDrawingId = -1;
-
-
-        public interface FrameCompleteListener {
-            void onFrameComplete(long frameNumber);
-        }
+        private Surface mSurface;
 
         ViewRenderer() {
             mRenderer = new HardwareRenderer();
+            mRenderer.setName("ScrollCapture");
             mCaptureRenderNode = new RenderNode("ScrollCaptureRoot");
             mRenderer.setContentRoot(mCaptureRenderNode);
 
@@ -173,6 +188,7 @@
         }
 
         public void setSurface(Surface surface) {
+            mSurface = surface;
             mRenderer.setSurface(surface);
         }
 
@@ -223,20 +239,38 @@
             mCaptureRenderNode.endRecording();
         }
 
-        public void renderView(View view, Rect sourceRect, Handler handler,
-                FrameCompleteListener frameListener) {
+        public long renderView(View view, Rect sourceRect) {
             if (updateForView(view)) {
                 setupLighting(view);
             }
             view.invalidate();
             updateRootNode(view, sourceRect);
             HardwareRenderer.FrameRenderRequest request = mRenderer.createRenderRequest();
-            request.setVsyncTime(SystemClock.elapsedRealtimeNanos());
-            // private API b/c request.setFrameCommitCallback does not provide access to frameNumber
-            mRenderer.setFrameCompleteCallback(
-                    frameNr -> handler.post(() -> frameListener.onFrameComplete(frameNr)));
+            long timestamp = System.nanoTime();
+            request.setVsyncTime(timestamp);
+
+            // Would be nice to access nextFrameNumber from HwR without having to hold on to Surface
+            final long frameNumber = mSurface.getNextFrameNumber();
+
+            // Block until a frame is presented to the Surface
             request.setWaitForPresent(true);
-            request.syncAndDraw();
+
+            switch (request.syncAndDraw()) {
+                case HardwareRenderer.SYNC_OK:
+                case HardwareRenderer.SYNC_REDRAW_REQUESTED:
+                    return frameNumber;
+
+                case HardwareRenderer.SYNC_FRAME_DROPPED:
+                    Log.e(TAG, "syncAndDraw(): SYNC_FRAME_DROPPED !");
+                    break;
+                case HardwareRenderer.SYNC_LOST_SURFACE_REWARD_IF_FOUND:
+                    Log.e(TAG, "syncAndDraw(): SYNC_LOST_SURFACE !");
+                    break;
+                case HardwareRenderer.SYNC_CONTEXT_IS_STOPPED:
+                    Log.e(TAG, "syncAndDraw(): SYNC_CONTEXT_IS_STOPPED !");
+                    break;
+            }
+            return NO_FRAME_PRODUCED;
         }
 
         public void trimMemory() {
@@ -244,6 +278,7 @@
         }
 
         public void destroy() {
+            mSurface = null;
             mRenderer.destroy();
         }
 
@@ -254,6 +289,5 @@
             mTempMatrix.mapRect(mTempRectF);
             mTempRectF.round(outRect);
         }
-
     }
 }
diff --git a/core/java/com/android/internal/view/ScrollViewCaptureHelper.java b/core/java/com/android/internal/view/ScrollViewCaptureHelper.java
index 1514b9a..a1d202e 100644
--- a/core/java/com/android/internal/view/ScrollViewCaptureHelper.java
+++ b/core/java/com/android/internal/view/ScrollViewCaptureHelper.java
@@ -57,10 +57,6 @@
 
     public ScrollResult onScrollRequested(@NonNull ViewGroup view, Rect scrollBounds,
             Rect requestRect) {
-        final View contentView = view.getChildAt(0); // returns null, does not throw IOOBE
-        if (contentView == null) {
-            return null;
-        }
         /*
                +---------+ <----+ Content [25,25 - 275,1025] (w=250,h=1000)
                |         |
@@ -88,9 +84,6 @@
             \__ Requested Bounds[0,300 - 200,400] (200x100)
        */
 
-        ScrollResult result = new ScrollResult();
-        result.requestedArea = new Rect(requestRect);
-
         // 0) adjust the requestRect to account for scroll change since start
         //
         //  Scroll Bounds[50,50 - 250,250]  (w=200,h=200)
@@ -99,6 +92,17 @@
         // (y-100) (scrollY - mStartScrollY)
         int scrollDelta = view.getScrollY() - mStartScrollY;
 
+        final ScrollResult result = new ScrollResult();
+        result.requestedArea = new Rect(requestRect);
+        result.scrollDelta = scrollDelta;
+        result.availableArea = new Rect();
+
+        final View contentView = view.getChildAt(0); // returns null, does not throw IOOBE
+        if (contentView == null) {
+            // No child view? Cannot continue.
+            return result;
+        }
+
         //  1) Translate request rect to make it relative to container view
         //
         //  Container View [0,0 - 300,300] (scrollY=200)
@@ -133,7 +137,7 @@
         // TODO: crop capture area to avoid occlusions/minimize scroll changes
 
         Point offset = new Point();
-        final Rect available = new Rect(requestedContentBounds); // empty
+        final Rect available = new Rect(requestedContentBounds);
         if (!view.getChildVisibleRect(contentView, available, offset)) {
             available.setEmpty();
             result.availableArea = available;
diff --git a/core/java/com/android/internal/widget/CachingIconView.java b/core/java/com/android/internal/widget/CachingIconView.java
index 84cde1b..0bf323f 100644
--- a/core/java/com/android/internal/widget/CachingIconView.java
+++ b/core/java/com/android/internal/widget/CachingIconView.java
@@ -16,12 +16,15 @@
 
 package com.android.internal.widget;
 
+import static com.android.internal.widget.ColoredIconHelper.applyGrayTint;
+
 import android.annotation.DrawableRes;
 import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
+import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
@@ -48,6 +51,7 @@
     private Consumer<Integer> mOnVisibilityChangedListener;
     private Consumer<Boolean> mOnForceHiddenChangedListener;
     private int mIconColor;
+    private int mBackgroundColor;
     private boolean mWillBeForceHidden;
 
     @UnsupportedAppUsage
@@ -230,9 +234,55 @@
         return mForceHidden;
     }
 
+    /**
+     * Provides the notification's background color to the icon.  This is only used when the icon
+     * is "inverted".  This should be called before calling {@link #setOriginalIconColor(int)}.
+     */
+    @RemotableViewMethod
+    public void setBackgroundColor(int color) {
+        mBackgroundColor = color;
+    }
+
+    /**
+     * Sets the icon color. If COLOR_INVALID is set, the icon's color filter will
+     * not be altered. If there is a background drawable, this method uses the value from
+     * {@link #setBackgroundColor(int)} which must have been already called.
+     */
     @RemotableViewMethod
     public void setOriginalIconColor(int color) {
         mIconColor = color;
+        Drawable background = getBackground();
+        Drawable icon = getDrawable();
+        boolean hasColor = color != ColoredIconHelper.COLOR_INVALID;
+        if (background == null) {
+            // This is the pre-S style -- colored icon with no background.
+            if (hasColor) {
+                icon.mutate().setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
+            }
+        } else {
+            // When there is a background drawable, color it with the foreground color and
+            // colorize the icon itself with the background color, creating an inverted effect.
+            if (hasColor) {
+                background.mutate().setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
+                icon.mutate().setColorFilter(mBackgroundColor, PorterDuff.Mode.SRC_ATOP);
+            } else {
+                background.mutate().setColorFilter(mBackgroundColor, PorterDuff.Mode.SRC_ATOP);
+            }
+        }
+    }
+
+    /**
+     * Set the icon's color filter: to gray if true, otherwise colored.
+     * If this icon has no original color, this has no effect.
+     */
+    public void setGrayedOut(boolean grayedOut) {
+        // If there is a background drawable, then it has the foreground color and the image
+        // drawable has the background color, creating an inverted efffect.
+        Drawable drawable = getBackground();
+        if (drawable == null) {
+            drawable = getDrawable();
+        }
+        applyGrayTint(mContext, drawable, grayedOut, mIconColor);
     }
 
     public int getOriginalIconColor() {
diff --git a/core/java/com/android/internal/widget/ColoredIconHelper.java b/core/java/com/android/internal/widget/ColoredIconHelper.java
new file mode 100644
index 0000000..97e5e86
--- /dev/null
+++ b/core/java/com/android/internal/widget/ColoredIconHelper.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.widget;
+
+import android.annotation.ColorInt;
+import android.app.Notification;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+
+import com.android.internal.util.ContrastColorUtil;
+
+/** Helpers for colored icons */
+final class ColoredIconHelper {
+
+    @ColorInt
+    static final int COLOR_INVALID = Notification.COLOR_INVALID;
+
+    private ColoredIconHelper() {
+    }
+
+    /**
+     * Apply a gray tint or the original color to a drawable, accounting for the night mode in
+     * selecting the gray.
+     */
+    static void applyGrayTint(Context ctx, Drawable drawable, boolean apply, int originalColor) {
+        if (originalColor == COLOR_INVALID) {
+            return;
+        }
+        if (apply) {
+            // lets gray it out
+            Configuration config = ctx.getResources().getConfiguration();
+            boolean inNightMode = (config.uiMode & Configuration.UI_MODE_NIGHT_MASK)
+                    == Configuration.UI_MODE_NIGHT_YES;
+            int grey = ContrastColorUtil.resolveColor(ctx, Notification.COLOR_DEFAULT, inNightMode);
+            drawable.mutate().setColorFilter(grey, PorterDuff.Mode.SRC_ATOP);
+        } else {
+            // lets reset it
+            drawable.mutate().setColorFilter(originalColor, PorterDuff.Mode.SRC_ATOP);
+        }
+    }
+}
diff --git a/core/java/com/android/internal/widget/NotificationExpandButton.java b/core/java/com/android/internal/widget/NotificationExpandButton.java
index a499806..986412d 100644
--- a/core/java/com/android/internal/widget/NotificationExpandButton.java
+++ b/core/java/com/android/internal/widget/NotificationExpandButton.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.widget;
 
+import static com.android.internal.widget.ColoredIconHelper.applyGrayTint;
+
 import android.annotation.Nullable;
 import android.content.Context;
 import android.graphics.Rect;
@@ -26,12 +28,15 @@
 import android.widget.ImageView;
 import android.widget.RemoteViews;
 
+import com.android.internal.R;
+
 /**
  * An expand button in a notification
  */
 @RemoteViews.RemoteView
 public class NotificationExpandButton extends ImageView {
 
+    private boolean mExpanded;
     private int mOriginalNotificationColor;
 
     public NotificationExpandButton(Context context) {
@@ -67,6 +72,14 @@
         return mOriginalNotificationColor;
     }
 
+    /**
+     * Set the button's color filter: to gray if true, otherwise colored.
+     * If this button has no original color, this has no effect.
+     */
+    public void setGrayedOut(boolean shouldApply) {
+        applyGrayTint(mContext, getDrawable(), shouldApply, mOriginalNotificationColor);
+    }
+
     private void extendRectToMinTouchSize(Rect rect) {
         int touchTargetSize = (int) (getResources().getDisplayMetrics().density * 48);
         rect.left = rect.centerX() - touchTargetSize / 2;
@@ -80,4 +93,28 @@
         super.onInitializeAccessibilityNodeInfo(info);
         info.setClassName(Button.class.getName());
     }
+
+    /**
+     * Update the button's drawable, content description, and color for the given expanded state.
+     */
+    @RemotableViewMethod
+    public void setExpanded(boolean expanded) {
+        mExpanded = expanded;
+        updateExpandButton();
+    }
+
+    private void updateExpandButton() {
+        int drawableId;
+        int contentDescriptionId;
+        if (mExpanded) {
+            drawableId = R.drawable.ic_collapse_notification;
+            contentDescriptionId = R.string.expand_button_content_description_expanded;
+        } else {
+            drawableId = R.drawable.ic_expand_notification;
+            contentDescriptionId = R.string.expand_button_content_description_collapsed;
+        }
+        setImageDrawable(getContext().getDrawable(drawableId));
+        setColorFilter(mOriginalNotificationColor);
+        setContentDescription(mContext.getText(contentDescriptionId));
+    }
 }
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index ed663cf..a761b4c 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -250,7 +250,7 @@
     // partition that is used to verify if an overlay package fulfills
     // the 'config_signature' policy by comparing their signatures:
     // if the overlay package is signed with the same certificate as
-    // the package declared in 'config-signature' tag, then the
+    // the package declared in 'overlay-config-signature' tag, then the
     // overlay package fulfills the 'config_signature' policy.
     private String mOverlayConfigSignaturePackage;
 
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index adb0fad..4f97975 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -184,6 +184,7 @@
                 "com_android_internal_os_ClassLoaderFactory.cpp",
                 "com_android_internal_os_FuseAppLoop.cpp",
                 "com_android_internal_os_KernelCpuUidBpfMapReader.cpp",
+                "com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp",
                 "com_android_internal_os_KernelSingleUidTimeReader.cpp",
                 "com_android_internal_os_Zygote.cpp",
                 "com_android_internal_os_ZygoteInit.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 5b1196d..27b23bd 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -189,6 +189,7 @@
 extern int register_com_android_internal_os_ClassLoaderFactory(JNIEnv* env);
 extern int register_com_android_internal_os_FuseAppLoop(JNIEnv* env);
 extern int register_com_android_internal_os_KernelCpuUidBpfMapReader(JNIEnv *env);
+extern int register_com_android_internal_os_KernelSingleProcessCpuThreadReader(JNIEnv* env);
 extern int register_com_android_internal_os_KernelSingleUidTimeReader(JNIEnv *env);
 extern int register_com_android_internal_os_Zygote(JNIEnv *env);
 extern int register_com_android_internal_os_ZygoteInit(JNIEnv *env);
@@ -1581,6 +1582,7 @@
         REG_JNI(register_com_android_internal_content_NativeLibraryHelper),
         REG_JNI(register_com_android_internal_os_FuseAppLoop),
         REG_JNI(register_com_android_internal_os_KernelCpuUidBpfMapReader),
+        REG_JNI(register_com_android_internal_os_KernelSingleProcessCpuThreadReader),
         REG_JNI(register_com_android_internal_os_KernelSingleUidTimeReader),
 };
 
diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp
index a30c37b..3e87cb5 100644
--- a/core/jni/android_graphics_BLASTBufferQueue.cpp
+++ b/core/jni/android_graphics_BLASTBufferQueue.cpp
@@ -53,9 +53,11 @@
     queue->decStrong((void*)nativeCreate);
 }
 
-static jobject nativeGetSurface(JNIEnv* env, jclass clazz, jlong ptr) {
+static jobject nativeGetSurface(JNIEnv* env, jclass clazz, jlong ptr,
+                                jboolean includeSurfaceControlHandle) {
     sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
-    return android_view_Surface_createFromIGraphicBufferProducer(env, queue->getIGraphicBufferProducer());
+    return android_view_Surface_createFromSurface(env,
+                                                  queue->getSurface(includeSurfaceControlHandle));
 }
 
 static void nativeSetNextTransaction(JNIEnv* env, jclass clazz, jlong ptr, jlong transactionPtr) {
@@ -69,13 +71,19 @@
     queue->update(reinterpret_cast<SurfaceControl*>(surfaceControl), width, height);
 }
 
+static void nativeFlushShadowQueue(JNIEnv* env, jclass clazz, jlong ptr) {
+    sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
+    queue->flushShadowQueue();
+}
+
 static const JNINativeMethod gMethods[] = {
         /* name, signature, funcPtr */
         {"nativeCreate", "(Ljava/lang/String;JJJZ)J", (void*)nativeCreate},
-        {"nativeGetSurface", "(J)Landroid/view/Surface;", (void*)nativeGetSurface},
+        {"nativeGetSurface", "(JZ)Landroid/view/Surface;", (void*)nativeGetSurface},
         {"nativeDestroy", "(J)V", (void*)nativeDestroy},
         {"nativeSetNextTransaction", "(JJ)V", (void*)nativeSetNextTransaction},
-        {"nativeUpdate", "(JJJJ)V", (void*)nativeUpdate}};
+        {"nativeUpdate", "(JJJJ)V", (void*)nativeUpdate},
+        {"nativeFlushShadowQueue", "(J)V", (void*)nativeFlushShadowQueue}};
 
 int register_android_graphics_BLASTBufferQueue(JNIEnv* env) {
     int res = jniRegisterNativeMethods(env, "android/graphics/BLASTBufferQueue",
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index a063820..463d909 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -60,8 +60,10 @@
     jfieldID hasWallpaper;
     jfieldID paused;
     jfieldID trustedOverlay;
+    jfieldID touchOcclusionMode;
     jfieldID ownerPid;
     jfieldID ownerUid;
+    jfieldID packageName;
     jfieldID inputFeatures;
     jfieldID displayId;
     jfieldID portalToDisplayId;
@@ -150,10 +152,13 @@
     mInfo.paused = env->GetBooleanField(obj,
             gInputWindowHandleClassInfo.paused);
     mInfo.trustedOverlay = env->GetBooleanField(obj, gInputWindowHandleClassInfo.trustedOverlay);
+    mInfo.touchOcclusionMode = static_cast<TouchOcclusionMode>(
+            env->GetIntField(obj, gInputWindowHandleClassInfo.touchOcclusionMode));
     mInfo.ownerPid = env->GetIntField(obj,
             gInputWindowHandleClassInfo.ownerPid);
     mInfo.ownerUid = env->GetIntField(obj,
             gInputWindowHandleClassInfo.ownerUid);
+    mInfo.packageName = getStringField(env, obj, gInputWindowHandleClassInfo.packageName, "<null>");
     mInfo.inputFeatures = static_cast<InputWindowInfo::Feature>(
             env->GetIntField(obj, gInputWindowHandleClassInfo.inputFeatures));
     mInfo.displayId = env->GetIntField(obj,
@@ -326,12 +331,17 @@
 
     GET_FIELD_ID(gInputWindowHandleClassInfo.trustedOverlay, clazz, "trustedOverlay", "Z");
 
+    GET_FIELD_ID(gInputWindowHandleClassInfo.touchOcclusionMode, clazz, "touchOcclusionMode", "I");
+
     GET_FIELD_ID(gInputWindowHandleClassInfo.ownerPid, clazz,
             "ownerPid", "I");
 
     GET_FIELD_ID(gInputWindowHandleClassInfo.ownerUid, clazz,
             "ownerUid", "I");
 
+    GET_FIELD_ID(gInputWindowHandleClassInfo.packageName, clazz, "packageName",
+                 "Ljava/lang/String;");
+
     GET_FIELD_ID(gInputWindowHandleClassInfo.inputFeatures, clazz,
             "inputFeatures", "I");
 
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 5c4c509..1ca45fe 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -338,7 +338,7 @@
             return (jint)AUDIO_JAVA_BAD_VALUE;
         }
         const char *address = env->GetStringUTFChars((jstring)addrJobj, NULL);
-        AudioDeviceTypeAddr dev = AudioDeviceTypeAddr(typesPtr[i], address);
+        AudioDeviceTypeAddr dev = AudioDeviceTypeAddr((audio_devices_t)typesPtr[i], address);
         audioDeviceTypeAddrVector.push_back(dev);
         env->ReleaseStringUTFChars((jstring)addrJobj, address);
     }
@@ -820,7 +820,8 @@
                                                bool useInMask)
 {
     nAudioGainConfig->index = env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mIndex);
-    nAudioGainConfig->mode = env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mMode);
+    nAudioGainConfig->mode =
+            (audio_gain_mode_t)env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mMode);
     ALOGV("convertAudioGainConfigToNative got gain index %d", nAudioGainConfig->index);
     jint jMask = env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mChannelMask);
     audio_channel_mask_t nMask;
@@ -940,8 +941,8 @@
 
     jobject jAudioDevicePort = env->GetObjectField(jAudioPortConfig,
             gAudioPortConfigFields.mPort);
-    nAudioPortConfig->ext.device.type = env->GetIntField(jAudioDevicePort,
-            gAudioPortFields.mType);
+    nAudioPortConfig->ext.device.type =
+            (audio_devices_t)env->GetIntField(jAudioDevicePort, gAudioPortFields.mType);
     jstring jDeviceAddress = (jstring)env->GetObjectField(jAudioDevicePort,
             gAudioPortFields.mAddress);
     const char *nDeviceAddress = env->GetStringUTFChars(jDeviceAddress, NULL);
@@ -2334,7 +2335,7 @@
 
 static jint
 android_media_AudioSystem_setAllowedCapturePolicy(JNIEnv *env, jobject thiz, jint uid, jint flags) {
-    return AudioSystem::setAllowedCapturePolicy(uid, flags);
+    return AudioSystem::setAllowedCapturePolicy(uid, static_cast<audio_flags_mask_t>(flags));
 }
 
 static jint
diff --git a/core/jni/android_opengl_GLES10.cpp b/core/jni/android_opengl_GLES10.cpp
index e4d138d..d65b498 100644
--- a/core/jni/android_opengl_GLES10.cpp
+++ b/core/jni/android_opengl_GLES10.cpp
@@ -640,7 +640,7 @@
 
 exit:
     if (_array) {
-        releasePointer(_env, _array, data, JNI_FALSE);
+        releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -684,7 +684,7 @@
 
 exit:
     if (_array) {
-        releasePointer(_env, _array, data, JNI_FALSE);
+        releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -929,7 +929,7 @@
 
 exit:
     if (_array) {
-        releasePointer(_env, _array, indices, JNI_FALSE);
+        releasePointer(_env, _array, (void *)((char *)indices - _bufferOffset), JNI_FALSE);
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -2801,7 +2801,8 @@
 
 exit:
     if (_array) {
-        releasePointer(_env, _array, pixels, _exception ? JNI_FALSE : JNI_TRUE);
+        releasePointer(_env, _array, (void *)((char *)pixels - _bufferOffset),
+                       _exception ? JNI_FALSE : JNI_TRUE);
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -3241,7 +3242,7 @@
         (GLvoid *)pixels
     );
     if (_array) {
-        releasePointer(_env, _array, pixels, JNI_FALSE);
+        releasePointer(_env, _array, (void *)((char *)pixels - _bufferOffset), JNI_FALSE);
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -3301,7 +3302,7 @@
         (GLvoid *)pixels
     );
     if (_array) {
-        releasePointer(_env, _array, pixels, JNI_FALSE);
+        releasePointer(_env, _array, (void *)((char *)pixels - _bufferOffset), JNI_FALSE);
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
diff --git a/core/jni/android_opengl_GLES11.cpp b/core/jni/android_opengl_GLES11.cpp
index 1069a1d..9724e6c 100644
--- a/core/jni/android_opengl_GLES11.cpp
+++ b/core/jni/android_opengl_GLES11.cpp
@@ -464,7 +464,7 @@
 
 exit:
     if (_array) {
-        releasePointer(_env, _array, data, JNI_FALSE);
+        releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -509,7 +509,7 @@
 
 exit:
     if (_array) {
-        releasePointer(_env, _array, data, JNI_FALSE);
+        releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
diff --git a/core/jni/android_opengl_GLES11Ext.cpp b/core/jni/android_opengl_GLES11Ext.cpp
index 86d7ecd..1ffa4ec 100644
--- a/core/jni/android_opengl_GLES11Ext.cpp
+++ b/core/jni/android_opengl_GLES11Ext.cpp
@@ -893,7 +893,8 @@
 
 exit:
     if (_array) {
-        releasePointer(_env, _array, image, _exception ? JNI_FALSE : JNI_TRUE);
+        releasePointer(_env, _array, (void *)((char *)image - _bufferOffset),
+                       _exception ? JNI_FALSE : JNI_TRUE);
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -930,7 +931,8 @@
 
 exit:
     if (_array) {
-        releasePointer(_env, _array, image, _exception ? JNI_FALSE : JNI_TRUE);
+        releasePointer(_env, _array, (void *)((char *)image - _bufferOffset),
+                       _exception ? JNI_FALSE : JNI_TRUE);
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
diff --git a/core/jni/android_opengl_GLES20.cpp b/core/jni/android_opengl_GLES20.cpp
index 49baa51..d832558 100644
--- a/core/jni/android_opengl_GLES20.cpp
+++ b/core/jni/android_opengl_GLES20.cpp
@@ -599,7 +599,7 @@
 
 exit:
     if (_array) {
-        releasePointer(_env, _array, data, JNI_FALSE);
+        releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -644,7 +644,7 @@
 
 exit:
     if (_array) {
-        releasePointer(_env, _array, data, JNI_FALSE);
+        releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -758,7 +758,7 @@
 
 exit:
     if (_array) {
-        releasePointer(_env, _array, data, JNI_FALSE);
+        releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -802,7 +802,7 @@
 
 exit:
     if (_array) {
-        releasePointer(_env, _array, data, JNI_FALSE);
+        releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -1379,7 +1379,7 @@
 
 exit:
     if (_array) {
-        releasePointer(_env, _array, indices, JNI_FALSE);
+        releasePointer(_env, _array, (void *)((char *)indices - _bufferOffset), JNI_FALSE);
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -4273,7 +4273,8 @@
 
 exit:
     if (_array) {
-        releasePointer(_env, _array, pixels, _exception ? JNI_FALSE : JNI_TRUE);
+        releasePointer(_env, _array, (void *)((char *)pixels - _bufferOffset),
+                       _exception ? JNI_FALSE : JNI_TRUE);
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -4380,7 +4381,7 @@
 
 exit:
     if (_array) {
-        releasePointer(_env, _array, binary, JNI_FALSE);
+        releasePointer(_env, _array, (void *)((char *)binary - _bufferOffset), JNI_FALSE);
     }
     if (shaders_base) {
         _env->ReleaseIntArrayElements(shaders_ref, (jint*)shaders_base,
@@ -4445,7 +4446,8 @@
 
 exit:
     if (_binaryArray) {
-        releasePointer(_env, _binaryArray, binary, JNI_FALSE);
+        releasePointer(_env, _binaryArray, (void *)((char *)binary - _binaryBufferOffset),
+                       JNI_FALSE);
     }
     if (_shadersArray) {
         _env->ReleaseIntArrayElements(_shadersArray, (jint*)shaders, JNI_ABORT);
@@ -4568,7 +4570,7 @@
         (GLvoid *)pixels
     );
     if (_array) {
-        releasePointer(_env, _array, pixels, JNI_FALSE);
+        releasePointer(_env, _array, (void *)((char *)pixels - _bufferOffset), JNI_FALSE);
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -4816,7 +4818,7 @@
         (GLvoid *)pixels
     );
     if (_array) {
-        releasePointer(_env, _array, pixels, JNI_FALSE);
+        releasePointer(_env, _array, (void *)((char *)pixels - _bufferOffset), JNI_FALSE);
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
diff --git a/core/jni/android_opengl_GLES30.cpp b/core/jni/android_opengl_GLES30.cpp
index 32a2a24..719c6b3 100644
--- a/core/jni/android_opengl_GLES30.cpp
+++ b/core/jni/android_opengl_GLES30.cpp
@@ -463,7 +463,7 @@
 
 exit:
     if (_array) {
-        releasePointer(_env, _array, indices, JNI_FALSE);
+        releasePointer(_env, _array, (void *)((char *)indices - _bufferOffset), JNI_FALSE);
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -516,7 +516,7 @@
         (GLvoid *)pixels
     );
     if (_array) {
-        releasePointer(_env, _array, pixels, JNI_FALSE);
+        releasePointer(_env, _array, (void *)((char *)pixels - _bufferOffset), JNI_FALSE);
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -580,7 +580,7 @@
 
 exit:
     if (_array) {
-        releasePointer(_env, _array, pixels, JNI_FALSE);
+        releasePointer(_env, _array, (void *)((char *)pixels - _bufferOffset), JNI_FALSE);
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -660,7 +660,7 @@
 
 exit:
     if (_array) {
-        releasePointer(_env, _array, data, JNI_FALSE);
+        releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -723,7 +723,7 @@
 
 exit:
     if (_array) {
-        releasePointer(_env, _array, data, JNI_FALSE);
+        releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -5445,7 +5445,8 @@
 
 exit:
     if (_array) {
-        releasePointer(_env, _array, binary, _exception ? JNI_FALSE : JNI_TRUE);
+        releasePointer(_env, _array, (void *)((char *)binary - _bufferOffset),
+                       _exception ? JNI_FALSE : JNI_TRUE);
     }
     if (binaryFormat_base) {
         _env->ReleaseIntArrayElements(binaryFormat_ref, (jint*)binaryFormat_base,
@@ -5519,7 +5520,8 @@
 
 exit:
     if (_binaryArray) {
-        releasePointer(_env, _binaryArray, binary, _exception ? JNI_FALSE : JNI_TRUE);
+        releasePointer(_env, _binaryArray, (void *)((char *)binary - _binaryBufferOffset),
+                       _exception ? JNI_FALSE : JNI_TRUE);
     }
     if (_binaryFormatArray) {
         _env->ReleaseIntArrayElements(_binaryFormatArray, (jint*)binaryFormat, _exception ? JNI_ABORT : 0);
@@ -5564,7 +5566,7 @@
 
 exit:
     if (_array) {
-        releasePointer(_env, _array, binary, JNI_FALSE);
+        releasePointer(_env, _array, (void *)((char *)binary - _bufferOffset), JNI_FALSE);
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
diff --git a/core/jni/android_opengl_GLES32.cpp b/core/jni/android_opengl_GLES32.cpp
index 07a794d..7ed7548 100644
--- a/core/jni/android_opengl_GLES32.cpp
+++ b/core/jni/android_opengl_GLES32.cpp
@@ -863,7 +863,7 @@
 
 exit:
     if (_array) {
-        releasePointer(_env, _array, indices, JNI_FALSE);
+        releasePointer(_env, _array, (void *)((char *)indices - _bufferOffset), JNI_FALSE);
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -911,7 +911,7 @@
 
 exit:
     if (_array) {
-        releasePointer(_env, _array, indices, JNI_FALSE);
+        releasePointer(_env, _array, (void *)((char *)indices - _bufferOffset), JNI_FALSE);
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -1048,7 +1048,8 @@
 
 exit:
     if (_array) {
-        releasePointer(_env, _array, data, _exception ? JNI_FALSE : JNI_TRUE);
+        releasePointer(_env, _array, (void *)((char *)data - _bufferOffset),
+                       _exception ? JNI_FALSE : JNI_TRUE);
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
diff --git a/core/jni/android_os_HwParcel.cpp b/core/jni/android_os_HwParcel.cpp
index ff336ee..4c4443f 100644
--- a/core/jni/android_os_HwParcel.cpp
+++ b/core/jni/android_os_HwParcel.cpp
@@ -990,6 +990,8 @@
     }
 
     if (!validateCanUseHwBinder(binder)) {
+        jniThrowException(env, "java/lang/IllegalArgumentException",
+                          "Local binder is not supported in Java");
         return nullptr;
     }
 
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index e118038..32b8fa6 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -960,17 +960,8 @@
     return IPCThreadState::self()->clearCallingIdentity();
 }
 
-static void android_os_Binder_restoreCallingIdentity(JNIEnv* env, jobject clazz, jlong token)
+static void android_os_Binder_restoreCallingIdentity(jlong token)
 {
-    // XXX temporary validation check to debug crashes.
-    int uid = (int)(token>>32);
-    if (uid > 0 && uid < 999) {
-        // In Android currently there are no uids in this range.
-        char buf[128];
-        sprintf(buf, "Restoring bad calling ident: 0x%" PRIx64, token);
-        jniThrowException(env, "java/lang/IllegalStateException", buf);
-        return;
-    }
     IPCThreadState::self()->restoreCallingIdentity(token);
 }
 
@@ -1064,6 +1055,7 @@
     { "isHandlingTransaction", "()Z", (void*)android_os_Binder_isHandlingTransaction },
     // @CriticalNative
     { "clearCallingIdentity", "()J", (void*)android_os_Binder_clearCallingIdentity },
+    // @CriticalNative
     { "restoreCallingIdentity", "(J)V", (void*)android_os_Binder_restoreCallingIdentity },
     // @CriticalNative
     { "setThreadStrictModePolicy", "(I)V", (void*)android_os_Binder_setThreadStrictModePolicy },
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index 3acd15a..e7e9c31 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -60,7 +60,7 @@
     sp<MessageQueue> mMessageQueue;
 
     void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count,
-                       int64_t sharedTimelineFrameCount) override;
+                       VsyncEventData vsyncEventData) override;
     void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
     void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
                                int32_t configId, nsecs_t vsyncPeriod) override;
@@ -91,14 +91,15 @@
 }
 
 void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId,
-                                               uint32_t count, int64_t frameTimelineVsyncId) {
+                                               uint32_t count, VsyncEventData vsyncEventData) {
     JNIEnv* env = AndroidRuntime::getJNIEnv();
 
     ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
     if (receiverObj.get()) {
         ALOGV("receiver %p ~ Invoking vsync handler.", this);
         env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchVsync,
-                            timestamp, displayId.value, count, frameTimelineVsyncId);
+                            timestamp, displayId.value, count, vsyncEventData.id,
+                            vsyncEventData.deadlineTimestamp);
         ALOGV("receiver %p ~ Returned from vsync handler.", this);
     }
 
@@ -198,7 +199,8 @@
     gDisplayEventReceiverClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
 
     gDisplayEventReceiverClassInfo.dispatchVsync =
-            GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchVsync", "(JJIJ)V");
+            GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchVsync",
+                             "(JJIJJ)V");
     gDisplayEventReceiverClassInfo.dispatchHotplug = GetMethodIDOrDie(env,
             gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug", "(JJZ)V");
     gDisplayEventReceiverClassInfo.dispatchConfigChanged = GetMethodIDOrDie(env,
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index 9ed71ac0..1ea918a 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -343,7 +343,7 @@
                                     jboolean(focusEvent->getHasFocus()),
                                     jboolean(focusEvent->getInTouchMode()));
                 finishInputEvent(seq, true /* handled */);
-                return OK;
+                continue;
             }
 
             default:
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 6a07cf7..3a1ccd9 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -313,7 +313,7 @@
         return nativeObject;
     }
 
-    sp<Surface> surface(new Surface(bufferProducer, true));
+    sp<Surface> surface = queue->getSurface(true /* includeSurfaceControlHandle */);
     if (surface != NULL) {
         surface->incStrong(&sRefBaseOwner);
     }
@@ -349,7 +349,8 @@
     sp<Surface> sur;
     if (surfaceShim.graphicBufferProducer != nullptr) {
         // we have a new IGraphicBufferProducer, create a new Surface for it
-        sur = new Surface(surfaceShim.graphicBufferProducer, true);
+        sur = new Surface(surfaceShim.graphicBufferProducer, true,
+                          surfaceShim.surfaceControlHandle);
         // and keep a reference before passing to java
         sur->incStrong(&sRefBaseOwner);
     }
@@ -373,6 +374,7 @@
     android::view::Surface surfaceShim;
     if (self != nullptr) {
         surfaceShim.graphicBufferProducer = self->getIGraphicBufferProducer();
+        surfaceShim.surfaceControlHandle = self->getSurfaceControlHandle();
     }
     // Calling code in Surface.java has already written the name of the Surface
     // to the Parcel
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 1419855..62f844e 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -318,8 +318,13 @@
         }
     }
 
-    status_t err = client->createSurfaceChecked(
-            String8(name.c_str()), w, h, format, &surface, flags, parent, std::move(metadata));
+    sp<IBinder> parentHandle;
+    if (parent != nullptr) {
+        parentHandle = parent->getHandle();
+    }
+
+    status_t err = client->createSurfaceChecked(String8(name.c_str()), w, h, format, &surface,
+                                                flags, parentHandle, std::move(metadata));
     if (err == NAME_NOT_FOUND) {
         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
         return 0;
@@ -1360,15 +1365,6 @@
     transaction->detachChildren(ctrl);
 }
 
-static void nativeSetOverrideScalingMode(JNIEnv* env, jclass clazz, jlong transactionObj,
-        jlong nativeObject,
-        jint scalingMode) {
-    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
-
-    auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
-    transaction->setOverrideScalingMode(ctrl, scalingMode);
-}
-
 static jobject nativeGetHdrCapabilities(JNIEnv* env, jclass clazz, jobject tokenObject) {
     sp<IBinder> token(ibinderForJavaObject(env, tokenObject));
     if (token == NULL) return NULL;
@@ -1694,8 +1690,6 @@
             (void*)nativeReparent },
     {"nativeSeverChildren", "(JJ)V",
             (void*)nativeSeverChildren } ,
-    {"nativeSetOverrideScalingMode", "(JJI)V",
-            (void*)nativeSetOverrideScalingMode },
     {"nativeCaptureDisplay",
             "(Landroid/view/SurfaceControl$DisplayCaptureArgs;Landroid/view/SurfaceControl$ScreenCaptureListener;)I",
             (void*)nativeCaptureDisplay },
diff --git a/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp b/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp
new file mode 100644
index 0000000..52bed6b
--- /dev/null
+++ b/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "core_jni_helpers.h"
+
+#include <cputimeinstate.h>
+#include <dirent.h>
+
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android_runtime/Log.h>
+
+#include <nativehelper/ScopedPrimitiveArray.h>
+#include <nativehelper/ScopedUtfChars.h>
+
+namespace android {
+
+// Number of milliseconds in a jiffy - the unit of time measurement for processes and threads
+static const uint32_t gJiffyMillis = (uint32_t)(1000 / sysconf(_SC_CLK_TCK));
+
+// Given a PID, returns a vector of all TIDs for the process' tasks. Thread IDs are
+// file names in the /proc/<pid>/task directory.
+static bool getThreadIds(const std::string &procPath, const pid_t pid,
+                         std::vector<pid_t> &outThreadIds) {
+    std::string taskPath = android::base::StringPrintf("%s/%u/task", procPath.c_str(), pid);
+
+    struct dirent **dirlist;
+    int threadCount = scandir(taskPath.c_str(), &dirlist, NULL, NULL);
+    if (threadCount == -1) {
+        ALOGE("Cannot read directory %s", taskPath.c_str());
+        return false;
+    }
+
+    outThreadIds.reserve(threadCount);
+
+    for (int i = 0; i < threadCount; i++) {
+        pid_t tid;
+        if (android::base::ParseInt<pid_t>(dirlist[i]->d_name, &tid)) {
+            outThreadIds.push_back(tid);
+        }
+        free(dirlist[i]);
+    }
+    free(dirlist);
+
+    return true;
+}
+
+// Reads contents of a time_in_state file and returns times as a vector of times per frequency
+// A time_in_state file contains pairs of frequency - time (in jiffies):
+//
+//    cpu0
+//    300000 30
+//    403200 0
+//    cpu4
+//    710400 10
+//    825600 20
+//    940800 30
+//
+static bool getThreadTimeInState(const std::string &procPath, const pid_t pid, const pid_t tid,
+                                 const size_t frequencyCount,
+                                 std::vector<uint64_t> &outThreadTimeInState) {
+    std::string timeInStateFilePath =
+            android::base::StringPrintf("%s/%u/task/%u/time_in_state", procPath.c_str(), pid, tid);
+    std::string data;
+
+    if (!android::base::ReadFileToString(timeInStateFilePath, &data)) {
+        ALOGE("Cannot read file: %s", timeInStateFilePath.c_str());
+        return false;
+    }
+
+    auto lines = android::base::Split(data, "\n");
+    size_t index = 0;
+    for (const auto &line : lines) {
+        if (line.empty()) {
+            continue;
+        }
+
+        auto numbers = android::base::Split(line, " ");
+        if (numbers.size() != 2) {
+            continue;
+        }
+        uint64_t timeInState;
+        if (!android::base::ParseUint<uint64_t>(numbers[1], &timeInState)) {
+            ALOGE("Invalid time_in_state file format: %s", timeInStateFilePath.c_str());
+            return false;
+        }
+        if (index < frequencyCount) {
+            outThreadTimeInState[index] = timeInState;
+        }
+        index++;
+    }
+
+    if (index != frequencyCount) {
+        ALOGE("Incorrect number of frequencies %u in %s. Expected %u",
+              (uint32_t)outThreadTimeInState.size(), timeInStateFilePath.c_str(),
+              (uint32_t)frequencyCount);
+        return false;
+    }
+
+    return true;
+}
+
+static int pidCompare(const void *a, const void *b) {
+    return (*(pid_t *)a - *(pid_t *)b);
+}
+
+static inline bool isSelectedThread(const pid_t tid, const pid_t *selectedThreadIds,
+                                    const size_t selectedThreadCount) {
+    return bsearch(&tid, selectedThreadIds, selectedThreadCount, sizeof(pid_t), pidCompare) != NULL;
+}
+
+// Reads all /proc/<pid>/task/*/time_in_state files and aggregates per-frequency
+// time in state data for all threads.  Also, separately aggregates time in state for
+// selected threads whose TIDs are passes as selectedThreadIds.
+static void aggregateThreadCpuTimes(const std::string &procPath, const pid_t pid,
+                                    const std::vector<pid_t> &threadIds,
+                                    const size_t frequencyCount, const pid_t *selectedThreadIds,
+                                    const size_t selectedThreadCount,
+                                    uint64_t *threadCpuTimesMillis,
+                                    uint64_t *selectedThreadCpuTimesMillis) {
+    for (size_t j = 0; j < frequencyCount; j++) {
+        threadCpuTimesMillis[j] = 0;
+        selectedThreadCpuTimesMillis[j] = 0;
+    }
+
+    for (size_t i = 0; i < threadIds.size(); i++) {
+        pid_t tid = threadIds[i];
+        std::vector<uint64_t> timeInState(frequencyCount);
+        if (!getThreadTimeInState(procPath, pid, tid, frequencyCount, timeInState)) {
+            continue;
+        }
+
+        bool selectedThread = isSelectedThread(tid, selectedThreadIds, selectedThreadCount);
+        for (size_t j = 0; j < frequencyCount; j++) {
+            threadCpuTimesMillis[j] += timeInState[j];
+            if (selectedThread) {
+                selectedThreadCpuTimesMillis[j] += timeInState[j];
+            }
+        }
+    }
+    for (size_t i = 0; i < frequencyCount; i++) {
+        threadCpuTimesMillis[i] *= gJiffyMillis;
+        selectedThreadCpuTimesMillis[i] *= gJiffyMillis;
+    }
+}
+
+// Reads process utime and stime from the /proc/<pid>/stat file.
+// Format of this file is described in https://man7.org/linux/man-pages/man5/proc.5.html.
+static bool getProcessCpuTime(const std::string &procPath, const pid_t pid,
+                              uint64_t &outTimeMillis) {
+    std::string statFilePath = android::base::StringPrintf("%s/%u/stat", procPath.c_str(), pid);
+    std::string data;
+    if (!android::base::ReadFileToString(statFilePath, &data)) {
+        return false;
+    }
+
+    auto fields = android::base::Split(data, " ");
+    uint64_t utime, stime;
+
+    // Field 14 (counting from 1) is utime - process time in user space, in jiffies
+    // Field 15 (counting from 1) is stime - process time in system space, in jiffies
+    if (fields.size() < 15 || !android::base::ParseUint(fields[13], &utime) ||
+        !android::base::ParseUint(fields[14], &stime)) {
+        ALOGE("Invalid file format %s", statFilePath.c_str());
+        return false;
+    }
+
+    outTimeMillis = (utime + stime) * gJiffyMillis;
+    return true;
+}
+
+// Estimates per cluster per frequency CPU time for the entire process
+// by distributing the total process CPU time proportionately to how much
+// CPU time its threads took on those clusters/frequencies.  This algorithm
+// works more accurately when when we have equally distributed concurrency.
+// TODO(b/169279846): obtain actual process CPU times from the kernel
+static void estimateProcessTimeInState(const uint64_t processCpuTimeMillis,
+                                       const uint64_t *threadCpuTimesMillis,
+                                       const size_t frequencyCount,
+                                       uint64_t *processCpuTimesMillis) {
+    uint64_t totalCpuTimeAllThreads = 0;
+    for (size_t i = 0; i < frequencyCount; i++) {
+        totalCpuTimeAllThreads += threadCpuTimesMillis[i];
+    }
+
+    if (totalCpuTimeAllThreads != 0) {
+        for (size_t i = 0; i < frequencyCount; i++) {
+            processCpuTimesMillis[i] =
+                    processCpuTimeMillis * threadCpuTimesMillis[i] / totalCpuTimeAllThreads;
+        }
+    } else {
+        for (size_t i = 0; i < frequencyCount; i++) {
+            processCpuTimesMillis[i] = 0;
+        }
+    }
+}
+
+static jboolean readProcessCpuUsage(JNIEnv *env, jclass, jstring procPath, jint pid,
+                                    jintArray selectedThreadIdArray,
+                                    jlongArray processCpuTimesMillisArray,
+                                    jlongArray threadCpuTimesMillisArray,
+                                    jlongArray selectedThreadCpuTimesMillisArray) {
+    ScopedUtfChars procPathChars(env, procPath);
+    ScopedIntArrayRO selectedThreadIds(env, selectedThreadIdArray);
+    ScopedLongArrayRW processCpuTimesMillis(env, processCpuTimesMillisArray);
+    ScopedLongArrayRW threadCpuTimesMillis(env, threadCpuTimesMillisArray);
+    ScopedLongArrayRW selectedThreadCpuTimesMillis(env, selectedThreadCpuTimesMillisArray);
+
+    std::string procPathStr(procPathChars.c_str());
+
+    // Get all thread IDs for the process.
+    std::vector<pid_t> threadIds;
+    if (!getThreadIds(procPathStr, pid, threadIds)) {
+        ALOGE("Could not obtain thread IDs from: %s", procPathStr.c_str());
+        return false;
+    }
+
+    size_t frequencyCount = processCpuTimesMillis.size();
+
+    if (threadCpuTimesMillis.size() != frequencyCount) {
+        ALOGE("Invalid array length: threadCpuTimesMillis");
+        return false;
+    }
+    if (selectedThreadCpuTimesMillis.size() != frequencyCount) {
+        ALOGE("Invalid array length: selectedThreadCpuTimesMillisArray");
+        return false;
+    }
+
+    aggregateThreadCpuTimes(procPathStr, pid, threadIds, frequencyCount, selectedThreadIds.get(),
+                            selectedThreadIds.size(),
+                            reinterpret_cast<uint64_t *>(threadCpuTimesMillis.get()),
+                            reinterpret_cast<uint64_t *>(selectedThreadCpuTimesMillis.get()));
+
+    uint64_t processCpuTime;
+    bool ret = getProcessCpuTime(procPathStr, pid, processCpuTime);
+    if (ret) {
+        estimateProcessTimeInState(processCpuTime,
+                                   reinterpret_cast<uint64_t *>(threadCpuTimesMillis.get()),
+                                   frequencyCount,
+                                   reinterpret_cast<uint64_t *>(processCpuTimesMillis.get()));
+    }
+    return ret;
+}
+
+static const JNINativeMethod g_single_methods[] = {
+        {"readProcessCpuUsage", "(Ljava/lang/String;I[I[J[J[J)Z", (void *)readProcessCpuUsage},
+};
+
+int register_com_android_internal_os_KernelSingleProcessCpuThreadReader(JNIEnv *env) {
+    return RegisterMethodsOrDie(env, "com/android/internal/os/KernelSingleProcessCpuThreadReader",
+                                g_single_methods, NELEM(g_single_methods));
+}
+
+} // namespace android
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 42aab6a..f791cb1 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -80,6 +80,7 @@
 #include <bionic/mte.h>
 #include <bionic/mte_kernel.h>
 #include <cutils/fs.h>
+#include <cutils/memory.h>
 #include <cutils/multiuser.h>
 #include <cutils/sockets.h>
 #include <private/android_filesystem_config.h>
@@ -630,6 +631,13 @@
 
   // Set the jemalloc decay time to 1.
   mallopt(M_DECAY_TIME, 1);
+
+  // Avoid potentially expensive memory mitigations, mostly meant for system
+  // processes, in apps. These may cause app compat problems, use more memory,
+  // or reduce performance. While it would be nice to have them for apps,
+  // we will have to wait until they are proven out, have more efficient
+  // hardware, and/or apply them only to new applications.
+  process_disable_memory_mitigations();
 }
 
 static void SetUpSeccompFilter(uid_t uid, bool is_child_zygote) {
diff --git a/core/jni/com_google_android_gles_jni_GLImpl.cpp b/core/jni/com_google_android_gles_jni_GLImpl.cpp
index ffc1ddc..21de723 100644
--- a/core/jni/com_google_android_gles_jni_GLImpl.cpp
+++ b/core/jni/com_google_android_gles_jni_GLImpl.cpp
@@ -424,7 +424,7 @@
 
 exit:
     if (_array) {
-        releasePointer(_env, _array, data, JNI_FALSE);
+        releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -468,7 +468,7 @@
 
 exit:
     if (_array) {
-        releasePointer(_env, _array, data, JNI_FALSE);
+        releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -713,7 +713,7 @@
 
 exit:
     if (_array) {
-        releasePointer(_env, _array, indices, JNI_FALSE);
+        releasePointer(_env, _array, (void *)((char *)indices - _bufferOffset), JNI_FALSE);
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -3488,7 +3488,8 @@
 
 exit:
     if (_array) {
-        releasePointer(_env, _array, pixels, _exception ? JNI_FALSE : JNI_TRUE);
+        releasePointer(_env, _array, (void *)((char *)pixels - _bufferOffset),
+                       _exception ? JNI_FALSE : JNI_TRUE);
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -3972,7 +3973,7 @@
         (GLvoid *)pixels
     );
     if (_array) {
-        releasePointer(_env, _array, pixels, JNI_FALSE);
+        releasePointer(_env, _array, (void *)((char *)pixels - _bufferOffset), JNI_FALSE);
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -4032,7 +4033,7 @@
         (GLvoid *)pixels
     );
     if (_array) {
-        releasePointer(_env, _array, pixels, JNI_FALSE);
+        releasePointer(_env, _array, (void *)((char *)pixels - _bufferOffset), JNI_FALSE);
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -4299,7 +4300,7 @@
 
 exit:
     if (_array) {
-        releasePointer(_env, _array, data, JNI_FALSE);
+        releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -4344,7 +4345,7 @@
 
 exit:
     if (_array) {
-        releasePointer(_env, _array, data, JNI_FALSE);
+        releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
diff --git a/core/jni/core_jni_helpers.h b/core/jni/core_jni_helpers.h
index 013c65f..5268049 100644
--- a/core/jni/core_jni_helpers.h
+++ b/core/jni/core_jni_helpers.h
@@ -47,7 +47,7 @@
 static inline jfieldID GetFieldIDOrDie(JNIEnv* env, jclass clazz, const char* field_name,
                                        const char* field_signature) {
     jfieldID res = env->GetFieldID(clazz, field_name, field_signature);
-    LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static field %s with signature %s", field_name,
+    LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find field %s with signature %s", field_name,
                         field_signature);
     return res;
 }
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 7fac615..45f64f9 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2751,4 +2751,19 @@
 
     // OS: R QPR2
     BLUETOOTH_PAIRING_RECEIVER = 1851;
+
+    // OPEN: Settings > Display > Screen timeout
+    // CATEGORY: SETTINGS
+    // OS: S
+    SCREEN_TIMEOUT = 1852;
+
+    // OPEN: Settings > Accessibility > Reduce Bright Colors
+    // CATEGORY: SETTINGS
+    // OS: S
+    REDUCE_BRIGHT_COLORS_SETTINGS = 1853;
+
+    // OPEN: Settings > Location > Time Zone Detection
+    // CATEGORY: SETTINGS
+    // OS: S
+    LOCATION_TIME_ZONE_DETECTION = 1854;
 }
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 9fed1b9..fe65bda3 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -511,9 +511,14 @@
         (section).args = "sensorservice --proto"
     ];
 
-    optional com.android.server.powerstats.PowerStatsServiceProto powerstats = 3054 [
+    optional com.android.server.powerstats.PowerStatsServiceMeterProto powerstats_meter = 3054 [
         (section).type = SECTION_DUMPSYS,
-        (section).args = "power_stats --proto"
+        (section).args = "power_stats --proto meter"
+    ];
+
+    optional com.android.server.powerstats.PowerStatsServiceModelProto powerstats_model = 3055 [
+        (section).type = SECTION_DUMPSYS,
+        (section).args = "power_stats --proto model"
     ];
 
     // Dumps in text format (on userdebug and eng builds only): 4000 ~ 4999
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index e1a980c..83c5391 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -452,6 +452,15 @@
     }
     optional QuickSettings qs = 45;
 
+    message ReduceBrightColors {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto activated = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto level = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto persist_across_reboots = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional ReduceBrightColors reduce_bright_colors = 87;
+
     message Rotation {
         option (android.msg_privacy).dest = DEST_EXPLICIT;
 
@@ -625,5 +634,5 @@
 
     // Please insert fields in alphabetical order and group them into messages
     // if possible (to avoid reaching the method limit).
-    // Next tag = 87;
+    // Next tag = 88;
 }
diff --git a/core/proto/android/server/powerstatsservice.proto b/core/proto/android/server/powerstatsservice.proto
index c805244..9a7ed7c 100644
--- a/core/proto/android/server/powerstatsservice.proto
+++ b/core/proto/android/server/powerstatsservice.proto
@@ -20,44 +20,99 @@
 
 option java_multiple_files = true;
 
-message IncidentReportProto {
+/**
+ * IncidentReportMeterProto is used only in the parsing tool located
+ * in frameworks/base/tools which is used to parse this data out of
+ * incident reports.
+ */
+message IncidentReportMeterProto {
     /** Section number matches that in incident.proto */
-    optional PowerStatsServiceProto incident_report = 3054;
-}
-
-message PowerStatsServiceProto {
-    repeated RailInfoProto rail_info = 1;
-    repeated EnergyDataProto energy_data = 2;
+    optional PowerStatsServiceMeterProto incident_report = 3054;
 }
 
 /**
- * Rail information:
- * Reports information related to the rails being monitored.
+ * IncidentReportModelProto is used only in the parsing tool located
+ * in frameworks/base/tools which is used to parse this data out of
+ * incident reports.
  */
-message RailInfoProto {
-    /** Index corresponding to the rail */
-    optional int32 index = 1;
-
-    /** Name of the rail (opaque to the framework) */
-    optional string rail_name = 2;
-
-    /** Name of the subsystem to which this rail belongs (opaque to the framework) */
-    optional string subsys_name = 3;
-
-    /** Hardware sampling rate */
-    optional int32 sampling_rate = 4;
+message IncidentReportModelProto {
+    /** Section number matches that in incident.proto */
+    optional PowerStatsServiceModelProto incident_report = 3055;
 }
 
 /**
- * Rail level energy measurements:
- * Reports accumulated energy since boot on each rail.
+ * EnergyConsumer (model) data is exposed by the PowerStats HAL.  This data
+ * represents modeled energy consumption estimates and is provided per
+ * subsystem.  The default subsystems are defined in EnergyConsumerId.aidl.
+ * Energy model estimates will be logged to incident reports in addition to
+ * the raw energy meter data.
  */
-message EnergyDataProto {
+message PowerStatsServiceModelProto {
+    repeated EnergyConsumerIdProto energy_consumer_id = 1;
+    repeated EnergyConsumerResultProto energy_consumer_result = 2;
+}
+
+/**
+ * EnergyMeasurement (meter) data is exposed by the PowerStats HAL.  This data
+ * represents measurements taken directly from on-device energy meters.
+ * This raw energy meter data will be logged to incident reports.
+ */
+message PowerStatsServiceMeterProto {
+    repeated ChannelInfoProto channel_info = 1;
+    repeated EnergyMeasurementProto energy_measurement = 2;
+}
+
+/**
+ * Energy consumer ID:
+ * A list of default subsystems for which energy consumption estimates
+ * may be provided (hardware dependent).
+ */
+message EnergyConsumerIdProto {
+    /** Unique index identifying the energy consumer. */
+    optional int32 energy_consumer_id = 1;
+}
+
+/**
+ * Energy consumer result:
+ * An estimate of energy consumption since boot for the subsystem identified
+ * by the unique energy_consumer_id.
+ */
+message EnergyConsumerResultProto {
+    /** Unique index identifying the energy consumer. */
+    optional int32 energy_consumer_id = 1;
+
+    /** Time since device boot(CLOCK_BOOTTIME) in milli-seconds */
+    optional int64 timestamp_ms = 2;
+
+    /** Accumulated energy since device boot in microwatt-seconds (uWs) */
+    optional int64 energy_uws = 3;
+}
+
+/**
+ * Channel information:
+ * Reports information related to the energy meter channels being monitored.
+ */
+message ChannelInfoProto {
     /**
-     * Index corresponding to the rail. This index matches
-     * the index returned in RailInfo
+     * Index corresponding to the energy meter channel. This index matches
+     * the index returned in ChannelInfo.
      */
-    optional int32 index = 1;
+    optional int32 channel_id = 1;
+
+    /** Name of the energy meter channel */
+    optional string channel_name = 2;
+}
+
+/**
+ * Energy measurements:
+ * Reports accumulated energy since boot for each energy meter.
+ */
+message EnergyMeasurementProto {
+    /**
+     * Index corresponding to the energy meter channel. This index matches
+     * the index returned in ChannelInfo.
+     */
+    optional int32 channel_id = 1;
 
     /** Time since device boot(CLOCK_BOOTTIME) in milli-seconds */
     optional int64 timestamp_ms = 2;
diff --git a/core/proto/android/server/vibratorservice.proto b/core/proto/android/server/vibratorservice.proto
index 281a25e..9e42e9e 100644
--- a/core/proto/android/server/vibratorservice.proto
+++ b/core/proto/android/server/vibratorservice.proto
@@ -21,6 +21,12 @@
 
 import "frameworks/base/core/proto/android/privacy.proto";
 
+message OneShotProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+    repeated int32 duration = 1;
+    repeated int32 amplitude = 2;
+}
+
 message WaveformProto {
    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
    repeated int32 timings = 1;
@@ -35,20 +41,41 @@
     optional int32 fallback = 3;
 }
 
+message ComposedProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+    repeated int32 effect_ids = 1;
+    repeated float effect_scales = 2;
+    repeated int32 delays = 3;
+}
+
 // A com.android.os.VibrationEffect object.
 message VibrationEffectProto {
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+    optional OneShotProto oneshot = 3;
     optional WaveformProto waveform = 1;
     optional PrebakedProto prebaked = 2;
+    optional ComposedProto composed = 4;
 }
 
+message VibrationAttributesProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+    optional int32 usage = 1;
+    optional int32 audio_usage = 2;
+    optional int32 flags = 3;
+}
+
+// Next id: 7
 message VibrationProto {
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
     optional int64 start_time = 1;
+    optional int64 end_time = 4;
     optional VibrationEffectProto effect = 2;
-    optional VibrationEffectProto origin_effect = 3;
+    optional VibrationEffectProto original_effect = 3;
+    optional VibrationAttributesProto attributes = 5;
+    optional int32 status = 6;
 }
 
+// Next id: 17
 message VibratorServiceDumpProto {
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
     optional VibrationProto current_vibration = 1;
@@ -57,10 +84,14 @@
     optional bool vibrator_under_external_control = 4;
     optional bool low_power_mode = 5;
     optional int32 haptic_feedback_intensity = 6;
+    optional int32 haptic_feedback_default_intensity = 14;
     optional int32 notification_intensity = 7;
+    optional int32 notification_default_intensity = 15;
     optional int32 ring_intensity = 8;
+    optional int32 ring_default_intensity = 16;
     repeated VibrationProto previous_ring_vibrations = 9;
     repeated VibrationProto previous_notification_vibrations = 10;
     repeated VibrationProto previous_alarm_vibrations = 11;
     repeated VibrationProto previous_vibrations = 12;
+    repeated VibrationProto previous_external_vibrations = 13;
 }
\ No newline at end of file
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index d315ff2..ce52a35 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -48,8 +48,8 @@
     optional string focused_app = 4;
     optional IdentifierProto input_method_window = 5;
     optional bool display_frozen = 6;
-    optional int32 rotation = 7 [(.android.typedef) = "android.view.Surface.Rotation"];
-    optional int32 last_orientation = 8 [(.android.typedef) = "android.content.pm.ActivityInfo.ScreenOrientation"];
+    optional int32 rotation = 7 [(.android.typedef) = "android.view.Surface.Rotation", deprecated=true];
+    optional int32 last_orientation = 8 [(.android.typedef) = "android.content.pm.ActivityInfo.ScreenOrientation", deprecated=true];
     optional int32 focused_display_id = 9;
     optional bool hard_keyboard_available = 10;
 }
@@ -183,7 +183,7 @@
     repeated WindowTokenProto ime_windows = 8 [deprecated=true];
     optional int32 dpi = 9;
     optional .android.view.DisplayInfoProto display_info = 10;
-    optional int32 rotation = 11 [(.android.typedef) = "android.view.Surface.Rotation"];
+    optional int32 rotation = 11 [(.android.typedef) = "android.view.Surface.Rotation", deprecated=true];
     optional ScreenRotationAnimationProto screen_rotation_animation = 12;
     optional DisplayFramesProto display_frames = 13;
     optional int32 surface_size = 14 [deprecated=true];
@@ -195,7 +195,7 @@
     repeated WindowTokenProto overlay_windows = 20 [deprecated=true];
     optional DisplayAreaProto root_display_area = 21;
 
-    optional bool single_task_instance = 22;
+    optional bool single_task_instance = 22 [deprecated=true];
     optional int32 focused_root_task_id = 23;
     optional .com.android.server.wm.IdentifierProto resumed_activity = 24;
     repeated TaskProto tasks = 25 [deprecated=true];
@@ -207,6 +207,8 @@
     optional WindowStateProto current_focus = 30;
     optional ImeInsetsSourceProviderProto ime_insets_source_provider = 31;
     optional bool can_show_ime = 32;
+
+    optional DisplayRotationProto display_rotation = 33;
 }
 
 /* represents DisplayArea object */
@@ -242,6 +244,16 @@
     optional .android.graphics.RectProto current = 3;
 }
 
+message DisplayRotationProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional int32 rotation = 1 [(.android.typedef) = "android.view.Surface.Rotation"];
+    optional bool frozen_to_user_rotation = 2;
+    optional int32 user_rotation = 3 [(.android.typedef) = "android.view.Surface.Rotation"];
+    optional int32 fixed_to_user_rotation_mode = 4;
+    optional int32 last_orientation = 5 [(.android.typedef) = "android.content.pm.ActivityInfo.ScreenOrientation"];
+}
+
 /* represents DockedStackDividerController */
 message DockedStackDividerControllerProto {
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -529,4 +541,4 @@
     optional InsetsSourceProviderProto insets_source_provider = 1;
     optional WindowStateProto ime_target_from_ime = 2;
     optional bool is_ime_layout_drawn = 3;
-}
\ No newline at end of file
+}
diff --git a/core/proto/android/service/package.proto b/core/proto/android/service/package.proto
index d289e00..5567109 100644
--- a/core/proto/android/service/package.proto
+++ b/core/proto/android/service/package.proto
@@ -129,6 +129,16 @@
         optional bool is_loading = 2;
     }
 
+    // TODO (b/170263003) refactor to permissiongr
+    // Runtime permission state that are granted for users.
+    message UserPermissionsProto {
+        option (android.msg_privacy).dest = DEST_AUTOMATIC;
+        // User id.
+        optional int32 id = 1;
+        // Pre-granted Android permissions.
+        repeated string granted_permissions = 2;
+    }
+
     // Name of package. e.g. "com.android.providers.telephony".
     optional string name = 1;
     // UID for this package as assigned by Android OS.
@@ -152,4 +162,6 @@
     optional InstallSourceProto install_source = 10;
     // Whether the package is startable or is still loading
     optional StatesProto states = 11;
+    // Granted runtime permissions for users.
+    repeated UserPermissionsProto user_permissions = 12;
 }
diff --git a/core/proto/android/telephony/enums.proto b/core/proto/android/telephony/enums.proto
index b56bd2b..2546b51 100644
--- a/core/proto/android/telephony/enums.proto
+++ b/core/proto/android/telephony/enums.proto
@@ -91,6 +91,14 @@
     NETWORK_TYPE_NR = 20;
 }
 
+// Roaming type enums, see android.telephony.ServiceState.RoamingType for definitions.
+enum RoamingTypeEnum {
+    ROAMING_TYPE_NOT_ROAMING = 0;
+    ROAMING_TYPE_ROAMING = 1;
+    ROAMING_TYPE_ROAMING_DOMESTIC = 2;
+    ROAMING_TYPE_ROAMING_INTERNATIONAL = 3;
+}
+
 // Signal strength levels, primarily used by android/telephony/SignalStrength.java.
 enum SignalStrengthEnum {
     SIGNAL_STRENGTH_NONE_OR_UNKNOWN = 0;
@@ -199,10 +207,64 @@
     SMS_TYPE_WAP_PUSH = 4;
 }
 
-// SMS errors
+// Incoming SMS errors
 enum SmsIncomingErrorEnum {
     SMS_SUCCESS = 0;
     SMS_ERROR_GENERIC = 1;
     SMS_ERROR_NO_MEMORY = 2;
     SMS_ERROR_NOT_SUPPORTED = 3;
 }
+
+// Outgoing SMS results
+enum SmsSendResultEnum {
+    // Unknown error
+    SMS_SEND_RESULT_UNKNOWN = 0;
+    // Success
+    SMS_SEND_RESULT_SUCCESS = 1;
+    // Permanent error
+    SMS_SEND_RESULT_ERROR = 2;
+    // Temporary error, retry
+    SMS_SEND_RESULT_ERROR_RETRY = 3;
+    // Error over IMS, retry on CS
+    SMS_SEND_RESULT_ERROR_FALLBACK = 4;
+}
+
+// Data profile of the data call. From
+// frameworks/base/telephony/java/com/android/internal/telephony/RILConstants.java
+enum DataProfileEnum {
+    DATA_PROFILE_DEFAULT = 0;
+    DATA_PROFILE_TETHERED = 1;
+    DATA_PROFILE_IMS = 2;
+    DATA_PROFILE_FOTA = 3;
+    DATA_PROFILE_CBS = 4;
+    DATA_PROFILE_OEM_BASE = 1000;
+    DATA_PROFILE_INVALID = -1;
+}
+
+// Reason of data call deactivation. From
+// frameworks/base/telephony/java/android/telephony/data/DataService.java#DeactivateDataReason
+enum DataDeactivateReasonEnum {
+    DEACTIVATE_REASON_UNKNOWN = 0;
+    DEACTIVATE_REASON_NORMAL = 1;
+    DEACTIVATE_REASON_RADIO_OFF = 2;
+    DEACTIVATE_REASON_HANDOVER = 3;
+}
+
+// IP type of the data call
+// see frameworks/base/telephony/java/android/telephony/data/ApnSetting.java#ProtocolType
+enum ApnProtocolEnum {
+    APN_PROTOCOL_IPV4 = 0;
+    APN_PROTOCOL_IPV6 = 1;
+    APN_PROTOCOL_IPV4V6 = 2;
+    APN_PROTOCOL_PPP = 3;
+}
+
+// Action taken to recover a data call that is stalled. From
+// frameworks/opt/telephony/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
+// #RecoveryAction
+enum DataStallRecoveryActionEnum {
+    RECOVERY_ACTION_GET_DATA_CALL_LIST = 0;
+    RECOVERY_ACTION_CLEANUP = 1;
+    RECOVERY_ACTION_REREGISTER = 2;
+    RECOVERY_ACTION_RADIO_RESTART = 3;
+}
\ No newline at end of file
diff --git a/core/proto/android/view/enums.proto b/core/proto/android/view/enums.proto
index 0172e78..a601abe 100644
--- a/core/proto/android/view/enums.proto
+++ b/core/proto/android/view/enums.proto
@@ -60,7 +60,7 @@
     TRANSIT_TASK_OPEN_BEHIND = 16;
     TRANSIT_TASK_IN_PLACE = 17;
     TRANSIT_ACTIVITY_RELAUNCH = 18;
-    TRANSIT_DOCK_TASK_FROM_RECENTS = 19;
+    TRANSIT_DOCK_TASK_FROM_RECENTS = 19 [deprecated=true];
     TRANSIT_KEYGUARD_GOING_AWAY = 20;
     TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER = 21;
     TRANSIT_KEYGUARD_OCCLUDE = 22;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c8b8ef2..0195451 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -149,7 +149,7 @@
     <protected-broadcast android:name="android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED" />
     <protected-broadcast android:name="android.bluetooth.device.action.UUID" />
     <protected-broadcast android:name="android.bluetooth.device.action.MAS_INSTANCE" />
-    <protected-broadcast android:name="android.bluetooth.action.ALIAS_CHANGED" />
+    <protected-broadcast android:name="android.bluetooth.device.action.ALIAS_CHANGED" />
     <protected-broadcast android:name="android.bluetooth.device.action.FOUND" />
     <protected-broadcast android:name="android.bluetooth.device.action.CLASS_CHANGED" />
     <protected-broadcast android:name="android.bluetooth.device.action.ACL_CONNECTED" />
@@ -517,6 +517,7 @@
     <protected-broadcast android:name="android.telecom.action.NUISANCE_CALL_STATUS_CHANGED" />
     <protected-broadcast android:name="android.telecom.action.PHONE_ACCOUNT_REGISTERED" />
     <protected-broadcast android:name="android.telecom.action.PHONE_ACCOUNT_UNREGISTERED" />
+    <protected-broadcast android:name="android.telecom.action.POST_CALL" />
     <protected-broadcast android:name="android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION" />
     <protected-broadcast android:name="android.telephony.action.CARRIER_CONFIG_CHANGED" />
     <protected-broadcast android:name="android.telephony.action.DEFAULT_SUBSCRIPTION_CHANGED" />
@@ -2538,8 +2539,7 @@
     <permission android:name="android.permission.START_ANY_ACTIVITY"
         android:protectionLevel="signature" />
 
-    <!-- Allows an application to start activities from background
-         @hide -->
+    <!-- @SystemApi @hide Allows an application to start activities from background -->
     <permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"
         android:protectionLevel="signature|privileged|vendorPrivileged|oem|verifier" />
 
@@ -3487,6 +3487,14 @@
     <permission android:name="android.permission.BIND_CONTENT_SUGGESTIONS_SERVICE"
                 android:protectionLevel="signature" />
 
+    <!-- Must be declared by a android.service.musicrecognition.MusicRecognitionService,
+         to ensure that only the system can bind to it.
+         @SystemApi @hide This is not a third-party API (intended for OEMs and system apps).
+         <p>Protection level: signature
+    -->
+    <permission android:name="android.permission.BIND_MUSIC_RECOGNITION_SERVICE"
+        android:protectionLevel="signature" />
+
     <!-- Must be required by a android.service.autofill.augmented.AugmentedAutofillService,
          to ensure that only the system can bind to it.
          @SystemApi @hide This is not a third-party API (intended for OEMs and system apps).
@@ -4509,6 +4517,12 @@
     <permission android:name="android.permission.MANAGE_NOTIFICATIONS"
                 android:protectionLevel="signature" />
 
+    <!-- @SystemApi @TestApi Allows adding/removing enabled notification listener components.
+        @hide -->
+    <permission android:name="android.permission.MANAGE_NOTIFICATION_LISTENERS"
+                android:protectionLevel="signature|installer" />
+    <uses-permission android:name="android.permission.MANAGE_NOTIFICATION_LISTENERS" />
+
     <!-- Allows notifications to be colorized
          <p>Not for use by third-party applications. @hide -->
     <permission android:name="android.permission.USE_COLORIZED_NOTIFICATIONS"
@@ -4532,6 +4546,12 @@
     <permission android:name="android.permission.RESET_FINGERPRINT_LOCKOUT"
         android:protectionLevel="signature" />
 
+    <!-- Allows access to TestApis for various components in the biometric stack, including
+         FingerprintService, FaceService, BiometricService. Used by com.android.server.biometrics
+         CTS tests. @hide @TestApi -->
+    <permission android:name="android.permission.TEST_BIOMETRIC"
+        android:protectionLevel="signature" />
+
     <!-- Allows direct access to the <Biometric>Service interfaces. Reserved for the system. @hide -->
     <permission android:name="android.permission.MANAGE_BIOMETRIC"
         android:protectionLevel="signature" />
@@ -4878,6 +4898,11 @@
     <permission android:name="android.permission.MANAGE_CONTENT_CAPTURE"
         android:protectionLevel="signature" />
 
+    <!-- @SystemApi Allows an application to manage the music recognition service.
+         @hide  <p>Not for use by third-party applications.</p> -->
+    <permission android:name="android.permission.MANAGE_MUSIC_RECOGNITION"
+        android:protectionLevel="signature" />
+
     <!-- @SystemApi Allows an application to manage the content suggestions service.
          @hide  <p>Not for use by third-party applications.</p> -->
     <permission android:name="android.permission.MANAGE_CONTENT_SUGGESTIONS"
diff --git a/core/res/res/layout/notification_material_media_transfer_action.xml b/core/res/res/layout/notification_material_media_transfer_action.xml
deleted file mode 100644
index 98d8f1e..0000000
--- a/core/res/res/layout/notification_material_media_transfer_action.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?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
-  -->
-<LinearLayout
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal"
-        android:visibility="gone"
-        android:padding="4dp"
-        android:layout_marginStart="10dp"
-        android:gravity="center"
-        android:background="@drawable/media_seamless_background">
-    <ImageView
-        android:layout_width="?attr/notificationHeaderIconSize"
-        android:layout_height="?attr/notificationHeaderIconSize"
-        android:src="@drawable/ic_media_seamless"
-        android:id="@+id/media_seamless_image" />
-    <TextView
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:textAppearance="?attr/notificationHeaderTextAppearance"
-        android:text="@string/ext_media_seamless_action"
-        android:id="@+id/media_seamless_text"
-        android:paddingEnd="2dp" />
-</LinearLayout>
\ No newline at end of file
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 9a1b592..88493c9 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -160,9 +160,5 @@
         android:visibility="gone"
         android:contentDescription="@string/notification_work_profile_content_description"
         />
-    <include
-        layout="@layout/notification_material_media_transfer_action"
-        android:id="@+id/media_seamless"
-    />
 </NotificationHeaderView>
 
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index e6b7c68..bd81519 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"verander jou klankinstellings"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Laat die program toe om globale klankinstellings soos volume en watter luidspreker vir uitvoer gebruik word, te verander."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"neem klank op"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Hierdie die program kan oudio met die mikrofoon opneem terwyl die program gebruik word."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"neem oudio op die agtergrond op"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Hierdie program kan enige tyd oudio met die mikrofoon opneem."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"stuur bevele na die SIM"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Laat die program toe om bevele na die SIM te stuur. Dit is baie gevaarlik."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"herken fisieke aktiwiteit"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Hierdie program kan jou fisieke aktiwiteit herken."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"neem foto\'s en video\'s"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Hierdie program kan met die kamera foto\'s neem en video\'s opneem terwyl die program gebruik word."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"neem foto\'s en video\'s op die agtergrond"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Hierdie program kan enige tyd met die kamera foto\'s neem en video\'s opneem."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Gee \'n program of diens toegang tot stelselkameras om foto\'s en video\'s te neem"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Hierdie bevoorregte of stelselprogram kan enige tyd met \'n stelselkamera foto\'s neem en video\'s opneem. Vereis dat die program ook die android.permission.CAMERA-toestemming het"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Laat \'n program of diens toe om terugbeloproepe te ontvang oor kameratoestelle wat oopgemaak of toegemaak word."</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 9750a0c..0bbe232 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"የድምፅ ቅንብሮችን ለውጥ"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"መተግበሪያው አንደ የድምጽ መጠን እና ለውጽአት የትኛውን የድምጽ ማጉያ ጥቅም ላይ እንደዋለ የመሳሰሉ ሁለንተናዊ የድምጽ ቅንብሮችን እንዲያስተካክል ይፈቅድለታል።"</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"ኦዲዮ ይቅዱ"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"ይህ መተግበሪያ መተግበሪያው ስራ ላይ ሳለ ማይክሮፎኑን በመጠቀም ኦዲዮን መቅዳት ይችላል።"</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"በበስተጀርባ ኦዲዮን ይቅዱ"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"ይህ መተግበሪያ በማናቸውም ጊዜ ማይክራፎኑን በመጠቀም ኦዲዮን መቅዳት ይችላል።"</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"ወደ ሲሙ ትዕዛዞችን መላክ"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"መተግበሪያው ትዕዛዞችን ወደ ሲሙ እንዲልክ ያስችለዋል። ይሄ በጣማ አደገኛ ነው።"</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"አካላዊ እንቅስቃሴን ለይቶ ማወቅ"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"ይህ መተግበሪያ አካላዊ እንቅስቃሴዎን ለይቶ ሊያውቅ ይችላል።"</string>
     <string name="permlab_camera" msgid="6320282492904119413">"ፎቶዎች እና ቪዲዮዎች ያንሱ"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"ይህ መተግበሪያ መተግበሪያው ስራ ላይ ሳለ ካሜራውን በመጠቀም ሥዕሎችን ማንሳት እና ቪዲዮዎችን መቅዳት ይችላል።"</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"በበስተጀርባ ስዕሎችን እና ቪዲዮዎችን ያንሱ"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"ይህ መተግበሪያ በማናቸውም ጊዜ ካሜራውን በመጠቀም ፎቶ ሊያነሳ እና ቪዲዮዎችን ሊቀርጽ ይችላል።"</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"ሥዕሎችን ለማንሣት እና ቪዲዮዎችን ለመቅረጽ እንዲችሉ ወደ ሥርዓት ካሜራዎች ለመተግበሪያ ወይም ለአገልግሎት መዳረሻ ይፍቀዱ"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"ይህ ልዩ ፈቃድ ያለው የሥርዓት መተግበሪያ በማንኛውም ጊዜ የሥርዓት ካሜራን በመጠቀም ሥዕሎችን ማንሣት እና ቪዲዮ መቅረጽ ይችላል። የandroid.permission.CAMERA ፈቃዱ በመተግበሪያውም ጭምር እንዲያዝ ያስፈልገዋል።"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"አንድ መተግበሪያ ወይም አገልግሎት እየተከፈቱ ወይም እየተዘጉ ስላሉ የካሜራ መሣሪያዎች መልሶ ጥሪዎችን እንዲቀበል ይፍቀዱ።"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index e672de3..8868fb6 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -444,23 +444,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"تغيير إعداداتك الصوتية"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"للسماح للتطبيق بتعديل إعدادات الصوت العامة مثل مستوى الصوت وأي السماعات يتم استخدامها للاستماع."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"تسجيل الصوت"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"يمكن لهذا التطبيق تسجيل الصوت باستخدام الميكروفون عندما يكون التطبيق قيد الاستخدام."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"تسجيل الصوت في الخلفية"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"يمكن لهذا التطبيق تسجيل الصوت باستخدام الميكروفون في أي وقت."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"‏إرسال أوامر إلى شريحة SIM"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"‏السماح للتطبيق بإرسال أوامر إلى شريحة SIM. وهذا أمر بالغ الخطورة."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"التعرّف على النشاط البدني"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"يمكن لهذا التطبيق التعرّف على نشاطك البدني."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"التقاط صور وفيديوهات"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"يمكن لهذا التطبيق التقاط صور وتسجيل فيديوهات باستخدام الكاميرا عندما يكون التطبيق قيد الاستخدام."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"التقاط صور وتصوير فيديوهات في الخلفية"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"يمكن لهذا التطبيق التقاط صور وتسجيل فيديوهات باستخدام الكاميرا في أي وقت."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"السماح لتطبيق أو خدمة بالوصول إلى كاميرات النظام لالتقاط صور وتسجيل فيديوهات"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"‏إنّ تطبيق النظام هذا، أو التطبيق المزوّد بأذونات مميّزة، يمكنه التقاط صور وتسجيل فيديوهات باستخدام كاميرا النظام في أي وقت. ويجب أن يحصل التطبيق أيضًا على الإذن android.permission.CAMERA."</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"يسمح الإذن لتطبيق أو خدمة بتلقّي استدعاءات عما إذا كانت أجهزة الكاميرات مفتوحة أو مغلقة."</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index b3e1680..619666b 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"আপোনাৰ অডিঅ\' ছেটিংসমূহ সলনি কৰক"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"এপটোক ভলিউমৰ দৰে গ্ল\'বেল অডিঅ\' ছেটিংসমূহ যাৰ স্পীকাৰক আউটপুটৰ বাবে ব্যৱহাৰ হয় তাক সলনি কৰিবলৈ অনুমতি দিয়ে৷"</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"অডিঅ\' ৰেকর্ড কৰক"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"এই এপ্‌টোৱে ইয়াক ব্যৱহাৰ কৰি থাকোঁতে মাইক্ৰ’ফ’ন ব্যৱহাৰ কৰি অডিঅ’ ৰেকর্ড কৰিব পাৰে।"</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"নেপথ্যত অডিঅ’ ৰেকৰ্ড কৰক"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"এই এপ্‌টোৱে যিকোনো সময়তে মাইক্ৰ’ফ’ন ব্যৱহাৰ কৰি অডিঅ’ ৰেকৰ্ড কৰিব পাৰে।"</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"ছিমলৈ নিৰ্দেশ পঠিয়াব পাৰে"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"ছিমলৈ নিৰ্দেশসমূহ প্ৰেৰণ কৰিবলৈ এপক অনুমতি দিয়ে। ই অতি ক্ষতিকাৰক।"</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"শাৰীৰিক কাৰ্যকলাপ চিনাক্ত কৰক"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"এই এপটোৱে আপোনাৰ শাৰীৰিক কাৰ্যকলাপ চিনাক্ত কৰিব পাৰে।"</string>
     <string name="permlab_camera" msgid="6320282492904119413">"ফট\' তোলা আৰু ভিডিঅ\' ৰেকৰ্ড কৰা"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"এই এপ্‌টোৱে ইয়াক ব্যৱহাৰ কৰি থাকোঁতে কেমেৰা ব্যৱহাৰ কৰি ফট’ তুলিব আৰু ভিডিঅ’ ৰেকর্ড কৰিব পাৰে।"</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"নেপথ্যত ফট’ তোলক আৰু ভিডিঅ’ ৰেকৰ্ড কৰক"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"এই এপ্‌টোৱে যিকোনো সময়তে কেমেৰা ব্যৱহাৰ কৰি ফট’ তুলিব আৰু ভিডিঅ’ ৰেকর্ড কৰিব পাৰে।"</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"ফট’ উঠাবলৈ আৰু ভিডিঅ’ ৰেকৰ্ড কৰিবলৈ এটা এপ্লিকেশ্বন অথবা সেৱাক ছিষ্টেম কেমেৰাসমূহ এক্সেছ কৰিবলৈ দিয়ক"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"এই বিশেষাধিকাৰ প্ৰাপ্ত অথবা ছিষ্টেম এপ্‌টোৱে এটা ছিষ্টেম কেমেৰা ব্যৱহাৰ কৰি যিকোনো সময়তে ফট’ উঠাব পাৰে আৰু ভিডিঅ’ ৰেকৰ্ড কৰিব পাৰে। লগতে এপ্‌টোৰো android.permission.CAMERAৰ অনুমতি থকাটো প্ৰয়োজনীয়"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"কোনো এপ্লিকেশ্বন অথবা সেৱাক কেমেৰা ডিভাইচসমূহ খোলা অথবা বন্ধ কৰাৰ বিষয়ে কলবেকসমূহ গ্ৰহণ কৰিবলৈ অনুমতি দিয়ক।"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index de399df..ed708cc 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"audio ayarlarınızı dəyişir"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Tətbiqə səs və hansı spikerin çıxış üçün istifadə olunduğu kimi qlobal səs ayarlarını dəyişdirməyə imkan verir."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"səs yaz"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Bu tətbiq istifadə edilən zaman mikrofondan istifadə edərək audio yaza bilər."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"arxa fonda audio yazmaq"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Bu tətbiq istənilən zaman mikrofondan istifadə edərək audio yaza bilər."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"əmrləri SIM\'ə göndərin"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Tətbiqə SIM-ə əmrlər göndərməyə imkan verir. Bu, çox təhlükəlidir."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"fiziki fəaliyyəti tanıyın"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Bu tətbiq fiziki fəaliyyətinizi tanıya bilər."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"şəkil və video çəkmək"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Bu tətbiq istifadə edilən zaman kameradan istifadə edərək şəkil çəkə və video yaza bilər."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"arxa fonda şəkillər və videolar çəkmək"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Bu tətbiq istənilən zaman kameradan istifadə edərək şəkil çəkə və video yaza bilər."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Şəkil və video çəkmək üçün tətbiq və ya xidmətlərin sistem kameralarına girişinə icazə verin"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Bu icazəli və ya sistem tətbiqi istənilən vaxt sistem kamerasından istifadə edərək şəkil və videolar çəkə bilər. android.permission.CAMERA icazəsinin də tətbiq tərəfindən saxlanılmasını tələb edir"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Tətbiqə və ya xidmətə kamera cihazlarının açılması və ya bağlanması haqqında geri zənglər qəbul etməyə icazə verin."</string>
@@ -714,7 +708,7 @@
     <string name="policylab_disableKeyguardFeatures" msgid="5071855750149949741">"Bəzi ekran kilidi funksiyalarını deaktiv edin"</string>
     <string name="policydesc_disableKeyguardFeatures" msgid="6641673177041195957">"Bəzi ekran funksiyaları istifadəsinin qarşısını alın."</string>
   <string-array name="phoneTypes">
-    <item msgid="8996339953292723951">"Əsas səhifə"</item>
+    <item msgid="8996339953292723951">"Ev"</item>
     <item msgid="7740243458912727194">"Mobil"</item>
     <item msgid="8526146065496663766">"İş"</item>
     <item msgid="8150904584178569699">"İş Faksı"</item>
@@ -724,19 +718,19 @@
     <item msgid="6216981255272016212">"Şəxsi"</item>
   </string-array>
   <string-array name="emailAddressTypes">
-    <item msgid="7786349763648997741">"Ana səhifə"</item>
+    <item msgid="7786349763648997741">"Ev"</item>
     <item msgid="435564470865989199">"İş"</item>
     <item msgid="4199433197875490373">"Digər"</item>
     <item msgid="3233938986670468328">"Fərdi"</item>
   </string-array>
   <string-array name="postalAddressTypes">
-    <item msgid="3861463339764243038">"Əsas səhifə"</item>
+    <item msgid="3861463339764243038">"Ev"</item>
     <item msgid="5472578890164979109">"İş"</item>
     <item msgid="5718921296646594739">"Digər"</item>
     <item msgid="5523122236731783179">"Düzənləyin"</item>
   </string-array>
   <string-array name="imAddressTypes">
-    <item msgid="588088543406993772">"Əsas səhifə"</item>
+    <item msgid="588088543406993772">"Ev"</item>
     <item msgid="5503060422020476757">"İş"</item>
     <item msgid="2530391194653760297">"Digər"</item>
     <item msgid="7640927178025203330">"Fərdi"</item>
@@ -782,16 +776,16 @@
     <string name="eventTypeAnniversary" msgid="4684702412407916888">"İldönümü"</string>
     <string name="eventTypeOther" msgid="530671238533887997">"Digər"</string>
     <string name="emailTypeCustom" msgid="1809435350482181786">"Fərdi"</string>
-    <string name="emailTypeHome" msgid="1597116303154775999">"Əsas səhifə"</string>
+    <string name="emailTypeHome" msgid="1597116303154775999">"Şəxsi"</string>
     <string name="emailTypeWork" msgid="2020095414401882111">"İş"</string>
     <string name="emailTypeOther" msgid="5131130857030897465">"Digər"</string>
     <string name="emailTypeMobile" msgid="787155077375364230">"Mobil"</string>
     <string name="postalTypeCustom" msgid="5645590470242939129">"Fərdi"</string>
-    <string name="postalTypeHome" msgid="7562272480949727912">"Əsas səhifə"</string>
+    <string name="postalTypeHome" msgid="7562272480949727912">"Ev"</string>
     <string name="postalTypeWork" msgid="8553425424652012826">"İş"</string>
     <string name="postalTypeOther" msgid="7094245413678857420">"Digər"</string>
     <string name="imTypeCustom" msgid="5653384545085765570">"Fərdi"</string>
-    <string name="imTypeHome" msgid="6996507981044278216">"Ana səhifə"</string>
+    <string name="imTypeHome" msgid="6996507981044278216">"Ev"</string>
     <string name="imTypeWork" msgid="2099668940169903123">"İş"</string>
     <string name="imTypeOther" msgid="8068447383276219810">"Digər"</string>
     <string name="imProtocolCustom" msgid="4437878287653764692">"Şəxsi"</string>
@@ -823,7 +817,7 @@
     <string name="relationTypeSister" msgid="3721676005094140671">"Bacı"</string>
     <string name="relationTypeSpouse" msgid="6916682664436031703">"Həyat yoldaşı"</string>
     <string name="sipAddressTypeCustom" msgid="6283889809842649336">"Fərdi"</string>
-    <string name="sipAddressTypeHome" msgid="5918441930656878367">"Əsas səhifə"</string>
+    <string name="sipAddressTypeHome" msgid="5918441930656878367">"Ev"</string>
     <string name="sipAddressTypeWork" msgid="7873967986701216770">"İş"</string>
     <string name="sipAddressTypeOther" msgid="6317012577345187275">"Digər"</string>
     <string name="quick_contacts_not_available" msgid="1262709196045052223">"Bu kontakta baxmaq üçün heç bir tətbiq tapılmadı."</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 928ade1..0b21545 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -435,23 +435,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"promena audio podešavanja"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Dozvoljava aplikaciji da menja globalna audio podešavanja kao što su jačina zvuka i izbor zvučnika koji se koristi kao izlaz."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"snimanje audio zapisa"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Ova aplikacija može da snima zvuk pomoću mikrofona dok se aplikacija koristi."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"da snima zvuk u pozadini"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Ova aplikacija može da snima zvuk pomoću mikrofona u bilo kom trenutku."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"slanje komandi na SIM"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Omogućava aplikaciji da šalje komande SIM kartici. To je veoma opasno."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"prepoznavanje fizičkih aktivnosti"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Ova aplikacija može da prepozna fizičke aktivnosti."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"snimanje fotografija i video snimaka"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Ova aplikacija može da snima slike i video snimke pomoću kamere dok se aplikacija koristi."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"da snima slike i video snimke u pozadini"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Ova aplikacija može da snima fotografije i video snimke pomoću kamere u bilo kom trenutku."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Dozvolite nekoj aplikaciji ili usluzi da pristupa kamerama sistema da bi snimala slike i video snimke"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Ova privilegovana sistemska aplikacija može da snima slike i video snimke pomoću kamere sistema u bilo kom trenutku. Aplikacija treba da ima i dozvolu android.permission.CAMERA"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Dozvolite aplikaciji ili usluzi da dobija povratne pozive o otvaranju ili zatvaranju uređaja sa kamerom."</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index e31c4b7..6a0ad98 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -98,9 +98,9 @@
     <string name="notification_channel_wfc" msgid="9048240466765169038">"Wi-Fi-тэлефанія"</string>
     <string name="notification_channel_sim" msgid="5098802350325677490">"Статус SIM-карты"</string>
     <string name="notification_channel_sim_high_prio" msgid="642361929452850928">"Стан SIM-карты з высокім прыярытэтам"</string>
-    <string name="peerTtyModeFull" msgid="337553730440832160">"Аднарангавая прылада запытала рэжым TTY FULL"</string>
-    <string name="peerTtyModeHco" msgid="5626377160840915617">"Аднарангавая прылада запытала рэжым TTY НСО"</string>
-    <string name="peerTtyModeVco" msgid="572208600818270944">"Аднарангавая прылада запытала рэжым TTY VCO"</string>
+    <string name="peerTtyModeFull" msgid="337553730440832160">"Аднарангавая прылада запытала рэжым поўнафункцыянальнага TTY"</string>
+    <string name="peerTtyModeHco" msgid="5626377160840915617">"Аднарангавая прылада запытала рэжым TTY з магчымасцю чуць суразмоўніка"</string>
+    <string name="peerTtyModeVco" msgid="572208600818270944">"Аднарангавая прылада запытала рэжым TTY з магчымасцю чуць суразмоўніка"</string>
     <string name="peerTtyModeOff" msgid="2420380956369226583">"Аднарангавая прылада запытала рэжым TTY OFF"</string>
     <string name="serviceClassVoice" msgid="2065556932043454987">"Голас"</string>
     <string name="serviceClassData" msgid="4148080018967300248">"Дадзеныя"</string>
@@ -438,23 +438,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"змяняць налады аудыё"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Дазваляе прыкладанням змяняць глабальныя налады гуку, такія як моц і тое, што дынамік выкарыстоўваецца для выхаду."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"запіс аўдыя"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Гэта праграма падчас яе выкарыстання можа запісваць аўдыя з дапамогай мікрафона."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"запісваць аўдыя ў фонавым рэжыме"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Гэта праграма можа ў любы час запісваць аўдыя з дапамогай мікрафона."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"адпраўляць каманды на SIM-карту"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Дазваляе праграме адпраўляць каманды SIM-карце. Гэта вельмі небяспечна."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"распазнаваць фізічную актыўнасць"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Гэта праграма можа распазнаваць фізічную актыўнасць."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"рабіць фатаграфіі і відэа"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Гэта праграма падчас яе выкарыстання можа рабіць фота і запісваць відэа з дапамогай камеры."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"знімаць фота і відэа ў фонавым рэжыме"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Гэта праграма можа ў любы час рабіць фота і запісваць відэа з дапамогай камеры."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Дазволіць праграме або сэрвісу атрымліваць доступ да сістэмных камер, каб здымаць фота і запісваць відэа"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Гэта прыярытэтная ці сістэмная праграма можа здымаць фота і запісваць відэа з дапамогай сістэмнай камеры. Праграме таксама патрэбны дазвол android.permission.CAMERA"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Дазволіць праграме ці сэрвісу атрымліваць зваротныя выклікі наконт адкрыцця ці закрыцця прылад камеры."</string>
@@ -1552,7 +1546,7 @@
     <string name="activitychooserview_choose_application_error" msgid="6937782107559241734">"Не атрымалася запусціць <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
     <string name="shareactionprovider_share_with" msgid="2753089758467748982">"Апублікаваць з дапамогай"</string>
     <string name="shareactionprovider_share_with_application" msgid="4902832247173666973">"Адправiць з дапамогай прыкладання <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="content_description_sliding_handle" msgid="982510275422590757">"Ручка для перасоўвання. Націсніце і ўтрымлівайце."</string>
+    <string name="content_description_sliding_handle" msgid="982510275422590757">"Маркер для перасоўвання. Дакраніцеся і ўтрымлівайце."</string>
     <string name="description_target_unlock_tablet" msgid="7431571180065859551">"Прагартайце, каб разблакаваць."</string>
     <string name="action_bar_home_description" msgid="1501655419158631974">"Перайсці да пачатковай старонкі"</string>
     <string name="action_bar_up_description" msgid="6611579697195026932">"Перайсці ўверх"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 5938fd9..cdd0ec0 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"промяна на настройките ви за звука"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Разрешава на приложението да променя глобалните настройки за звука, като например силата и това, кой високоговорител се използва за изход."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"записва звук"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Когато се използва, това приложение може да записва аудио посредством микрофона."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"записва аудио на заден план"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Това приложение може по всяко време да записва аудио посредством микрофона."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"изпращане на команди до SIM картата"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Разрешава на приложението да изпраща команди до SIM картата. Това е много опасно."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"разпознаване на физическата активност"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Това приложение може да разпознава физическата ви активност."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"правене на снимки и видеоклипове"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Когато се използва, това приложение може да прави снимки и да записва видеоклипове посредством камерата."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"прави снимки и видеоклипове на заден план"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Това приложение може по всяко време да прави снимки и да записва видеоклипове посредством камерата."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Разрешаване на достъп на приложение или услуга до системните камери с цел правене на снимки и видеоклипове"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Това привилегировано или системно приложение може по всяко време да прави снимки и да записва видео посредством системна камера. Необходимо е също на приложението да бъде дадено разрешението android.permission.CAMERA"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Разрешаване на приложение или услуга да получават обратни повиквания за отварянето или затварянето на снимачни устройства."</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 3567d79..779fd3e 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"আপনার অডিও সেটিংস পরিবর্তন করে"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"ভলিউম এবং যেখানে স্পিকার আউটপুট হিসাবে ব্যবহৃত হয় সেই সব ক্ষেত্রে গ্লোবাল অডিও সেটিংসের সংশোধন করতে অ্যাপ্লিকেশনটিকে মঞ্জুর করে৷"</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"অডিও রেকর্ড"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"এই অ্যাপটি যখন ব্যবহার করা হচ্ছে, তখন মাইক্রোফোন ব্যবহার করে অডিও রেকর্ড করতে পারবে।"</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"ব্যাকগ্রাউন্ডে অডিও রেকর্ড করতে পারবে"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"এই অ্যাপটি মাইক্রোফোন ব্যবহার করে যেকোনও সময় অডিও রেকর্ড করতে পারবে।"</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"সিম এ আদেশগুলি পাঠান"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"অ্যাপ্লিকেশানটিকে সিম কার্ডে কমান্ডগুলি পাঠানোর অনুমতি দেয়৷ এটি খুবই বিপজ্জনক৷"</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"শারীরিক অ্যাক্টিভিটি শনাক্ত করুন"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"এই অ্যাপ আপনার শারীরিক অ্যাক্টিভিটি শনাক্ত করতে পারবে।"</string>
     <string name="permlab_camera" msgid="6320282492904119413">"ছবি এবং ভিডিও তোলে"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"এই অ্যাপটি যখন ব্যবহার করা হচ্ছে, তখন ক্যামেরা ব্যবহার করে ছবি তুলতে বা ভিডিও রেকর্ড করতে পারবে।"</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"ব্যাকগ্রাউন্ডে ছবি এবং ভিডিও তুলতে পারবে"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"এই অ্যাপটি যেকোনও সময় ক্যামেরা ব্যবহার করে ছবি তুলতে বা ভিডিও রেকর্ড করতে পারবে।"</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"সিস্টেম ক্যামেরা ব্যবহার করে ফটো এবং ভিডিও নেওয়ার জন্য অ্যাপ্লিকেশন বা পরিষেবা অ্যাক্সেসের অনুমতি দিন"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"এই প্রিভিলেজ বা সিস্টেম অ্যাপ যেকোনও সময় সিস্টেম ক্যামেরা ব্যবহার করে ছবি তুলতে ও ভিডিও রেকর্ড করতে পারে। এই অ্যাপকে android.permission.CAMERA অনুমতি দিতে হবে"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"কোনও অ্যাপ্লিকেশন বা পরিষেবাকে ক্যামেরা ডিভাইসগুলি খোলা বা বন্ধ হওয়া সম্পর্কে কলব্যাকগুলি গ্রহণ করার অনুমতি দিন।"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 2c05aa5..fab6c91 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -435,23 +435,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"izmjene postavki zvuka"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Omogućava aplikaciji izmjenu općih postavki zvuka, kao što su jačina zvuka i izbor izlaznog zvučnika."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"snimanje audiozapisa"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Za vrijeme korištenja, ova aplikacija može snimati zvuk koristeći mikrofon."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"snimanje zvuka u pozadini"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Ova aplikacija može u svakom trenutku snimati zvuk koristeći mikrofon."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"slanje komandi SIM kartici"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Omogućava aplikaciji slanje naredbi na SIM. Ovo je vrlo opasno."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"prepoznavanje fizičke aktivnosti"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Ova aplikacija može prepoznati vašu fizičku aktivnost."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"snimanje slika i videozapisa"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Za vrijeme korištenja, ova aplikacija može snimati slike i videozapise koristeći kameru."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"snimanje slika i videozapisa u pozadini"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Ova aplikacija može snimati slike i videozapise koristeći kameru bilo kada."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Dopustite aplikaciji ili usluzi da pristupa kamerama sistema radi snimanja fotografija i videozapisa"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Ova povlaštena ili sistemska aplikacija u svakom trenutku može snimati fotografije i videozapise pomoću kamere sistema. Aplikacija također mora imati odobrenje android.permission.CAMERA"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Dozvoliti aplikaciji ili usluzi da prima povratne pozive o otvaranju ili zatvaranju kamera."</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 0a25ba0..96c6b6a 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"canviar la configuració d\'àudio"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Permet que l\'aplicació modifiqui la configuració d\'àudio general, com ara el volum i l\'altaveu de sortida que es fa servir."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"gravar àudio"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Aquesta aplicació pot gravar àudio amb el micròfon mentre s\'està utilitzant."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"gravar àudio en segon pla"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Aquesta aplicació pot gravar àudio amb el micròfon en qualsevol moment."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"enviar ordres a la SIM"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Permet que l\'aplicació enviï ordres a la SIM. Això és molt perillós."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"reconèixer l\'activitat física"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Aquesta aplicació pot reconèixer la teva activitat física."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"fer fotos i vídeos"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Aquesta aplicació pot fer fotos i gravar vídeos amb la càmera mentre s\'està utilitzant."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"fer fotos i gravar vídeos en segon pla"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Aquesta aplicació pot fer fotos i gravar vídeos amb la càmera en qualsevol moment."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Permet que una aplicació o un servei tinguin accés a les càmeres del sistema per fer fotos i vídeos"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Aquesta aplicació del sistema amb privilegis pot fer fotos i gravar vídeos amb una càmera del sistema en qualsevol moment. L\'aplicació també ha de tenir el permís android.permission.CAMERA per accedir-hi."</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Permet que una aplicació o un servei pugui rebre crides de retorn sobre els dispositius de càmera que s\'obren o es tanquen."</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 8321f22..9a5f069 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -438,23 +438,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"změna nastavení zvuku"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Umožňuje aplikaci změnit globální nastavení zvuku, například hlasitost či reproduktor pro výstup zvuku."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"nahrávání zvuku"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Tato aplikace může pomocí mikrofonu během svého používání zaznamenat zvuk."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"zaznamenávat zvuk na pozadí"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Tato aplikace může pomocí mikrofonu kdykoli zaznamenat zvuk."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"odesílání příkazů do SIM karty"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Umožňuje aplikaci odesílat příkazy na kartu SIM. Toto oprávnění je velmi nebezpečné."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"rozpoznávání fyzické aktivity"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Tyto aplikace dokážou rozpoznat vaši fyzickou aktivitu."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"pořizování fotografií a videí"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Tato aplikace může pomocí fotoaparátu během svého používání pořídit snímek nebo nahrát video."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"pořizovat snímky a nahrávat videa na pozadí"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Tato aplikace může pomocí fotoaparátu kdykoli pořídit snímek nebo nahrát video."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Povolte aplikaci nebo službě k systémovým fotoaparátům za účelem pořizování fotek a videí"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Tato privilegovaná nebo systémová aplikace může pomocí fotoaparátu kdykoli pořídit snímek nebo nahrát video. Aplikace musí zároveň mít oprávnění android.permission.CAMERA."</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Povolte aplikaci nebo službě přijímat zpětná volání o otevření nebo zavření zařízení s fotoaparátem."</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index d85db8f..480e87c 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"skifte dine lydindstillinger"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Tillader, at appen kan ændre globale lydindstillinger, som f.eks. lydstyrke og hvilken højttaler der bruges til output."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"optage lyd"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Denne app kan optage lyd med mikrofonen, mens appen er i brug."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"optag lyd i baggrunden"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Denne app kan optage lyd med mikrofonen når som helst."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"send kommandoer til SIM"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Tillader, at appen sender kommandoer til SIM-kortet. Dette er meget farligt."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"genkend fysisk aktivitet"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Denne app kan genkende din fysiske aktivitet."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"tage billeder og optage video"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Denne app kan tage billeder og optage video med kameraet, mens appen er i brug."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"tag billeder, og optag video i baggrunden"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Med denne app kan du tage billeder og optage video med kameraet når som helst."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Giv en app eller tjeneste adgang til systemkameraer for at tage billeder og optage video"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Denne privilegerede app eller systemapp kan til enhver tid tage billeder og optage video med et systemkamera. Appen skal også have tilladelsen android.permission.CAMERA"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Tillad, at en app eller tjeneste modtager tilbagekald om kameraenheder, der åbnes eller lukkes."</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index e4fabbc..ac9a597 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"Audio-Einstellungen ändern"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Ermöglicht der App, globale Audio-Einstellungen zu ändern, etwa die Lautstärke und den Lautsprecher für die Ausgabe."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"Audio aufnehmen"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Diese App darf mit dem Mikrofon Audioaufnahmen machen, solange sie verwendet wird."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"Audioaufnahmen im Hintergrund machen"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Diese App darf mit dem Mikrofon jederzeit Audioaufnahmen machen."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"Befehle an die SIM senden"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Ermöglicht der App das Senden von Befehlen an die SIM-Karte. Dies ist äußerst risikoreich."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"Körperliche Aktivitäten erkennen"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Diese App kann deine körperliche Aktivität erkennen."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"Bilder und Videos aufnehmen"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Diese App darf mit der Kamera Bilder und Videos aufnehmen, solange die App verwendet wird."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"Bilder und Videos im Hintergrund aufnehmen"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Diese App darf mit der Kamera jederzeit Bilder und Videos aufnehmen."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Einer App oder einem Dienst Zugriff auf Systemkameras erlauben, um Fotos und Videos aufnehmen zu können"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Diese privilegierte App oder System-App kann jederzeit mit einer Systemkamera Bilder und Videos aufnehmen. Die App benötigt auch die Berechtigung \"android.permission.CAMERA\"."</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Einer App oder einem Dienst den Empfang von Callbacks erlauben, wenn eine Kamera geöffnet oder geschlossen wird."</string>
@@ -1802,8 +1796,8 @@
     <string name="package_updated_device_owner" msgid="7560272363805506941">"Von deinem Administrator aktualisiert"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Von deinem Administrator gelöscht"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
-    <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Der Stromsparmodus sorgt für eine längere Akkulaufzeit:\n\n• Das dunkle Design wird aktiviert\n• Hintergrundaktivitäten, einige optische Effekte und weitere Funktionen wie \"Ok Google\" werden abgeschaltet oder eingeschränkt\n\n"<annotation id="url">"Weitere Informationen"</annotation></string>
-    <string name="battery_saver_description" msgid="6794188153647295212">"Der Stromsparmodus sorgt für eine längere Akkulaufzeit:\n\n• Das dunkle Design wird aktiviert\n• Hintergrundaktivitäten, einige optische Effekte und weitere Funktionen wie \"Ok Google\" werden abgeschaltet oder eingeschränkt"</string>
+    <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Der Energiesparmodus sorgt für eine längere Akkulaufzeit:\n\n• Das dunkle Design wird aktiviert\n• Hintergrundaktivitäten, einige optische Effekte und weitere Funktionen wie \"Ok Google\" werden abgeschaltet oder eingeschränkt\n\n"<annotation id="url">"Weitere Informationen"</annotation></string>
+    <string name="battery_saver_description" msgid="6794188153647295212">"Der Energiesparmodus sorgt für eine längere Akkulaufzeit:\n\n• Das dunkle Design wird aktiviert\n• Hintergrundaktivitäten, einige optische Effekte und weitere Funktionen wie \"Ok Google\" werden abgeschaltet oder eingeschränkt"</string>
     <string name="data_saver_description" msgid="4995164271550590517">"Der Datensparmodus verhindert zum einen, dass manche Apps im Hintergrund Daten senden oder empfangen, sodass weniger Daten verbraucht werden. Zum anderen werden die Datenzugriffe der gerade aktiven App eingeschränkt, was z. B. dazu führen kann, dass Bilder erst angetippt werden müssen, bevor sie sichtbar werden."</string>
     <string name="data_saver_enable_title" msgid="7080620065745260137">"Datensparmodus aktivieren?"</string>
     <string name="data_saver_enable_button" msgid="4399405762586419726">"Aktivieren"</string>
@@ -2011,9 +2005,9 @@
     <string name="notification_feedback_indicator" msgid="663476517711323016">"Feedback geben"</string>
     <string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Infomitteilung zum Ablaufmodus"</string>
     <string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Dein Akku könnte vor der gewöhnlichen Ladezeit leer sein"</string>
-    <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Stromsparmodus aktiviert, um die Akkulaufzeit zu verlängern"</string>
-    <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"Stromsparmodus"</string>
-    <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"Stromsparmodus deaktiviert"</string>
+    <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Energiesparmodus aktiviert, um die Akkulaufzeit zu verlängern"</string>
+    <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"Energiesparmodus"</string>
+    <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"Energiesparmodus deaktiviert"</string>
     <string name="battery_saver_charged_notification_summary" product="default" msgid="5544457317418624367">"Das Smartphone ist ausreichend geladen. Es sind keine Funktionen mehr beschränkt."</string>
     <string name="battery_saver_charged_notification_summary" product="tablet" msgid="4426317048139996888">"Das Tablet ist ausreichend geladen. Es sind keine Funktionen mehr beschränkt."</string>
     <string name="battery_saver_charged_notification_summary" product="device" msgid="1031562417867646649">"Das Gerät ist ausreichend geladen. Es sind keine Funktionen mehr beschränkt."</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index d9badc6..6f03f80 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"αλλάζει τις ρυθμίσεις ήχου"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Επιτρέπει στην εφαρμογή την τροποποίηση καθολικών ρυθμίσεων ήχου, όπως η ένταση και ποιο ηχείο χρησιμοποιείται για έξοδο."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"εγγράφει ήχο"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Αυτή η εφαρμογή μπορεί να εγγράφει ήχο μέσω του μικροφώνου, όταν τη χρησιμοποιείτε."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"εγγραφή ήχου στο παρασκήνιο"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Αυτή η εφαρμογή μπορεί να εγγράφει ήχο μέσω του μικροφώνου, ανά πάσα στιγμή."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"στέλνει εντολές στην κάρτα SIM"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Επιτρέπει στην εφαρμογή την αποστολή εντολών στην κάρτα SIM. Αυτό είναι εξαιρετικά επικίνδυνο."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"αναγνώριση σωματικής δραστηριότητας"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Αυτή η εφαρμογή μπορεί να αναγνωρίσει τη σωματική σας δραστηριότητα."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"κάνει λήψη φωτογραφιών και βίντεο"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Αυτή η εφαρμογή μπορεί να τραβάει φωτογραφίες και να εγγράφει βίντεο μέσω της κάμερας, όταν τη χρησιμοποιείτε."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"λήψη φωτογραφιών και βίντεο στο παρασκήνιο"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Αυτή η εφαρμογή μπορεί να τραβάει φωτογραφίες και να εγγράφει βίντεο μέσω της κάμερας, ανά πάσα στιγμή."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Παραχωρήστε σε μια εφαρμογή ή υπηρεσία πρόσβαση στις κάμερες του συστήματος για τη λήψη φωτογραφιών και βίντεο"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Αυτή η προνομιακή εφαρμογή ή εφαρμογή συστήματος μπορεί να τραβάει φωτογραφίες και να εγγράφει βίντεο, χρησιμοποιώντας μια κάμερα του συστήματος ανά πάσα στιγμή. Απαιτείται, επίσης, η εφαρμογή να έχει την άδεια android.permission.CAMERA"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Επιτρέψτε σε μια εφαρμογή ή μια υπηρεσία να λαμβάνει επανάκλησεις σχετικά με το άνοιγμα ή το κλείσιμο συσκευών κάμερας."</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index f933a2f..8e7d1a33 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"cambiar tu configuración de audio"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Permite que la aplicación modifique la configuración de audio global, por ejemplo, el volumen y el altavoz de salida."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"grabar audio"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Esta app puede grabar audio con el micrófono mientras está en uso."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"grabar audio en segundo plano"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Esta app puede grabar audio con el micrófono en cualquier momento."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"enviar comandos a la tarjeta SIM"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Permite que la aplicación envíe comandos a la tarjeta SIM. Usar este permiso es peligroso."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"reconocer actividad física"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Esta app puede reconocer tu actividad física."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"tomar fotografías y grabar videos"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Esta app puede tomar fotos y grabar videos con la cámara mientras está en uso."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"capturar fotos y videos en segundo plano"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Esta app puede tomar fotos y grabar videos con la cámara en cualquier momento."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Permitir que una aplicación o un servicio accedan a las cámaras del sistema para tomar fotos y grabar videos"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Esta app del sistema o con privilegios puede tomar fotografías y grabar videos con una cámara del sistema en cualquier momento. Para ello, requiere tener el permiso android.permission.CAMERA."</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Permite que una aplicación o un servicio reciba devoluciones de llamada cuando se abren o cierran dispositivos de cámara."</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 834c901..b9f0f58 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -327,7 +327,7 @@
     <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Controla el posicionamiento y el nivel de zoom de la pantalla."</string>
     <string name="capability_title_canPerformGestures" msgid="9106545062106728987">"Realizar gestos"</string>
     <string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"Puedes tocar y pellizcar la pantalla, deslizar el dedo y hacer otros gestos."</string>
-    <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"Gestos de huellas digitales"</string>
+    <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"Gestos de huella digital"</string>
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Puede capturar los gestos realizados en el sensor de huellas digitales del dispositivo."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Hacer captura"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Puede hacer capturas de la pantalla."</string>
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"cambiar la configuración de audio"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Permite que la aplicación modifique la configuración de audio global (por ejemplo, el volumen y el altavoz de salida)."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"grabar sonido"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Esta aplicación puede grabar audio con el micrófono mientras la estés usando."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"Grabar audio en segundo plano"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Esta aplicación puede grabar audio con el micrófono en cualquier momento."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"enviar comandos a la tarjeta SIM"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Permite que la aplicación envíe comandos a la tarjeta SIM. Este permiso es muy peligroso."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"reconocer actividad física"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Esta aplicación puede reconocer tu actividad física."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"realizar fotografías y vídeos"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Esta aplicación puede hacer fotografías y grabar vídeos con la cámara mientras la estés usando."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"Hacer fotografías y grabar vídeos en segundo plano"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Esta aplicación puede hacer fotografías y grabar vídeos con la cámara en cualquier momento."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Permitir que una aplicación o servicio acceda a las cámaras del sistema para hacer fotos y vídeos"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Esta aplicación del sistema o con privilegios puede hacer fotos y grabar vídeos en cualquier momento con una cámara del sistema, aunque debe tener también el permiso android.permission.CAMERA para hacerlo"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Permitir que una aplicación o servicio reciba retrollamadas cada vez que se abra o cierre una cámara."</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index babd58d..d825429 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"muuda heliseadeid"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Võimaldab rakendusel muuta üldiseid heliseadeid, näiteks helitugevust ja seda, millist kõlarit kasutatakse väljundiks."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"salvesta heli"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"See rakendus saab mikrofoniga heli salvestada siis, kui rakendus on kasutusel."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"Taustal heli salvestamine."</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"See rakendus saab mikrofoniga heli salvestada mis tahes ajal."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"SIM-kaardile käskluste saatmine"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Lubab rakendusel saata käske SIM-kaardile. See on väga ohtlik."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"füüsiliste tegevuste tuvastamine"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"See rakendus saab tuvastada teie füüsilised tegevused."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"piltide ja videote tegemine"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"See rakendus saab kaameraga pildistada ja videoid salvestada siis, kui rakendus on kasutusel."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"Taustal pildistamine ja videote salvestamine."</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"See rakendus saab kaameraga pildistada ja videoid salvestada mis tahes ajal."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Rakendusel või teenusel lubatakse süsteemi kaameratele juurde pääseda, et pilte ja videoid jäädvustada"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"See privileegidega või süsteemirakendus saab süsteemi kaameraga alati pilte ja videoid jäädvustada. Rakendusel peab olema ka luba android.permission.CAMERA"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Lubab rakendusel või teenusel kaameraseadmete avamise või sulgemise kohta tagasikutseid vastu võtta."</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 3ba099b..05d4050 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"aldatu audio-ezarpenak"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Audio-ezarpen orokorrak aldatzeko baimena ematen dio; besteak beste, bolumena eta irteerarako zer bozgorailu erabiltzen den."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"grabatu audioa"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Aplikazioak abian den bitartean erabil dezake mikrofonoa audioa grabatzeko."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"Audioa grabatu atzeko planoan."</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Aplikazioak edonoiz erabil dezake mikrofonoa audioa grabatzeko."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"bidali aginduak SIM txartelera"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"SIM txartelera aginduak bidaltzeko aukera ematen die aplikazioei. Oso arriskutsua da."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"hauteman ariketa fisikoa"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Aplikazioak ariketa fisikoa hauteman dezake."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"atera argazkiak eta grabatu bideoak"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Aplikazioak abian den bitartean erabil dezake kamera argazkiak ateratzeko eta bideoak grabatzeko."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"Argazkiak atera eta bideoak grabatu atzeko planoan."</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Aplikazioak edonoiz erabil dezake kamera argazkiak ateratzeko eta bideoak grabatzeko."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"onartu aplikazio edo zerbitzu bati sistemako kamerak atzitzea argazkiak eta bideoak ateratzeko"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Pribilegioa duen edo sistemakoa den aplikazio honek edonoiz erabil dezake kamera argazkiak ateratzeko eta bideoak grabatzeko. Halaber, android.permission.CAMERA baimena izan behar du aplikazioak."</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"eman jakinarazpenak jasotzeko baimena aplikazioari edo zerbitzuari kamerak ireki edo ixten direnean."</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 7750d5d..2146554 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -156,9 +156,9 @@
     <string name="httpErrorOk" msgid="6206751415788256357">"تأیید"</string>
     <string name="httpError" msgid="3406003584150566720">"خطایی در شبکه وجود داشت."</string>
     <string name="httpErrorLookup" msgid="3099834738227549349">"نشانی اینترنتی پیدا نشد."</string>
-    <string name="httpErrorUnsupportedAuthScheme" msgid="3976195595501606787">"‏طرح کلی احراز هویت سایت پشتیبانی نمی‌‎شود."</string>
+    <string name="httpErrorUnsupportedAuthScheme" msgid="3976195595501606787">"‏طرح کلی اصالت‌سنجی سایت پشتیبانی نمی‌‎شود."</string>
     <string name="httpErrorAuth" msgid="469553140922938968">"راستی‌آزمایی ناموفق بود."</string>
-    <string name="httpErrorProxyAuth" msgid="7229662162030113406">"احراز هویت از طریق سرور پروکسی انجام نشد."</string>
+    <string name="httpErrorProxyAuth" msgid="7229662162030113406">"اصالت‌سنجی از طریق سرور پروکسی انجام نشد."</string>
     <string name="httpErrorConnect" msgid="3295081579893205617">"اتصال به سرور انجام نشد."</string>
     <string name="httpErrorIO" msgid="3860318696166314490">"برقراری ارتباط با سرور ممکن نبود. بعداً دوباره امتحان کنید."</string>
     <string name="httpErrorTimeout" msgid="7446272815190334204">"زمان اتصال به سرور تمام شده است."</string>
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"تغییر تنظیمات صوتی"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"به برنامه امکان می‌دهد تنظیمات صوتی کلی مانند میزان صدا و بلندگوی مورد استفاده برای پخش صدا را تغییر دهد."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"ضبط صدا"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"این برنامه وقتی درحال استفاده است، می‌تواند بااستفاده از میکروفون صدا ضبط کند."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"ضبط صدا در پس‌زمینه"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"این برنامه می‌تواند در هرزمانی با استفاده از میکروفون صدا ضبط کند."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"ارسال فرمان به سیم کارت"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"به برنامه اجازه ارسال دستورات به سیم کارت را می‌دهد. این بسیار خطرناک است."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"تشخیص فعالیت فیزیکی"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"این برنامه نمی‌تواند فعالیت فیزیکی‌تان را تشخیص دهد."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"عکسبرداری و فیلمبرداری"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"این برنامه وقتی درحال استفاده است، می‌تواند بااستفاده از دوربین عکس و فیلم بگیرد."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"گرفتن عکس و فیلم در پس‌زمینه"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"این برنامه می‌تواند در هرزمانی با استفاده از دوربین عکس و فیلم بگیرد."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"به برنامه یا سرویسی اجازه دهید برای عکس‌برداری و فیلم‌برداری به دوربین‌های سیستم دسترسی داشته باشد"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"‏این برنامه سیستم یا دارای امتیاز، می‌تواند با استفاده از دوربین سیستم در هرزمانی عکس‌برداری و فیلم‌برداری کند. برنامه به اجازه android.permission.CAMERA هم نیاز دارد."</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"مجاز کردن برنامه یا سرویس برای دریافت پاسخ تماس درباره دستگاه‌های دوربینی که باز یا بسته می‌شوند."</string>
@@ -535,11 +529,11 @@
     <string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"درخواست پیچیدگی قفل صفحه"</string>
     <string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"به برنامه اجازه می‌دهد سطح پیچیدگی قفل صفحه (بالا، متوسط، پایین، یا هیچ‌کدام) را بیاموزد که نشان‌دهنده بازه ممکن طول و نوع قفل صفحه است. همچنین برنامه می‌تواند به کاربران پیشنهاد دهد قفل صفحه را به سطح خاصی به‌روزرسانی کنند، اما کاربران می‌توانند آزادانه این پیشنهاد را نادیده بگیرند و به سطح دیگری بروند. توجه داشته باشید که قفل صفحه در قالب نوشتار ساده ذخیره نمی‌شود، بنابراین برنامه گذرواژه دقیق را نمی‌داند."</string>
     <string name="permlab_useBiometric" msgid="6314741124749633786">"استفاده از سخت‌افزار بیومتریک"</string>
-    <string name="permdesc_useBiometric" msgid="7502858732677143410">"به برنامه امکان می‌دهد از سخت‌افزار بیومتریک برای احراز هویت استفاده کند"</string>
+    <string name="permdesc_useBiometric" msgid="7502858732677143410">"به برنامه امکان می‌دهد از سخت‌افزار بیومتریک برای اصالت‌سنجی استفاده کند"</string>
     <string name="permlab_manageFingerprint" msgid="7432667156322821178">"مدیریت سخت‌افزار اثر انگشت"</string>
     <string name="permdesc_manageFingerprint" msgid="2025616816437339865">"به برنامه امکان می‌دهد روش‌هایی را برای افزودن و حذف الگوهای اثر انگشت جهت استفاده، فعال کند."</string>
     <string name="permlab_useFingerprint" msgid="1001421069766751922">"استفاده از سخت‌افزار اثر انگشت"</string>
-    <string name="permdesc_useFingerprint" msgid="412463055059323742">"به برنامه امکان می‌دهد از سخت‌افزار اثر انگشت برای احراز هویت استفاده کند"</string>
+    <string name="permdesc_useFingerprint" msgid="412463055059323742">"به برنامه امکان می‌دهد از سخت‌افزار اثر انگشت برای اصالت‌سنجی استفاده کند"</string>
     <string name="permlab_audioWrite" msgid="8501705294265669405">"تغییر مجموعه موسیقی شما"</string>
     <string name="permdesc_audioWrite" msgid="8057399517013412431">"به برنامه اجازه می‌دهد مجموعه موسیقی‌تان را تغییر دهد."</string>
     <string name="permlab_videoWrite" msgid="5940738769586451318">"تغییر مجموعه ویدیوی شما"</string>
@@ -550,11 +544,11 @@
     <string name="permdesc_mediaLocation" msgid="597912899423578138">"به برنامه اجازه می‌دهد مکان‌ها را از مجموعه رسانه‌تان بخواند."</string>
     <string name="biometric_dialog_default_title" msgid="55026799173208210">"تأیید کنید این شما هستید"</string>
     <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"سخت‌افزار زیست‌سنجی دردسترس نیست"</string>
-    <string name="biometric_error_user_canceled" msgid="6732303949695293730">"احراز هویت لغو شد"</string>
+    <string name="biometric_error_user_canceled" msgid="6732303949695293730">"اصالت‌سنجی لغو شد"</string>
     <string name="biometric_not_recognized" msgid="5106687642694635888">"شناسایی نشد"</string>
-    <string name="biometric_error_canceled" msgid="8266582404844179778">"احراز هویت لغو شد"</string>
+    <string name="biometric_error_canceled" msgid="8266582404844179778">"اصالت‌سنجی لغو شد"</string>
     <string name="biometric_error_device_not_secured" msgid="3129845065043995924">"پین، الگو یا گذرواژه‌ای تنظیم نشده است"</string>
-    <string name="biometric_error_generic" msgid="6784371929985434439">"خطا هنگام احراز هویت"</string>
+    <string name="biometric_error_generic" msgid="6784371929985434439">"خطا هنگام اصالت‌سنجی"</string>
     <string name="fingerprint_acquired_partial" msgid="8532380671091299342">"بخشی از اثر انگشت شناسایی شد. لطفاً دوباره امتحان کنید."</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"اثرانگشت پردازش نشد. لطفاً دوباره امتحان کنید."</string>
     <string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"حسگر اثر انگشت کثیف است. لطفاً آن را تمیز کنید و دوباره امتحان نمایید."</string>
@@ -562,9 +556,9 @@
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"حرکت انگشت خیلی آهسته بود. لطفاً دوباره امتحان کنید."</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <string name="fingerprint_authenticated" msgid="2024862866860283100">"اثر انگشت احراز هویت شد"</string>
-    <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"چهره احراز هویت شد"</string>
-    <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"چهره احراز هویت شد، لطفاً تأیید را فشار دهید"</string>
+    <string name="fingerprint_authenticated" msgid="2024862866860283100">"اثر انگشت اصالت‌سنجی شد"</string>
+    <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"چهره اصالت‌سنجی شد"</string>
+    <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"چهره اصالت‌سنجی شد، لطفاً تأیید را فشار دهید"</string>
     <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"سخت‌افزار اثرانگشت در دسترس نیست."</string>
     <string name="fingerprint_error_no_space" msgid="6126456006769817485">"ذخیره اثر انگشت ممکن نیست. لطفاً یک اثر انگشت موجود را حذف کنید."</string>
     <string name="fingerprint_error_timeout" msgid="2946635815726054226">"درنگ ثبت اثر انگشت به پایان رسید. دوباره امتحان کنید."</string>
@@ -583,7 +577,7 @@
     <string name="permlab_manageFace" msgid="4569549381889283282">"مدیریت سخت‌افزار «بازگشایی با چهره»"</string>
     <string name="permdesc_manageFace" msgid="6204569688492710471">"به برنامه امکان می‌دهد روش‌هایی را برای افزودن و حذف الگوهای چهره جهت استفاده فرابخواند."</string>
     <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"استفاده از سخت‌افزار «بازگشایی با چهره»"</string>
-    <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"به برنامه امکان می‌دهد از سخت‌افزار «بازگشایی با چهره» برای احراز هویت استفاده کند"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"به برنامه امکان می‌دهد از سخت‌افزار «بازگشایی با چهره» برای اصالت‌سنجی استفاده کند"</string>
     <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"بازگشایی با چهره"</string>
     <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"ثبت مجدد چهره"</string>
     <string name="face_recalibrate_notification_content" msgid="892757485125249962">"برای بهبود تشخیص، لطفاً چهره‌تان را دوباره ثبت کنید"</string>
@@ -1664,11 +1658,11 @@
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"کلیدهای میزان صدا پایین نگه داشته شد. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> خاموش شد."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"برای استفاده از <xliff:g id="SERVICE_NAME">%1$s</xliff:g>، هر دو کلید صدا را فشار دهید و سه ثانیه نگه دارید"</string>
     <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"ویژگی را انتخاب کنید که هنگام ضربه زدن روی دکمه دسترس‌پذیری استفاده می‌شود:"</string>
-    <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"ویژگی را برای استفاده با اشاره دسترس‌پذیری انتخاب کنید (با دو انگشت صفحه را از پایین تند به بالا بکشید):"</string>
-    <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"ویزگی را برای استفاده با اشاره دسترس‌پذیری انتخاب کنید (با سه انگشت صفحه را از پایین تند به بالا بکشید):"</string>
+    <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"ویژگی را برای استفاده با اشاره دسترس‌پذیری انتخاب کنید (با دو انگشت صفحه را از پایین تند به‌بالا بکشید):"</string>
+    <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"ویزگی را برای استفاده با اشاره دسترس‌پذیری انتخاب کنید (با سه انگشت صفحه را از پایین تند به‌بالا بکشید):"</string>
     <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"برای جابه‌جایی بین ویژگی‌ها، دکمه دسترس‌پذیری را لمس کنید و نگه دارید."</string>
-    <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"برای جابه‌جایی بین ویژگی‌ها، با دو انگشت صفحه را تند به بالا بکشید و نگه دارید."</string>
-    <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"برای جابه‌جایی بین ویژگی‌ها، با سه انگشت صفحه را تند به بالا بکشید و نگه دارید."</string>
+    <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"برای جابه‌جایی بین ویژگی‌ها، با دو انگشت صفحه را تند به‌بالا بکشید و نگه دارید."</string>
+    <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"برای جابه‌جایی بین ویژگی‌ها، با سه انگشت صفحه را تند به‌بالا بکشید و نگه دارید."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"درشت‌نمایی"</string>
     <string name="user_switched" msgid="7249833311585228097">"کاربر کنونی <xliff:g id="NAME">%1$s</xliff:g>."</string>
     <string name="user_switching_message" msgid="1912993630661332336">"در حالت تغییر به <xliff:g id="NAME">%1$s</xliff:g>…"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 616223b..e7bcf48 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -304,7 +304,7 @@
     <string name="permgrouplab_sms" msgid="795737735126084874">"Tekstiviestit"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"lähettää ja tarkastella tekstiviestejä"</string>
     <string name="permgrouplab_storage" msgid="1938416135375282333">"Tiedostot ja media"</string>
-    <string name="permgroupdesc_storage" msgid="6351503740613026600">"käyttää laitteellesi tallennettuja valokuvia, mediatiedostoja ja muita tiedostoja"</string>
+    <string name="permgroupdesc_storage" msgid="6351503740613026600">"käyttää laitteellesi tallennettuja kuvia, mediatiedostoja ja muita tiedostoja"</string>
     <string name="permgrouplab_microphone" msgid="2480597427667420076">"Mikrofoni"</string>
     <string name="permgroupdesc_microphone" msgid="1047786732792487722">"tallentaa ääntä"</string>
     <string name="permgrouplab_activityRecognition" msgid="3324466667921775766">"Liikkuminen"</string>
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"muuta ääniasetuksia"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Antaa sovelluksen muokata yleisiä ääniasetuksia, kuten äänenvoimakkuutta ja käytettävää kaiutinta."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"tallentaa ääntä"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Tämä sovellus voi tallentaa mikrofonilla audiota, kun sovellusta käytetään."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"tallentaa audiota taustalla"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Tämä sovellus voi tallentaa mikrofonilla audiota koska tahansa."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"lähettää komentoja SIM-kortille"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Antaa sovelluksen lähettää komentoja SIM-kortille. Tämä ei ole turvallista."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"tunnistaa liikkumisen"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Sovellus voi tunnistaa liikkumisesi."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"ota kuvia ja videoita"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Tämä sovellus voi ottaa kameralla kuvia ja videoita, kun sovellusta käytetään."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"ottaa kuvia ja videoita taustalla"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Tämä sovellus voi ottaa kameralla kuvia ja videoita koska tahansa."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Salli sovellukselle tai palvelulle pääsy järjestelmän kameroihin, jotta se voi ottaa kuvia ja nauhoittaa videoita"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Tämä oikeutettu tai järjestelmäsovellus voi ottaa järjestelmän kameralla kuvia ja videoita koska tahansa. Sovelluksella on oltava myös android.permission.CAMERA-lupa"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Salli sovelluksen tai palvelun vastaanottaa vastakutsuja kameralaitteiden avaamisesta tai sulkemisesta."</string>
@@ -1925,7 +1919,7 @@
     <string name="app_category_game" msgid="4534216074910244790">"Pelit"</string>
     <string name="app_category_audio" msgid="8296029904794676222">"Musiikki ja ääni"</string>
     <string name="app_category_video" msgid="2590183854839565814">"Elokuvat ja videot"</string>
-    <string name="app_category_image" msgid="7307840291864213007">"Kuvat ja valokuvat"</string>
+    <string name="app_category_image" msgid="7307840291864213007">"Kuvat ja kuvat"</string>
     <string name="app_category_social" msgid="2278269325488344054">"Some ja viestintä"</string>
     <string name="app_category_news" msgid="1172762719574964544">"Uutiset ja lehdet"</string>
     <string name="app_category_maps" msgid="6395725487922533156">"Kartat ja navigointi"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 6111270..aad5749 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"modifier vos paramètres audio"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Permet à l\'application de modifier les paramètres audio généraux, tels que le volume et la sortie audio utilisée."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"enregistrer des fichiers audio"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Cette application peut enregistrer de l\'audio à l\'aide du microphone lorsque vous utilisez l\'application."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"enregistrer de l\'audio en arrière-plan"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Cette application peut enregistrer de l\'audio à l\'aide du microphone en tout temps."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"envoyer des commandes à la carte SIM"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Permet à l\'application d\'envoyer des commandes à la carte SIM. Cette fonctionnalité est très dangereuse."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"reconnaître les activités physiques"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Cette application peut reconnaître vos activités physiques."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"prendre des photos et filmer des vidéos"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Cette application peut prendre des photos et enregistrer des vidéos à l\'aide de l\'appareil photo lorsque vous utilisez l\'application."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"prendre des photos et enregistrer des vidéos en arrière-plan"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Cette application peut prendre des photos et enregistrer des vidéos à l\'aide de l\'appareil photo en tout temps."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Autoriser une application ou un service à accéder aux appareils photo système pour prendre des photos et filmer des vidéos"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Cette application privilégiée ou système peut prendre des photos ou filmer des vidéos à l\'aide d\'un appareil photo système en tout temps. L\'application doit également posséder l\'autorisation android.permission.CAMERA"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Autoriser une application ou un service de recevoir des rappels relatifs à l\'ouverture ou à la fermeture des appareils photos."</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index fa991a9..d2bdbbb 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -327,7 +327,7 @@
     <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Contrôle le niveau de zoom et le positionnement de l\'écran."</string>
     <string name="capability_title_canPerformGestures" msgid="9106545062106728987">"Effectuer des gestes"</string>
     <string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"Permet d\'appuyer sur l\'écran, de le balayer, de le pincer et d\'effectuer d\'autres gestes."</string>
-    <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"Gestes avec l\'empreinte digitale"</string>
+    <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"Gestes d\'empreinte digitale"</string>
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Peut enregistrer des gestes effectués sur le lecteur d\'empreinte digitale de l\'appareil."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Prendre une capture d\'écran"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Peut prendre des captures d\'écran."</string>
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"modifier vos paramètres audio"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Permet à l\'application de modifier les paramètres audio généraux, tels que le volume et la sortie audio utilisée."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"enregistrer des fichiers audio"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Cette application peut utiliser le micro pour réaliser des enregistrements audio quand elle est en cours d\'utilisation."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"réaliser des enregistrements audio en arrière-plan"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Cette application peut utiliser le micro pour réaliser des enregistrements audio à tout moment."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"envoyer des commandes à la carte SIM"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Autoriser l\'envoi de commandes à la carte SIM via l\'application. Cette fonctionnalité est très risquée."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"reconnaître l\'activité physique"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Cette application peut reconnaître votre activité physique."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"prendre des photos et enregistrer des vidéos"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Cette application peut utiliser l\'appareil photo pour prendre des photos et enregistrer des vidéos quand elle est en cours d\'utilisation."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"prendre des photos et enregistrer des vidéos en arrière-plan"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Cette application peut utiliser l\'appareil photo pour prendre des photos et enregistrer des vidéos à tout moment."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Autoriser une application ou un service à accéder aux caméras système pour prendre des photos et enregistrer des vidéos"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Cette application privilégiée ou système peut utiliser une caméra photo système pour prendre des photos et enregistrer des vidéos à tout moment. Pour cela, l\'application doit également disposer de l\'autorisation android.permission.CAMERA"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Autoriser une application ou un service à recevoir des rappels liés à l\'ouverture ou à la fermeture de caméras"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 115850a..79380d5 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"cambiar a configuración de son"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Permite á aplicación modificar a configuración de audio global, como o volume e que altofalante se utiliza para a saída."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"gravar audio"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Mentres a empregas, esta aplicación pode utilizar o micrófono para gravar audio."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"gravar audio en segundo plano"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Esta aplicación pode utilizar o micrófono en calquera momento para gravar audio."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"enviar comandos á SIM"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Permite á aplicación enviar comandos á SIM. Isto é moi perigoso."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"recoñecer actividade física"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Esta aplicación pode recoñecer a túa actividade física."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"facer fotos e vídeos"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Mentres a empregas, esta aplicación pode utilizar a cámara para sacar fotos e gravar vídeos."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"sacar fotos e gravar vídeos en segundo plano"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Esta aplicación pode utilizar a cámara en calquera momento para sacar fotos e gravar vídeos."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Permitir que unha aplicación ou un servizo acceda ás cámaras do sistema para sacar fotos e gravar vídeos"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Esta aplicación do sistema con privilexios pode utilizar unha cámara do sistema en calquera momento para tirar fotos e gravar vídeos. Require que a aplicación tamén teña o permiso android.permission.CAMERA"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Permitir que unha aplicación ou servizo reciba retrochamadas cando se abran ou se pechen dispositivos con cámara."</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 918a6ff..bf4d6c0 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"તમારી ઑડિઓ સેટિંગ્સ બદલો"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"એપ્લિકેશનને વૈશ્વિક ઑડિઓ સેટિંગ્સને સંશોધિત કરવાની મંજૂરી આપે છે, જેમ કે વૉલ્યૂમ અને આઉટપુટ માટે કયા સ્પીકરનો ઉપયોગ કરવો."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"ઑડિઓ રેકોર્ડ કરવાની"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"આ ઍપ ઉપયોગમાં હોય ત્યારે તે માઇક્રોફોનનો ઉપયોગ કરીને ઑડિયો રેકોર્ડ કરી શકે છે."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"બૅકગ્રાઉન્ડમાં ઑડિયો રેકોર્ડ કરો"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"આ ઍપ, માઇક્રોફોનનો ઉપયોગ કરીને કોઈપણ સમયે ઑડિયો રેકોર્ડ કરી શકે છે."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"સિમ ને આદેશો મોકલો"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"એપ્લિકેશનને સિમ પરા આદેશો મોકલવાની મંજૂરી આપે છે. આ ખૂબ જ ખતરનાક છે."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"શારીરિક પ્રવૃત્તિને ઓળખો"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"આ ઍપ તમારી શારીરિક પ્રવૃત્તિને ઓળખી શકે છે."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"ચિત્રો અને વિડિઓઝ લો"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"આ ઍપ ઉપયોગમાં હોય ત્યારે તે કૅમેરાનો ઉપયોગ કરીને ફોટા લઈ શકે છે અને વીડિયો રેકોર્ડ કરી શકે છે."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"બૅકગ્રાઉન્ડમાં ફોટા અને વીડિયો લો"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"આ ઍપ, કૅમેરાનો ઉપયોગ કરીને કોઈપણ સમયે ફોટા લઈ શકે છે અને વીડિયો રેકોર્ડ કરી શકે છે."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"ઍપ્લિકેશન અથવા સેવા ઍક્સેસને સિસ્ટમ કૅમેરાનો ઉપયોગ કરીને ફોટા અને વીડિયો લેવાની મંજૂરી આપો"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"આ વિશેષાધિકૃત અથવા સિસ્ટમ ઍપ કોઈપણ સમયે સિસ્ટમ કૅમેરાનો ઉપયોગ કરીને ફોટા લઈ અને વીડિયો રેકૉર્ડ કરી શકે છે. ઍપ દ્વારા આયોજિત કરવા માટે android.permission.CAMERAની પરવાનગી પણ જરૂરી છે"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"કૅમેરા ડિવાઇસ ચાલુ કે બંધ થવા વિશે કૉલબૅક પ્રાપ્ત કરવાની ઍપ્લિકેશન કે સેવાને મંજૂરી આપો."</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index db049b1..b867e9d 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"अपनी ऑडियो सेटिंग बदलें"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"ऐप्स  को वैश्विक ऑडियो सेटिंग, जैसे वॉल्‍यूम और कौन-सा स्पीकर आउटपुट के लिए उपयोग किया गया, संशोधित करने देता है."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"ऑडियो रिकॉर्ड करने"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"जब इस ऐप्लिकेशन का इस्तेमाल किया जा रहा हो, तब यह माइक्रोफ़ोन का इस्तेमाल करके ऑडियो रिकॉर्ड कर सकता है."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"ऐप्लिकेशन बैकग्राउंड में ऑडियो रिकॉर्ड कर सकता है"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"यह ऐप्लिकेशन जब चाहे, माइक्रोफ़ोन का इस्तेमाल करके ऑडियो रिकॉर्ड कर सकता है."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"सिम पर निर्देश भेजें"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"ऐप को सिम पर निर्देश भेजने देती है. यह बहुत ही खतरनाक है."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"शरीर की गतिविधि को पहचानना"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"यह ऐप्लिकेशन आपके शरीर की गतिविधि को पहचान सकता है."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"चित्र और वीडियो लें"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"जब इस ऐप्लिकेशन का इस्तेमाल किया जा रहा हो, तब यह कैमरे का इस्तेमाल करके, तस्वीरें ले सकता है और वीडियो रिकॉर्ड कर सकता है."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"ऐप्लिकेशन बैकग्राउंड में तस्वीरें ले सकता है और वीडियो रिकॉर्ड कर सकता है"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"यह ऐप्लिकेशन जब चाहे, कैमरे का इस्तेमाल करके तस्वीरें ले सकता है और वीडियो रिकॉर्ड कर सकता है."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"तस्वीरें और वीडियो लेने के लिए ऐप्लिकेशन या सेवा को सिस्टम के कैमरे का ऐक्सेस दें"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"यह खास सिस्टम ऐप्लिकेशन जब चाहे, तस्वीरें लेने और वीडियो रिकॉर्ड करने के लिए सिस्टम के कैमरे का इस्तेमाल कर सकता है. इसके लिए ऐप्लिकेशन को android.permission.CAMERA की अनुमति देना भी ज़रूरी है"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"डिवाइस का कैमरे चालू या बंद होने पर, किसी ऐप्लिकेशन या सेवा को कॉलबैक पाने की मंज़ूरी दें."</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index b9b3665..dd2bdbe 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -435,23 +435,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"promjena postavki zvuka"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Aplikaciji omogućuje izmjenu globalnih postavki zvuka, primjerice glasnoće i zvučnika koji se upotrebljava za izlaz."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"snimanje zvuka"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Aplikacija može snimati audiozapise pomoću mikrofona dok se upotrebljava."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"snimati audiozapise u pozadini"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Aplikacija može snimati audiozapise pomoću mikrofona u svakom trenutku."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"slati naredbe SIM-u"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Omogućuje aplikaciji slanje naredbi SIM-u. To je vrlo opasno."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"prepoznati tjelesnu aktivnost"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Ova aplikacija može prepoznati vašu tjelesnu aktivnost."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"snimi fotografije i videozapise"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Aplikacija može snimati fotografije i videozapise pomoću kamere dok se upotrebljava."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"snimati fotografije i videozapise u pozadini"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Aplikacija može snimati fotografije i videozapise pomoću kamere u svakom trenutku."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Dopustite aplikaciji ili usluzi da pristupa kamerama sustava radi snimanja fotografija i videozapisa"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Ova povlaštena aplikacija ili aplikacija sustava u svakom trenutku može snimati fotografije i videozapise kamerom sustava. Aplikacija mora imati i dopuštenje android.permission.CAMERA"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Dopustite aplikaciji ili usluzi da prima povratne pozive o otvaranju ili zatvaranju fotoaparata."</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 16d60dc..9d1c2f4 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"hangbeállítások módosítása"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Lehetővé teszi az alkalmazás számára az általános hangbeállítások, például a hangerő és a használni kívánt kimeneti hangszóró módosítását."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"hanganyag rögzítése"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Az alkalmazás a mikrofon használatával akkor készíthet hangfelvételt, amikor az alkalmazás használatban van."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"hangfelvétel készítése a háttérben"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Az alkalmazás a mikrofon használatával bármikor készíthet hangfelvételt."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"parancsok küldése a SIM-kártyára"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Engedélyezi, hogy az alkalmazás parancsokat küldjön a SIM kártyára. Ez rendkívül veszélyes lehet."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"testmozgás felismerése"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Az alkalmazás képes felismerni a testmozgást."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"fotók és videók készítése"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Az alkalmazás a kamera használatával akkor készíthet fényképeket és videókat, amikor az alkalmazás használatban van."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"fényképek és videók készítése a háttérben"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Az alkalmazás a kamera használatával bármikor készíthet fényképeket és videókat."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"A rendszerkamerákhoz való hozzáférés, illetve képek és videók rögzítésének engedélyezése alkalmazás vagy szolgáltatás számára"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"A rendszerkamera használatával ez az előnyben részesített vagy rendszeralkalmazás bármikor készíthet fényképeket és videókat. Az alkalmazásnak az „android.permission.CAMERA” engedéllyel is rendelkeznie kell."</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Visszahívás fogadásának engedélyezése alkalmazás vagy szolgáltatás számára, ha a kamerákat megnyitják vagy bezárják."</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index aa4197f..6fade98 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"փոխել ձեր աուդիո կարգավորումները"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Թույլ է տալիս հավելվածին փոփոխել ձայնանյութի գլոբալ կարգավորումները, ինչպես օրինակ՝ ձայնը և թե որ խոսափողն է օգտագործված արտածման համար:"</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"ձայնագրել աուդիո ֆայլ"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Այս հավելվածը կարող է օգտագործել խոսափողը՝ ձայնագրություններ անելու համար, միայն երբ հավելվածն ակտիվ է։"</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"ձայնագրել ֆոնային ռեժիմում"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Այս հավելվածը կարող է ցանկացած ժամանակ օգտագործել խոսափողը՝ ձայնագրություններ անելու համար։"</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"ուղարկել հրամաններ SIM քարտին"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Թույլ է տալիս հավելվածին հրամաններ ուղարկել SIM-ին: Սա շատ վտանգավոր է:"</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"ֆիզիկական ակտիվության ճանաչում"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Հավելվածը կարող է ճանաչել ձեր ֆիզիկական ակտիվությունը:"</string>
     <string name="permlab_camera" msgid="6320282492904119413">"լուսանկարել և տեսանկարել"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Այս հավելվածը կարող է օգտագործել տեսախցիկը՝ լուսանկարելու և տեսագրելու համար, միայն երբ հավելվածն ակտիվ է։"</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"լուսանկարել և տեսագրել ֆոնային ռեժիմում"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Այս հավելվածը կարող է ցանկացած ժամանակ օգտագործել տեսախցիկը՝ լուսանկարելու և տեսագրելու համար։"</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Թույլատրել որևէ հավելվածի կամ ծառայության օգտագործել համակարգի տեսախցիկները՝ լուսանկարելու և տեսանկարելու համար"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Այս արտոնյալ կամ համակարգային հավելվածը կարող է ցանկացած պահի լուսանկարել և տեսագրել՝ օգտագործելով համակարգի տեսախցիկները։ Հավելվածին նաև անհրաժեշտ է android.permission.CAMERA թույլտվությունը։"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Թույլատրել հավելվածին կամ ծառայությանը հետզանգեր ստանալ՝ տեսախցիկների բացվելու և փակվելու դեպքում։"</string>
@@ -1918,7 +1912,7 @@
     <string name="app_info" msgid="6113278084877079851">"Հավելվածի մասին"</string>
     <string name="negative_duration" msgid="1938335096972945232">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="demo_starting_message" msgid="6577581216125805905">"Ցուցադրական օգտատերը գործարկվում է…"</string>
-    <string name="demo_restarting_message" msgid="1160053183701746766">"Սարաքը վերակայվում է…"</string>
+    <string name="demo_restarting_message" msgid="1160053183701746766">"Սարքը վերակայվում է…"</string>
     <string name="suspended_widget_accessibility" msgid="6331451091851326101">"Անջատած <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="5731633152336490471">"Կոնֆերանս զանգ"</string>
     <string name="tooltip_popup_title" msgid="7863719020269945722">"Հուշակ"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 3e6c23e..202664f 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"ubah setelan audio Anda"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Memungkinkan aplikasi mengubah setelan audio global, misalnya volume dan pengeras suara mana yang digunakan untuk keluaran."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"rekam audio"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Aplikasi ini dapat merekam audio menggunakan mikrofon saat aplikasi sedang digunakan."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"merekam audio di latar belakang"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Aplikasi ini dapat merekam audio menggunakan mikrofon kapan saja."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"kirimkan perintah ke SIM"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Mengizinkan aplikasi mengirim perintah ke SIM. Ini sangat berbahaya."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"kenali aktivitas fisik"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Aplikasi ini dapat mengenali aktivitas fisik Anda."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"ambil gambar dan video"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Aplikasi ini dapat mengambil gambar dan merekam video menggunakan kamera saat aplikasi sedang digunakan."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"mengambil gambar dan merekam video di latar belakang"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Aplikasi ini dapat mengambil gambar dan merekam video menggunakan kamera kapan saja."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Izinkan akses aplikasi atau layanan ke kamera sistem untuk mengambil gambar dan video"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Aplikasi sistem atau yang diberi hak istimewa ini dapat mengambil gambar dan merekam video menggunakan kamera sistem kapan saja. Mewajibkan aplikasi untuk memiliki izin android.permission.CAMERA"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Izinkan aplikasi atau layanan untuk menerima callback tentang perangkat kamera yang sedang dibuka atau ditutup."</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 272145d..efb23d4 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"breyta hljóðstillingum"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Leyfir forriti að breyta altækum hljóðstillingum, s.s. hljóðstyrk og hvaða hátalari er notaður sem úttak."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"taka upp hljóð"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Þetta forrit getur tekið upp hljóð með hljóðnemanum meðan forritið er í notkun."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"taka upp hljóð í bakgrunni"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Þetta forrit getur tekið upp hljóð með hljóðnemanum hvenær sem er."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"senda skipanir til SIM-kortsins"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Leyfir forriti að senda SIM-kortinu skipanir. Þetta er mjög hættulegt."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"greina hreyfingu"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Þetta forrit getur greint hreyfingu þína."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"taka myndir og myndskeið"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Þetta forrit getur tekið myndir og tekið upp myndskeið með myndavélinni þegar forritið er í notkun."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"taka myndir og taka upp myndskeið í bakgrunni"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Þetta forrit getur tekið myndir og tekið upp myndskeið með myndavélinni hvenær sem er."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Veittu forriti eða þjónustu aðgang að myndavélum kerfis til að taka myndir og myndskeið"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Þetta forgangs- eða kerfisforrit hefur heimild til að taka myndir og taka upp myndskeið með myndavél kerfisins hvenær sem er. Forritið þarf einnig að vera með heimildina android.permission.CAMERA"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Leyfa forriti eða þjónustu að taka við svörum um myndavélar sem verið er að opna eða loka."</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 9970915..ec446d7 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"modifica impostazioni audio"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Consente all\'applicazione di modificare le impostazioni audio globali, come il volume e quale altoparlante viene utilizzato per l\'uscita."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"registrazione audio"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Questa app può registrare audio tramite il microfono mentre l\'app è in uso."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"Registrazione di audio in background"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Questa app può registrare audio tramite il microfono in qualsiasi momento."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"invio di comandi alla SIM"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Consente all\'app di inviare comandi alla SIM. Questo è molto pericoloso."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"riconoscimento dell\'attività fisica"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Questa app può riconoscere la tua attività fisica."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"acquisizione di foto e video"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Questa app può scattare foto e registrare video tramite la fotocamera mentre l\'app è in uso."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"Acquisizione di foto e video in background"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Questa app può scattare foto e registrare video tramite la fotocamera in qualsiasi momento."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Consenti a un\'applicazione o a un servizio di accedere alle videocamere del sistema per fare foto e video"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Questa app di sistema o con privilegi può scattare foto e registrare video tramite una videocamera di sistema in qualsiasi momento. Richiede che anche l\'app disponga dell\'autorizzazione android.permission.CAMERA"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Consenti a un\'applicazione o a un servizio di ricevere callback relativi all\'apertura o alla chiusura di videocamere."</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index e8413d6..874d577 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -438,23 +438,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"שנה את הגדרות האודיו שלך"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"מאפשר לאפליקציה לשנות הגדרות אודיו גלובליות כמו עוצמת קול ובחירת הרמקול המשמש לפלט."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"הקלט אודיו"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"האפליקציה הזו יכולה להשתמש במיקרופון כדי להקליט אודיו כאשר היא בשימוש."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"הקלטת אודיו ברקע"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"האפליקציה הזו יכולה להשתמש במיקרופון כדי להקליט אודיו בכל זמן."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"‏שליחת פקודות אל ה-SIM"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"‏מאפשרת ליישום לשלוח פקודות ל-SIM. זוהי הרשאה מסוכנת מאוד."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"זיהוי הפעילות הגופנית"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"האפליקציה מזהה את הפעילות הגופנית שלך."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"צלם תמונות וסרטונים"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"האפליקציה הזו יכולה להשתמש במצלמה כדי לצלם תמונות ולהקליט סרטונים כאשר היא בשימוש."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"צילום תמונות וסרטונים ברקע"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"האפליקציה הזו יכולה להשתמש במצלמה כדי לצלם תמונות ולהקליט סרטונים בכל זמן."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"הרשאת גישה לאפליקציה או לשירות למצלמות המערכת כדי לצלם תמונות וסרטונים"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"‏אפליקציה זו בעלת ההרשאות, או אפליקציית המערכת הזו, יכולה לצלם תמונות ולהקליט סרטונים באמצעות מצלמת מערכת בכל זמן. בנוסף, לאפליקציה נדרשת ההרשאה android.permission.CAMERA"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"‏אפליקציה או שירות יוכלו לקבל קריאות חוזרות (callback) כשמכשירי מצלמה ייפתחו או ייסגרו."</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 012ee3f..32a4ded 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"音声設定の変更"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"音声全般の設定(音量、出力に使用するスピーカーなど)の変更をアプリに許可します。"</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"録音"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"このアプリは、ユーザーがアプリを使用している場合にマイクを使用して音声を録音できます。"</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"バックグラウンドでの音声の録音"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"このアプリは、いつでもマイクを使用して音声を録音できます。"</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"SIMへのコマンド送信"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"SIMにコマンドを送信することをアプリに許可します。この許可は非常に危険です。"</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"身体活動の認識"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"このアプリで身体活動が認識されるようにします。"</string>
     <string name="permlab_camera" msgid="6320282492904119413">"写真と動画の撮影"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"このアプリは、ユーザーがアプリを使用している場合にカメラを使用して写真や動画を撮影できます。"</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"バックグラウンドでの写真と動画の撮影"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"このアプリは、いつでもカメラを使用して写真や動画を撮影できます。"</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"写真と動画を撮影するには、システムカメラへのアクセスをアプリまたはサービスに許可してください"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"権限を付与されたこのアプリまたはシステムアプリは、いつでもシステムカメラを使用して写真と動画を撮影できます。アプリには android.permission.CAMERA 権限も必要です"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"カメラデバイスが起動または終了したときにコールバックを受け取ることを、アプリまたはサービスに許可してください。"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index f6e8f5d..349bd16 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"თქვენი აუდიო პარამეტრების შეცვლა"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"აპს შეეძლება აუდიოს გლობალური პარამეტრების შეცვლა. მაგ.: ხმის სიმაღლე და რომელი დინამიკი გამოიყენება სიგნალის გამოსტანად."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"აუდიოს ჩაწერა"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"ამ აპს გამოყენების დროს შეუძლია მიკროფონით აუდიოს ჩაწერა."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"ფონურად ჩაწერს აუდიოს"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"ამ აპს ნებისმიერ დროს შეუძლია მიკროფონით აუდიოს ჩაწერა."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"ბრძანებების SIM-ზე გაგზავნა"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"აპისთვის ნების დართვა გაუგზავნოს ბრძანებები SIM-ბარათს. ეს ძალიან საშიშია."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"ფიზიკური აქტივობის ამოცნობა"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"ამ აპს შეუძლია თქვენი ფიზიკური აქტივობის ამოცნობა."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"სურათებისა და ვიდეოების გადაღება"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"ამ აპს გამოყენების დროს შეუძლია კამერით სურათების გადაღება და ვიდეოების ჩაწერა."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"ფონურად გადაიღებს სურათებს და ჩაწერს ვიდეოს"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"ამ აპს ნებისმიერ დროს შეუძლია კამერით სურათების გადაღება და ვიდეოების ჩაწერა."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"ნება დაერთოს აპლიკაციას ან სერვისს, ჰქონდეს წვდომა სისტემის კამერებზე სურათების და ვიდეოების გადასაღებად"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"ამ პრივილეგირებულ ან სისტემის აპს შეუძლია ფოტოების გადაღება და ვიდეოების ჩაწერა ნებისმიერ დროს სისტემის კამერის გამოყენებით. საჭიროა, რომ აპს ჰქოდეს android.permission.CAMERA ნებართვაც"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"ნება დაერთოს აპლიკაციას ან სერვისს, მიიღოს გადმორეკვები კამერის მოწყობილობის გახსნის ან დახურვისას."</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index b0d363c..4d4eef0 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"аудио параметрлерін өзгерту"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Қолданбаға дыбыс қаттылығы және аудио шығыс үндеткішін таңдау сияқты жаһандық аудио параметрлерін өзгерту мүмкіндігін береді."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"аудио жазу"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Бұл қолданба жұмыс барысында микрофон арқылы аудиомазмұн жаза алады."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"Фондық режимде аудиомазмұн жазу"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Бұл қолданба кез келген уақытта микрофон арқылы аудиомазмұн жаза алады."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"SIM картасына пәрмендер жіберу"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Қолданбаға SIM картасына пәрмен жіберу мүмкіндігін береді. Бұл өте қауіпті."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"физикалық әрекетті тану"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Бұл қолданба физикалық әрекетті тани алады."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"фотосурет жасау және бейне жазу"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Бұл қолданба жұмыс барысында камерамен суретке түсіріп, бейнелер жаза алады."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"Фондық режимде сурет пен бейне түсіру"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Бұл қолданба кез келген уақытта камерамен суретке түсіріп, бейнелер жаза алады."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Суретке немесе бейнеге түсіру үшін қолданбаға немесе қызметке жүйелік камераларды пайдалануға рұқсат беру"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Осы айрықша немесе жүйе қолданбасы кез келген уақытта жүйелік камера арқылы суретке не бейнеге түсіре алады. Қолданбаға android.permission.CAMERA рұқсаты қажет болады."</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Қолданбаға не қызметке ашылып не жабылып жатқан камера құрылғылары туралы кері шақыру алуға рұқсат ету"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index a16251d..3ebd736 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"ប្ដូរ​ការ​កំណត់​អូឌីយូ​របស់​អ្នក"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"ឲ្យ​កម្មវិធី​កែ​ការ​កំណត់​សំឡេង​សកល ដូច​ជា​កម្រិត​សំឡេង និង​អូប៉ាល័រ​ដែល​បាន​ប្រើ​សម្រាប់​លទ្ធផល។"</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"ថត​សំឡេង"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"កម្មវិធី​នេះ​អាច​ថតសំឡេងដោយប្រើមីក្រូហ្វូន នៅពេលកំពុងប្រើប្រាស់កម្មវិធី។"</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"ថតសំឡេងនៅផ្ទៃខាងក្រោយ"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"កម្មវិធី​នេះ​អាច​ថត​សំឡេង​ដោយ​ប្រើ​មីក្រូហ្វូន​បាន​គ្រប់​ពេល។"</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"ផ្ញើពាក្យបញ្ជាទៅស៊ីមកាត"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"ឲ្យ​កម្មវិធី​ផ្ញើ​ពាក្យ​បញ្ជា​ទៅ​ស៊ីម​កាត។ វា​គ្រោះ​ថ្នាក់​ណាស់។"</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"ស្គាល់​សកម្មភាព​រាងកាយ"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"កម្មវិធីនេះ​អាចស្គាល់សកម្មភាព​រាងកាយ​របស់អ្នក។"</string>
     <string name="permlab_camera" msgid="6320282492904119413">"ថត​រូប និងវីដេអូ"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"កម្មវិធី​នេះ​អាច​ថត​រូប​ និង​វីដេអូ​ដោយ​ប្រើ​កាមេរ៉ា នៅពេលកំពុងប្រើប្រាស់កម្មវិធី។"</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"ថតរូបភាព និងវីដេអូនៅផ្ទៃខាងក្រោយ"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"កម្មវិធី​នេះ​អាច​ថត​រូប​ និង​វីដេអូ​ដោយ​ប្រើ​កាមេរ៉ា​បាន​គ្រប់​ពេល​។"</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"អនុញ្ញាតឱ្យកម្មវិធី ឬសេវាកម្ម​ចូលប្រើកាមេរ៉ា​ប្រព័ន្ធ ដើម្បីថតរូប និង​ថតវីដេអូ"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"កម្មវិធីប្រព័ន្ធ ឬកម្មវិធីដែលមានសិទ្ធិអនុញ្ញាត​នេះអាចថត​រូប និង​ថតវីដេអូ ដោយប្រើ​កាមេរ៉ា​ប្រព័ន្ធបាន​គ្រប់ពេល។ តម្រូវឱ្យមាន​ការអនុញ្ញាត android.permission.CAMERA ដើម្បីឱ្យ​កម្មវិធីអាចធ្វើ​សកម្មភាព​បានផងដែរ"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"អនុញ្ញាតឱ្យកម្មវិធី ឬសេវាកម្ម​ទទួលការហៅត្រឡប់វិញអំពី​កាមេរ៉ាដែលកំពុងបិទ ឬបើក។"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 58ecb62..0cf6f49 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"ನಿಮ್ಮ ಆಡಿಯೊ ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ಬದಲಾಯಿಸಿ"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"ವಾಲ್ಯೂಮ್ ರೀತಿಯ ಮತ್ತು ಔಟ್‍‍ಪುಟ್‍‍ಗಾಗಿ ಯಾವ ಸ್ಪೀಕರ್ ಬಳಸಬೇಕು ಎಂಬ ರೀತಿಯ ಜಾಗತಿಕ ಆಡಿಯೊ ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ಮಾರ್ಪಡಿಸಲು ಅಪ್ಲಿಕೇಶನ್‍‍ಗೆ ಅವಕಾಶ ಮಾಡಿಕೊಡುತ್ತದೆ."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"ಆಡಿಯೊ ರೆಕಾರ್ಡ್ ಮಾಡಿ"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"ಆ್ಯಪ್ ಬಳಕೆಯಲ್ಲಿರುವಾಗ ಈ ಆ್ಯಪ್ ಮೈಕ್ರೊಫೋನ್ ಬಳಸಿ ಆಡಿಯೊವನ್ನು ರೆಕಾರ್ಡ್ ಮಾಡಬಹುದು."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"ಹಿನ್ನೆಲೆಯಲ್ಲಿ ಆಡಿಯೊವನ್ನು ರೆಕಾರ್ಡ್ ಮಾಡಿ"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"ಈ ಆ್ಯಪ್ ಮೈಕ್ರೋಫೋನ್ ಬಳಸುವ ಮೂಲಕ ಯಾವುದೇ ಸಮಯದಲ್ಲಾದರೂ ಆಡಿಯೊ ರೆಕಾರ್ಡ್ ಮಾಡಬಹುದು."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"ಸಿಮ್‌ಗೆ ಆಜ್ಞೆಗಳನ್ನು ಕಳುಹಿಸಿ"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"ಸಿಮ್‌ ಗೆ ಆದೇಶಗಳನ್ನು ಕಳುಹಿಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ. ಇದು ತುಂಬಾ ಅಪಾಯಕಾರಿ."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"ದೈಹಿಕ ಚಟುವಟಿಕೆಯನ್ನು ಗುರುತಿಸಿ"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"ಈ ಆ್ಯಪ್‌ ನಿಮ್ಮ ದೈಹಿಕ ಚಟುವಟಿಕೆಯನ್ನು ಗುರುತಿಸಬಹುದು."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"ಚಿತ್ರಗಳು ಮತ್ತು ವೀಡಿಯೊಗಳನ್ನು ಸೆರೆಹಿಡಿಯಿರಿ"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"ಆ್ಯಪ್ ಬಳಕೆಯಲ್ಲಿರುವಾಗ, ಈ ಆ್ಯಪ್ ಕ್ಯಾಮರಾ ಬಳಸಿ ಚಿತ್ರಗಳನ್ನು ತೆಗೆಯಬಹುದು ಮತ್ತು ವೀಡಿಯೊಗಳನ್ನು ರೆಕಾರ್ಡ್ ಮಾಡಬಹುದು."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"ಹಿನ್ನೆಲೆಯಲ್ಲಿ ಚಿತ್ರಗಳು ಮತ್ತು ವೀಡಿಯೊಗಳನ್ನು ತೆಗೆದುಕೊಳ್ಳಿ"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"ಈ ಆ್ಯಪ್ ಯಾವ ಸಮಯದಲ್ಲಾದರೂ ಕ್ಯಾಮರಾ ಬಳಸಿಕೊಂಡು ಚಿತ್ರಗಳು ಮತ್ತು ವಿಡಿಯೋಗಳನ್ನು ರೆಕಾರ್ಡ್ ಮಾಡಬಹುದು."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"ಫೋಟೋಗಳು ಮತ್ತು ವೀಡಿಯೊಗಳನ್ನು ತೆಗೆದುಕೊಳ್ಳಲು ಸಿಸ್ಟಂ ಕ್ಯಾಮರಾಗಳಿಗೆ ಅಪ್ಲಿಕೇಶನ್ ಅಥವಾ ಸೇವಾ ಪ್ರವೇಶವನ್ನು ಅನುಮತಿಸಿ"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"ಈ ವಿಶೇಷ ಅಥವಾ ಸಿಸ್ಟಂ ಆ್ಯಪ್, ಯಾವುದೇ ಸಮಯದಲ್ಲಾದರೂ ಸಿಸ್ಟಂ ಕ್ಯಾಮರಾವನ್ನು ಬಳಸಿಕೊಂಡು ಫೋಟೋಗಳನ್ನು ತೆಗೆದುಕೊಳ್ಳಬಹುದು ಮತ್ತು ವೀಡಿಯೋಗಳನ್ನು ರೆಕಾರ್ಡ್ ಮಾಡಬಹುದು. ಆ್ಯಪ್‌ಗೆ android.permission.CAMERA ಅನುಮತಿಯ ಅಗತ್ಯವಿರುತ್ತದೆ"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"ಕ್ಯಾಮರಾ ಸಾಧನಗಳನ್ನು ತೆರೆಯುತ್ತಿರುವ ಅಥವಾ ಮುಚ್ಚುತ್ತಿರುವ ಕುರಿತು ಕಾಲ್‌ಬ್ಯಾಕ್‌ಗಳನ್ನು ಸ್ವೀಕರಿಸಲು ಆ್ಯಪ್‌ ಅಥವಾ ಸೇವೆಗೆ ಅನುಮತಿಸಿ."</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 394bd21..3dfdf3d 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"오디오 설정 변경"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"앱이 음량이나 출력을 위해 사용하는 스피커 등 전체 오디오 설정을 변경할 수 있도록 허용합니다."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"오디오 녹음"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"앱을 사용하는 동안 앱에서 마이크를 사용하여 오디오를 녹음할 수 있습니다."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"백그라운드에서 오디오 녹음"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"언제든지 앱에서 마이크를 사용하여 오디오를 녹음할 수 있습니다."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"SIM 카드로 명령 전송"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"앱이 SIM에 명령어를 전송할 수 있도록 허용합니다. 이 기능은 매우 신중히 허용해야 합니다."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"신체 활동 확인"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"이 앱에서 내 신체 활동을 확인할 수 있습니다."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"사진과 동영상 찍기"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"앱을 사용하는 동안 앱에서 카메라를 사용하여 사진을 촬영하고 동영상을 녹화할 수 있습니다."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"백그라운드에서 사진 촬영 및 동영상 녹화"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"언제든지 앱에서 카메라를 사용하여 사진을 촬영하고 동영상을 녹화할 수 있습니다."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"사진 및 동영상 촬영을 위해 애플리케이션 또는 서비스에서 시스템 카메라에 액세스하도록 허용"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"이 권한이 있는 시스템 앱은 언제든지 시스템 카메라를 사용하여 사진을 촬영하고 동영상을 녹화할 수 있습니다. 또한 앱에 android.permission.CAMERA 권한이 필요합니다."</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"애플리케이션 또는 서비스에서 카메라 기기 열림 또는 닫힘에 대한 콜백을 수신하도록 허용"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 0774f6a..012acf5 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"аудио жөндөөлөрүңүздү өзгөртүңүз"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Колдонмого үн деңгээли жана кайсы динамик аркылуу үн чыгарылышы керек сыяктуу түзмөктүн аудио тууралоолорун өзгөртүүгө уруксат берет."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"аудио жаздыруу"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Бул колдонмо иштеп жатканда микрофон менен аудио файлдарды жаздыра алат."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"Фондо аудио жаздыруу"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Бул колдонмо каалаган убакта микрофон менен аудио файлдарды жаздыра алат."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"SIM-картага буйруктарды жөнөтүү"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Колдонмого SIM-картага буйруктарды жөнөтүү мүмкүнчүлүгүн берет. Бул абдан кооптуу."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"кыймыл-аракетти аныктоо"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Бул колдонмо кыймыл-аракетиңизди аныктап турат."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"сүрөт жана видео тартуу"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Бул колдонмо иштеп жатканда камера менен сүрөт же видеолорду тарта алат."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"Фондо сүрөткө жана видеого тартат"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Бул колдонмо каалаган убакта камера менен сүрөт же видеолорду тарта алат."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Сүрөткө тартып, видеолорду жаздыруу үчүн бул колдонмого же кызматка тутумдун камерасын колдонууга уруксат берүү"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Бул артыкчылыктуу тутум колдонмосу тутумдун камерасын каалаган убакта колдонуп, сүрөткө тартып, видео жаздыра алат. Ошондой эле колдонмого android.permission.CAMERA уруксатын берүү керек"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Колдонмого же кызматка камера ачылып же жабылып жатканда чалууларды кабыл алууга уруксат берүү."</string>
@@ -1212,7 +1206,7 @@
     <string name="android_upgrading_apk" msgid="1339564803894466737">"<xliff:g id="NUMBER_1">%2$d</xliff:g> ичинен <xliff:g id="NUMBER_0">%1$d</xliff:g> колдонмо ыңгайлаштырылууда."</string>
     <string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> даярдалууда."</string>
     <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Колдонмолорду иштетип баштоо"</string>
-    <string name="android_upgrading_complete" msgid="409800058018374746">"Жүктөө аякталууда."</string>
+    <string name="android_upgrading_complete" msgid="409800058018374746">"Жүктөлүүдө"</string>
     <string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> иштеп жатат"</string>
     <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Оюнга кайтуу үчүн таптаңыз"</string>
     <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Оюн тандоо"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 875d6817..315ee32 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"ປ່ຽນການຕັ້ງຄ່າສຽງຂອງທ່ານ"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"ອະນຸຍາດໃຫ້ແອັບຯແກ້ໄຂການຕັ້ງຄ່າສຽງສ່ວນກາງ ເຊັ່ນ: ລະດັບສຽງ ແລະລຳໂພງໃດທີ່ຖືກໃຊ້ສົ່ງສຽງອອກ."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"ບັນທຶກສຽງ"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"ແອັບນີ້ສາມາດບັນທຶກສຽງດ້ວຍໄມໂຄຣໂຟນໃນຂະນະທີ່ກຳລັງໃຊ້ແອັບຢູ່ໄດ້."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"ບັນທຶກສຽງໃນພື້ນຫຼັງ"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"ແອັບນີ້ສາມາດບັນທຶກສຽງດ້ວຍໄມໂຄຣໂຟນຕອນໃດກໍໄດ້."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"ສົ່ງ​ຄຳ​ສັ່ງ​ຫາ SIM"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"ອະນຸຍາດໃຫ້ແອັບຯສົ່ງຄຳສັ່ງຫາ SIM. ສິ່ງນີ້ອັນຕະລາຍຫຼາຍ."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"ຈຳແນກກິດຈະກຳທາງກາຍ"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"ແອັບນີ້ສາມາດຈັດລະບຽບການເຄື່ອນໄຫວທາງກາຍຂອງທ່ານໄດ້."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"ຖ່າຍຮູບ ແລະວິດີໂອ"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"ແອັບນີ້ສາມາດຖ່າຍຮູບ ແລະ ບັນທຶກວິດີໂອໂດຍໃຊ້ກ້ອງຖ່າຍຮູບໃນເວລາທີ່ກຳລັງໃຊ້ແອັບຢູ່ໄດ້."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"ຖ່າຍຮູບ ແລະ ວິດີໂອໃນພື້ນຫຼັງ"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"ແອັບນີ້ສາມາດຖ່າຍຮູບ ແລະ ບັນທຶກວິດີໂອໂດຍໃຊ້ກ້ອງຖ່າຍຮູບຕອນໃດກໍໄດ້."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"ອະນຸຍາດໃຫ້ແອັບພລິເຄຊັນ ຫຼື ບໍລິການເຂົ້າເຖິງກ້ອງຂອງລະບົບໄດ້ເພື່ອຖ່າຍຮູບ ແລະ ວິດີໂອ"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"ສິດ ຫຼື ແອັບລະບົບນີ້ສາມາດຖ່າຍຮູບ ແລະ ບັນທຶກວິດີໂອໂດຍໃຊ້ກ້ອງຂອງລະບົບຕອນໃດກໍໄດ້. ຕ້ອງໃຊ້ສິດອະນຸຍາດ android.permission.CAMERA ໃຫ້ແອັບຖືນຳ"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"ອະນຸຍາດໃຫ້ແອັບພລິເຄຊັນ ຫຼື ບໍລິການຮັບການເອີ້ນກັບກ່ຽວກັບອຸປະກອນກ້ອງຖືກເປີດ ຫຼື ປິດໄດ້."</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index a3e8391..1d6b2c7 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -438,23 +438,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"keisti garso nustatymus"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Leidžiama programai keisti visuotinius garso nustatymus, pvz., garsumą ir tai, kuris garsiakalbis naudojamas išvesčiai."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"įrašyti garsą"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Ši programa gali įrašyti garsą naudodama mikrofoną, kol programa naudojama."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"įrašyti garsą fone"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Ši programa gali bet kada įrašyti garsą naudodama mikrofoną."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"siųsti komandas į SIM kortelę"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Programai leidžiama siųsti komandas į SIM kortelę. Tai labai pavojinga."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"atpažinti fizinę veiklą"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Ši programa gali atpažinti jūsų fizinę veiklą."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"fotografuoti ir filmuoti"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Ši programa gali fotografuoti ir įrašyti vaizdo įrašų naudodama fotoaparatą, kol programa naudojama."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"fotografuoti ir įrašyti vaizdo įrašų fone"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Ši programa gali bet kada fotografuoti ir įrašyti vaizdo įrašų naudodama fotoaparatą."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Suteikti programai arba paslaugai prieigą prie sistemos fotoaparatų, kad būtų galima daryti nuotraukas ir įrašyti vaizdo įrašus"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Ši privilegijuota arba sistemos programa gali daryti nuotraukas ir įrašyti vaizdo įrašus naudodama sistemos fotoaparatą bet kuriuo metu. Programai taip pat būtinas leidimas „android.permission.CAMERA“"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Leisti programai ar paslaugai sulaukti atgalinio skambinimo, kai atidaromas ar uždaromas fotoaparatas."</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index f086662..ba790a0 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -435,23 +435,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"mainīt audio iestatījumus"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Ļauj lietotnei mainīt globālos audio iestatījumus, piemēram, skaļumu un izejai izmantoto skaļruni."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"ierakstīt audio"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Šī lietotne var ierakstīt audio, izmantojot mikrofonu, kamēr lietotne tiek izmantota."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"ierakstīt audio fonā"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Šī lietotne var jebkurā brīdī ierakstīt audio, izmantojot mikrofonu."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"Sūtīt komandas SIM kartei"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Ļauj lietotnei sūtīt komandas uz SIM karti. Tas ir ļoti bīstami!"</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"noteikt fiziskās aktivitātes"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Šī lietotne var noteikt jūsu fiziskās aktivitātes."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"uzņemt attēlus un videoklipus"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Šī lietotne var uzņemt attēlus un ierakstīt videoklipus, izmantojot kameru, kamēr lietotne tiek izmantota."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"uzņemt attēlus un videoklipus fonā"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Šī lietotne var jebkurā brīdī uzņemt attēlus un ierakstīt videoklipus, izmantojot kameru."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Atļauja lietojumprogrammai vai pakalpojumam piekļūt sistēmas kamerām, lai uzņemtu attēlus un videoklipus"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Šī privileģētā vai sistēmas lietotne var jebkurā brīdī uzņemt attēlus un ierakstīt videoklipus, izmantojot sistēmas kameru. Lietotnei nepieciešama arī atļauja android.permission.CAMERA."</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Atļaut lietojumprogrammai vai pakalpojumam saņemt atzvanus par kameras ierīču atvēršanu vai aizvēršanu"</string>
diff --git a/core/res/res/values-mcc310-mnc560-as/strings.xml b/core/res/res/values-mcc310-mnc560-as/strings.xml
deleted file mode 100644
index 13f3657..0000000
--- a/core/res/res/values-mcc310-mnc560-as/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT 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="mmcc_imsi_unknown_in_hlr" msgid="3526528316378889524">"ছিমখন প্ৰ\'ভিজন কৰা হোৱা নাই MM#2"</string>
-    <string name="mmcc_illegal_ms" msgid="4618730283812066268">"ছিমৰ অনুমতি নাই MM#3"</string>
-    <string name="mmcc_illegal_me" msgid="8522039751358990401">"ফ\'নৰ অনুমতি নাই MM#6"</string>
-</resources>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index b57307c..6f82395 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"менува аудио поставки"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Овозможува апликацијата да ги менува глобалните аудио поставки, како што се јачината на звукот и кој звучник се користи за излез."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"снимај аудио"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Апликацијава може да снима аудио со микрофонот додека се користи апликацијата."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"снима аудио во заднината"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Апликацијава може да снима аудио со микрофонот во секое време."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"испраќање наредби до SIM-картичката"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Овозможува апликацијата да испраќа наредби до SIM картичката. Ова е многу опасно."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"препознавајте ја физичката активност"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Апликацијава може да ја препознава вашата физичка активност."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"снимај слики и видеа"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Апликацијава може да фотографира и снима видеа со камерата додека се користи апликацијата."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"снима слики и видеа во заднината"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Апликацијава може да фотографира и да снима видеа со камерата во секое време."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Дозволете апликацијата или услугата да пристапува до системските камери за да фотографира и да снима видеа"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Оваа привилегирана или системска апликација може да фотографира и да снима видеа со системската камера во секое време. Потребно е апликацијата да ја има и дозволата android.permission.CAMERA"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Дозволете апликацијатa или услугата да прима повратни повици за отворањето или затворањето на уредите со камера."</string>
@@ -911,7 +905,7 @@
     <string name="keyguard_accessibility_user_selector" msgid="1466067610235696600">"Избирач на корисник"</string>
     <string name="keyguard_accessibility_status" msgid="6792745049712397237">"Статус"</string>
     <string name="keyguard_accessibility_camera" msgid="7862557559464986528">"Камера"</string>
-    <string name="keygaurd_accessibility_media_controls" msgid="2267379779900620614">"Контроли на медиуми"</string>
+    <string name="keygaurd_accessibility_media_controls" msgid="2267379779900620614">"Контроли за аудио/видео содржини"</string>
     <string name="keyguard_accessibility_widget_reorder_start" msgid="7066213328912939191">"Прередувањето виџети започна."</string>
     <string name="keyguard_accessibility_widget_reorder_end" msgid="1083806817600593490">"Прередувањето виџети заврши."</string>
     <string name="keyguard_accessibility_widget_deleted" msgid="1509738950119878705">"Виџетот <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> е избришан."</string>
@@ -1357,7 +1351,7 @@
     <string name="ext_media_new_notification_title" product="automotive" msgid="9085349544984742727">"<xliff:g id="NAME">%s</xliff:g> не работи"</string>
     <string name="ext_media_new_notification_message" msgid="6095403121990786986">"Допрете за поставување"</string>
     <string name="ext_media_new_notification_message" product="automotive" msgid="5140127881613227162">"Можеби ќе треба да го преформатирате уредот. Допрете за отстранување."</string>
-    <string name="ext_media_ready_notification_message" msgid="777258143284919261">"За пренесување фотографии и медиуми"</string>
+    <string name="ext_media_ready_notification_message" msgid="777258143284919261">"За пренесување фотографии и аудио/видео содржини"</string>
     <string name="ext_media_unmountable_notification_title" msgid="4895444667278979910">"Проблем со <xliff:g id="NAME">%s</xliff:g>"</string>
     <string name="ext_media_unmountable_notification_title" product="automotive" msgid="3142723758949023280">"<xliff:g id="NAME">%s</xliff:g> не работи"</string>
     <string name="ext_media_unmountable_notification_message" msgid="3256290114063126205">"Допрете за да го поправите ова"</string>
@@ -1398,8 +1392,8 @@
     <string name="ext_media_status_formatting" msgid="774148701503179906">"Се форматира..."</string>
     <string name="ext_media_status_missing" msgid="6520746443048867314">"Не е внесено"</string>
     <string name="activity_list_empty" msgid="4219430010716034252">"Не се пронајдени соодветни активности."</string>
-    <string name="permlab_route_media_output" msgid="8048124531439513118">"насочување излез за медиуми"</string>
-    <string name="permdesc_route_media_output" msgid="1759683269387729675">"Овозможува апликацијата да насочува излез за медиуми кон други надворешни уреди."</string>
+    <string name="permlab_route_media_output" msgid="8048124531439513118">"насочување излез за аудио/видео"</string>
+    <string name="permdesc_route_media_output" msgid="1759683269387729675">"Овозможува апликацијата да насочува излез за аудио/видео содржини кон други надворешни уреди."</string>
     <string name="permlab_readInstallSessions" msgid="7279049337895583621">"читање сесии на инсталирање"</string>
     <string name="permdesc_readInstallSessions" msgid="4012608316610763473">"Дозволува апликација да чита сесии на инсталирање. Тоа овозможува апликацијата да гледа детали за активни инсталации на пакет."</string>
     <string name="permlab_requestInstallPackages" msgid="7600020863445351154">"барање пакети за инсталирање"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index ce346c9..ffe073c 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -327,7 +327,7 @@
     <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"ഡിസ്പ്ലേയുടെ സൂം നിലയും പൊസിഷനിംഗും നിയന്ത്രിക്കുക."</string>
     <string name="capability_title_canPerformGestures" msgid="9106545062106728987">"ജെസ്‌റ്ററുകൾ നിർവഹിക്കുക"</string>
     <string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"ടാപ്പുചെയ്യാനോ സ്വൈപ്പുചെയ്യാനോ പിഞ്ചുചെയ്യാനോ മറ്റ് ജെസ്‌റ്ററുകൾ നിർവഹിക്കാനോ കഴിയും."</string>
-    <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"ഫിംഗർപ്രിന്റ് ജെസ്‌റ്ററുകൾ"</string>
+    <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"ഫിംഗർപ്രിന്റ് ജെസ്‌ച്ചറുകൾ"</string>
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"ഉപകരണത്തിന്റെ ഫിംഗർപ്രിന്റ് സെൻസറിൽ ചെയ്‌ത ജെസ്‌റ്ററുകൾ ക്യാപ്‌ചർ ചെയ്യാനാകും."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"സ്ക്രീന്‍ഷോട്ട് എടുക്കുക"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"ഡിസ്‌പ്ലേയുടെ സ്‌ക്രീൻഷോട്ട് എടുക്കാൻ കഴിയും."</string>
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"നിങ്ങളുടെ ഓഡിയോ ക്രമീകരണങ്ങൾ മാറ്റുക"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"വോളിയവും ഔട്ട്പുട്ടിനായി ഉപയോഗിച്ച സ്‌പീക്കറും പോലുള്ള ആഗോള ഓഡിയോ ക്രമീകരണങ്ങൾ പരിഷ്‌ക്കരിക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"ഓഡിയോ റെക്കോർഡ് ചെയ്യുക"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"ആപ്പ് ഉപയോഗത്തിലായിരിക്കുമ്പോൾ മൈക്രോഫോൺ ഉപയോഗിച്ച് ഓഡിയോ റെക്കോർഡ് ചെയ്യാൻ ഈ ആപ്പിന് കഴിയും."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"പശ്ചാത്തലത്തിൽ ഓഡിയോ റെക്കോർഡ് ചെയ്യുക"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"ഈ ആപ്പിന് ഏത് സമയത്തും മൈക്രോഫോൺ ഉപയോഗിച്ച് ഓഡിയോ റെക്കോർഡ് ചെയ്യാൻ കഴിയും."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"SIM-ലേക്ക് കമാൻഡുകൾ അയയ്ക്കുക"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"സിമ്മിലേക്ക് കമാൻഡുകൾ അയയ്‌ക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഇത് വളരെ അപകടകരമാണ്."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"ശാരീരിക പ്രവർത്തനം തിരിച്ചറിയുക"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"നിങ്ങളുടെ ശാരീരിക പ്രവർത്തനം ഈ ആപ്പിന് തിരിച്ചറിയാനാവും."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"ചിത്രങ്ങളും വീഡിയോകളും എടുക്കുക"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"ആപ്പ് ഉപയോഗത്തിലായിരിക്കുമ്പോൾ ക്യാമറ ഉപയോഗിച്ച് ചിത്രങ്ങളെടുക്കാനും വീഡിയോകൾ റെക്കോർഡ് ചെയ്യാനും ഈ ആപ്പിന് കഴിയും."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"പശ്ചാത്തലത്തിൽ ചിത്രങ്ങളും വീഡിയോകളും എടുക്കുക"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"ഏതുസമയത്തും ക്യാമറ ഉപയോഗിച്ച് ചിത്രങ്ങൾ എടുക്കാനും വീഡിയോകൾ റെക്കോർഡ് ചെയ്യാനും ഈ ആപ്പിന് കഴിയും."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"ചിത്രങ്ങളും വീഡിയോകളും എടുക്കാൻ, സിസ്‌റ്റം ക്യാമറ ആക്‌സസ് ചെയ്യുന്നതിന് ആപ്പിനെയോ സേവനത്തെയോ അനുവദിക്കുക"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"സിസ്‌റ്റം ക്യാമറ ഉപയോഗിച്ച് ഏത് സമയത്തും ചിത്രങ്ങളെടുക്കാനും വീഡിയോകൾ റെക്കോർഡ് ചെയ്യാനും ഈ വിശേഷാധികാര അല്ലെങ്കിൽ സിസ്‌റ്റം ആപ്പിന് കഴിയും. ആപ്പിലും android.permission.CAMERA അനുമതി ഉണ്ടായിരിക്കണം"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"ക്യാമറയുള്ള ഉപകരണങ്ങൾ ഓണാക്കുന്നതിനെയോ അടയ്ക്കുന്നതിനെയോ കുറിച്ചുള്ള കോൾബാക്കുകൾ സ്വീകരിക്കാൻ ആപ്പിനെയോ സേവനത്തെയോ അനുവദിക്കുക."</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 313c406..2183d010 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"Аудио тохиргоо солих"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Апп нь дууны хэмжээ, спикерын гаралтад ашиглагдах глобал аудио тохиргоог өөрчлөх боломжтой."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"аудио бичих"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Энэ аппыг ашиглаж байх үед энэ нь микрофон ашиглан аудио бичих боломжтой."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"ард видео бичих"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Энэ апп ямар ч үед микрофон ашиглан аудио бичих боломжтой."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"SIM картад тушаал илгээх"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Апп-д SIM рүү комманд илгээхийг зөвшөөрнө. Энэ маш аюултай."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"биеийн дасгал хөдөлгөөн таних"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Энэ апп таны биеийн дасгал хөдөлгөөнийг таних боломжтой."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"зураг авах болон видео бичих"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Энэ аппыг ашиглаж байх үед энэ нь камер ашиглан зураг авж, видео бичих боломжтой."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"ард зураг авж, видео хийх"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Энэ апп ямар ч үед камер ашиглан зураг авж, видео бичих боломжтой."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Видео болон зураг авахын тулд апп эсвэл үйлчилгээнд хандахыг системийн камерт зөвшөөрөх"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Энэ хамгаалагдсан эсвэл системийн апп нь системийн камер ашиглан ямар ч үед зураг авч, видео бичих боломжтой. Мөн түүнчлэн, апп нь android.permission.CAMERA-н зөвшөөрөлтэй байх шаардлагатай"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Аппликэйшн эсвэл үйлчилгээнд камерын төхөөрөмжүүдийг нээж эсвэл хааж байгаа тухай залгасан дуудлага хүлээн авахыг зөвшөөрөх."</string>
@@ -963,18 +957,18 @@
     <string name="autofill_parish" msgid="6847960518334530198">"Мөргөлч"</string>
     <string name="autofill_area" msgid="8289022370678448983">"Хэсэг"</string>
     <string name="autofill_emirate" msgid="2544082046790551168">"Эмират"</string>
-    <string name="permlab_readHistoryBookmarks" msgid="9102293913842539697">"өөрийн Вэб хавчуурга болон түүхийг унших"</string>
+    <string name="permlab_readHistoryBookmarks" msgid="9102293913842539697">"өөрийн Веб хавчуурга болон түүхийг унших"</string>
     <string name="permdesc_readHistoryBookmarks" msgid="2323799501008967852">"Апп нь Хөтчийн зочилж байсан бүх URL-н түүх болон Хөтчийн бүх хавчуургыг унших боломжтой. Анхаар: Энэ зөвшөөрөл нь гуравдагч талын хөтөч эсвэл вебээр хөтөчлөх чадавхтай аппликейшнүүдэд ашиглагдахгүй байх боломжтой."</string>
-    <string name="permlab_writeHistoryBookmarks" msgid="6090259925187986937">"вэб хавчуурга болон түүхийг бичих"</string>
+    <string name="permlab_writeHistoryBookmarks" msgid="6090259925187986937">"веб хавчуурга болон түүхийг бичих"</string>
     <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="573341025292489065">"Апп нь таны таблет дээр хадгалагдсан Хөтчийн түүх эсвэл хавчуургыг өөрчлөх боломжтой. Энэ нь апп-д Хөтчийн датаг арилгах эсвэл өөрчлөх боломжийг олгоно. Анхаар: Энэ зөвшөөрөл нь гуравдагч талын хөтөч эсвэл вебээр хөтөчлөх чадвартай аппликейшнд ажиллахгүй байх боломжтой."</string>
-    <string name="permdesc_writeHistoryBookmarks" product="tv" msgid="88642768580408561">"Аппад таны Android TV төхөөрөмжид хадгалсан Хөтчийн түүх эсвэл хавчуургыг өөрчлөхийг зөвшөөрнө. Энэ нь аппад Хөтчийн өгөгдлийг устгах эсвэл өөрчлөхийг зөвшөөрч болзошгүй. Санамж: энэ зөвшөөрөл нь гуравдагч талын хөтөч эсвэл вэб хөтчийн чадамжтай бусад аппад хэрэгжихгүй байж болзошгүй."</string>
+    <string name="permdesc_writeHistoryBookmarks" product="tv" msgid="88642768580408561">"Аппад таны Android TV төхөөрөмжид хадгалсан Хөтчийн түүх эсвэл хавчуургыг өөрчлөхийг зөвшөөрнө. Энэ нь аппад Хөтчийн өгөгдлийг устгах эсвэл өөрчлөхийг зөвшөөрч болзошгүй. Санамж: энэ зөвшөөрөл нь гуравдагч талын хөтөч эсвэл веб хөтчийн чадамжтай бусад аппад хэрэгжихгүй байж болзошгүй."</string>
     <string name="permdesc_writeHistoryBookmarks" product="default" msgid="2245203087160913652">"Апп нь таны утсан дээр хадгалагдсан Хөтчийн түүх эсвэл хавчуургыг өөрчлөх боломжтой. Энэ нь апп-д Хөтчийн датаг арилгах эсвэл өөрчлөх боломжийг олгоно. Анхаар: Энэ зөвшөөрөл нь гуравдагч талын хөтөч эсвэл вебээр хөтөчлөх чадвартай аппликейшнд ажиллахгүй байх боломжтой."</string>
     <string name="permlab_setAlarm" msgid="1158001610254173567">"сэрүүлэг тохируулах"</string>
     <string name="permdesc_setAlarm" msgid="2185033720060109640">"Апп нь суулгагдсан сэрүүлэгний апп дээр сэрүүлэг тохируулах боломжтой. Зарим сэрүүлэгний апп нь энэ функцийг дэмжихгүй байж болзошгүй."</string>
     <string name="permlab_addVoicemail" msgid="4770245808840814471">"дуут шуудан нэмэх"</string>
     <string name="permdesc_addVoicemail" msgid="5470312139820074324">"Таны дуут шуудангийн ирсэн мэйлд зурвас нэмэхийг апп-д зөвшөөрөх."</string>
     <string name="permlab_writeGeolocationPermissions" msgid="8605631647492879449">"Хөтчийн геобайршлын зөвшөөрлийг өөрчлөх"</string>
-    <string name="permdesc_writeGeolocationPermissions" msgid="5817346421222227772">"Апп нь Хөтчийн гео байршлын зөвшөөрлийг өөрчлөх боломжтой. Хортой апп нь энийг ашиглан дурын вэб хуудасруу байршлын мэдээллийг илгээх боломжтой."</string>
+    <string name="permdesc_writeGeolocationPermissions" msgid="5817346421222227772">"Апп нь Хөтчийн гео байршлын зөвшөөрлийг өөрчлөх боломжтой. Хортой апп нь энийг ашиглан дурын веб хуудасруу байршлын мэдээллийг илгээх боломжтой."</string>
     <string name="save_password_message" msgid="2146409467245462965">"Та хөтчид энэ нууц үгийг сануулах уу?"</string>
     <string name="save_password_notnow" msgid="2878327088951240061">"Одоо биш"</string>
     <string name="save_password_remember" msgid="6490888932657708341">"Санах"</string>
@@ -1465,7 +1459,7 @@
     <string name="progress_erasing" msgid="6891435992721028004">"Хуваалцсан хадгалах санг устгаж байна…"</string>
     <string name="share" msgid="4157615043345227321">"Хуваалцах"</string>
     <string name="find" msgid="5015737188624767706">"Олох"</string>
-    <string name="websearch" msgid="5624340204512793290">"Вэб хайлт"</string>
+    <string name="websearch" msgid="5624340204512793290">"Веб хайлт"</string>
     <string name="find_next" msgid="5341217051549648153">"Дараагийнхыг хайх"</string>
     <string name="find_previous" msgid="4405898398141275532">"Өмнөхөөс олох"</string>
     <string name="gpsNotifTicker" msgid="3207361857637620780">"<xliff:g id="NAME">%s</xliff:g>-н байршлын хүсэлт"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index effa6b6..9b4849f 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -197,7 +197,7 @@
     <string name="country_detector" msgid="7023275114706088854">"कंट्री डिटेक्टर"</string>
     <string name="location_service" msgid="2439187616018455546">"स्थान सेवा"</string>
     <string name="gnss_service" msgid="8907781262179951385">"GNSS सेवा"</string>
-    <string name="sensor_notification_service" msgid="7474531979178682676">"सेंसर सूचना सेवा"</string>
+    <string name="sensor_notification_service" msgid="7474531979178682676">"सेन्सर सूचना सेवा"</string>
     <string name="twilight_service" msgid="8964898045693187224">"ट्वायलाइट सेवा"</string>
     <string name="factory_reset_warning" msgid="6858705527798047809">"तुमचे डिव्हाइस मिटविले जाईल"</string>
     <string name="factory_reset_message" msgid="2657049595153992213">"प्रशासक अ‍ॅप वापरता येणार नाही. तुमचे डिव्हाइस आता साफ केले जाईल.\n\nतुम्हाला कुठलेही प्रश्न असल्यास, तुमच्या संस्थेच्या प्रशासकाशी संपर्क साधा."</string>
@@ -316,7 +316,7 @@
     <string name="permgrouplab_phone" msgid="570318944091926620">"फोन"</string>
     <string name="permgroupdesc_phone" msgid="270048070781478204">"फोन कॉल आणि व्यवस्थापित"</string>
     <string name="permgrouplab_sensors" msgid="9134046949784064495">"शरीर सेन्सर"</string>
-    <string name="permgroupdesc_sensors" msgid="2610631290633747752">"आपल्‍या महत्त्वाच्या मापनांविषयी सेंसर डेटा अ‍ॅक्सेस करा"</string>
+    <string name="permgroupdesc_sensors" msgid="2610631290633747752">"आपल्‍या महत्त्वाच्या मापनांविषयी सेन्सर डेटा अ‍ॅक्सेस करा"</string>
     <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"विंडोमधील आशय पुन्हा मिळवा"</string>
     <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"तुम्ही वापरत असलेल्‍या विंडोमधील आशय तपासा."</string>
     <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"स्पर्श करून अन्वेषण सुरू करा"</string>
@@ -388,7 +388,7 @@
     <string name="permlab_getPackageSize" msgid="375391550792886641">"अ‍ॅप संचयन स्थान मोजा"</string>
     <string name="permdesc_getPackageSize" msgid="742743530909966782">"अ‍ॅप ला त्याचा कोड, डेटा आणि कॅशे    आकार पुनर्प्राप्त करण्यासाठी अनुमती देते"</string>
     <string name="permlab_writeSettings" msgid="8057285063719277394">"सिस्टम सेटिंग्ज सुधारित करा"</string>
-    <string name="permdesc_writeSettings" msgid="8293047411196067188">"सिस्टीमचा सेटिंग्ज डेटा सुधारित करण्यासाठी अ‍ॅप ला अनुमती देते. दुर्भावनापूर्ण अ‍ॅप्स आपल्या सिस्टीमचे कॉन्फिगरेशन दूषित करू शकतात."</string>
+    <string name="permdesc_writeSettings" msgid="8293047411196067188">"सिस्टीमचा सेटिंग्ज डेटा सुधारित करण्यासाठी अ‍ॅप ला अनुमती देते. दुर्भावनापूर्ण अ‍ॅप्स आपल्या सिस्टीमचे कॉंफिगरेशन दूषित करू शकतात."</string>
     <string name="permlab_receiveBootCompleted" msgid="6643339400247325379">"सुरूवातीस चालवा"</string>
     <string name="permdesc_receiveBootCompleted" product="tablet" msgid="5565659082718177484">"जसे सिस्टम बूट करणे समाप्त करते तसे अ‍ॅप ला स्वतः प्रारंभ करण्यास अनुमती देते. यामुळे टॅबलेट प्रारंभ करण्यास वेळ लागू शकतो आणि नेहमी सुरू राहून एकंदर टॅबलेटला धीमे करण्यास अ‍ॅप ला अनुमती देते."</string>
     <string name="permdesc_receiveBootCompleted" product="tv" msgid="4900842256047614307">"सिस्टम बूट होणे संपल्यावर ॲपला स्वतः सुरू होण्याची अनुमती देते. यामुळे तुमच्या Android TV डिव्हाइसला सुरू होण्यास वेळ लागू शकतो आणि नेहमी सुरू राहून एकंदर डिव्हाइसलाच धीमे करण्याची अनुमती ॲपला देते."</string>
@@ -411,7 +411,7 @@
     <string name="permdesc_writeCallLog" product="tablet" msgid="2657525794731690397">"येणार्‍या आणि केल्या जाणार्‍या कॉलविषयीच्या डेटासह, आपल्या टॅब्लेटचा कॉल लॉग सुधारित करण्यासाठी अ‍ॅप ला अनुमती देते. दुर्भावनापूर्ण अ‍ॅप्स तुमचा कॉल लॉग मिटवण्यासाठी किंवा सुधारित करण्यासाठी याचा वापर करू शकतात."</string>
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"येणार्‍या आणि केल्या जाणार्‍या कॉलविषयीच्या डेटासह, तुमच्या Android TV डिव्हाइसचा कॉल लॉग सुधारित करण्यासाठी ॲपला अनुमती देते. दुर्भावनापूर्ण अ‍ॅप्स तुमचा कॉल लॉग मिटवण्यासाठी किंवा सुधारित करण्यासाठी याचा वापर करू शकतात."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"येणार्‍या आणि केल्या जाणार्‍या कॉलविषयीच्या डेटासह, आपल्या फोनचा कॉल लॉग सुधारित करण्यासाठी अ‍ॅप ला अनुमती देते. दुर्भावनापूर्ण अ‍ॅप्स तुमचा कॉल लॉग मिटवण्यासाठी किंवा सुधारित करण्यासाठी याचा वापर करू शकतात."</string>
-    <string name="permlab_bodySensors" msgid="3411035315357380862">"शरीर सेंसर (हृदय गती मॉनिटरसारखे) अ‍ॅक्सेस करा"</string>
+    <string name="permlab_bodySensors" msgid="3411035315357380862">"शरीर सेन्सर (हृदय गती मॉनिटरसारखे) अ‍ॅक्सेस करा"</string>
     <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"हृदय गती सारख्या, आपल्या शारीरिक स्थितीचे नियंत्रण करणार्‍या सेन्सरवरून डेटामध्ये प्रवेश करण्यासाठी ॲपला अनुमती देते."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"कॅलेंडर इव्हेंट आणि तपशील वाचा"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"हा अ‍ॅप आपल्या टॅब्लेटवर स्टोअर केलेले सर्व कॅलेंडर इव्हेंट वाचू आणि शेअर करू शकतो किंवा तुमचा कॅलेंडर डेटा सेव्ह करू शकतो."</string>
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"आपल्या ऑडिओ सेटिंग्ज बदला"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"व्हॉल्यूम आणि आउटपुटसाठी कोणता स्पीकर वापरला आहे यासारख्या समग्र ऑडिओ सेटिंग्ज सुधारित करण्यासाठी अ‍ॅप ला अनुमती देते."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"ऑडिओ रेकॉर्ड"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"ॲप वापरात असताना, हे ॲप मायक्रोफोन वापरून ऑडिओ रेकॉर्ड करू शकते."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"बॅकग्राउंडमध्ये ऑडिओ रेकॉर्ड करा"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"हे ॲप मायक्रोफोन वापरून ऑडिओ कधीही रेकॉर्ड करू शकते."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"सिम वर कमांड पाठवा"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"अ‍ॅप ला सिम वर कमांड पाठविण्‍याची अनुमती देते. हे खूप धोकादायक असते."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"शारीरिक ॲक्टिव्हिटी ओळखा"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"हे अ‍ॅप तुमच्या शारीरिक ॲक्टिव्हिटी ओळखू शकते."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"चित्रे आणि व्हिडिओ घ्या"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"ॲप वापरात असताना, हे ॲप कॅमेरा वापरून फोटो काढू शकते आणि व्हिडिओ रेकॉर्ड करू शकते."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"बॅकग्राउंडमध्ये फोटो काढा आणि व्हिडिओ रेकॉर्ड करा"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"हे ॲप कॅमेरा वापरून कधीही फोटो काढू शकते आणि व्हिडिओ रेकॉर्ड करू शकते."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"फोटो आणि व्हिडिओ काढण्यासाठी ॲप्लिकेशन किंवा सेवेला सिस्टम कॅमेरे ॲक्सेस करण्याची अनुमती द्या"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"हे विशेषाधिकृत किंवा सिस्टम ॲप कधीही सिस्टम कॅमेरा वापरून फोटो आणि व्हिडिओ रेकॉर्ड करू शकते. ॲपकडे android.permission.CAMERA परवानगी असण्याचीदेखील आवश्यकता आहे"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"एखाद्या अ‍ॅप्लिकेशन किंवा सेवेला कॅमेरा डिव्हाइस सुरू किंवा बंद केल्याची कॉलबॅक मिळवण्याची अनुमती द्या."</string>
@@ -571,7 +565,7 @@
     <string name="fingerprint_error_canceled" msgid="540026881380070750">"फिंगरप्रिंट ऑपरेशन रद्द झाले."</string>
     <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"वापरकर्त्याने फिंगरप्रिंट ऑपरेशन रद्द केले."</string>
     <string name="fingerprint_error_lockout" msgid="7853461265604738671">"खूप प्रयत्न केले. नंतर पुन्हा प्रयत्न करा."</string>
-    <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"खूप प्रयत्न करून झाले. फिंगरप्रिंट सेंसर बंद आहे."</string>
+    <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"खूप प्रयत्न करून झाले. फिंगरप्रिंट सेन्सर बंद आहे."</string>
     <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"पुन्हा प्रयत्न करा."</string>
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"कोणत्याही फिंगरप्रिंटची नोंद झाली नाही"</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"या डिव्हाइसमध्ये फिंगरप्रिंट सेन्सर नाही."</string>
@@ -663,7 +657,7 @@
     <string name="permlab_bindDreamService" msgid="4776175992848982706">"स्‍वप्न सेवेवर प्रतिबद्ध करा"</string>
     <string name="permdesc_bindDreamService" msgid="9129615743300572973">"होल्‍डरला स्‍वप्नसेवेच्या शीर्ष-स्‍तराच्या इंटरफेसशी प्रतिबद्ध करण्‍यास अनुमती देते. सामान्‍य अ‍ॅप्‍सकरिता कधीही आवश्‍यक नसते."</string>
     <string name="permlab_invokeCarrierSetup" msgid="5098810760209818140">"वाहकाद्वारे-प्रदान केलेल्‍या कॉन्‍फिगरेशन अ‍ॅपची विनंती करा"</string>
-    <string name="permdesc_invokeCarrierSetup" msgid="4790845896063237887">"होल्‍डरला वाहकद्वारे-प्रदान केलेल्या कॉन्फिगरेशन अ‍ॅपची विनंती करण्‍याची अनुमती देते. सामान्‍य अ‍ॅप्‍ससाठी कधीही आवश्‍यक नसावे."</string>
+    <string name="permdesc_invokeCarrierSetup" msgid="4790845896063237887">"होल्‍डरला वाहकद्वारे-प्रदान केलेल्या कॉंफिगरेशन अ‍ॅपची विनंती करण्‍याची अनुमती देते. सामान्‍य अ‍ॅप्‍ससाठी कधीही आवश्‍यक नसावे."</string>
     <string name="permlab_accessNetworkConditions" msgid="1270732533356286514">"नेटवर्क स्‍थितींवरील निरीक्षणांसाठी ऐका"</string>
     <string name="permdesc_accessNetworkConditions" msgid="2959269186741956109">"अनु्प्रयोगाला नेटवर्क स्‍थितींवरील निरीक्षणे ऐकण्‍यासाठी अनुमती देते. सामान्‍य अ‍ॅप्‍ससाठी कधीही आवश्‍यक नसावे."</string>
     <string name="permlab_setInputCalibration" msgid="932069700285223434">"इनपुट डिव्हाइस कॅलिब्रेशन बदला"</string>
@@ -679,7 +673,7 @@
     <string name="permlab_bindCarrierServices" msgid="2395596978626237474">"वाहक सेवांवर प्रतिबद्ध करा"</string>
     <string name="permdesc_bindCarrierServices" msgid="9185614481967262900">"वाहक सेवांवर प्रतिबद्ध करण्यासाठी होल्डरला अनुमती देते. सामान्य ॲप्ससाठी कधीही आवश्यकता नसावी."</string>
     <string name="permlab_access_notification_policy" msgid="5524112842876975537">"व्यत्यय आणू नका अ‍ॅक्सेस करा"</string>
-    <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"व्यत्यय आणू नका कॉन्फिगरेशन वाचण्यासाठी आणि लिहिण्यासाठी ॲपला अनुमती देते."</string>
+    <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"व्यत्यय आणू नका कॉंफिगरेशन वाचण्यासाठी आणि लिहिण्यासाठी ॲपला अनुमती देते."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"व्ह्यू परवानगी वापर सुरू करा"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"धारकास अ‍ॅपसाठी परवानगी वापरणे सुरू करण्याची अनुमती देते. सामान्य अ‍ॅप्ससाठी कधीही आवश्यकता नसते."</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"पासवर्ड नियम सेट करा"</string>
@@ -758,7 +752,7 @@
   </string-array>
     <string name="phoneTypeCustom" msgid="5120365721260686814">"कस्टम"</string>
     <string name="phoneTypeHome" msgid="3880132427643623588">"घर"</string>
-    <string name="phoneTypeMobile" msgid="1178852541462086735">"मोबाईल"</string>
+    <string name="phoneTypeMobile" msgid="1178852541462086735">"मोबाइल"</string>
     <string name="phoneTypeWork" msgid="6604967163358864607">"कार्य"</string>
     <string name="phoneTypeFaxWork" msgid="6757519896109439123">"कार्य फॅक्स"</string>
     <string name="phoneTypeFaxHome" msgid="6678559953115904345">"निवास फॅक्स"</string>
@@ -773,7 +767,7 @@
     <string name="phoneTypeRadio" msgid="2637819130239264771">"रेडिओ"</string>
     <string name="phoneTypeTelex" msgid="2558783611711876562">"टेलेक्स"</string>
     <string name="phoneTypeTtyTdd" msgid="532038552105328779">"TTY TDD"</string>
-    <string name="phoneTypeWorkMobile" msgid="7522314392003565121">"कार्य मोबाईल"</string>
+    <string name="phoneTypeWorkMobile" msgid="7522314392003565121">"कार्य मोबाइल"</string>
     <string name="phoneTypeWorkPager" msgid="3748332310638505234">"कार्य पेजर"</string>
     <string name="phoneTypeAssistant" msgid="757550783842231039">"असिस्टंट"</string>
     <string name="phoneTypeMms" msgid="1799747455131365989">"MMS"</string>
@@ -785,7 +779,7 @@
     <string name="emailTypeHome" msgid="1597116303154775999">"घर"</string>
     <string name="emailTypeWork" msgid="2020095414401882111">"कार्य"</string>
     <string name="emailTypeOther" msgid="5131130857030897465">"अन्य"</string>
-    <string name="emailTypeMobile" msgid="787155077375364230">"मोबाईल"</string>
+    <string name="emailTypeMobile" msgid="787155077375364230">"मोबाइल"</string>
     <string name="postalTypeCustom" msgid="5645590470242939129">"कस्टम"</string>
     <string name="postalTypeHome" msgid="7562272480949727912">"घर"</string>
     <string name="postalTypeWork" msgid="8553425424652012826">"कार्य"</string>
@@ -1279,8 +1273,8 @@
     <string name="sms_control_yes" msgid="4858845109269524622">"अनुमती द्या"</string>
     <string name="sms_control_no" msgid="4845717880040355570">"नकार द्या"</string>
     <string name="sms_short_code_confirm_message" msgid="1385416688897538724">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; हा &lt;b&gt;<xliff:g id="DEST_ADDRESS">%2$s</xliff:g>&lt;/b&gt;वर एक मेसेज पाठवू इच्छितो."</string>
-    <string name="sms_short_code_details" msgid="2723725738333388351">"यामुळे आपल्या मोबाईल खात्यावर "<b>"शुल्क आकारले जाऊ शकते"</b>"."</string>
-    <string name="sms_premium_short_code_details" msgid="1400296309866638111"><b>"यामुळे आपल्या मोबाईल खात्यावर शुल्क आकारले जाऊ शकते."</b></string>
+    <string name="sms_short_code_details" msgid="2723725738333388351">"यामुळे आपल्या मोबाइल खात्यावर "<b>"शुल्क आकारले जाऊ शकते"</b>"."</string>
+    <string name="sms_premium_short_code_details" msgid="1400296309866638111"><b>"यामुळे आपल्या मोबाइल खात्यावर शुल्क आकारले जाऊ शकते."</b></string>
     <string name="sms_short_code_confirm_allow" msgid="920477594325526691">"पाठवा"</string>
     <string name="sms_short_code_confirm_deny" msgid="1356917469323768230">"रद्द करा"</string>
     <string name="sms_short_code_remember_choice" msgid="1374526438647744862">"माझी वड लक्षात ठेवा"</string>
@@ -1288,10 +1282,10 @@
     <string name="sms_short_code_confirm_always_allow" msgid="2223014893129755950">"नेहमी अनुमती द्या"</string>
     <string name="sms_short_code_confirm_never_allow" msgid="2688828813521652079">"कधीही अनुमती देऊ नका"</string>
     <string name="sim_removed_title" msgid="5387212933992546283">"सिम कार्ड काढले"</string>
-    <string name="sim_removed_message" msgid="9051174064474904617">"तुम्ही एक वैध सिम कार्ड घालून प्रारंभ करेपर्यंत मोबाईल नेटवर्क अनुपलब्ध असेल."</string>
+    <string name="sim_removed_message" msgid="9051174064474904617">"तुम्ही एक वैध सिम कार्ड घालून प्रारंभ करेपर्यंत मोबाइल नेटवर्क अनुपलब्ध असेल."</string>
     <string name="sim_done_button" msgid="6464250841528410598">"पूर्ण झाले"</string>
     <string name="sim_added_title" msgid="7930779986759414595">"सिम कार्ड जोडले"</string>
-    <string name="sim_added_message" msgid="6602906609509958680">"मोबाईल नेटवर्कवर अ‍ॅक्सेस करण्यासाठी तुमचे डिव्हाइस रीस्टार्ट करा."</string>
+    <string name="sim_added_message" msgid="6602906609509958680">"मोबाइल नेटवर्कवर अ‍ॅक्सेस करण्यासाठी तुमचे डिव्हाइस रीस्टार्ट करा."</string>
     <string name="sim_restart_button" msgid="8481803851341190038">"रीस्टार्ट"</string>
     <string name="install_carrier_app_notification_title" msgid="5712723402213090102">"मोबाइल सेवा अ‍ॅक्टिव्हेट करा"</string>
     <string name="install_carrier_app_notification_text" msgid="2781317581274192728">"तुमचे नवीन सिम अ‍ॅक्टिव्हेट करण्यासाठी वाहकाचे अ‍ॅप डाउनलोड करा"</string>
@@ -1342,7 +1336,7 @@
     <string name="select_input_method" msgid="3971267998568587025">"इनपुट पद्धत निवडा"</string>
     <string name="show_ime" msgid="6406112007347443383">"भौतिक कीबोर्ड सक्रिय असताना त्यास स्क्रीनवर ठेवा"</string>
     <string name="hardware" msgid="1800597768237606953">"व्हर्च्युअल कीबोर्ड दर्शवा"</string>
-    <string name="select_keyboard_layout_notification_title" msgid="4427643867639774118">"वास्तविक कीबोर्ड कॉन्फिगर करा"</string>
+    <string name="select_keyboard_layout_notification_title" msgid="4427643867639774118">"वास्तविक कीबोर्ड कॉंफिगर करा"</string>
     <string name="select_keyboard_layout_notification_message" msgid="8835158247369158154">"भाषा आणि लेआउट निवडण्यासाठी टॅप करा"</string>
     <string name="fast_scroll_alphabet" msgid="8854435958703888376">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="2529539945421557329">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
@@ -1920,7 +1914,7 @@
     <string name="demo_starting_message" msgid="6577581216125805905">"डेमो प्रारंभ करत आहे..."</string>
     <string name="demo_restarting_message" msgid="1160053183701746766">"डिव्हाइस रीसेट करत आहे..."</string>
     <string name="suspended_widget_accessibility" msgid="6331451091851326101">"<xliff:g id="LABEL">%1$s</xliff:g> अक्षम केले"</string>
-    <string name="conference_call" msgid="5731633152336490471">"कॉन्फरन्स कॉल"</string>
+    <string name="conference_call" msgid="5731633152336490471">"कॉंफरन्स कॉल"</string>
     <string name="tooltip_popup_title" msgid="7863719020269945722">"टूलटिप"</string>
     <string name="app_category_game" msgid="4534216074910244790">"गेम"</string>
     <string name="app_category_audio" msgid="8296029904794676222">"संगीत आणि ऑडिओ"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 5849c36..5c35ba1 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"tukar tetapan audio anda"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Membenarkan apl untuk mengubah suai tetapan audio global seperti kelantangan dan pembesar suara mana digunakan untuk output."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"rakam audio"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Aplikasi ini boleh merakam audio menggunakan mikrofon semasa apl sedang digunakan."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"rakam audio di latar"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Apl ini boleh merakam audio menggunakan mikrofon pada bila-bila masa."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"hantar perintah ke SIM"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Membenarkan apl menghantar arahan kepada SIM. Ini amat berbahaya."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"camkan aktiviti fizikal"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Apl ini dapat mengecam aktiviti fizikal anda."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"ambil gambar dan video"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Apl ini boleh mengambil gambar dan merakam video menggunakan kamera semasa apl sedang digunakan."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"ambil gambar dan video di latar"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Apl ini boleh mengambil gambar dan merakam video menggunakan kamera pada bila-bila masa."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Benarkan aplikasi atau perkhidmatan mengakses kamera sistem untuk mengambil gambar dan video"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Apl terlindung atau apl sistem ini boleh mengambil gambar dan merakam video menggunakan kamera sistem pada bila-bila masa. Apl juga perlu mempunyai kebenaran android.permission.CAMERA"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Benarkan aplikasi atau perkhidmatan menerima panggilan balik tentang peranti kamera yang dibuka atau ditutup."</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index be88208..38f1aa8 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"သင့်အသံအပြင်အဆင်အားပြောင်းခြင်း"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"အပလီကေးရှင်းအား အသံအတိုးအကျယ်နှင့် အထွက်ကို မည်သည့်စပီကာကို သုံးရန်စသည်ဖြင့် စက်တစ်ခုလုံးနှင့်ဆိုင်သော အသံဆိုင်ရာ ဆက်တင်များ ပြင်ဆင်ခွင့် ပြုရန်"</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"အသံဖမ်းခြင်း"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"ဤအက်ပ်ကို အသုံးပြုနေစဉ် ၎င်းက မိုက်ခရိုဖုန်းကို အသုံးပြု၍ အသံဖမ်းနိုင်သည်။"</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"နောက်ခံတွင် အသံဖမ်းပါ"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"ဤအက်ပ်သည် မိုက်ခရိုဖုန်းကို အသုံးပြု၍ အချိန်မရွေး အသံဖမ်းနိုင်သည်။"</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"SIM ထံသို့ ညွှန်ကြားချက်များကို ပို့ပါ"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"အက်ပ်အား ဆင်းမ်ကဒ်ဆီသို့ အမိန့်များ ပေးပို့ခွင့် ပြုခြင်း။ ဤခွင့်ပြုမှုမှာ အန္တရာယ်အလွန် ရှိပါသည်။"</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"ကိုယ်လက်လှုပ်ရှားမှုကို မှတ်သားပါ"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"ဤအက်ပ်က သင်၏ကိုယ်လက်လှုပ်ရှားမှုကို မှတ်သားနိုင်ပါသည်။"</string>
     <string name="permlab_camera" msgid="6320282492904119413">"ဓါတ်ပုံနှင့်ဗွီဒီယိုရိုက်ခြင်း"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"ဤအက်ပ်ကို အသုံးပြုနေစဉ် ၎င်းက ကင်မရာကို အသုံးပြု၍ ဓာတ်ပုံနှင့် ဗီဒီယိုများကို ရိုက်ကူးနိုင်သည်။"</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"ဓာတ်ပုံနှင့် ဗီဒီယိုများကို နောက်ခံတွင် ရိုက်ကူးပါ"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"ဤအက်ပ်သည် ကင်မရာကို အသုံးပြု၍ ဓာတ်ပုံနှင့် ဗီဒီယိုများကို အချိန်မရွေး ရိုက်ကူးနိုင်သည်။"</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"ဓာတ်ပုံနှင့် ဗီဒီယိုများရိုက်ရန်အတွက် စနစ်ကင်မရာများကို အက်ပ် သို့မဟုတ် ဝန်‌ဆောင်မှုအား အသုံးပြုခွင့်ပေးခြင်း"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"ဤခွင့်ပြုထားသည့် သို့မဟုတ် စနစ်အက်ပ်က စနစ်ကင်မရာအသုံးပြုပြီး ဓာတ်ပုံနှင့် ဗီဒီယိုများကို အချိန်မရွေး ရိုက်ကူးနိုင်သည်။ အက်ပ်ကလည်း android.permission.CAMERA ခွင့်ပြုချက် ရှိရပါမည်"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"ကင်မရာစက်များ ပွင့်နေခြင်း သို့မဟုတ် ပိတ်နေခြင်းနှင့် ပတ်သက်ပြီး ပြန်လည်ခေါ်ဆိုမှုများ ရယူရန် အပလီကေးရှင်း သို့မဟုတ် ဝန်ဆောင်မှုကို ခွင့်ပြုခြင်း။"</string>
@@ -1106,7 +1100,7 @@
     <string name="cut" msgid="2561199725874745819">"ဖြတ်ရန်"</string>
     <string name="copy" msgid="5472512047143665218">"ကူးရန်"</string>
     <string name="failed_to_copy_to_clipboard" msgid="725919885138539875">"ကလစ်ဘုတ်သို့ မိတ္တူကူးခြင်း မအောင်မြင်ပါ"</string>
-    <string name="paste" msgid="461843306215520225">"Paste"</string>
+    <string name="paste" msgid="461843306215520225">"ကူးထည့်ရန်"</string>
     <string name="paste_as_plain_text" msgid="7664800665823182587">"စာသားအတိုင်း ကူးထည့်ပါ"</string>
     <string name="replace" msgid="7842675434546657444">"အစားထိုခြင်း"</string>
     <string name="delete" msgid="1514113991712129054">"ဖျက်ရန်"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index a18df16..5b0a4ad 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"endre lydinnstillinger"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Lar appen endre globale lydinnstillinger slik som volum og hvilken høyttaler som brukes for lydavspilling."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"ta opp lyd"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Denne appen kan ta opp lyd med mikrofonen mens den er i bruk."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"ta opp lyd i bakgrunnen"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Denne appen kan når som helst ta opp lyd med mikrofonen."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"sende kommandoer til SIM-kortet"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Lar appen sende kommandoer til SIM-kortet. Dette er veldig farlig."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"gjenkjenn fysisk aktivitet"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Denne appen kan gjenkjenne den fysiske aktiviteten din."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"ta bilder og videoer"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Denne appen kan ta bilder og spille inn videoer med kameraet mens den er i bruk."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"ta bilder og spille inn videoer i bakgrunnen"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Denne appen kan når som helst ta bilder og spille inn videoer med kameraet."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Gi en app eller tjeneste tilgang til systemkameraene for å ta bilder og spille inn videoer"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Denne privilegerte appen eller systemappen kan når som helst ta bilder og spille inn videoer med et systemkamera. Dette krever at appen også har tillatelsen android.permission.CAMERA"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Tillat at en app eller tjeneste mottar tilbakekallinger om kameraenheter som åpnes eller lukkes."</string>
@@ -1104,7 +1098,7 @@
     <string name="elapsed_time_short_format_h_mm_ss" msgid="2302144714803345056">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="1532369154488982046">"Merk alt"</string>
     <string name="cut" msgid="2561199725874745819">"Klipp ut"</string>
-    <string name="copy" msgid="5472512047143665218">"Kopier"</string>
+    <string name="copy" msgid="5472512047143665218">"Kopiér"</string>
     <string name="failed_to_copy_to_clipboard" msgid="725919885138539875">"Kunne ikke kopiere til utklippstavlen"</string>
     <string name="paste" msgid="461843306215520225">"Lim inn"</string>
     <string name="paste_as_plain_text" msgid="7664800665823182587">"Lim inn som ren tekst"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 7b376ff..0326c23 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -78,18 +78,18 @@
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"सेवाको व्यवस्था छैन।"</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"तपाईं कलर ID सेटिङ परिवर्तन गर्न सक्नुहुन्न।"</string>
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"कुनै पनि मोबाइल डेटा सेवा उपलब्ध छैन"</string>
-    <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"आपतकालीन कल सेवा उपलब्ध छैन"</string>
+    <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"आपत्‌कालीन कल सेवा उपलब्ध छैन"</string>
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"कुनै पनि भ्वाइस सेवा उपलब्ध छैन"</string>
     <string name="RestrictedOnAllVoiceTitle" msgid="3982069078579103087">"कुनै पनि भ्वाइस वा आपातकालीन कल सेवा उपलब्ध छैन"</string>
     <string name="RestrictedStateContent" msgid="7693575344608618926">"तपाईंको सेवा प्रदायकले अस्थायी रूपमा निष्क्रिय पार्नुभएको"</string>
     <string name="RestrictedStateContentMsimTemplate" msgid="5228235722511044687">"SIM <xliff:g id="SIMNUMBER">%d</xliff:g> का लागि तपाईंको सेवा प्रदायकले अस्थायी रूपमा निष्क्रिय पार्नुभएको"</string>
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"मोबाइल नेटवर्कमाथि पहुँच राख्न सकिएन"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"रुचाइएको नेटवर्क परिवर्तन गरी हेर्नुहोस्‌। परिवर्तन गर्न ट्याप गर्नुहोस्‌।"</string>
-    <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"आपतकालीन कल सेवा अनुपलब्ध छ"</string>
-    <string name="EmergencyCallWarningSummary" msgid="1194185880092805497">"Wi‑Fi मार्फत आपतकालीन कल गर्न सकिँदैन"</string>
+    <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"आपत्‌कालीन कल सेवा अनुपलब्ध छ"</string>
+    <string name="EmergencyCallWarningSummary" msgid="1194185880092805497">"Wi‑Fi मार्फत आपत्‌कालीन कल गर्न सकिँदैन"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"अलर्टहरू"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"कल फर्वार्ड गर्ने सेवा"</string>
-    <string name="notification_channel_emergency_callback" msgid="54074839059123159">"आपतकालीन कलब्याक मोड"</string>
+    <string name="notification_channel_emergency_callback" msgid="54074839059123159">"आपत्‌कालीन कलब्याक मोड"</string>
     <string name="notification_channel_mobile_data_status" msgid="1941911162076442474">"मोबाइल डेटाको स्थिति"</string>
     <string name="notification_channel_sms" msgid="1243384981025535724">"SMS सन्देशहरू"</string>
     <string name="notification_channel_voice_mail" msgid="8457433203106654172">"भ्वाइस मेल सन्देशहरू"</string>
@@ -241,7 +241,7 @@
     <string name="global_action_power_off" msgid="4404936470711393203">"बन्द गर्नुहोस्"</string>
     <string name="global_action_power_options" msgid="1185286119330160073">"पावर"</string>
     <string name="global_action_restart" msgid="4678451019561687074">"पुनः सुरु गर्नुहोस्"</string>
-    <string name="global_action_emergency" msgid="1387617624177105088">"आपतकालीन"</string>
+    <string name="global_action_emergency" msgid="1387617624177105088">"आपत्‌कालीन"</string>
     <string name="global_action_bug_report" msgid="5127867163044170003">"बग रिपोर्ट"</string>
     <string name="global_action_logout" msgid="6093581310002476511">"सत्रको अन्त्य गर्नुहोस्"</string>
     <string name="global_action_screenshot" msgid="2610053466156478564">"स्क्रिनसट"</string>
@@ -345,24 +345,24 @@
     <string name="permdesc_processOutgoingCalls" msgid="7833149750590606334">"एपलाई अन्य नम्बरमा कल पुर्ननिर्देश वा समग्र कल परित्याग विकल्प सहित बहिर्गमन कल समयमा डायल गर्दाको नम्बर हेर्न अनुमति दिन्छ।"</string>
     <string name="permlab_answerPhoneCalls" msgid="4131324833663725855">"फोन कलहरूको जवाफ दिनुहोस्"</string>
     <string name="permdesc_answerPhoneCalls" msgid="894386681983116838">"एपलाई आगमन फोन कलको जवाफ दिन अनुमति दिन्छ।"</string>
-    <string name="permlab_receiveSms" msgid="505961632050451881">"पाठ सन्देशहरू (SMS) प्राप्त गर्नुहोस्"</string>
+    <string name="permlab_receiveSms" msgid="505961632050451881">"टेक्स्ट म्यासेजहरू (SMS) प्राप्त गर्नुहोस्"</string>
     <string name="permdesc_receiveSms" msgid="1797345626687832285">"एपलाई SMS सन्देशहरू प्राप्त गर्न र प्रक्रिया गर्न अनुमति दिन्छ। यसको मतलब अनुप्रयोगले तपाईंको उपकरणमा पठाइएको सन्देशहरू तपाईंलाई नदेखाईनै मोनिटर गर्न वा मेटाउन सक्दछ।"</string>
     <string name="permlab_receiveMms" msgid="4000650116674380275">"पाठ सन्देश (MMS) प्राप्त गर्नुहोस्"</string>
     <string name="permdesc_receiveMms" msgid="958102423732219710">"एपलाई MMS सन्देशहरू प्राप्त गर्न र प्रकृया गर्न अनुमति दिन्छ। यसको मतलब अनुप्रयोगले तपाईंको उपकरणमा पठाइएको सन्देशहरू तपाईंलाई नदेखाईनै मोनिटर गर्न वा मेटाउन सक्दछ।"</string>
     <string name="permlab_bindCellBroadcastService" msgid="586746677002040651">"मोबाइल प्रसारणसम्बन्धी सन्देशहरू फर्वार्ड गर्नुहोस्"</string>
-    <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"मोबाइल प्रसारणसम्बन्धी सन्देशहरू प्राप्त हुनासाथै तिनीहरूलाई फर्वार्ड गर्नका लागि यसले एपलाई मोबाइल प्रसारण मोड्युलमा जोडिने अनुमति दिन्छ। तपाईंलाई कतिपय स्थानमा आपत्कालीन अवस्थाका बारेमा जानकारी दिनका लागि मोबाइल प्रसारणसम्बन्धी अलर्टहरू पठाइन्छ। हानिकारक एपहरूले आपत्कालीन मोबाइल प्रसारण प्राप्त हुँदा तपाईंको यन्त्रलाई कार्य सम्पादन गर्ने वा सञ्चालित हुने क्रममा हस्तक्षेप गर्न सक्छन्।"</string>
+    <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"मोबाइल प्रसारणसम्बन्धी सन्देशहरू प्राप्त हुनासाथै तिनीहरूलाई फर्वार्ड गर्नका लागि यसले एपलाई मोबाइल प्रसारण मोड्युलमा जोडिने अनुमति दिन्छ। तपाईंलाई कतिपय स्थानमा आपत्‌कालीन अवस्थाका बारेमा जानकारी दिनका लागि मोबाइल प्रसारणसम्बन्धी अलर्टहरू पठाइन्छ। हानिकारक एपहरूले आपत्‌कालीन मोबाइल प्रसारण प्राप्त हुँदा तपाईंको यन्त्रलाई कार्य सम्पादन गर्ने वा सञ्चालित हुने क्रममा हस्तक्षेप गर्न सक्छन्।"</string>
     <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"सेल प्रसारित सन्देशहरू पढ्नुहोस्"</string>
-    <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"तपाईंको उपकरणद्वारा प्राप्त सेल प्रसारण सन्देशहरू एपलाई पढ्न अनुमति दिन्छ। सेल प्रसारण चेतावनीहरू केही स्थानहरूमा तपाईंलाई आपतकालीन गतिविधिहरूको बारेमा सचेत गराउन गरिएका छन्। खराब एपहरूले एउटा आपतकालीन सेल प्रसारण प्राप्त गर्दछ जब तपाईंको उपकरणको प्रदर्शन वा अपरेशनको साथ हस्तक्षेप गर्न सक्दछन्।"</string>
+    <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"तपाईंको उपकरणद्वारा प्राप्त सेल प्रसारण सन्देशहरू एपलाई पढ्न अनुमति दिन्छ। सेल प्रसारण चेतावनीहरू केही स्थानहरूमा तपाईंलाई आपत्‌कालीन गतिविधिहरूको बारेमा सचेत गराउन गरिएका छन्। खराब एपहरूले एउटा आपत्‌कालीन सेल प्रसारण प्राप्त गर्दछ जब तपाईंको उपकरणको प्रदर्शन वा अपरेशनको साथ हस्तक्षेप गर्न सक्दछन्।"</string>
     <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"सदस्य बनाइका फिडहरू पढ्नुहोस्"</string>
     <string name="permdesc_subscribedFeedsRead" msgid="6911349196661811865">"एपलाई अहिलेको समीकरण गरिएका सूचकहरू बारे विवरणहरू लिने अनुमति दिन्छ।"</string>
     <string name="permlab_sendSms" msgid="7757368721742014252">"SMS सन्देशहरू पठाउनुहोस् र हेर्नुहोस्"</string>
     <string name="permdesc_sendSms" msgid="6757089798435130769">"एपलाई SMS सन्देशहरू पठाउन अनुमति दिन्छ। यसले अप्रत्यासित चार्जहरूको परिणाम दिन सक्दछ। खराब एपहरूले तपाईंको पुष्टि बिना सन्देशहरू पठाएर तपाईंको पैसा खर्च गराउन सक्दछ।"</string>
-    <string name="permlab_readSms" msgid="5164176626258800297">"तपाईंका पाठ सन्देशहरू (SMS वा MMS) पढ्नुहोस्"</string>
+    <string name="permlab_readSms" msgid="5164176626258800297">"तपाईंका टेक्स्ट म्यासेजहरू (SMS वा MMS) पढ्नुहोस्"</string>
     <string name="permdesc_readSms" product="tablet" msgid="7912990447198112829">"यस अनुप्रयोगले तपाईंको ट्याब्लेटमा भण्डारण गरिएका सबै SMS (पाठ) सन्देशहरू पढ्न सक्छ।"</string>
     <string name="permdesc_readSms" product="tv" msgid="3054753345758011986">"यस अनुप्रयोगले तपाईंको Android टिभी यन्त्रमा भण्डार गरिएका सबै SMS.(पाठ) सन्देशहरू पढ्न सक्छ।"</string>
     <string name="permdesc_readSms" product="default" msgid="774753371111699782">"यस अनुप्रयोगले तपाईंको फोनमा भण्डारण गरिएका सबै SMS (पाठ) सन्देशहरू पढ्न सक्छ।"</string>
-    <string name="permlab_receiveWapPush" msgid="4223747702856929056">"पाठ सन्देशहरू (WAP) प्राप्त गर्नुहोस्"</string>
-    <string name="permdesc_receiveWapPush" msgid="1638677888301778457">"WAP सन्देशहरू प्राप्त गर्न र प्रशोधन गर्न एपलाई अनुमति दिन्छ। यो अनुमतिमा मोनिटर गर्ने वा तपाईँलाई पठाइएका सन्देशहरू तपाईँलाई नदेखाई मेट्ने क्षमता समावेश हुन्छ।"</string>
+    <string name="permlab_receiveWapPush" msgid="4223747702856929056">"टेक्स्ट म्यासेजहरू (WAP) प्राप्त गर्नुहोस्"</string>
+    <string name="permdesc_receiveWapPush" msgid="1638677888301778457">"WAP सन्देशहरू प्राप्त गर्न र प्रशोधन गर्न एपलाई अनुमति दिन्छ। यो अनुमतिमा मोनिटर गर्ने वा तपाईँलाई पठाइएका म्यासेजहरू तपाईँलाई नदेखाई मेट्ने क्षमता समावेश हुन्छ।"</string>
     <string name="permlab_getTasks" msgid="7460048811831750262">"चलिरहेका एपहरू पुनःबहाली गर्नुहोस्"</string>
     <string name="permdesc_getTasks" msgid="7388138607018233726">"वर्तमानमा र भरखरै चलिरहेका कार्यहरू बारेको सूचना पुनःबहाली गर्न एपलाई अनुमित दिन्छ। यसले उपकरणमा प्रयोग भएका अनुप्रयोगहरूको बारेमा सूचना पत्ता लगाउन एपलाई अनुमति दिन सक्छ।"</string>
     <string name="permlab_manageProfileAndDeviceOwners" msgid="639849495253987493">"प्रोफाइल र यन्त्र मालिकहरूको व्यवस्थापन गराउनुहोस्"</string>
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"तपाईँका अडियो सेटिङहरू परिवर्तन गर्नुहोस्"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"एपलाई ग्लोबल अडियो सेटिङहरू परिमार्जन गर्न अनुमति दिन्छ, जस्तै भोल्युम र आउटपुटको लागि कुन स्पिकर प्रयोग गर्ने।"</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"अडियो रेकर्ड गर्नुहोस्"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"यो एप प्रयोग भइरहेका बेला यसले माइक्रोफोन प्रयोग गरेर अडियो रेकर्ड गर्न सक्छ।"</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"ब्याकग्राउन्डमा अडियो रेकर्ड गर्नुहोस्"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"यो एपले जुनसुकै बेला माइक्रोफोन प्रयोग गरी अडियो रेकर्ड गर्न सक्छ।"</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"SIM मा आदेशहरू पठाउन दिनुहोस्"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"SIM लाई आदेश पठाउन एपलाई अनुमति दिन्छ। यो निकै खतरनाक हुन्छ।"</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"शारीरिक गतिविधि पहिचान गर्नुहोस्‌"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"यो अनुप्रयोगले तपाईंको शारीरिक गतिविधिको पहिचान गर्न सक्छ।"</string>
     <string name="permlab_camera" msgid="6320282492904119413">"फोटोहरू र भिडियोहरू लिनुहोस्।"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"यो एप प्रयोग भइरहेका बेला यसले क्यामेरा प्रयोग गरेर फोटो खिच्न तथा भिडियोहरू रेकर्ड गर्न सक्छ।"</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"ब्याकग्राउन्डमा फोटो खिच्नुहोस् तथा भिडियो रेकर्ड गर्नुहोस्"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"यो एपले जुनसुकै बेला क्यामेराको प्रयोग गरी फोटो खिच्न र भिडियो रेकर्ड गर्न सक्छ।"</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"एप वा सेवालाई फोटो र भिडियो खिच्न प्रणालीका क्यामेराहरूमाथि पहुँच राख्न दिनुहोस्"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"प्रणालीको यस विशेषाधिकार प्राप्त अनुप्रयोगले जुनसुकै बेला प्रणालीको क्यामेरा प्रयोग गरी फोटो खिच्न र भिडियो रेकर्ड गर्न सक्छ। अनुप्रयोगसँग पनि android.permission.CAMERA प्रयोग गर्ने अनुमति हुनु पर्छ"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"कुनै एप वा सेवालाई खोलिँदै वा बन्द गरिँदै गरेका क्यामेरा यन्त्रहरूका बारेमा कलब्याक प्राप्त गर्ने अनुमति दिनुहोस्।"</string>
@@ -457,7 +451,7 @@
     <string name="permdesc_vibrate" msgid="8733343234582083721">"एपलाई भाइब्रेटर नियन्त्रण गर्न अनुमति दिन्छ।"</string>
     <string name="permdesc_vibrator_state" msgid="7050024956594170724">"यो एपलाई कम्पनको स्थितिमाथि पहुँच राख्न दिनुहोस्।"</string>
     <string name="permlab_callPhone" msgid="1798582257194643320">"फोन नम्बरहरूमा सीधै कल गर्नुहोस्"</string>
-    <string name="permdesc_callPhone" msgid="5439809516131609109">"तपाईँको हस्तक्षेप बेगरै फोन नम्बर कल गर्न एपलाई अनुमति दिन्छ। यसले अनपेक्षित शुल्क वा कलहरू गराउन सक्छ। यसले एपलाई आपतकालीन नम्बरहरू कल गर्न अनुमति दिँदैन विचार गर्नुहोस्। खराब एपहरूले तपाईँको स्वीकार बिना कलहरू गरेर तपाईँलाई बढी पैसा तिराउन सक्छ।"</string>
+    <string name="permdesc_callPhone" msgid="5439809516131609109">"तपाईँको हस्तक्षेप बेगरै फोन नम्बर कल गर्न एपलाई अनुमति दिन्छ। यसले अनपेक्षित शुल्क वा कलहरू गराउन सक्छ। यसले एपलाई आपत्‌कालीन नम्बरहरू कल गर्न अनुमति दिँदैन विचार गर्नुहोस्। खराब एपहरूले तपाईँको स्वीकार बिना कलहरू गरेर तपाईँलाई बढी पैसा तिराउन सक्छ।"</string>
     <string name="permlab_accessImsCallService" msgid="442192920714863782">"IMS कल सेवा पहुँच गर्नुहोस्"</string>
     <string name="permdesc_accessImsCallService" msgid="6328551241649687162">"तपाईँको हस्तक्षेप बिना नै कल गर्न IMS सेवा प्रयोग गर्न एपलाई अनुमति दिन्छ।"</string>
     <string name="permlab_readPhoneState" msgid="8138526903259297969">"फोन स्थिति र पहिचान पढ्नुहोस्"</string>
@@ -836,13 +830,13 @@
     <string name="keyguard_password_enter_pin_password_code" msgid="7792964196473964340">"अनलक गर्न PIN कोड टाइप गर्नुहोस्"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="8583732939138432793">"गलत PIN कोड।"</string>
     <string name="keyguard_label_text" msgid="3841953694564168384">"अनलक गर्न मेनु थिच्नुहोस् र त्यसपछि ० थिच्नुहोस्।"</string>
-    <string name="emergency_call_dialog_number_for_display" msgid="2978165477085612673">"आपतकालीन नम्बर"</string>
+    <string name="emergency_call_dialog_number_for_display" msgid="2978165477085612673">"आपत्‌कालीन नम्बर"</string>
     <string name="lockscreen_carrier_default" msgid="6192313772955399160">"कुनै सेवा छैन"</string>
     <string name="lockscreen_screen_locked" msgid="7364905540516041817">"स्क्रिन लक गरिएको।"</string>
-    <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"अनलक वा आपतकालीन कल गर्न मेनु थिच्नुहोस्।"</string>
+    <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"अनलक वा आपत्‌कालीन कल गर्न मेनु थिच्नुहोस्।"</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"अनलक गर्न मेनु थिच्नुहोस्।"</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"अनलक गर्नु ढाँचा खिच्नुहोस्"</string>
-    <string name="lockscreen_emergency_call" msgid="7549683825868928636">"आपतकालीन कल"</string>
+    <string name="lockscreen_emergency_call" msgid="7549683825868928636">"आपत्‌कालीन कल"</string>
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"कलमा फर्किनुहोस्"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"सही!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"फेरि प्रयास गर्नुहोस्"</string>
@@ -864,7 +858,7 @@
     <string name="lockscreen_transport_stop_description" msgid="1449552232598355348">"रोक्नुहोस्"</string>
     <string name="lockscreen_transport_rew_description" msgid="7680106856221622779">"दोहोर्याउनुहोस्"</string>
     <string name="lockscreen_transport_ffw_description" msgid="4763794746640196772">"फास्ट फर्वार्ड"</string>
-    <string name="emergency_calls_only" msgid="3057351206678279851">"आपतकालीन कलहरू मात्र"</string>
+    <string name="emergency_calls_only" msgid="3057351206678279851">"आपत्‌कालीन कलहरू मात्र"</string>
     <string name="lockscreen_network_locked_message" msgid="2814046965899249635">"नेटवर्क लक छ"</string>
     <string name="lockscreen_sim_puk_locked_message" msgid="6618356415831082174">"SIM कार्ड PUK-लक गरिएको छ।"</string>
     <string name="lockscreen_sim_puk_locked_instructions" msgid="5307979043730860995">"प्रयोगकर्ता निर्देशक वा ग्राहक सेवा सम्पर्क हर्नुहोस्।"</string>
@@ -1395,7 +1389,7 @@
     <string name="ext_media_status_unmountable" msgid="7043574843541087748">"बिग्रेको"</string>
     <string name="ext_media_status_unsupported" msgid="5460509911660539317">"असमर्थित"</string>
     <string name="ext_media_status_ejecting" msgid="7532403368044013797">"निकाल्दै..."</string>
-    <string name="ext_media_status_formatting" msgid="774148701503179906">"फरम्याट गर्दै…"</string>
+    <string name="ext_media_status_formatting" msgid="774148701503179906">"फर्म्याट गर्दै…"</string>
     <string name="ext_media_status_missing" msgid="6520746443048867314">"सम्मिलित छैन"</string>
     <string name="activity_list_empty" msgid="4219430010716034252">"कुनै मिल्ने गतिविधि पाइएन।"</string>
     <string name="permlab_route_media_output" msgid="8048124531439513118">"मिडिया निकास दिशानिर्देश गराउनुहोस्"</string>
@@ -1483,7 +1477,7 @@
     <string name="add_account_button_label" msgid="322390749416414097">"खाता थप्नुहोस्"</string>
     <string name="number_picker_increment_button" msgid="7621013714795186298">"बढाउनुहोस्"</string>
     <string name="number_picker_decrement_button" msgid="5116948444762708204">"घटाउनुहोस्"</string>
-    <string name="number_picker_increment_scroll_mode" msgid="8403893549806805985">"<xliff:g id="VALUE">%s</xliff:g> छोइराख्नुहोस्।"</string>
+    <string name="number_picker_increment_scroll_mode" msgid="8403893549806805985">"<xliff:g id="VALUE">%s</xliff:g> टच एण्ड होल्ड गर्नुहोस्।"</string>
     <string name="number_picker_increment_scroll_action" msgid="8310191318914268271">"बढाउन माथि र घटाउन तल सार्नुहोस्।"</string>
     <string name="time_picker_increment_minute_button" msgid="7195870222945784300">"मिनेट बढाउनुहोस्"</string>
     <string name="time_picker_decrement_minute_button" msgid="230925389943411490">"मिनेट घटाउनुहोस्"</string>
@@ -1666,9 +1660,9 @@
     <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"तपाईंले पहुँचको बटन ट्याप गर्दा प्रयोग गर्न चाहनुभएको सुविधा छनौट गर्नुहोस्:"</string>
     <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"तपाईंले पहुँचको इसारामार्फत प्रयोग गर्न चाहनुभएको सुविधा छनौट गर्नुहोस् (दुईवटा औँलाले स्क्रिनको फेदबाट माथितिर स्वाइप गर्नुहोस्):"</string>
     <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"तपाईंले पहुँचको इसारामार्फत प्रयोग गर्न चाहनुभएको सुविधा छनौट गर्नुहोस् (तीनवटा औँलाले स्क्रिनको फेदबाट माथितिर स्वाइप गर्नुहोस्):"</string>
-    <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"एउटा सुविधाबाट अर्को सुविधामा जान पहुँच बटन छोइराख्नुहोस्।"</string>
-    <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"एउटा सुविधाबाट अर्को सुविधामा जान दुईवटा औँलाले माथितिर स्वाइप गरी स्क्रिनमा छोइराख्नुहोस्।"</string>
-    <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"एउटा सुविधाबाट अर्को सुविधामा जान तीनवटा औँलाले माथितिर स्वाइप गरी स्क्रिनमा छोइराख्नुहोस्।"</string>
+    <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"एउटा सुविधाबाट अर्को सुविधामा जान पहुँच बटन टच एण्ड होल्ड गर्नुहोस्।"</string>
+    <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"एउटा सुविधाबाट अर्को सुविधामा जान दुईवटा औँलाले माथितिर स्वाइप गरी स्क्रिनमा टच एण्ड होल्ड गर्नुहोस्।"</string>
+    <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"एउटा सुविधाबाट अर्को सुविधामा जान तीनवटा औँलाले माथितिर स्वाइप गरी स्क्रिनमा टच एण्ड होल्ड गर्नुहोस्।"</string>
     <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"म्याग्निफिकेसन"</string>
     <string name="user_switched" msgid="7249833311585228097">"अहिलेको प्रयोगकर्ता <xliff:g id="NAME">%1$s</xliff:g>।"</string>
     <string name="user_switching_message" msgid="1912993630661332336">"<xliff:g id="NAME">%1$s</xliff:g> मा स्विच गर्दै..."</string>
@@ -1972,7 +1966,7 @@
     <string name="etws_primary_default_message_earthquake" msgid="8401079517718280669">"शान्त रहनुहोस् र नजिकै आश्रयस्थल खोज्नुहोस्।"</string>
     <string name="etws_primary_default_message_tsunami" msgid="5828171463387976279">"तटीय क्षेत्र र नदीछेउका ठाउँहरू छाडी उच्च सतहमा अवस्थित कुनै अझ सुरक्षित ठाउँमा जानुहोस्।"</string>
     <string name="etws_primary_default_message_earthquake_and_tsunami" msgid="4888224011071875068">"शान्त रहनुहोस् र नजिकै आश्रयस्थल खोज्नुहोस्।"</string>
-    <string name="etws_primary_default_message_test" msgid="4583367373909549421">"आपतकालीन सन्देशहरूको परीक्षण"</string>
+    <string name="etws_primary_default_message_test" msgid="4583367373909549421">"आपत्‌कालीन सन्देशहरूको परीक्षण"</string>
     <string name="notification_reply_button_accessibility" msgid="5235776156579456126">"जवाफ दिनु…"</string>
     <string name="etws_primary_default_message_others" msgid="7958161706019130739"></string>
     <string name="mmcc_authentication_reject" msgid="4891965994643876369">"SIM मार्फत भ्वाइस कल गर्न मिल्दैन"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index d601905..17e988e4 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"je audio-instellingen wijzigen"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Hiermee kan de app algemene audio-instellingen wijzigen zoals het volume en welke luidspreker wordt gebruikt voor de uitvoer."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"audio opnemen"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Deze app kan audio opnemen met de microfoon als de app wordt gebruikt."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"audio opnemen op de achtergrond"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Deze app kan altijd audio opnemen met de microfoon."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"opdrachten verzenden naar de simkaart"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Hiermee kan de app opdrachten verzenden naar de simkaart. Dit is erg gevaarlijk."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"fysieke activiteit herkennen"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Deze app kan je fysieke activiteit herkennen."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"foto\'s en video\'s maken"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Deze app kan foto\'s maken en video\'s opnemen met de camera als de app wordt gebruikt."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"foto\'s maken en video\'s opnemen op de achtergrond"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Deze app kan altijd foto\'s maken en video\'s opnemen met de camera."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Een app of service toegang tot systeemcamera\'s geven om foto\'s en video\'s te maken"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Deze gemachtigde app of systeem-app kan op elk gewenst moment foto\'s maken en video\'s opnemen met een systeemcamera. De app moet ook het recht android.permission.CAMERA hebben."</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Een app of service toestaan callbacks te ontvangen over camera-apparaten die worden geopend of gesloten."</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index f3a225a..0f10cde 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"ଆପଣଙ୍କ ଅଡିଓ ସେଟିଙ୍ଗକୁ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"ଆପ୍‌କୁ ଗ୍ଲୋବାଲ୍ ଅଡିଓ ସେଟିଙ୍ଗ, ଯେପରିକି ଭଲ୍ୟୁମ୍‌କୁ ସଂଶୋଧିତ କରିବାକୁ ଏବଂ ଆଉଟପୁଟ୍ ପାଇଁ ସ୍ପିକର୍‌ ବ୍ୟବହାର କରିବାକୁ ଅନୁମତି ଦେଇଥାଏ।"</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"ଅଡିଓ ରେକର୍ଡ କରନ୍ତୁ"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"ଆପକୁ ବ୍ୟବହାର କରାଯାଉଥିବା ସମୟରେ ଏହା ମାଇକ୍ରୋଫୋନକୁ ବ୍ୟବହାର କରି ଅଡିଓ ରେକର୍ଡ କରିପାରିବ।"</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"ପୃଷ୍ଠପଟରେ ଅଡିଓ ରେକର୍ଡ କରନ୍ତୁ"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"ଏହି ଆପ୍ ଯେ କୌଣସି ସମୟରେ ମାଇକ୍ରୋଫୋନକୁ ବ୍ୟବହାର କରି ଅଡିଓ ରେକର୍ଡ କରିପାରିବ।"</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"SIMକୁ କମାଣ୍ଡ ପଠାନ୍ତୁ"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"SIMକୁ କମାଣ୍ଡ ପଠାଇବା ପାଇଁ ଆପ୍‍କୁ ଅନୁମତି ଦେଇଥାଏ। ଏହା ବହୁତ ବିପଦପୂର୍ଣ୍ଣ।"</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"ଶାରୀରିକ ଗତିବିଧି ଚିହ୍ନଟକରେ"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"ଏହି ଆପ୍‍ଣ ଆପଣଙ୍କ ଶାରୀରିକ ଗତିବିଧିକୁ ଚିହ୍ନଟ କରିପାରେ"</string>
     <string name="permlab_camera" msgid="6320282492904119413">"ଫଟୋ ଓ ଭିଡିଓଗୁଡ଼ିକୁ ନିଅନ୍ତୁ"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"ଆପକୁ ବ୍ୟବହାର କରାଯାଉଥିବା ସମୟରେ ଏହା କ୍ୟାମେରାକୁ ବ୍ୟବହାର କରି ଛବି ନେଇପାରିବ ଏବଂ ଭିଡିଓ ରେକର୍ଡ କରିପାରିବ।"</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"ପୃଷ୍ଠପଟରେ ଛବି ନିଅନ୍ତୁ ଏବଂ ଭିଡିଓ ରେକର୍ଡ କରନ୍ତୁ"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"କ୍ୟାମେରାକୁ ବ୍ୟବହାର କରି ଯେ କୌଣସି ସମୟରେ ଏହି ଆପ୍ ଛବି ନେଇପାରିବ ଏବଂ ଭିଡିଓ ରେକର୍ଡ କରିପାରିବ।"</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"ଛବି ଏବଂ ଭିଡିଓଗୁଡ଼ିକୁ ନେବା ପାଇଁ ସିଷ୍ଟମ୍ କ୍ୟାମେରା‌ଗୁଡ଼ିକୁ କୌଣସି ଆପ୍ଲିକେସନ୍ କିମ୍ବା ସେବା ଆକ୍ସେସ୍ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"ବିଶେଷ ଅଧିକାର ଥିବା ଏହି ଆପ୍ କିମ୍ବା ସିଷ୍ଟମ୍ ଆପ୍ ଯେ କୌଣସି ସମୟରେ ଏକ ସିଷ୍ଟମ୍ କ୍ୟାମେରା ବ୍ୟବହାର କରି ଛବି ଉଠାଇପାରିବ ଏବଂ ଭିଡିଓ ରେକର୍ଡ କରିପାରିବ। ଆପରେ ମଧ୍ୟ android.permission.CAMERA ଅନୁମତି ଆବଶ୍ୟକ"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"କ୍ୟାମେରା ଡିଭାଇସଗୁଡ଼ିକ ଖୋଲିବା କିମ୍ବା ବନ୍ଦ କରିବା ବିଷୟରେ କଲବ୍ୟାକଗୁଡ଼ିକ ପାଇବାକୁ ଏକ ଆପ୍ଲିକେସନ୍ କିମ୍ବା ସେବାକୁ ଅନୁମତି ଦିଅନ୍ତୁ।"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 6ea5f52..11bfd78 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"ਆਪਣੀਆਂ ਆਡੀਓ ਸੈਟਿੰਗਾਂ ਬਦਲੋ"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"ਐਪ ਨੂੰ ਗਲੋਬਲ ਆਡੀਓ ਸੈਟਿੰਗਾਂ ਸੰਸ਼ੋਧਿਤ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ ਜਿਵੇਂ ਅਵਾਜ਼ ਅਤੇ ਆਊਟਪੁਟ ਲਈ ਕਿਹੜਾ ਸਪੀਕਰ ਵਰਤਿਆ ਜਾਂਦਾ ਹੈ।"</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">" ਆਡੀਓ  ਰਿਕਾਰਡ ਕਰਨ"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"ਇਹ ਐਪ ਵਰਤੋਂ ਵਿੱਚ ਹੋਣ ਵੇਲੇ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਨੂੰ ਵਰਤ ਕੇ ਆਡੀਓ ਰਿਕਾਰਡ ਕਰ ਸਕਦੀ ਹੈ।"</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਆਡੀਓ ਰਿਕਾਰਡ ਕਰੋ"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"ਇਹ ਐਪ ਕਿਸੇ ਵੇਲੇ ਵੀ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਨੂੰ ਵਰਤ ਕੇ ਆਡੀਓ ਰਿਕਾਰਡ ਕਰ ਸਕਦੀ ਹੈ।"</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"SIM ਨੂੰ ਕਮਾਂਡਾਂ ਭੇਜੋ"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"ਐਪ ਨੂੰ SIM ਨੂੰ ਕਮਾਂਡਾਂ ਭੇਜਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਹ ਬਹੁਤ ਘਾਤਕ ਹੈ।"</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"ਸਰੀਰਕ ਸਰਗਰਮੀ ਨੂੰ ਪਛਾਣਨਾ"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"ਇਹ ਐਪ ਤੁਹਾਡੀ ਸਰੀਰਕ ਸਰਗਰਮੀ ਨੂੰ ਪਛਾਣ ਸਕਦੀ ਹੈ।"</string>
     <string name="permlab_camera" msgid="6320282492904119413">"ਤਸਵੀਰਾਂ ਅਤੇ ਵੀਡੀਓ ਬਣਾਓ"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"ਇਹ ਐਪ ਵਰਤੋਂ ਵਿੱਚ ਹੋਣ ਵੇਲੇ ਕੈਮਰੇ ਨੂੰ ਵਰਤ ਕੇ ਤਸਵੀਰਾਂ ਖਿੱਚ ਸਕਦੀ ਹੈ ਅਤੇ ਵੀਡੀਓ ਰਿਕਾਰਡ ਕਰ ਸਕਦੀ ਹੈ।"</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਤਸਵੀਰਾਂ ਖਿੱਚੋ ਅਤੇ ਵੀਡੀਓ ਰਿਕਾਰਡ ਕਰੋ"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"ਇਹ ਐਪ ਕਿਸੇ ਵੇਲੇ ਵੀ ਕੈਮਰੇ ਨੂੰ ਵਰਤ ਕੇ ਤਸਵੀਰਾਂ ਖਿੱਚ ਸਕਦੀ ਹੈ ਅਤੇ ਵੀਡੀਓ ਰਿਕਾਰਡ ਕਰ ਸਕਦੀ ਹੈ।"</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"ਸਿਸਟਮ ਕੈਮਰੇ ਨੂੰ ਤਸਵੀਰਾਂ ਅਤੇ ਵੀਡੀਓ ਬਣਾਉਣ ਲਈ ਐਪਲੀਕੇਸ਼ਨ ਜਾਂ ਸੇਵਾ ਤੱਕ ਪਹੁੰਚ ਦਿਓ"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"ਇਹ ਵਿਸ਼ੇਸ਼ ਅਧਿਕ੍ਰਿਤ ਜਾਂ ਸਿਸਟਮ ਐਪ ਕਿਸੇ ਵੇਲੇ ਵੀ ਸਿਸਟਮ ਕੈਮਰੇ ਨੂੰ ਵਰਤ ਕੇ ਤਸਵੀਰਾਂ ਖਿੱਚ ਸਕਦੀ ਹੈ ਅਤੇ ਵੀਡੀਓ ਫ਼ਾਈਲਾਂ ਰਿਕਾਰਡ ਕਰ ਸਕਦੀ ਹੈ। ਐਪ ਨੂੰ ਵੀ android.permission.CAMERA ਇਜਾਜ਼ਤ ਦੀ ਲੋੜ ਹੈ"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"ਐਪਲੀਕੇਸ਼ਨ ਜਾਂ ਸੇਵਾ ਨੂੰ ਕੈਮਰਾ ਡੀਵਾਈਸਾਂ ਦੇ ਚਾਲੂ ਜਾਂ ਬੰਦ ਕੀਤੇ ਜਾਣ ਬਾਰੇ ਕਾਲਬੈਕ ਪ੍ਰਾਪਤ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ।"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index ca8522b..d626513 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -438,23 +438,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"zmienianie ustawień audio"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Pozwala aplikacji na modyfikowanie globalnych ustawień dźwięku, takich jak głośność oraz urządzenie wyjściowe."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"nagrywanie dźwięku"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Ta aplikacja może nagrywać dźwięk przy użyciu mikrofonu, gdy jest używana."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"nagrywanie dźwięku w tle"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Ta aplikacja może w dowolnym momencie nagrywać dźwięk przy użyciu mikrofonu."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"wysyłanie poleceń do karty SIM"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Pozwala aplikacji na wysyłanie poleceń do karty SIM. To bardzo niebezpieczne."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"rozpoznawanie aktywności fizycznej"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Ta aplikacja może rozpoznawać Twoją aktywność fizyczną."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"wykonywanie zdjęć i filmów wideo"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Ta aplikacja może robić zdjęcia i nagrywać filmy przy użyciu aparatu, gdy jest używana."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"robienie zdjęć i nagrywanie filmów w tle"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Ta aplikacja może w dowolnym momencie robić zdjęcia i nagrywać filmy przy użyciu aparatu."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Zezwól na dostęp aplikacji lub usługi do aparatów systemu i robienie zdjęć oraz nagrywanie filmów"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Ta aplikacja systemowa z podwyższonymi uprawnieniami może w dowolnym momencie robić zdjęcia i nagrywać filmy przy użyciu aparatu systemowego. Wymaga przyznania uprawnień android.permission.CAMERA również aplikacji."</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Zezwól na dostęp aplikacji lub usługi na otrzymywanie wywoływania zwrotnego o urządzeniach z aparatem, kiedy są one uruchamiane lub zamykane."</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index c318a24..6862001 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"alterar as suas definições de áudio"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Permite que a app modifique definições de áudio globais, tais como o volume e qual o altifalante utilizado para a saída de som."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"gravar áudio"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Esta app pode gravar áudio através do microfone enquanto estiver a ser utilizada."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"gravar áudio em segundo plano"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Esta app pode gravar áudio através do microfone em qualquer altura."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"enviar comandos para o SIM"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Permite que a app envie comandos para o SIM. Esta ação é muito perigosa."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"reconhecer a atividade física"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Esta app consegue reconhecer a sua atividade física."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"tirar fotos e vídeos"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Esta app pode tirar fotos e gravar vídeos através da câmara enquanto estiver a ser utilizada."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"tirar fotos e gravar vídeos em segundo plano"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Esta app pode tirar fotos e gravar vídeos através da câmara em qualquer altura."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Permitir que uma app ou um serviço aceda às câmaras do sistema para tirar fotos e vídeos"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Esta app do sistema ou privilegiada pode tirar fotos e gravar vídeos através de uma câmara do sistema em qualquer altura. Também necessita da autorização android.permission.CAMERA para a app."</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Permitir que uma app ou um serviço receba chamadas de retorno sobre dispositivos de câmara que estão a ser abertos ou fechados"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 0efa74a..f9e608f 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -435,23 +435,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"modificare setări audio"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Permite aplicației să modifice setările audio globale, cum ar fi volumul și difuzorul care este utilizat pentru ieșire."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"înregistreze sunet"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Această aplicație poate să înregistreze conținut audio folosind microfonul când este în uz."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"să înregistreze conținut audio în fundal"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Această aplicație poate înregistra conținut audio folosind microfonul oricând."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"să trimită comenzi către SIM"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Permite aplicației să trimită comenzi pe cardul SIM. Această permisiune este foarte periculoasă."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"recunoașterea activității fizice"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Această aplicație vă poate recunoaște activitatea fizică."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"realizarea de fotografii și videoclipuri"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Această aplicație poate să fotografieze și să înregistreze videoclipuri folosind camera foto când este în uz."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"să fotografieze și să înregistreze videoclipuri în fundal"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Această aplicație poate să fotografieze și să înregistreze videoclipuri folosind camera foto oricând."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Permiteți unei aplicații sau unui serviciu accesul la camerele de sistem, ca să fotografieze și să înregistreze videoclipuri"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Această aplicație de sistem privilegiată poate să fotografieze și să înregistreze videoclipuri folosind o cameră de sistem în orice moment. Necesită și permisiunea android.permission.CAMERA pentru aplicație"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Permiteți unei aplicații sau unui serviciu să primească apeluri inverse atunci când sunt deschise sau închise dispozitive cu cameră."</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index d074960..be24770 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -438,23 +438,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"Изменение настроек аудио"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Приложение сможет изменять системные настройки звука, например уровень громкости и активный динамик."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"Запись аудио"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Когда приложение используется, оно может записывать аудио с помощью микрофона."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"Записывать аудио в фоновом режиме"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Приложение может в любое время записывать аудио с помощью микрофона."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"Отправка команд SIM-карте"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Приложение сможет отправлять команды SIM-карте (данное разрешение представляет большую угрозу)."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"распознавать физическую активность"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Приложение может распознавать физическую активность."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"Фото- и видеосъемка"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Когда приложение используется, оно может делать фотографии и снимать видео с помощью камеры."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"Делать фотографии и снимать видео в фоновом режиме"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Приложение может в любое время делать фотографии и снимать видео с помощью камеры."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Доступ приложения или сервиса к системным настройкам камеры, позволяющим снимать фото и видео"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Это привилегированное или системное приложение может в любое время делать фотографии и записывать видео с помощью камеры. Для этого приложению также требуется разрешение android.permission.CAMERA."</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Разрешить приложению или сервису получать обратные вызовы при открытии и закрытии камер"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 6d88677..c23e475 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"ඔබගේ ශ්‍රව්‍ය සැකසීම් වෙනස් කරන්න"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"ශබ්දය ආදී ගෝලීය ශබ්ද සැකසීම් වෙනස් කිරීමට සහ ප්‍රතිදානය සඳහා භාවිත කරන්නේ කුමන නාදකය දැයි තේරීමට යෙදුමට අවසර දෙන්න."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"ශබ්ද පටිගත කරන්න"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"මෙම යෙදුමට එය භාවිතයෙහි ඇති අතරතුර මයික්‍රෆෝනය භාවිත කර ඕඩියෝ පටිගත කිරීමට හැකිය."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"පසුබිමෙහි ඕඩියෝ පටිගත කරන්න"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"මෙම යෙදුමට ඕනෑම වේලාවක මයික්‍රෆෝනය භාවිත කර ඕඩියෝ පටිගත කිරීමට හැකිය."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"SIM වෙත විධාන යැවීම"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"SIM වෙත විධාන ගෙන යාමට යෙදුමට අවසර දෙයි. මෙය ඉතා භයානක වේ."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"ශාරීරික ක්‍රියාකාරකම හඳුනා ගන්න"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"මෙම යෙදුමට ඔබේ ශාරීරික ක්‍රියාකාරකම හඳුනා ගැනීමට නොහැකිය"</string>
     <string name="permlab_camera" msgid="6320282492904119413">"පින්තූර සහ වීඩියෝ ගන්න"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"මෙම යෙදුමට එය භාවිතයෙහි ඇති අතරතුර කැමරාව භාවිත කර පින්තූර ගැනීමට සහ වීඩියෝ පටිගත කිරීමට හැකිය."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"පසුබිමෙහි පින්තූර සහ වීඩියෝ ගන්න"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"මෙම යෙදුමට ඕනෑම වේලාවක කැමරාව භාවිත කර පින්තූර ගැනීමට සහ වීඩියෝ පටිගත කිරීමට හැකිය."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"පින්තූර සහ වීඩියෝ ගැනීමට පද්ධති කැමරාවලට යෙදුමකට හෝ සේවාවකට ප්‍රවේශය ඉඩ දෙන්න"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"මෙම වරප්‍රසාද ලත් හෝ පද්ධති යෙදුමට ඕනෑම වේලාවක පද්ධති කැමරාව භාවිත කර පින්තූර ගැනීමට සහ වීඩියෝ පටිගත කිරීමට හැකිය. යෙදුම විසින් රඳවා තබා ගැනීමට android.permission.CAMERA ප්‍රවේශයද අවශ්‍ය වේ"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"විවෘත වෙමින් හෝ වැසෙමින් පවතින කැමරා උපාංග පිළිබඳ පසු ඇමතුම් ලබා ගැනීමට යෙදුමකට හෝ සේවාවකට ඉඩ දෙන්න."</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 5d7f8f6..980a945 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -438,23 +438,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"meniť nastavenia zvuku"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Umožňuje aplikácii upraviť globálne nastavenia zvuku, ako je hlasitosť, alebo určiť, z ktorého reproduktora bude zvuk vychádzať."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"nahrávať zvuk"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Táto aplikácia môže nahrávať zvuk pomocou mikrofónu, keď ju používate."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"nahrávanie zvuku na pozadí"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Táto aplikácia môže kedykoľvek nahrávať zvuk pomocou mikrofónu."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"posielanie príkazov do SIM karty"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Umožňuje aplikácii odosielať príkazy na SIM kartu. Toto je veľmi nebezpečné povolenie."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"rozpoznávanie fyzickej aktivity"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Táto aplikácia dokáže rozpoznať vašu fyzickú aktivitu."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"fotiť a nakrúcať videá"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Táto aplikácia môže fotiť a nahrávať videá pomocou fotoaparátu, keď ju používate."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"fotenie a nahrávanie videí na pozadí"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Táto aplikácia môže kedykoľvek fotiť a nahrávať videá pomocou fotoaparátu."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Povoľte aplikácii alebo službe prístup k fotoaparátom systému na snímanie fotiek a videí"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Táto oprávnená alebo systémová aplikácia môže kedykoľvek fotiť a nahrávať videá fotoaparátom systému. Aplikácia musí mať tiež povolenie android.permission.CAMERA."</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Povoliť aplikácii alebo službe prijímať spätné volanie, keď sú zariadenia s kamerou otvorené alebo zatvorené."</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 7e6e300..573a082 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -438,23 +438,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"spreminjanje nastavitev zvoka"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Aplikaciji omogoča spreminjanje splošnih zvočnih nastavitev, na primer glasnost in kateri zvočnik se uporablja."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"snemanje zvoka"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Ta aplikacija lahko uporablja mikrofon za snemanje zvoka med uporabo aplikacije."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"snemanje zvoka v ozadju"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Ta aplikacija lahko poljubno uporablja mikrofon za snemanje zvoka."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"pošiljanje ukazov na kartico SIM"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Aplikaciji dovoli pošiljanje ukazov kartici SIM. To je lahko zelo nevarno."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"prepoznavanje telesne dejavnosti"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Ta aplikacija lahko prepoznava vašo telesno dejavnost."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"fotografiranje in snemanje videoposnetkov"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Ta aplikacija lahko uporablja fotoaparat za snemanje fotografij in videoposnetkov med uporabo aplikacije."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"snemanje fotografij in videoposnetkov v ozadju"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Ta aplikacija lahko poljubno uporablja fotoaparat za snemanje fotografij in videoposnetkov."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Aplikaciji ali storitvi dovoli dostop do vgrajenih fotoaparatov za snemanje fotografij in videoposnetkov"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Ta prednostna ali sistemska aplikacija lahko z vgrajenim fotoaparatom kadar koli snema fotografije in videoposnetke. Aplikacija mora imeti omogočeno tudi dovoljenje android.permission.CAMERA"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Aplikaciji ali storitvi dovoli prejemanje povratnih klicev o odpiranju ali zapiranju naprav s fotoaparati."</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index f6ef6e9..f6f3594 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"ndrysho cilësimet e audios"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Lejon aplikacionin të modifikojë cilësimet globale të audios siç është volumi dhe se cili altoparlant përdoret për daljen."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"regjistro audio"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Ky aplikacion mund të regjistrojë audion duke përdorur mikrofonin kur aplikacioni është në përdorim."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"të regjistrojë audion në sfond"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Ky aplikacion mund të regjistrojë audion me mikrofonin në çdo kohë."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"dërgo komanda te karta SIM"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Lejon aplikacionin t\'i dërgojë komanda kartës SIM. Kjo është shumë e rrezikshme."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"njih aktivitetin fizik"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Ky aplikacion mund të njohë aktivitetin tënd fizik."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"bëj fotografi dhe video"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Ky aplikacion mund të nxjerrë fotografi dhe të regjistrojë video duke përdorur kamerën kur aplikacioni është në përdorim."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"të nxjerrë fotografi dhe video në sfond"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Ky aplikacion mund të nxjerrë fotografi dhe të regjistrojë video me kamerën tënde në çdo kohë."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Lejo një aplikacion ose shërbim të ketë qasje në kamerat e sistemit për të shkrepur fotografi dhe regjistruar video"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Ky aplikacion sistemi ose i privilegjuar mund të nxjerrë fotografi dhe të regjistrojë video duke përdorur një kamerë në çdo moment. Kërkon që autorizimi i android.permission.CAMERA të mbahet edhe nga aplikacioni"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Lejo që një aplikacion ose shërbim të marrë telefonata mbrapsht për pajisjet e kamerës që hapen ose mbyllen."</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index d799ae9..366d29b 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -435,23 +435,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"промена аудио подешавања"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Дозвољава апликацији да мења глобална аудио подешавања као што су јачина звука и избор звучника који се користи као излаз."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"снимање аудио записа"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Ова апликација може да снима звук помоћу микрофона док се апликација користи."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"да снима звук у позадини"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Ова апликација може да снима звук помоћу микрофона у било ком тренутку."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"слање команди на SIM"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Омогућава апликацији да шаље команде SIM картици. То је веома опасно."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"препознавање физичких активности"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Ова апликација може да препозна физичке активности."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"снимање фотографија и видео снимака"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Ова апликација може да снима слике и видео снимке помоћу камере док се апликација користи."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"да снима слике и видео снимке у позадини"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Ова апликација може да снима фотографије и видео снимке помоћу камере у било ком тренутку."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Дозволите некој апликацији или услузи да приступа камерама система да би снимала слике и видео снимке"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Ова привилегована системска апликација може да снима слике и видео снимке помоћу камере система у било ком тренутку. Апликација треба да има и дозволу android.permission.CAMERA"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Дозволите апликацији или услузи да добија повратне позиве о отварању или затварању уређаја са камером."</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 318f5a8..988fb93 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"ändra dina ljudinställningar"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Tillåter att appen ändrar globala ljudinställningar som volym och vilken högtalarutgång som används."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"spela in ljud"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Appen kan ta spela in ljud med mikrofonen när appen används."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"spela in ljud i bakgrunden"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Appen kan spela in ljud med mikrofonen när som helst."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"skicka kommandon till SIM-kortet"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Tillåter att appen skickar kommandon till SIM-kortet. Detta är mycket farligt."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"känn igen fysisk aktivitet"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Den här appen kan känna igen fysisk aktivitet."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"ta bilder och spela in videoklipp"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Appen kan ta bilder och spela in video med kameran när appen används."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"ta bilder och spela in video i bakgrunden"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Appen kan ta bilder och spela in video med kameran när som helst."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Ge en app eller tjänst behörighet att ta bilder och spela in videor med systemets kameror"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Denna systemapp med särskild behörighet kan ta bilder och spela in videor med systemets kamera när som helst. Appen måste även ha behörigheten android.permission.CAMERA"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Tillåt att en app eller tjänst får återanrop när en kameraenhet öppnas eller stängs."</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index a59499f..fd390d8 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"badilisha mipangilio yako ya sauti"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Inaruhusu programu kurekebisha mipangilio ya sauti kila mahali kama vile sauti na ni kipaza sauti kipi ambacho kinatumika kwa kutoa."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"kurekodi sauti"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Programu hii inaweza kurekodi sauti kwa kutumia maikrofoni wakati programu inatumika."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"rekodi sauti chinichini"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Programu hii inaweza kurekodi sauti kwa kutumia maikrofoni wakati wowote."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"tuma amri kwenye SIM"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Huruhusu programu kutuma amri kwa SIM. Hii ni hatari sana."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"itambue shughuli unazofanya"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Programu hii inaweza kutambua shughuli unazofanya."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"Kupiga picha na kurekodi video"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Programu hii inaweza kupiga picha na kurekodi video kwa kutumia kamera wakati programu inatumika."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"piga picha na urekodi video chinichini"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Programu hii inaweza kupiga picha na kurekodi video kwa kutumia kamera wakati wowote."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Ruhusu programu au huduma ifikie kamera za mfumo ili ipige picha na irekodi video"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Programu hii ya mfumo au inayopendelewa inaweza kupiga picha na kurekodi video ikitumia kamera ya mfumo wakati wowote. Inahitaji ruhusa ya android.permission.CAMERA iwepo kwenye programu pia"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Ruhusu programu au huduma ipokee simu zinazopigwa tena kuhusu vifaa vya kamera kufunguliwa au kufungwa."</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 49e6c7c..c764a20 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"எனது ஆடியோ அமைப்புகளை மாற்றுதல்"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"ஒலியளவு மற்றும் வெளியீட்டிற்கு ஸ்பீக்கர்கள் பயன்படுத்தப்படுவது போன்ற ஒட்டுமொத்த ஆடியோ அமைப்புகளைக் கட்டுப்படுத்தப் ஆப்ஸை அனுமதிக்கிறது."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"ஆடியோவைப் பதிவுசெய்தல்"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"இந்த ஆப்ஸ் உபயோகத்தில் இருக்கும்போதே இதனால் மைக்ரோஃபோனைப் பயன்படுத்தி ஆடியோவை ரெக்கார்டு செய்ய முடியும்."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"பின்புலத்தில் ஆடியோ ரெக்கார்டு செய்தல்"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"இந்த ஆப்ஸால் எப்போது வேண்டுமானாலும் மைக்ரோஃபோனைப் பயன்படுத்தி ஆடியோவை ரெக்கார்டு செய்ய முடியும்."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"கட்டளைகளை சிம்மிற்கு அனுப்புதல்"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"சிம் க்குக் கட்டளைகளை அனுப்ப ஆப்ஸை அனுமதிக்கிறது. இது மிகவும் ஆபத்தானதாகும்."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"உடல் செயல்பாட்டைக் கண்டறிதல்"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"உங்கள் உடல் செயல்பாட்டை இந்த ஆப்ஸால் கண்டறிய முடியும்."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"படங்கள் மற்றும் வீடியோக்களை எடுத்தல்"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"இந்த ஆப்ஸ் உபயோகத்தில் இருக்கும்போதே இதனால் கேமராவைப் பயன்படுத்தி படங்கள் எடுக்கவும் வீடியோக்களை ரெக்கார்டு செய்யவும் முடியும்."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"பின்புலத்தில் படங்களையும் வீடியோக்களையும் எடுத்தல்"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"இந்த ஆப்ஸால் எப்போது வேண்டுமானாலும் கேமராவைப் பயன்படுத்தி படங்கள் எடுக்கவும் வீடியோக்களை ரெக்கார்டு செய்யவும் முடியும்."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"படங்களையும் வீடியோக்களையும் எடுப்பதற்கு சிஸ்டம் கேமராக்களை அணுக ஆப்ஸையோ சேவையையோ அனுமதி"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"இந்த முன்னுரிமை பெற்ற அல்லது சிஸ்டம் ஆப்ஸால் சிஸ்டம் கேமராவைப் பயன்படுத்தி எப்போது வேண்டுமானாலும் படங்களை எடுக்கவோ வீடியோக்களை ரெக்கார்டு செய்யவோ முடியும். android.permission.CAMERA அனுமதியும் ஆப்ஸிற்குத் தேவை"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"கேமரா சாதனங்கள் திறக்கப்படும்போதோ மூடப்படும்போதோ அது குறித்த கால்பேக்குகளைப் பெற ஒரு ஆப்ஸையோ சேவையையோ அனுமதிக்கவும்."</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index caaf8a9..23292a2 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"మీ ఆడియో సెట్టింగ్‌లను మార్చడం"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"వాల్యూమ్ మరియు అవుట్‌పుట్ కోసం ఉపయోగించాల్సిన స్పీకర్ వంటి సార్వజనీన ఆడియో సెట్టింగ్‌లను సవరించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"ఆడియోను రికార్డ్ చేయడం"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"యాప్ ఉపయోగంలో ఉన్నపుడు మైక్రోఫోన్‌ను ఉపయోగించి ఈ యాప్, ఆడియోను రికార్డ్ చేయగలదు."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"బ్యాక్‌గ్రౌండ్‌లో ఆడియోను రికార్డ్ చేయగలదు"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"మైక్రోఫోన్‌ను ఉపయోగించి ఈ యాప్ ఎప్పుడైనా ఆడియోను రికార్డ్ చేయగలదు."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"SIMకి ఆదేశాలను పంపడం"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"సిమ్‌కు ఆదేశాలను పంపడానికి అనువర్తనాన్ని అనుమతిస్తుంది. ఇది చాలా ప్రమాదకరం."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"భౌతిక కార్యాకలాపాన్ని గుర్తించండి"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"ఈ యాప్ మీ భౌతిక కార్యాకలాపాన్ని గుర్తించగలదు."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"చిత్రాలు మరియు వీడియోలు తీయడం"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"యాప్ ఉపయోగంలో ఉన్నపుడు కెమెరాను ఉపయోగించి ఈ యాప్ ఎప్పుడైనా ఫోటోలను తీయగలదు, వీడియోలను రికార్డ్ చేయగలదు."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"బ్యాక్‌గ్రౌండ్‌లో ఫోటోలు, వీడియోలను తీయగలదు"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"కెమెరాను ఉపయోగించి ఈ యాప్ ఎప్పుడైనా ఫోటోలను తీయగలదు, వీడియోలను రికార్డ్ చేయగలదు."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"ఫోటోలు, వీడియోలు తీయడానికి సిస్టమ్ కెమెరాలకు యాప్, లేదా సేవా యాక్సెస్‌ను అనుమతించండి"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"ఈ విశేష లేదా సిస్టమ్ యాప్ ఎప్పుడైనా సిస్టమ్ కెమెరాను ఉపయోగించి ఫోటోలు తీయగలదు, వీడియోలను రికార్డ్ చేయగలదు. యాప్‌కు android.permission.CAMERA అనుమతి ఇవ్వడం కూడా అవసరం"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"కెమెరా పరికరాలు తెరుచుకుంటున్నప్పుడు లేదా మూసుకుంటున్నప్పుడు కాల్‌బ్యాక్‌లను స్వీకరించడానికి యాప్‌ను లేదా సర్వీస్‌ను అనుమతించండి."</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 8c1917f..d5d21c6 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"เปลี่ยนการตั้งค่าเสียงของคุณ"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"อนุญาตให้แอปพลิเคชันปรับเปลี่ยนการตั้งค่าเสียงทั้งหมดได้ เช่น ระดับเสียงและลำโพงที่จะใช้งาน"</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"บันทึกเสียง"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"แอปนี้บันทึกเสียงด้วยไมโครโฟนขณะที่มีการใช้แอปได้"</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"บันทึกเสียงในเบื้องหลัง"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"แอปนี้บันทึกเสียงด้วยไมโครโฟนได้ทุกเมื่อ"</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"ส่งคำสั่งไปยังซิม"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"อนุญาตให้แอปส่งคำสั่งไปยัง SIM ซึ่งอันตรายมาก"</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"จดจำกิจกรรมการเคลื่อนไหวร่างกาย"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"แอปนี้จดจำกิจกรรมการเคลื่อนไหวร่างกายของคุณได้"</string>
     <string name="permlab_camera" msgid="6320282492904119413">"ถ่ายภาพและวิดีโอ"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"แอปนี้ถ่ายภาพและวิดีโอด้วยกล้องขณะที่มีการใช้แอปได้"</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"ถ่ายภาพและวิดีโอในเบื้องหลัง"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"แอปนี้ถ่ายภาพและวิดีโอด้วยกล้องได้ทุกเมื่อ"</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"อนุญาตให้แอปพลิเคชันหรือบริการเข้าถึงกล้องของระบบเพื่อถ่ายภาพและวิดีโอ"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"แอปของระบบหรือที่ได้รับสิทธิ์นี้จะถ่ายภาพและบันทึกวิดีโอโดยใช้กล้องของระบบได้ทุกเมื่อ แอปต้องมีสิทธิ์ android.permission.CAMERA ด้วย"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"อนุญาตให้แอปพลิเคชันหรือบริการได้รับโค้ดเรียกกลับเมื่อมีการเปิดหรือปิดอุปกรณ์กล้อง"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index af0073b..f53851d 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"baguhin ang mga setting ng iyong audio"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Pinapayagan ang app na baguhin ang mga pandaigdigang setting ng audio gaya ng volume at kung aling speaker ang ginagamit para sa output."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"mag-record ng audio"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Makakapag-record ng audio ang app na ito gamit ang mikropono habang ginagamit ang app."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"mag-record ng audio sa background"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Makakapag-record ng audio ang app na ito gamit ang mikropono anumang oras."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"magpadala ng mga command sa SIM"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Pinapahintulutang magpadala ang app ng mga command sa SIM. Napakapanganib nito."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"tukuyin ang pisikal na aktibidad"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Matutukoy ng app na ito ang iyong pisikal na aktibidad."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"kumuha ng mga larawan at video"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Makakakuha ng mga larawan at makakapag-record ng mga video ang app na ito gamit ang camera habang ginagamit ang app."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"kumuha ng mga larawan at video sa background"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Makakakuha ng mga larawan at makakapag-record ng mga video ang app na ito gamit ang camera anumang oras."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Bigyan ang isang application o serbisyo ng access sa mga camera ng system para kumuha ng mga larawan at video"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Ang may pribilehiyong app o system app na ito ay makakakuha ng mga larawan at makakapag-record ng mga video gamit ang isang camera ng system anumang oras. Kinakailangang may android.permission.CAMERA na pahintulot din ang app"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Payagan ang isang application o serbisyo na makatanggap ng mga callback tungkol sa pagbubukas o pagsasara ng mga camera device."</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index c0b2831..5aa336e 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"ses ayarlarınızı değiştirin"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Uygulamaya ses düzeyi ve ses çıkışı için kullanılan hoparlör gibi genel ses ayarlarını değiştirme izni verir."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"ses kaydet"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Bu uygulama, kullanıldığı sırada mikrofonu kullanarak ses kaydedebilir."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"arka planda ses kaydeder"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Bu uygulama, herhangi bir zaman mikrofonu kullanarak ses kaydedebilir."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"SIM karta komut gönderme"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Uygulamanın SIM karta komut göndermesine izin verir. Bu izin çok tehlikelidir."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"fiziksel aktiviteyi algıla"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Bu uygulama fiziksel aktivitenizi algılayabilir."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"resim çekme ve görüntü kaydetme"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Bu uygulama, kullanıldığı sırada kamerayı kullanarak fotoğraf ve video çekebilir."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"arka planda resim ve video çeker"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Bu uygulama, herhangi bir zaman kamerayı kullanarak fotoğraf ve video çekebilir."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Bir uygulama veya hizmetin fotoğraf ve video çekmek için sistem kameralarına erişmesine izin verin"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Ayrıcalık tanınmış bu veya sistem uygulaması herhangi bir zamanda sistem kamerası kullanarak fotoğraf çekebilir ve video kaydedebilir. Uygulamanın da bu ayrıcalığa sahip olması için android.permission.CAMERA izni gerektirir"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Bir uygulama veya hizmetin açılıp kapatılan kamera cihazları hakkında geri çağırmalar almasına izin verin."</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 4c5e13f..da41508 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -438,23 +438,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"змінювати налаштув-ня звуку"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Дозволяє програмі змінювати загальні налаштування звуку, як-от гучність і динамік, який використовується для виводу сигналу."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"запис-ти аудіо"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Цей додаток може записувати звук за допомогою мікрофона, коли ви використовуєте його."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"записувати звук у фоновому режимі"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Цей додаток може будь-коли записувати звук за допомогою мікрофона."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"надсилати команди на SIM-карту"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Дозволяє програмі надсилати команди на SIM-карту. Це дуже небезпечно."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"розпізнавати фізичну активність"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Цей додаток може розпізнавати фізичну активність."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"фотограф. та знімати відео"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Цей додаток може робити фотографії та записувати відео за допомогою камери, коли ви використовуєте його."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"робити фотографії та записувати відео у фоновому режимі"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Цей додаток може будь-коли робити фотографії та записувати відео за допомогою камери."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Дозволити додатку або сервісу отримувати доступ до системних камер, робити фото й записувати відео"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Цей пріоритетний системний додаток може будь-коли робити фото й записувати відео, використовуючи камеру системи. Додатку потрібен дозвіл android.permission.CAMERA"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Дозволити додатку або сервісу отримувати зворотні виклики щодо відкриття чи закриття камер."</string>
@@ -1143,7 +1137,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="8689459651807876423">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="2302144714803345056">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="1532369154488982046">"Вибрати все"</string>
-    <string name="cut" msgid="2561199725874745819">"Виріз."</string>
+    <string name="cut" msgid="2561199725874745819">"Вирізати"</string>
     <string name="copy" msgid="5472512047143665218">"Копіювати"</string>
     <string name="failed_to_copy_to_clipboard" msgid="725919885138539875">"Не вдалося скопіювати в буфер обміну"</string>
     <string name="paste" msgid="461843306215520225">"Вставити"</string>
@@ -1866,7 +1860,7 @@
       <item quantity="other">Протягом %1$d хв (до <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
     </plurals>
     <plurals name="zen_mode_duration_hours_summary" formatted="false" msgid="7725354244196466758">
-      <item quantity="one">%1$d годину (до <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
+      <item quantity="one">%1$d година (до <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
       <item quantity="few">%1$d години (до <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
       <item quantity="many">%1$d годин (до <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
       <item quantity="other">%1$d години (до <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 7809d5b..698e9c4 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -274,8 +274,8 @@
     <string name="notification_channel_security" msgid="8516754650348238057">"سیکیورٹی"</string>
     <string name="notification_channel_car_mode" msgid="2123919247040988436">"کار وضع"</string>
     <string name="notification_channel_account" msgid="6436294521740148173">"اکاؤنٹ اسٹیٹس"</string>
-    <string name="notification_channel_developer" msgid="1691059964407549150">"ڈیولپر کے پیغامات"</string>
-    <string name="notification_channel_developer_important" msgid="7197281908918789589">"اہم ڈیولپر پیغامات"</string>
+    <string name="notification_channel_developer" msgid="1691059964407549150">"ڈویلپر کے پیغامات"</string>
+    <string name="notification_channel_developer_important" msgid="7197281908918789589">"اہم ڈویلپر پیغامات"</string>
     <string name="notification_channel_updates" msgid="7907863984825495278">"اپ ڈیٹس"</string>
     <string name="notification_channel_network_status" msgid="2127687368725272809">"نیٹ ورک اسٹیٹس"</string>
     <string name="notification_channel_network_alerts" msgid="6312366315654526528">"نیٹ ورک الرٹس"</string>
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"اپنے آڈیو کی ترتیبات کو تبدیل کریں"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"ایپ کو مجموعی آڈیو ترتیبات جیسے والیوم اور آؤٹ پٹ کیلئے جو اسپیکر استعمال ہوتا ہے اس میں ترمیم کرنے کی اجازت دیتا ہے۔"</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"آڈیو ریکارڈ کریں"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"ایپ کے استعمال ہونے کے دوران یہ ایپ مائیکروفون استعمال کرتے ہوئے آڈیو ریکارڈ کر سکتی ہے۔"</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"پس منظر میں آڈیو ریکارڈ کریں"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"یہ ایپ کسی بھی وقت مائیکروفون استعمال کرتے ہوئے آڈیو ریکارڈ کر سکتی ہے۔"</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"‏SIM کو ہدایات بھیجیں"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"‏ایپ کو SIM کو کمانڈز بھیجنے کی اجازت دیتا ہے۔ یہ بہت خطرناک ہے۔"</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"جسمانی سرگرمی کی شناخت کریں"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"یہ ایپ آپ کی جسمانی سرگرمی کی شناخت کر سکتی ہے۔"</string>
     <string name="permlab_camera" msgid="6320282492904119413">"تصاویر لیں اور ویڈیوز بنائیں"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"ایپ کے استعمال ہونے کے دوران یہ ایپ کیمرا استعمال کرتے ہوئے تصاویر لے سکتی ہے اور ویڈیوز ریکارڈ کر سکتی ہے۔"</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"پس منظر میں تصاویر لیں اور ویڈیوز ریکارڈ کریں"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"یہ ایپ کسی بھی وقت کیمرا استعمال کرتے ہوئے تصاویر لے سکتی ہے اور ویڈیوز ریکارڈ کر سکتی ہے۔"</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"ایپلیکیشن یا سروس کو سسٹم کے کیمرے تک رسائی حاصل کرنے کی اجازت دیتا ہے تاکہ وہ تصاویر لیں اور ویڈیوز ریکارڈ کریں۔"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"‏یہ مراعات یافتہ یا سسٹم ایپ کسی بھی وقت ایک سسٹم کیمرا استعمال کرتے ہوئے تصاویر اور ویڈیوز ریکارڈ کر سکتی ہے۔ ایپ کے پاس android.permission.CAMERA کے ليے بھی اجازت ہونا ضروری ہے۔"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"ایپلیکیشن یا سروس کو کیمرا کے آلات کے کُھلنے یا بند ہونے سے متعلق کال بیکس موصول کرنے کی اجازت دیں۔"</string>
@@ -1224,7 +1218,7 @@
     <string name="dump_heap_ready_notification" msgid="2302452262927390268">"<xliff:g id="PROC">%1$s</xliff:g> ہیپ ڈمپ تیار ہے"</string>
     <string name="dump_heap_notification_detail" msgid="8431586843001054050">"ہیپ ڈمپ جمع ہو گیا ہے۔ اشتراک کرنے کیلئے تھپتھپائیں۔"</string>
     <string name="dump_heap_title" msgid="4367128917229233901">"ہیپ ڈمپ کا اشتراک کریں؟"</string>
-    <string name="dump_heap_text" msgid="1692649033835719336">"<xliff:g id="PROC">%1$s</xliff:g> کارروائی اپنی میموری کی حد <xliff:g id="SIZE">%2$s</xliff:g> سے تجاوز کر گئی ہے۔ آپ کے لیے ایک ہیپ ڈمپ اس کے ڈیولپر کے ساتھ اشتراک کرنے کے لیے دستیاب ہے۔ محتاط رہیں: اس ہیپ ڈمپ میں آپ کی کوئی ایسی ذاتی معلومات بھی شامل ہو سکتی ہے جس تک ایپلیکیشن کو رسائی حاصل ہے۔"</string>
+    <string name="dump_heap_text" msgid="1692649033835719336">"<xliff:g id="PROC">%1$s</xliff:g> کارروائی اپنی میموری کی حد <xliff:g id="SIZE">%2$s</xliff:g> سے تجاوز کر گئی ہے۔ آپ کے لیے ایک ہیپ ڈمپ اس کے ڈویلپر کے ساتھ اشتراک کرنے کے لیے دستیاب ہے۔ محتاط رہیں: اس ہیپ ڈمپ میں آپ کی کوئی ایسی ذاتی معلومات بھی شامل ہو سکتی ہے جس تک ایپلیکیشن کو رسائی حاصل ہے۔"</string>
     <string name="dump_heap_system_text" msgid="6805155514925350849">"<xliff:g id="PROC">%1$s</xliff:g> پروسیس نے اپنی میموری کی حد <xliff:g id="SIZE">%2$s</xliff:g> سے بڑھا لی ہے۔ آپ کے اشتراک کرنے کے لیے ہیپ ڈمپ دستیاب ہے۔ محتاط رہیں: اس ہیپ ڈمپ میں حساس ذاتی معلومات ہو سکتی ہے، جس میں آپ کے ذریعے ٹائپ کردہ چیزیں شامل ہو سکتی ہیں، جس تک پروسیس کو رسائی حاصل ہو سکتی ہے۔"</string>
     <string name="dump_heap_ready_text" msgid="5849618132123045516">"<xliff:g id="PROC">%1$s</xliff:g> کے پروسیس کا ہیپ ڈمپ آپ کے اشتراک کے لیے دستیاب ہے۔ محتاط رہیں: اس ہیپ ڈمپ میں ممکنہ طور پر حساس ذاتی معلومات ہو سکتی ہے، جس میں آپ کے ذریعے ٹائپ کردہ چیزیں شامل ہو سکتی ہیں، جس تک پروسیس کو رسائی حاصل ہو سکتی ہے۔"</string>
     <string name="sendText" msgid="493003724401350724">"متن کیلئے ایک کارروائی منتخب کریں"</string>
@@ -1902,7 +1896,7 @@
     <string name="work_mode_turn_on" msgid="3662561662475962285">"آن کریں"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"ایپ دستیاب نہیں ہے"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ابھی دستیاب نہیں ہے۔"</string>
-    <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"‏یہ ایپ Android کے پرانے ورژن کے لئے بنائی گئی ہے اور ہو سکتا ہے صحیح طور پر کام نہ کرے۔ اپ ڈیٹس چیک کر کے آزمائیں یا ڈیولپر سے رابطہ کریں۔"</string>
+    <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"‏یہ ایپ Android کے پرانے ورژن کے لئے بنائی گئی ہے اور ہو سکتا ہے صحیح طور پر کام نہ کرے۔ اپ ڈیٹس چیک کر کے آزمائیں یا ڈویلپر سے رابطہ کریں۔"</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"اپ ڈیٹ چیک کریں"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"آپ کے پاس نئے پیغامات ہیں"</string>
     <string name="new_sms_notification_content" msgid="3197949934153460639">"‏دیکھنے کیلئے SMS ایپ کھولیں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index c8ba3c6..61d29dc 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"audio sozlamalaringizni o‘zgartirish"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Ilovalarga tovush va ovoz chiqarish uchun foydalaniladigan karnay kabi global audio sozlamalarini o‘zgartirish uchun ruxsat beradi."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"ovoz yozib olish"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Bu ilova ishlayotganida u mikrofon orqali audio yozib olishi mumkin."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"orqa fonda ovoz yozib olish"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Bu ilova xohlagan vaqtda mikrofon yordami audio yozib olishi mumkin."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"SIM kartaga buyruqlar yuborish"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Dasturga SIM kartaga buyruqlar jo‘natishga ruxsat beradi. Bu juda ham xavfli."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"jismoniy harakatni aniqlash"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Bu ilova jismoniy harakatlaringizni aniqlay oladi."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"rasm va videoga olish"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Bu ilova ishlayotganida u kamera orqali suratga olishi va video yozib olishi mumkin."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"orqa fonda surat va videoga olish"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Bu ilova xohlagan vaqtda kamera orqali suratga olishi va video yozib olishi mumkin."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Ilova yoki xizmatga tizim kamerasi orqali surat va videolar olishga ruxsat berish"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Bu imtiyozli yoki tizim ilovasi istalgan vaqtda tizim kamerasi orqali surat va videolar olishi mumkin. Ilovada android.permission.CAMERA ruxsati ham yoqilgan boʻlishi talab qilinadi"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Ilova yoki xizmatga kamera qurilmalari ochilayotgani yoki yopilayotgani haqida qayta chaqiruvlar qabul qilishi uchun ruxsat berish."</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index ea83fe8..fd7205d 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"thay đổi cài đặt âm thanh của bạn"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Cho phép ứng dụng sửa đổi cài đặt âm thanh chung chẳng hạn như âm lượng và loa nào được sử dụng cho thiết bị ra."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"ghi âm"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Ứng dụng này có thể ghi âm bằng micrô khi bạn đang dùng ứng dụng."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"ghi âm trong nền"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Ứng dụng này có thể ghi âm bằng micrô bất kỳ lúc nào."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"gửi lệnh đến SIM"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Cho phép ứng dụng gửi lệnh đến SIM. Việc này rất nguy hiểm."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"nhận dạng hoạt động thể chất"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Ứng dụng này có thể nhận dạng hoạt động thể chất của bạn."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"chụp ảnh và quay video"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Ứng dụng này có thể chụp ảnh và quay video bằng máy ảnh khi bạn đang dùng ứng dụng."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"chụp ảnh và quay video trong nền"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Ứng dụng này có thể chụp ảnh và quay video bằng máy ảnh bất cứ lúc nào."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Cho phép một ứng dụng hoặc dịch vụ truy cập vào máy ảnh hệ thống để chụp ảnh và quay video"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Ứng dụng hệ thống có đặc quyền này có thể dùng máy ảnh hệ thống để chụp ảnh và quay video bất cứ lúc nào. Ngoài ra, ứng dụng này cũng cần có quyền android.permission.CAMERA"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Cho phép một ứng dụng hoặc dịch vụ nhận lệnh gọi lại khi các thiết bị máy ảnh đang được mở/đóng."</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 7d3645b..16775d5 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"更改您的音频设置"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"允许该应用修改全局音频设置,例如音量和用于输出的扬声器。"</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"录音"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"当您使用此应用时,它可以使用麦克风录音。"</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"在后台录音"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"此应用可以随时使用麦克风录音。"</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"向 SIM 卡发送命令"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"允许应用向SIM卡发送命令(此权限具有很高的危险性)。"</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"识别身体活动"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"此应用可以识别您的身体活动。"</string>
     <string name="permlab_camera" msgid="6320282492904119413">"拍摄照片和视频"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"当您使用此应用时,它可以使用相机拍摄照片和录制视频。"</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"在后台拍摄照片和视频"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"此应用可以随时使用相机拍摄照片和录制视频。"</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"要拍照或录制视频,请允许应用或服务访问系统相机"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"这个具有特权的系统应用随时可以使用系统相机拍照及录制视频。另外,应用还需要获取 android.permission.CAMERA 权限"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"允许应用或服务接收与打开或关闭摄像头设备有关的回调。"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 893f3e9..bd13574 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"更改音效設定"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"允許應用程式修改全域音頻設定,例如音量和用於輸出的喇叭。"</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"錄製音效"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"此應用程式在使用期間可使用麥克風錄音。"</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"在背景錄音"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"此應用程式可隨時使用麥克風錄音。"</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"發送指令至 SIM 卡"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"允許應用程式傳送指令到 SIM 卡。這項操作具有高危險性。"</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"識別體能活動"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"此應用程式可識別您的體能活動。"</string>
     <string name="permlab_camera" msgid="6320282492904119413">"拍照和拍攝影片"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"此應用程式在使用期間可使用相機拍照及錄影。"</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"在背景拍照及錄影"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"此應用程式可隨時使用相機拍照及錄影。"</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"允許應用程式或服務存取系統相機來拍照和攝錄"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"這個獲特別權限的系統應用程式可以在任何時候使用系統相機來拍照和攝錄。此外,應用程式亦需要 android.permission.CAMERA 權限"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"允許應用程式或服務接收相機裝置開啟或關閉的相關回電。"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index f92cf4f..0637742 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"變更音訊設定"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"允許應用程式修改全域音訊設定,例如音量和用來輸出的喇叭。"</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"錄製音訊"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"這個應用程式在使用期間可以使用麥克風錄音。"</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"在背景錄音"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"這個應用程式隨時可以使用麥克風錄音。"</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"傳送指令到 SIM 卡"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"允許應用程式傳送指令到 SIM 卡。這麼做非常危險。"</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"辨識體能活動"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"這個應用程式可以辨識你從事的體能活動。"</string>
     <string name="permlab_camera" msgid="6320282492904119413">"拍攝相片和影片"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"這個應用程式在使用期間可以使用相機拍照及錄影。"</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"在背景拍照及錄影"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"這個應用程式隨時可以使用相機拍照及錄影。"</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"如要拍照或錄影,請允許應用程式或服務存取系統攝影機"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"這個具有特殊權限的系統應用程式隨時可以使用系統攝影機拍照及錄影。此外,你也必須將 android.permission.CAMERA 權限授予這個應用程式"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"允許應用程式或服務接收相機裝置開啟或關閉的相關回呼。"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index c43588b..ea97a6e 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"shintsha izilungiselelo zakho zomsindo"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Ivumela uhlelo lokusebenza ukushintsha izilungiselelo zomsindo we-global njengevolomu nokuthi isiphi isipika esisetshenziselwa okukhiphayo."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"qopha umsindo"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"Lolu hlelo lokusebenza lungarekhoda umsindo lisebenzisa imakrofoni kuyilapho uhlelo lokusebenza lusetshenziswa."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"rekhoda umsindo ngemuva"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Lolu hlelo lokusebenza lungafunda umsindo lisebenzisa imakrofoni noma kunini."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"thumela imilayezo ku-SIM"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Ivumela uhlelo lokusebenza ukuthumela imiyalo ku-SIM. Lokhu kuyingozi kakhulu."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"bona umsebenzi"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Lolu hlelo lokusebenza lingabona umsebenzi wakho."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"thatha izithombe namavidiyo"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"Lolu hlelo lokusebenza lungathatha izithombe futhi lirekhode amavidiyo lisebenzisa ikhamera kuyilapho uhlelo lokusebenza lusetshenziswa."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"thatha izithombe namavidiyo ngemuva"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Lolu hlelo lokusebenza lungathatha izithombe futhi lirekhode amavidiyo lusebenzisa ikhamera noma kunini."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Vumela uhlelo lokusebenza noma isevisi ukufinyelela kumakhamera wesistimu ukuze uthathe izithombe namavidiyo"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Lolu hlelo lokusebenza oluhle noma lwesistimu lingathatha izithombe futhi lirekhode amavidiyo lisebenzisa ikhamera yesistimu noma kunini. Idinga imvume ye-android.permission.CAMERA ukuthi iphathwe nawuhlelo lokusebenza"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Vumela uhlelo lokusebenza noma isevisi ukwamukela ukuphinda ufonelwe mayelana namadivayisi wekhamera avuliwe noma avaliwe."</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 644ed3a..b74f96d 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -199,6 +199,9 @@
         <!-- The underline color and thickness for auto correction suggestion -->
         <attr name="textAppearanceAutoCorrectionSuggestion" format="reference" />
 
+        <!-- The underline color and thickness for grammar error suggestion -->
+        <attr name="textAppearanceGrammarErrorSuggestion" format="reference" />
+
         <!--  The underline color -->
         <attr name="textUnderlineColor" format="reference|color" />
         <!--  The underline thickness -->
@@ -3745,6 +3748,7 @@
             <flag name="flagServiceHandlesDoubleTap" value="0x00000800" />
             <!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_MULTI_FINGER_GESTURES}. -->
             <flag name="flagRequestMultiFingerGestures" value="0x00001000" />
+            <flag name="flagSendMotionEvents" value="0x0004000" />
         </attr>
         <!-- Component name of an activity that allows the user to modify
              the settings for this service. This setting cannot be changed at runtime. -->
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 96ebc12..e3ddbd8 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2016,6 +2016,22 @@
         <attr name="requiredNotFeature" format="string" />
     </declare-styleable>
 
+    <!-- <code>required-feature</code> and <code>required-not-feature</code> elements inside
+         <code>uses-permission<code/> can be used to request the permission based on the fact
+         whether the system supports or does not support certain features.
+         If multiple <code>required-feature</code> and/or <code>required-not-feature</code> elements
+         are present, the permission will be “requested” only if the system supports all of the
+         listed "required-features" and does not support any of the "required-not-features".
+         -->
+    <declare-styleable name="AndroidManifestRequiredFeature">
+        <!-- The name of the feature. -->
+        <attr name="name" />
+    </declare-styleable>
+    <declare-styleable name="AndroidManifestRequiredNotFeature">
+        <!-- The name of the feature. -->
+        <attr name="name" />
+    </declare-styleable>
+
     <!-- The <code>uses-configuration</code> tag specifies
          a specific hardware configuration value used by the application.
          For example an application might specify that it requires
@@ -3184,6 +3200,15 @@
          then the system will set the same minimal height on all other activities in the task. It
          will also ignore any other minimal height attributes of non-root activities. -->
         <attr name="minHeight" />
+
+        <!-- Window layout affinity of this activity. Activities with the same window layout
+          affinity will share the same layout record. If an activity is launched in freeform window,
+          the activity will be launched to the latest position and size where any task, if the root
+          activity of that task shares the same window layout affinity with the activity being
+          launched. Window layout affinity is shared only among activities with the same UID.
+
+          <p>By default activity doesn't share any affinity with other activities. -->
+        <attr name="windowLayoutAffinity" format="string" />
     </declare-styleable>
 
     <!-- <code>restrict-update</code> tag restricts system apps from being updated unless the
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index f55114c..8d51294 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1588,6 +1588,16 @@
          config_enablePrimaryLocationTimeZoneOverlay is false. -->
     <string name="config_primaryLocationTimeZoneProviderPackageName" translatable="false">@null</string>
 
+    <!-- Whether to enable secondary location time zone provider overlay which allows the secondary
+         location time zone provider to be replaced by an app at run-time. When disabled, only the
+         config_secondaryLocationTimeZoneProviderPackageName package will be searched for the
+         secondary location time zone provider, otherwise any system package is eligible. Anyone who
+         wants to disable the overlay mechanism can set it to false. -->
+    <bool name="config_enableSecondaryLocationTimeZoneOverlay" translatable="false">false</bool>
+    <!-- Package name providing the secondary location time zone provider. Used only when
+         config_enableSecondaryLocationTimeZoneOverlay is false. -->
+    <string name="config_secondaryLocationTimeZoneProviderPackageName" translatable="false">@null</string>
+
     <!-- Whether to enable network location overlay which allows network location provider to be
          replaced by an app at run-time. When disabled, only the
          config_networkLocationProviderPackageName package will be searched for network location
@@ -1676,6 +1686,21 @@
         -->
     </string-array>
 
+    <!-- Optional IPsec algorithms enabled by this device, defaulting to empty. OEMs can override
+         it by providing a list of algorithm names in an overlay config.xml file.
+
+         As Android releases new versions, more algorithms are becoming mandatory. Mandatory
+         algorithms will be automatically enabled on the device. Optional algorithms need
+         to be explicitly declared in this resource to be enabled.
+             * SDK level 28 makes the following algorithms mandatory : "cbc(aes)", "hmac(md5)",
+               "hmac(sha1)", "hmac(sha256)", "hmac(sha384)", "hmac(sha512)", "rfc4106(gcm(aes))"
+             * SDK level 30 makes the following algorithms mandatory : "rfc3686(ctr(aes))",
+               "xcbc(aes)", "rfc7539esp(chacha20,poly1305)"
+     -->
+    <string-array name="config_optionalIpSecAlgorithms" translatable="false">
+        <!-- Add algorithm here -->
+    </string-array>
+
     <!-- Boolean indicating if current platform supports bluetooth SCO for off call
     use cases -->
     <bool name="config_bluetooth_sco_off_call">true</bool>
@@ -1793,6 +1818,9 @@
          Note: This config is deprecated, please use config_defaultSms instead. -->
     <string name="default_sms_application" translatable="false">com.android.messaging</string>
 
+    <!-- Flag indicating whether the current device supports "Ask every time" for sms-->
+    <bool name="config_sms_ask_every_time_support">true</bool>
+
     <!-- Flag indicating whether the current device allows data.
          If true, this means that the device supports data connectivity through
          the telephony network.
@@ -1829,6 +1857,8 @@
     <string name="config_defaultCallScreening" translatable="false"></string>
     <!-- The name of the package that will hold the system gallery role. -->
     <string name="config_systemGallery" translatable="false">com.android.gallery3d</string>
+    <!-- The names of the packages that will hold the automotive projection role. -->
+    <string name="config_systemAutomotiveProjection" translatable="false"></string>
     <!-- The name of the package that will hold the system cluster service role. -->
     <string name="config_systemAutomotiveCluster" translatable="false"></string>
     <!-- The name of the package that will hold the system video call role. -->
@@ -3650,6 +3680,8 @@
     -->
     <string name="config_defaultContentSuggestionsService" translatable="false"></string>
 
+    <string name="config_defaultMusicRecognitionService" translatable="false"></string>
+
     <!-- The package name for the default retail demo app.
          This package must be trusted, as it has the permissions to query the usage stats on the
          device.
@@ -4428,4 +4460,13 @@
 
     <!-- WindowsManager JetPack display features -->
     <string name="config_display_features" translatable="false" />
+
+    <!-- Physical Display IDs of the display-devices that are swapped when a folding device folds.
+         This list is expected to contain two elements: the first is the display to use
+         when the device is folded, the second is the display to use when unfolded. If the array
+         is empty or the display IDs are not recognized, this feature is turned off and the value
+         ignored.
+         TODO: b/170470621 - remove once we can have multiple Internal displays in DMS as
+               well as a notification from DisplayStateManager. -->
+    <string-array name="config_internalFoldedPhysicalDisplayIds" translatable="false" />
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index fe17eca..39989dd 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3046,6 +3046,7 @@
   <public-group type="attr" first-id="0x01010617">
     <public name="rollbackDataPolicy" />
     <public name="allowClickWhenDisabled" />
+    <public name="windowLayoutAffinity" />
   </public-group>
 
   <public-group type="drawable" first-id="0x010800b5">
@@ -3073,6 +3074,8 @@
     <public name="config_systemAutomotiveCluster" />
     <!-- @hide @SystemApi @TestApi -->
     <public name="config_systemVideoCall" />
+    <!-- @hide @SystemApi @TestApi -->
+    <public name="config_systemAutomotiveProjection" />
   </public-group>
 
   <public-group type="id" first-id="0x01020055">
diff --git a/core/res/res/values/required_apps_managed_device.xml b/core/res/res/values/required_apps_managed_device.xml
index 40db9df..e17d214 100644
--- a/core/res/res/values/required_apps_managed_device.xml
+++ b/core/res/res/values/required_apps_managed_device.xml
@@ -21,6 +21,7 @@
             Takes precedence over the disallowed apps lists. -->
     <string-array translatable="false" name="required_apps_managed_device">
         <item>com.android.settings</item>
+        <item>com.android.systemui</item>
         <item>com.android.contacts</item>
         <item>com.android.dialer</item>
         <item>com.android.stk</item>  <!-- Required by com.android.phone by certain carriers -->
diff --git a/core/res/res/values/required_apps_managed_profile.xml b/core/res/res/values/required_apps_managed_profile.xml
index c6b37e8..6ed385a 100644
--- a/core/res/res/values/required_apps_managed_profile.xml
+++ b/core/res/res/values/required_apps_managed_profile.xml
@@ -22,6 +22,7 @@
     <string-array translatable="false" name="required_apps_managed_profile">
         <item>com.android.contacts</item>
         <item>com.android.settings</item>
+        <item>com.android.systemui</item>
         <item>com.android.providers.downloads</item>
         <item>com.android.providers.downloads.ui</item>
         <item>com.android.documentsui</item>
diff --git a/core/res/res/values/required_apps_managed_user.xml b/core/res/res/values/required_apps_managed_user.xml
index 8800e535..a6fc573 100644
--- a/core/res/res/values/required_apps_managed_user.xml
+++ b/core/res/res/values/required_apps_managed_user.xml
@@ -21,6 +21,7 @@
             Takes precedence over the disallowed apps lists. -->
     <string-array translatable="false" name="required_apps_managed_user">
         <item>com.android.settings</item>
+        <item>com.android.systemui</item>
         <item>com.android.contacts</item>
         <item>com.android.dialer</item>
         <item>com.android.stk</item>  <!-- Required by com.android.phone by certain carriers -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index fc489b1..c2120de 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4494,6 +4494,10 @@
      shown in the warning dialog about the accessibility shortcut. -->
     <string name="color_correction_feature_name">Color Correction</string>
 
+    <!-- TODO(b/170970602): remove translatable=false when RBC has official name and strings -->
+    <!-- Title of Reduce Bright Colors feature, shown in the warning dialog about the accessibility shortcut. [CHAR LIMIT=none] -->
+    <string name="reduce_bright_colors_feature_name" translatable="false">Reduce Bright Colors</string>
+
     <!-- Text in toast to alert the user that the accessibility shortcut turned on an accessibility service. [CHAR LIMIT=none] -->
     <string name="accessibility_shortcut_enabling_service">Held volume keys. <xliff:g id="service_name" example="TalkBack">%1$s</xliff:g> turned on.</string>
 
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index f920083..24afe07 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -306,6 +306,10 @@
         <item name="textUnderlineColor">@color/holo_blue_light</item>
     </style>
 
+    <style name="TextAppearance.GrammarErrorSuggestion" parent="TextAppearance.Suggestion">
+        <item name="textUnderlineColor">@color/holo_blue_light</item>
+    </style>
+
     <!-- Widget Styles -->
 
     <style name="Widget">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 3e59549..caa3dff 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -200,9 +200,6 @@
   <java-symbol type="id" name="action2" />
   <java-symbol type="id" name="action3" />
   <java-symbol type="id" name="action4" />
-  <java-symbol type="id" name="media_seamless" />
-  <java-symbol type="id" name="media_seamless_image" />
-  <java-symbol type="id" name="media_seamless_text" />
   <java-symbol type="id" name="notification_media_seekbar_container" />
   <java-symbol type="id" name="notification_media_content" />
   <java-symbol type="id" name="notification_media_progress" />
@@ -263,6 +260,7 @@
   <java-symbol type="attr" name="searchDialogTheme" />
   <java-symbol type="attr" name="textAppearanceAutoCorrectionSuggestion" />
   <java-symbol type="attr" name="textAppearanceEasyCorrectSuggestion" />
+  <java-symbol type="attr" name="textAppearanceGrammarErrorSuggestion" />
   <java-symbol type="attr" name="textAppearanceMisspelledSuggestion" />
   <java-symbol type="attr" name="textColorSearchUrl" />
   <java-symbol type="attr" name="timePickerStyle" />
@@ -312,6 +310,7 @@
   <java-symbol type="bool" name="config_networkSamplingWakesDevice" />
   <java-symbol type="bool" name="config_showMenuShortcutsWhenKeyboardPresent" />
   <java-symbol type="bool" name="config_sip_wifi_only" />
+  <java-symbol type="bool" name="config_sms_ask_every_time_support" />
   <java-symbol type="bool" name="config_sms_capable" />
   <java-symbol type="bool" name="config_sms_utf8_support" />
   <java-symbol type="bool" name="config_mobile_data_capable" />
@@ -2160,6 +2159,8 @@
   <java-symbol type="string" name="config_deviceConfiguratorPackageName" />
   <java-symbol type="bool" name="config_enablePrimaryLocationTimeZoneOverlay" />
   <java-symbol type="string" name="config_primaryLocationTimeZoneProviderPackageName" />
+  <java-symbol type="bool" name="config_enableSecondaryLocationTimeZoneOverlay" />
+  <java-symbol type="string" name="config_secondaryLocationTimeZoneProviderPackageName" />
 
   <java-symbol type="layout" name="resolver_list" />
   <java-symbol type="id" name="resolver_list" />
@@ -3192,6 +3193,9 @@
   <!-- Network Recommendation -->
   <java-symbol type="string" name="config_defaultNetworkRecommendationProviderPackage" />
 
+  <!-- Optional IPsec algorithms -->
+  <java-symbol type="array" name="config_optionalIpSecAlgorithms" />
+
   <!-- Whether allow 3rd party apps on internal storage. -->
   <java-symbol type="bool" name="config_allow3rdPartyAppOnInternal" />
 
@@ -3246,6 +3250,7 @@
   <java-symbol type="string" name="accessibility_shortcut_disabling_service" />
   <java-symbol type="string" name="color_inversion_feature_name" />
   <java-symbol type="string" name="color_correction_feature_name" />
+  <java-symbol type="string" name="reduce_bright_colors_feature_name" />
   <java-symbol type="string" name="config_defaultAccessibilityService" />
   <java-symbol type="string" name="accessibility_shortcut_spoken_feedback" />
 
@@ -3451,6 +3456,7 @@
   <java-symbol type="string" name="config_defaultAugmentedAutofillService" />
   <java-symbol type="string" name="config_defaultAppPredictionService" />
   <java-symbol type="string" name="config_defaultContentSuggestionsService" />
+  <java-symbol type="string" name="config_defaultMusicRecognitionService" />
   <java-symbol type="string" name="config_defaultAttentionService" />
   <java-symbol type="string" name="config_defaultSystemCaptionsService" />
   <java-symbol type="string" name="config_defaultSystemCaptionsManagerService" />
@@ -3545,6 +3551,7 @@
   <java-symbol type="id" name="bubble_button" />
   <java-symbol type="dimen" name="bubble_visible_padding_end" />
   <java-symbol type="dimen" name="bubble_gone_padding_end" />
+  <java-symbol type="dimen" name="text_size_body_2_material" />
   <java-symbol type="dimen" name="messaging_avatar_size" />
   <java-symbol type="dimen" name="messaging_group_sending_progress_size" />
   <java-symbol type="dimen" name="messaging_image_rounding" />
@@ -4069,4 +4076,5 @@
 
   <java-symbol type="array" name="config_keep_warming_services" />
   <java-symbol type="string" name="config_display_features" />
+  <java-symbol type="array" name="config_internalFoldedPhysicalDisplayIds" />
 </resources>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 47a0e7d..049ba23 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -93,6 +93,7 @@
         <item name="textAppearanceEasyCorrectSuggestion">@style/TextAppearance.EasyCorrectSuggestion</item>
         <item name="textAppearanceMisspelledSuggestion">@style/TextAppearance.MisspelledSuggestion</item>
         <item name="textAppearanceAutoCorrectionSuggestion">@style/TextAppearance.AutoCorrectionSuggestion</item>
+        <item name="textAppearanceGrammarErrorSuggestion">@style/TextAppearance.GrammarErrorSuggestion</item>
 
         <item name="textAppearanceButton">@style/TextAppearance.Widget.Button</item>
 
diff --git a/core/sysprop/Android.bp b/core/sysprop/Android.bp
index 7f20a0b..237ede2 100644
--- a/core/sysprop/Android.bp
+++ b/core/sysprop/Android.bp
@@ -19,3 +19,11 @@
     api_packages: ["android.sysprop"],
     vendor_available: false,
 }
+
+sysprop_library {
+    name: "com.android.sysprop.watchdog",
+    srcs: ["WatchdogProperties.sysprop"],
+    property_owner: "Platform",
+    api_packages: ["android.sysprop"],
+    vendor_available: false,
+}
diff --git a/core/sysprop/WatchdogProperties.sysprop b/core/sysprop/WatchdogProperties.sysprop
new file mode 100644
index 0000000..1bcc773
--- /dev/null
+++ b/core/sysprop/WatchdogProperties.sysprop
@@ -0,0 +1,45 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module: "android.sysprop.WatchdogProperties"
+owner: Platform
+
+# To escape the watchdog timeout loop, fatal reboot the system when
+# watchdog timed out 'fatal_count' times in 'fatal_window_second'
+# seconds, if both values are not 0. Default value of both is 0.
+prop {
+    api_name: "fatal_count"
+    type: Integer
+    prop_name: "framework_watchdog.fatal_count"
+    scope: Internal
+    access: Readonly
+}
+
+prop {
+    api_name: "fatal_window_second"
+    type: Integer
+    prop_name: "framework_watchdog.fatal_window.second"
+    scope: Internal
+    access: Readonly
+}
+
+# The fatal counting can be disabled by setting property
+# 'is_fatal_ignore' to true.
+prop {
+    api_name: "is_fatal_ignore"
+    type: Boolean
+    prop_name: "persist.debug.framework_watchdog.fatal_ignore"
+    scope: Internal
+    access: Readonly
+}
diff --git a/core/sysprop/api/com.android.sysprop.localization-current.txt b/core/sysprop/api/com.android.sysprop.localization-current.txt
index fe4f457..e69de29 100644
--- a/core/sysprop/api/com.android.sysprop.localization-current.txt
+++ b/core/sysprop/api/com.android.sysprop.localization-current.txt
@@ -1,9 +0,0 @@
-props {
-  module: "android.sysprop.LocalizationProperties"
-  prop {
-    api_name: "locale_filter"
-    type: String
-    scope: Internal
-    prop_name: "ro.localization.locale_filter"
-  }
-}
diff --git a/core/sysprop/api/com.android.sysprop.watchdog-current.txt b/core/sysprop/api/com.android.sysprop.watchdog-current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/core/sysprop/api/com.android.sysprop.watchdog-current.txt
diff --git a/core/sysprop/api/com.android.sysprop.watchdog-latest.txt b/core/sysprop/api/com.android.sysprop.watchdog-latest.txt
new file mode 100644
index 0000000..d901aef
--- /dev/null
+++ b/core/sysprop/api/com.android.sysprop.watchdog-latest.txt
@@ -0,0 +1,20 @@
+props {
+  module: "android.sysprop.WatchdogProperties"
+  prop {
+    api_name: "fatal_count"
+    type: Integer
+    scope: Internal
+    prop_name: "framework_watchdog.fatal_count"
+  }
+  prop {
+    api_name: "fatal_window_second"
+    type: Integer
+    scope: Internal
+    prop_name: "framework_watchdog.fatal_window.second"
+  }
+  prop {
+    api_name: "is_fatal_ignore"
+    scope: Internal
+    prop_name: "persist.debug.framework_watchdog.fatal_ignore"
+  }
+}
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index c6ef094..e2b975f 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -54,7 +54,6 @@
         "android.test.mock",
         "framework-atb-backward-compatibility",
         "framework",
-        "icing-java-proto-lite",
         "ext",
         "framework-res",
     ],
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 56f18d5..bb826de 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -138,6 +138,9 @@
     <!-- accessibility test permissions -->
     <uses-permission android:name="android.permission.RETRIEVE_WINDOW_CONTENT" />
 
+    <!-- vibrator test permissions -->
+    <uses-permission android:name="android.permission.VIBRATE" />
+
     <!-- vr test permissions -->
     <uses-permission android:name="android.permission.RESTRICTED_VR_ACCESS" />
 
@@ -161,6 +164,14 @@
                 <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
             </intent-filter>
         </activity>
+        <activity android:name="android.view.ViewInputConnectionTestActivity"
+                  android:label="View Input Connection Test"
+                  android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+            </intent-filter>
+        </activity>
         <activity android:name="StubTestBrowserActivity"
             android:label="Stubbed Test Browser"
             android:exported="true">
@@ -179,6 +190,17 @@
             </intent-filter>
         </activity>
 
+        <activity android:name="android.widget.CustomInputConnectionEditTextActivity"
+                  android:label="CustomInputConnectionEditTextActivity"
+                  android:screenOrientation="portrait"
+                  android:exported="true"
+                  android:theme="@android:style/Theme.Material.Light">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+            </intent-filter>
+        </activity>
+
         <activity android:name="android.widget.TextViewActivity"
                 android:label="TextViewActivity"
                 android:screenOrientation="portrait"
diff --git a/core/tests/coretests/apks/install_decl_perm/AndroidManifest.xml b/core/tests/coretests/apks/install_decl_perm/AndroidManifest.xml
index 9a1f0ff..6a0c421 100644
--- a/core/tests/coretests/apks/install_decl_perm/AndroidManifest.xml
+++ b/core/tests/coretests/apks/install_decl_perm/AndroidManifest.xml
@@ -17,17 +17,14 @@
         package="com.android.frameworks.coretests.install_decl_perm">
 
     <permission android:name="com.android.frameworks.coretests.NORMAL"
-        android:permissionGroup="android.permission-group.COST_MONEY"
         android:protectionLevel="normal"
         android:label="test normal perm" />
         
     <permission android:name="com.android.frameworks.coretests.DANGEROUS"
-        android:permissionGroup="android.permission-group.COST_MONEY"
         android:protectionLevel="dangerous"
         android:label="test dangerous perm" />
         
     <permission android:name="com.android.frameworks.coretests.SIGNATURE"
-        android:permissionGroup="android.permission-group.COST_MONEY"
         android:protectionLevel="signature"
         android:label="test signature perm" />
         
diff --git a/core/tests/coretests/res/layout/activity_custom_input_connection_edit_text.xml b/core/tests/coretests/res/layout/activity_custom_input_connection_edit_text.xml
new file mode 100644
index 0000000..c4db8be
--- /dev/null
+++ b/core/tests/coretests/res/layout/activity_custom_input_connection_edit_text.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+
+    <android.widget.CustomInputConnectionEditText
+        android:id="@+id/edittext1"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+    <android.widget.CustomInputConnectionEditText
+        android:id="@+id/edittext2"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+</LinearLayout>
diff --git a/core/tests/coretests/res/layout/activity_view_ic_test.xml b/core/tests/coretests/res/layout/activity_view_ic_test.xml
new file mode 100644
index 0000000..fa26869
--- /dev/null
+++ b/core/tests/coretests/res/layout/activity_view_ic_test.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:id="@+id/root"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+</LinearLayout>
diff --git a/core/tests/coretests/src/android/app/NotificationHistoryTest.java b/core/tests/coretests/src/android/app/NotificationHistoryTest.java
index 7fa1613..3df0a68 100644
--- a/core/tests/coretests/src/android/app/NotificationHistoryTest.java
+++ b/core/tests/coretests/src/android/app/NotificationHistoryTest.java
@@ -117,8 +117,8 @@
         history.addNotificationToWrite(n);
 
         assertThat(history.getNotificationsToWrite().size()).isEqualTo(2);
-        assertThat(history.getNotificationsToWrite().get(0)).isSameAs(n2);
-        assertThat(history.getNotificationsToWrite().get(1)).isSameAs(n);
+        assertThat(history.getNotificationsToWrite().get(0)).isSameInstanceAs(n2);
+        assertThat(history.getNotificationsToWrite().get(1)).isSameInstanceAs(n);
         assertThat(history.getHistoryCount()).isEqualTo(2);
     }
 
@@ -141,11 +141,11 @@
         history.addNotificationsToWrite(secondHistory);
 
         assertThat(history.getNotificationsToWrite().size()).isEqualTo(5);
-        assertThat(history.getNotificationsToWrite().get(0)).isSameAs(n3);
-        assertThat(history.getNotificationsToWrite().get(1)).isSameAs(n);
-        assertThat(history.getNotificationsToWrite().get(2)).isSameAs(n4);
-        assertThat(history.getNotificationsToWrite().get(3)).isSameAs(n2);
-        assertThat(history.getNotificationsToWrite().get(4)).isSameAs(n5);
+        assertThat(history.getNotificationsToWrite().get(0)).isSameInstanceAs(n3);
+        assertThat(history.getNotificationsToWrite().get(1)).isSameInstanceAs(n);
+        assertThat(history.getNotificationsToWrite().get(2)).isSameInstanceAs(n4);
+        assertThat(history.getNotificationsToWrite().get(3)).isSameInstanceAs(n2);
+        assertThat(history.getNotificationsToWrite().get(4)).isSameInstanceAs(n5);
         assertThat(history.getHistoryCount()).isEqualTo(5);
 
         assertThat(history.getPooledStringsToWrite()).asList().contains(n2.getChannelName());
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 8b25afb..400b05c0 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -31,6 +31,7 @@
 
 import android.annotation.Nullable;
 import android.app.Activity;
+import android.app.ActivityManager;
 import android.app.ActivityThread;
 import android.app.ActivityThread.ActivityClientRecord;
 import android.app.IApplicationThread;
@@ -51,13 +52,15 @@
 import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
 import android.os.IBinder;
+import android.platform.test.annotations.Presubmit;
 import android.util.DisplayMetrics;
 import android.util.MergedConfiguration;
 import android.view.Display;
 import android.view.View;
 
-import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.FlakyTest;
 import androidx.test.filters.MediumTest;
+import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.rule.ActivityTestRule;
 import androidx.test.runner.AndroidJUnit4;
 
@@ -78,6 +81,8 @@
  */
 @RunWith(AndroidJUnit4.class)
 @MediumTest
+@Presubmit
+@FlakyTest(detail = "Promote once confirmed non-flaky")
 public class ActivityThreadTest {
     private static final int TIMEOUT_SEC = 10;
 
@@ -538,6 +543,53 @@
     }
 
     @Test
+    public void testHandleProcessConfigurationChanged_DependOnProcessState() {
+        final ActivityThread activityThread = ActivityThread.currentActivityThread();
+        final Configuration origConfig = activityThread.getConfiguration();
+        final int newDpi = origConfig.densityDpi + 10;
+        final Configuration newConfig = new Configuration(origConfig);
+        newConfig.seq++;
+        newConfig.densityDpi = newDpi;
+
+        activityThread.updateProcessState(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY,
+                false /* fromIPC */);
+
+        applyProcessConfiguration(activityThread, newConfig);
+        try {
+            // In the cached state, the configuration is only set as pending and not applied.
+            assertEquals(origConfig.densityDpi, activityThread.getConfiguration().densityDpi);
+            assertTrue(activityThread.isCachedProcessState());
+        } finally {
+            // The foreground state is the default state of instrumentation.
+            activityThread.updateProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE,
+                    false /* fromIPC */);
+        }
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+        try {
+            // The state becomes non-cached, the pending configuration should be applied.
+            assertEquals(newConfig.densityDpi, activityThread.getConfiguration().densityDpi);
+            assertFalse(activityThread.isCachedProcessState());
+        } finally {
+            // Restore to the original configuration.
+            activityThread.getConfiguration().seq = origConfig.seq - 1;
+            applyProcessConfiguration(activityThread, origConfig);
+        }
+    }
+
+    private static void applyProcessConfiguration(ActivityThread thread, Configuration config) {
+        final ClientTransaction clientTransaction = newTransaction(thread,
+                null /* activityToken */);
+        clientTransaction.addCallback(ConfigurationChangeItem.obtain(config));
+        final IApplicationThread appThread = thread.getApplicationThread();
+        try {
+            appThread.scheduleTransaction(clientTransaction);
+        } catch (Exception ignored) {
+        }
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+    }
+
+    @Test
     public void testResumeAfterNewIntent() {
         final Activity activity = mActivityTestRule.launchActivity(new Intent());
         final ActivityThread activityThread = activity.getActivityThread();
@@ -545,14 +597,15 @@
         rIntents.add(new ReferrerIntent(new Intent(), "android.app.activity"));
 
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
-            activityThread.executeTransaction(newNewIntentTransaction(activity, rIntents, false));
-        });
-        assertThat(activity.isResumed()).isFalse();
-
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             activityThread.executeTransaction(newNewIntentTransaction(activity, rIntents, true));
         });
         assertThat(activity.isResumed()).isTrue();
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            activityThread.executeTransaction(newStopTransaction(activity));
+            activityThread.executeTransaction(newNewIntentTransaction(activity, rIntents, false));
+        });
+        assertThat(activity.isResumed()).isFalse();
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/app/appsearch/AppSearchSchemaTest.java b/core/tests/coretests/src/android/app/appsearch/AppSearchSchemaTest.java
deleted file mode 100644
index cdc6d25..0000000
--- a/core/tests/coretests/src/android/app/appsearch/AppSearchSchemaTest.java
+++ /dev/null
@@ -1,171 +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 android.app.appsearch;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.testng.Assert.assertThrows;
-import static org.testng.Assert.expectThrows;
-
-import android.app.appsearch.AppSearchSchema.PropertyConfig;
-import android.app.appsearch.proto.IndexingConfig;
-import android.app.appsearch.proto.PropertyConfigProto;
-import android.app.appsearch.proto.SchemaTypeConfigProto;
-import android.app.appsearch.proto.TermMatchType;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-
-@SmallTest
-public class AppSearchSchemaTest {
-    @Test
-    public void testGetProto_Email() {
-        AppSearchSchema emailSchema = new AppSearchSchema.Builder("Email")
-                .addProperty(new AppSearchSchema.PropertyConfig.Builder("subject")
-                        .setDataType(PropertyConfig.DATA_TYPE_STRING)
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
-                        .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .build()
-                ).addProperty(new AppSearchSchema.PropertyConfig.Builder("body")
-                        .setDataType(PropertyConfig.DATA_TYPE_STRING)
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
-                        .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .build()
-                ).build();
-
-        SchemaTypeConfigProto expectedEmailProto = SchemaTypeConfigProto.newBuilder()
-                .setSchemaType("Email")
-                .addProperties(PropertyConfigProto.newBuilder()
-                        .setPropertyName("subject")
-                        .setDataType(PropertyConfigProto.DataType.Code.STRING)
-                        .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
-                        .setIndexingConfig(
-                                android.app.appsearch.proto.IndexingConfig.newBuilder()
-                                        .setTokenizerType(IndexingConfig.TokenizerType.Code.PLAIN)
-                                        .setTermMatchType(TermMatchType.Code.PREFIX)
-                        )
-                ).addProperties(PropertyConfigProto.newBuilder()
-                        .setPropertyName("body")
-                        .setDataType(PropertyConfigProto.DataType.Code.STRING)
-                        .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
-                        .setIndexingConfig(
-                                android.app.appsearch.proto.IndexingConfig.newBuilder()
-                                        .setTokenizerType(
-                                                android.app.appsearch.proto.IndexingConfig
-                                                        .TokenizerType.Code.PLAIN)
-                                        .setTermMatchType(TermMatchType.Code.PREFIX)
-                        )
-                ).build();
-
-        assertThat(emailSchema.getProto()).isEqualTo(expectedEmailProto);
-    }
-
-    @Test
-    public void testGetProto_MusicRecording() {
-        AppSearchSchema musicRecordingSchema = new AppSearchSchema.Builder("MusicRecording")
-                .addProperty(new AppSearchSchema.PropertyConfig.Builder("artist")
-                        .setDataType(PropertyConfig.DATA_TYPE_STRING)
-                        .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
-                        .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
-                        .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .build()
-                ).addProperty(new AppSearchSchema.PropertyConfig.Builder("pubDate")
-                        .setDataType(PropertyConfig.DATA_TYPE_INT64)
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setIndexingType(PropertyConfig.INDEXING_TYPE_NONE)
-                        .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_NONE)
-                        .build()
-                ).build();
-
-        SchemaTypeConfigProto expectedMusicRecordingProto = SchemaTypeConfigProto.newBuilder()
-                .setSchemaType("MusicRecording")
-                .addProperties(PropertyConfigProto.newBuilder()
-                        .setPropertyName("artist")
-                        .setDataType(PropertyConfigProto.DataType.Code.STRING)
-                        .setCardinality(PropertyConfigProto.Cardinality.Code.REPEATED)
-                        .setIndexingConfig(
-                                android.app.appsearch.proto.IndexingConfig.newBuilder()
-                                        .setTokenizerType(
-                                                android.app.appsearch.proto.IndexingConfig
-                                                        .TokenizerType.Code.PLAIN)
-                                        .setTermMatchType(TermMatchType.Code.PREFIX)
-                        )
-                ).addProperties(PropertyConfigProto.newBuilder()
-                        .setPropertyName("pubDate")
-                        .setDataType(PropertyConfigProto.DataType.Code.INT64)
-                        .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
-                        .setIndexingConfig(
-                                android.app.appsearch.proto.IndexingConfig.newBuilder()
-                                        .setTokenizerType(
-                                                android.app.appsearch.proto.IndexingConfig
-                                                        .TokenizerType.Code.NONE)
-                                        .setTermMatchType(TermMatchType.Code.UNKNOWN)
-                        )
-                ).build();
-
-        assertThat(musicRecordingSchema.getProto()).isEqualTo(expectedMusicRecordingProto);
-    }
-
-    @Test
-    public void testInvalidEnums() {
-        PropertyConfig.Builder builder = new AppSearchSchema.PropertyConfig.Builder("test");
-        assertThrows(IllegalArgumentException.class, () -> builder.setDataType(99));
-        assertThrows(IllegalArgumentException.class, () -> builder.setCardinality(99));
-    }
-
-    @Test
-    public void testMissingFields() {
-        PropertyConfig.Builder builder = new AppSearchSchema.PropertyConfig.Builder("test");
-        Exception e = expectThrows(IllegalSchemaException.class, builder::build);
-        assertThat(e).hasMessageThat().contains("Missing field: dataType");
-
-        builder.setDataType(PropertyConfig.DATA_TYPE_DOCUMENT);
-        e = expectThrows(IllegalSchemaException.class, builder::build);
-        assertThat(e).hasMessageThat().contains("Missing field: schemaType");
-
-        builder.setSchemaType("TestType");
-        e = expectThrows(IllegalSchemaException.class, builder::build);
-        assertThat(e).hasMessageThat().contains("Missing field: cardinality");
-
-        builder.setCardinality(PropertyConfig.CARDINALITY_REPEATED);
-        builder.build();
-    }
-
-    @Test
-    public void testDuplicateProperties() {
-        AppSearchSchema.Builder builder = new AppSearchSchema.Builder("Email")
-                .addProperty(new AppSearchSchema.PropertyConfig.Builder("subject")
-                        .setDataType(PropertyConfig.DATA_TYPE_STRING)
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
-                        .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .build()
-                ).addProperty(new AppSearchSchema.PropertyConfig.Builder("subject")
-                        .setDataType(PropertyConfig.DATA_TYPE_STRING)
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
-                        .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .build()
-                );
-
-        Exception e = expectThrows(IllegalSchemaException.class, builder::build);
-        assertThat(e).hasMessageThat().contains("Property defined more than once: subject");
-    }
-}
diff --git a/core/tests/coretests/src/android/app/appsearch/SearchResultsTest.java b/core/tests/coretests/src/android/app/appsearch/SearchResultsTest.java
deleted file mode 100644
index 1d71ec4..0000000
--- a/core/tests/coretests/src/android/app/appsearch/SearchResultsTest.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.appsearch;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.testng.Assert.assertThrows;
-
-import android.app.appsearch.proto.DocumentProto;
-import android.app.appsearch.proto.SearchResultProto;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-
-@SmallTest
-public class SearchResultsTest {
-
-    @Test
-    public void testSearchResultsEqual() {
-        final String uri = "testUri";
-        final String schemaType = "testSchema";
-        SearchResultProto.ResultProto result1 = SearchResultProto.ResultProto.newBuilder()
-                .setDocument(DocumentProto.newBuilder()
-                        .setUri(uri)
-                        .setSchema(schemaType)
-                        .build())
-                .build();
-        SearchResultProto searchResults1 = SearchResultProto.newBuilder()
-                .addResults(result1)
-                .build();
-        SearchResults res1 = new SearchResults(searchResults1);
-        SearchResultProto.ResultProto result2 = SearchResultProto.ResultProto.newBuilder()
-                .setDocument(DocumentProto.newBuilder()
-                        .setUri(uri)
-                        .setSchema(schemaType)
-                        .build())
-                .build();
-        SearchResultProto searchResults2 = SearchResultProto.newBuilder()
-                .addResults(result2)
-                .build();
-        SearchResults res2 = new SearchResults(searchResults2);
-        assertThat(res1.toString()).isEqualTo(res2.toString());
-    }
-
-    @Test
-    public void buildSearchSpecWithoutTermMatchType() {
-        assertThrows(RuntimeException.class, () -> SearchSpec.newBuilder()
-                .setSchemaTypes("testSchemaType")
-                .build());
-    }
-}
diff --git a/core/tests/coretests/src/android/app/appsearch/AppSearchEmailTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java
similarity index 94%
rename from core/tests/coretests/src/android/app/appsearch/AppSearchEmailTest.java
rename to core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java
index 6aa16cc..ac2d4b5 100644
--- a/core/tests/coretests/src/android/app/appsearch/AppSearchEmailTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright 2020 The Android Open 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,11 +18,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import androidx.test.filters.SmallTest;
-
 import org.junit.Test;
 
-@SmallTest
 public class AppSearchEmailTest {
 
     @Test
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchSchemaTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchSchemaTest.java
new file mode 100644
index 0000000..c171270
--- /dev/null
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchSchemaTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch;
+
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.expectThrows;
+
+import android.app.appsearch.AppSearchSchema.PropertyConfig;
+import android.app.appsearch.exceptions.IllegalSchemaException;
+
+import org.junit.Test;
+
+public class AppSearchSchemaTest {
+    @Test
+    public void testInvalidEnums() {
+        PropertyConfig.Builder builder = new PropertyConfig.Builder("test");
+        expectThrows(IllegalArgumentException.class, () -> builder.setDataType(99));
+        expectThrows(IllegalArgumentException.class, () -> builder.setCardinality(99));
+    }
+
+    @Test
+    public void testMissingFields() {
+        PropertyConfig.Builder builder = new PropertyConfig.Builder("test");
+        IllegalSchemaException e = expectThrows(IllegalSchemaException.class, builder::build);
+        assertThat(e).hasMessageThat().contains("Missing field: dataType");
+
+        builder.setDataType(PropertyConfig.DATA_TYPE_DOCUMENT);
+        e = expectThrows(IllegalSchemaException.class, builder::build);
+        assertThat(e).hasMessageThat().contains("Missing field: schemaType");
+
+        builder.setSchemaType("TestType");
+        e = expectThrows(IllegalSchemaException.class, builder::build);
+        assertThat(e).hasMessageThat().contains("Missing field: cardinality");
+
+        builder.setCardinality(PropertyConfig.CARDINALITY_REPEATED);
+        builder.build();
+    }
+
+    @Test
+    public void testDuplicateProperties() {
+        AppSearchSchema.Builder builder = new AppSearchSchema.Builder("Email")
+                .addProperty(new PropertyConfig.Builder("subject")
+                        .setDataType(PropertyConfig.DATA_TYPE_STRING)
+                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                        .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build()
+                );
+        IllegalSchemaException e = expectThrows(IllegalSchemaException.class,
+                () -> builder.addProperty(new PropertyConfig.Builder("subject")
+                        .setDataType(PropertyConfig.DATA_TYPE_STRING)
+                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                        .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build()));
+        assertThat(e).hasMessageThat().contains("Property defined more than once: subject");
+    }
+}
diff --git a/core/tests/coretests/src/android/app/appsearch/AppSearchDocumentTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java
similarity index 69%
rename from core/tests/coretests/src/android/app/appsearch/AppSearchDocumentTest.java
rename to core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java
index 54a281f2..1f2c12b 100644
--- a/core/tests/coretests/src/android/app/appsearch/AppSearchDocumentTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright 2020 The Android Open 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,36 +18,25 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.testng.Assert.assertThrows;
-
-import android.app.appsearch.proto.DocumentProto;
-import android.app.appsearch.proto.PropertyProto;
-import android.app.appsearch.protobuf.ByteString;
-
-import androidx.test.filters.SmallTest;
-
+import static org.testng.Assert.expectThrows;
 
 import org.junit.Test;
 
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-
-@SmallTest
-public class AppSearchDocumentTest {
+public class GenericDocumentTest {
     private static final byte[] sByteArray1 = new byte[]{(byte) 1, (byte) 2, (byte) 3};
-    private static final byte[] sByteArray2 = new byte[]{(byte) 4, (byte) 5, (byte) 6};
-    private static final AppSearchDocument sDocumentProperties1 = new AppSearchDocument
+    private static final byte[] sByteArray2 = new byte[]{(byte) 4, (byte) 5, (byte) 6, (byte) 7};
+    private static final GenericDocument sDocumentProperties1 = new GenericDocument
             .Builder("sDocumentProperties1", "sDocumentPropertiesSchemaType1")
+            .setCreationTimestampMillis(12345L)
             .build();
-    private static final AppSearchDocument sDocumentProperties2 = new AppSearchDocument
+    private static final GenericDocument sDocumentProperties2 = new GenericDocument
             .Builder("sDocumentProperties2", "sDocumentPropertiesSchemaType2")
+            .setCreationTimestampMillis(6789L)
             .build();
 
     @Test
     public void testDocumentEquals_Identical() {
-        AppSearchDocument document1 = new AppSearchDocument.Builder("uri1", "schemaType1")
+        GenericDocument document1 = new GenericDocument.Builder("uri1", "schemaType1")
                 .setCreationTimestampMillis(5L)
                 .setTtlMillis(1L)
                 .setProperty("longKey1", 1L, 2L, 3L)
@@ -57,7 +46,7 @@
                 .setProperty("byteKey1", sByteArray1, sByteArray2)
                 .setProperty("documentKey1", sDocumentProperties1, sDocumentProperties2)
                 .build();
-        AppSearchDocument document2 = new AppSearchDocument.Builder("uri1", "schemaType1")
+        GenericDocument document2 = new GenericDocument.Builder("uri1", "schemaType1")
                 .setCreationTimestampMillis(5L)
                 .setTtlMillis(1L)
                 .setProperty("longKey1", 1L, 2L, 3L)
@@ -73,7 +62,7 @@
 
     @Test
     public void testDocumentEquals_DifferentOrder() {
-        AppSearchDocument document1 = new AppSearchDocument.Builder("uri1", "schemaType1")
+        GenericDocument document1 = new GenericDocument.Builder("uri1", "schemaType1")
                 .setCreationTimestampMillis(5L)
                 .setProperty("longKey1", 1L, 2L, 3L)
                 .setProperty("byteKey1", sByteArray1, sByteArray2)
@@ -84,7 +73,7 @@
                 .build();
 
         // Create second document with same parameter but different order.
-        AppSearchDocument document2 = new AppSearchDocument.Builder("uri1", "schemaType1")
+        GenericDocument document2 = new GenericDocument.Builder("uri1", "schemaType1")
                 .setCreationTimestampMillis(5L)
                 .setProperty("booleanKey1", true, false, true)
                 .setProperty("documentKey1", sDocumentProperties1, sDocumentProperties2)
@@ -99,13 +88,13 @@
 
     @Test
     public void testDocumentEquals_Failure() {
-        AppSearchDocument document1 = new AppSearchDocument.Builder("uri1", "schemaType1")
+        GenericDocument document1 = new GenericDocument.Builder("uri1", "schemaType1")
                 .setCreationTimestampMillis(5L)
                 .setProperty("longKey1", 1L, 2L, 3L)
                 .build();
 
         // Create second document with same order but different value.
-        AppSearchDocument document2 = new AppSearchDocument.Builder("uri1", "schemaType1")
+        GenericDocument document2 = new GenericDocument.Builder("uri1", "schemaType1")
                 .setCreationTimestampMillis(5L)
                 .setProperty("longKey1", 1L, 2L, 4L) // Different
                 .build();
@@ -115,13 +104,13 @@
 
     @Test
     public void testDocumentEquals_Failure_RepeatedFieldOrder() {
-        AppSearchDocument document1 = new AppSearchDocument.Builder("uri1", "schemaType1")
+        GenericDocument document1 = new GenericDocument.Builder("uri1", "schemaType1")
                 .setCreationTimestampMillis(5L)
                 .setProperty("booleanKey1", true, false, true)
                 .build();
 
         // Create second document with same order but different value.
-        AppSearchDocument document2 = new AppSearchDocument.Builder("uri1", "schemaType1")
+        GenericDocument document2 = new GenericDocument.Builder("uri1", "schemaType1")
                 .setCreationTimestampMillis(5L)
                 .setProperty("booleanKey1", true, true, false) // Different
                 .build();
@@ -131,11 +120,10 @@
 
     @Test
     public void testDocumentGetSingleValue() {
-        AppSearchDocument document = new AppSearchDocument.Builder("uri1", "schemaType1")
+        GenericDocument document = new GenericDocument.Builder("uri1", "schemaType1")
                 .setCreationTimestampMillis(5L)
                 .setScore(1)
                 .setTtlMillis(1L)
-                .setScore(1)
                 .setProperty("longKey1", 1L)
                 .setProperty("doubleKey1", 1.0)
                 .setProperty("booleanKey1", true)
@@ -159,7 +147,7 @@
 
     @Test
     public void testDocumentGetArrayValues() {
-        AppSearchDocument document = new AppSearchDocument.Builder("uri1", "schemaType1")
+        GenericDocument document = new GenericDocument.Builder("uri1", "schemaType1")
                 .setCreationTimestampMillis(5L)
                 .setProperty("longKey1", 1L, 2L, 3L)
                 .setProperty("doubleKey1", 1.0, 2.0, 3.0)
@@ -185,8 +173,51 @@
     }
 
     @Test
+    public void testDocument_ToString() throws Exception {
+        GenericDocument document = new GenericDocument.Builder("uri1", "schemaType1")
+                .setCreationTimestampMillis(5L)
+                .setProperty("longKey1", 1L, 2L, 3L)
+                .setProperty("doubleKey1", 1.0, 2.0, 3.0)
+                .setProperty("booleanKey1", true, false, true)
+                .setProperty("stringKey1", "String1", "String2", "String3")
+                .setProperty("byteKey1", sByteArray1, sByteArray2)
+                .setProperty("documentKey1", sDocumentProperties1, sDocumentProperties2)
+                .build();
+        String exceptedString = "{ key: 'creationTimestampMillis' value: 5 } "
+                + "{ key: 'namespace' value:  } "
+                + "{ key: 'properties' value: "
+                +       "{ key: 'booleanKey1' value: [ 'true' 'false' 'true' ] } "
+                +       "{ key: 'byteKey1' value: "
+                +             "{ key: 'byteArray' value: [ '1' '2' '3' ] } "
+                +             "{ key: 'byteArray' value: [ '4' '5' '6' '7' ] }  } "
+                +       "{ key: 'documentKey1' value: [ '"
+                +             "{ key: 'creationTimestampMillis' value: 12345 } "
+                +             "{ key: 'namespace' value:  } "
+                +             "{ key: 'properties' value:  } "
+                +             "{ key: 'schemaType' value: sDocumentPropertiesSchemaType1 } "
+                +             "{ key: 'score' value: 0 } "
+                +             "{ key: 'ttlMillis' value: 0 } "
+                +             "{ key: 'uri' value: sDocumentProperties1 } ' '"
+                +             "{ key: 'creationTimestampMillis' value: 6789 } "
+                +             "{ key: 'namespace' value:  } "
+                +             "{ key: 'properties' value:  } "
+                +             "{ key: 'schemaType' value: sDocumentPropertiesSchemaType2 } "
+                +             "{ key: 'score' value: 0 } "
+                +             "{ key: 'ttlMillis' value: 0 } "
+                +             "{ key: 'uri' value: sDocumentProperties2 } ' ] } "
+                +       "{ key: 'doubleKey1' value: [ '1.0' '2.0' '3.0' ] } "
+                +       "{ key: 'longKey1' value: [ '1' '2' '3' ] } "
+                +       "{ key: 'stringKey1' value: [ 'String1' 'String2' 'String3' ] }  } "
+                + "{ key: 'schemaType' value: schemaType1 } "
+                + "{ key: 'score' value: 0 } "
+                + "{ key: 'ttlMillis' value: 0 } "
+                + "{ key: 'uri' value: uri1 } ";
+        assertThat(document.toString()).isEqualTo(exceptedString);
+    }
+
+    @Test
     public void testDocumentGetValues_DifferentTypes() {
-        AppSearchDocument document = new AppSearchDocument.Builder("uri1", "schemaType1")
+        GenericDocument document = new GenericDocument.Builder("uri1", "schemaType1")
                 .setScore(1)
                 .setProperty("longKey1", 1L)
                 .setProperty("booleanKey1", true, false, true)
@@ -213,53 +244,8 @@
 
     @Test
     public void testDocumentInvalid() {
-        AppSearchDocument.Builder builder = new AppSearchDocument.Builder("uri1", "schemaType1");
-        assertThrows(
+        GenericDocument.Builder builder = new GenericDocument.Builder("uri1", "schemaType1");
+        expectThrows(
                 IllegalArgumentException.class, () -> builder.setProperty("test", new boolean[]{}));
     }
-
-    @Test
-    public void testDocumentProtoPopulation() {
-        AppSearchDocument document = new AppSearchDocument.Builder("uri1", "schemaType1")
-                .setCreationTimestampMillis(5L)
-                .setScore(1)
-                .setTtlMillis(1L)
-                .setProperty("longKey1", 1L)
-                .setProperty("doubleKey1", 1.0)
-                .setProperty("booleanKey1", true)
-                .setProperty("stringKey1", "test-value1")
-                .setProperty("byteKey1", sByteArray1)
-                .setProperty("documentKey1", sDocumentProperties1)
-                .build();
-
-        // Create the Document proto. Need to sort the property order by key.
-        DocumentProto.Builder documentProtoBuilder = DocumentProto.newBuilder()
-                .setUri("uri1")
-                .setSchema("schemaType1")
-                .setCreationTimestampMs(5L)
-                .setScore(1)
-                .setTtlMs(1L)
-                .setNamespace("");
-        HashMap<String, PropertyProto.Builder> propertyProtoMap = new HashMap<>();
-        propertyProtoMap.put("longKey1",
-                PropertyProto.newBuilder().setName("longKey1").addInt64Values(1L));
-        propertyProtoMap.put("doubleKey1",
-                PropertyProto.newBuilder().setName("doubleKey1").addDoubleValues(1.0));
-        propertyProtoMap.put("booleanKey1",
-                PropertyProto.newBuilder().setName("booleanKey1").addBooleanValues(true));
-        propertyProtoMap.put("stringKey1",
-                PropertyProto.newBuilder().setName("stringKey1").addStringValues("test-value1"));
-        propertyProtoMap.put("byteKey1",
-                PropertyProto.newBuilder().setName("byteKey1").addBytesValues(
-                        ByteString.copyFrom(sByteArray1)));
-        propertyProtoMap.put("documentKey1",
-                PropertyProto.newBuilder().setName("documentKey1")
-                        .addDocumentValues(sDocumentProperties1.getProto()));
-        List<String> sortedKey = new ArrayList<>(propertyProtoMap.keySet());
-        Collections.sort(sortedKey);
-        for (int i = 0; i < sortedKey.size(); i++) {
-            documentProtoBuilder.addProperties(propertyProtoMap.get(sortedKey.get(i)));
-        }
-        assertThat(document.getProto()).isEqualTo(documentProtoBuilder.build());
-    }
 }
diff --git a/media/java/android/media/tv/TvChannelInfo.aidl b/core/tests/coretests/src/android/app/appsearch/external/app/SearchResultsTest.java
similarity index 62%
copy from media/java/android/media/tv/TvChannelInfo.aidl
copy to core/tests/coretests/src/android/app/appsearch/external/app/SearchResultsTest.java
index 71cd0a7..acbf11a 100644
--- a/media/java/android/media/tv/TvChannelInfo.aidl
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/SearchResultsTest.java
@@ -14,6 +14,17 @@
  * limitations under the License.
  */
 
-package android.media.tv;
+package android.app.appsearch;
 
-parcelable TvChannelInfo;
+import static org.testng.Assert.expectThrows;
+
+import org.junit.Test;
+
+public class SearchResultsTest {
+    @Test
+    public void buildSearchSpecWithoutTermMatchType() {
+        expectThrows(RuntimeException.class, () -> new SearchSpec.Builder()
+                .setSchemaTypes("testSchemaType")
+                .build());
+    }
+}
diff --git a/core/tests/coretests/src/android/app/appsearch/impl/CustomerDocumentTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/customer/CustomerDocumentTest.java
similarity index 80%
rename from core/tests/coretests/src/android/app/appsearch/impl/CustomerDocumentTest.java
rename to core/tests/coretests/src/android/app/appsearch/external/app/customer/CustomerDocumentTest.java
index b29483c..2c7c35f 100644
--- a/core/tests/coretests/src/android/app/appsearch/impl/CustomerDocumentTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/customer/CustomerDocumentTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright 2020 The Android Open 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,33 +14,30 @@
  * limitations under the License.
  */
 
-package android.app.appsearch.impl;
-
-import static com.google.common.truth.Truth.assertThat;
+package android.app.appsearch.customer;
 
 import android.annotation.NonNull;
-import android.app.appsearch.AppSearchDocument;
+import android.app.appsearch.GenericDocument;
 
-import androidx.test.filters.SmallTest;
+import static com.google.common.truth.Truth.assertThat;
 
 import org.junit.Test;
 
 /**
- * Tests that {@link AppSearchDocument} and {@link AppSearchDocument.Builder} are extendable by
+ * Tests that {@link GenericDocument} and {@link GenericDocument.Builder} are extendable by
  * developers.
  *
- * <p>This class is intentionally in a different package than {@link AppSearchDocument} to make sure
+ * <p>This class is intentionally in a different package than {@link GenericDocument} to make sure
  * there are no package-private methods required for external developers to add custom types.
  */
-@SmallTest
 public class CustomerDocumentTest {
 
     private static byte[] sByteArray1 = new byte[]{(byte) 1, (byte) 2, (byte) 3};
     private static byte[] sByteArray2 = new byte[]{(byte) 4, (byte) 5, (byte) 6};
-    private static AppSearchDocument sDocumentProperties1 = new AppSearchDocument
+    private static GenericDocument sDocumentProperties1 = new GenericDocument
             .Builder("sDocumentProperties1", "sDocumentPropertiesSchemaType1")
             .build();
-    private static AppSearchDocument sDocumentProperties2 = new AppSearchDocument
+    private static GenericDocument sDocumentProperties2 = new GenericDocument
             .Builder("sDocumentProperties2", "sDocumentPropertiesSchemaType2")
             .build();
 
@@ -77,19 +74,21 @@
 
     /**
      * An example document type for test purposes, defined outside of
-     * {@link android.app.appsearch.AppSearch} (the way an external developer would define it).
+     * {@link GenericDocument} (the way an external developer would define
+     * it).
      */
-    private static class CustomerDocument extends AppSearchDocument {
-        private CustomerDocument(AppSearchDocument document) {
+    private static class CustomerDocument extends GenericDocument {
+        private CustomerDocument(GenericDocument document) {
             super(document);
         }
 
-        public static class Builder extends AppSearchDocument.Builder<CustomerDocument.Builder> {
+        public static class Builder extends GenericDocument.Builder<CustomerDocument.Builder> {
             private Builder(@NonNull String uri) {
                 super(uri, "customerDocument");
             }
 
             @Override
+            @NonNull
             public CustomerDocument build() {
                 return new CustomerDocument(super.build());
             }
diff --git a/core/tests/coretests/src/android/content/integrity/CompoundFormulaTest.java b/core/tests/coretests/src/android/content/integrity/CompoundFormulaTest.java
index ba060fa..593e70e 100644
--- a/core/tests/coretests/src/android/content/integrity/CompoundFormulaTest.java
+++ b/core/tests/coretests/src/android/content/integrity/CompoundFormulaTest.java
@@ -45,7 +45,8 @@
                         CompoundFormula.AND, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2));
 
         assertThat(compoundFormula.getConnector()).isEqualTo(CompoundFormula.AND);
-        assertThat(compoundFormula.getFormulas()).containsAllOf(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2);
+        assertThat(compoundFormula.getFormulas())
+                .containsAtLeast(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2);
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/content/pm/parsing/result/ParseInputAndResultTest.kt b/core/tests/coretests/src/android/content/pm/parsing/result/ParseInputAndResultTest.kt
index d45fee9..9ad63ad 100644
--- a/core/tests/coretests/src/android/content/pm/parsing/result/ParseInputAndResultTest.kt
+++ b/core/tests/coretests/src/android/content/pm/parsing/result/ParseInputAndResultTest.kt
@@ -113,7 +113,7 @@
         assertError(result)
         assertThat(result.errorCode).isEqualTo(errorCode)
         assertThat(result.errorMessage).isEqualTo(errorMessage)
-        assertThat(result.exception).isSameAs(exception)
+        assertThat(result.exception).isSameInstanceAs(exception)
     }
 
     @Test
@@ -125,13 +125,13 @@
         assertError(result)
         assertThat(result.errorCode).isEqualTo(errorCode)
         assertThat(result.errorMessage).isEqualTo(errorMessage)
-        assertThat(result.exception).isSameAs(exception)
+        assertThat(result.exception).isSameInstanceAs(exception)
 
         val carriedResult = input.error<Int>(result)
         assertError(carriedResult)
         assertThat(carriedResult.errorCode).isEqualTo(errorCode)
         assertThat(carriedResult.errorMessage).isEqualTo(errorMessage)
-        assertThat(carriedResult.exception).isSameAs(exception)
+        assertThat(carriedResult.exception).isSameInstanceAs(exception)
     }
 
     @Test
@@ -259,7 +259,7 @@
     private fun assertSuccess(expected: Any? = null, result: ParseResult<*>) {
         assertThat(result.isError).isFalse()
         assertThat(result.isSuccess).isTrue()
-        assertThat(result.result).isSameAs(expected)
+        assertThat(result.result).isSameInstanceAs(expected)
         assertThat(result.errorCode).isEqualTo(PackageManager.INSTALL_SUCCEEDED)
         assertThat(result.errorMessage).isNull()
         assertThat(result.exception).isNull()
diff --git a/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java b/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java
index 0f6284d..01cf311 100644
--- a/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java
+++ b/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java
@@ -362,14 +362,12 @@
 
     private final class TestPresentation extends Presentation {
         private final int mColor;
-        private final int mWindowType;
         private final int mWindowFlags;
 
         public TestPresentation(Context context, Display display,
                 int color, int windowType, int windowFlags) {
-            super(context, display);
+            super(context, display, 0 /* theme */, windowType);
             mColor = color;
-            mWindowType = windowType;
             mWindowFlags = windowFlags;
         }
 
@@ -378,7 +376,6 @@
             super.onCreate(savedInstanceState);
 
             setTitle(TAG);
-            getWindow().setType(mWindowType);
             getWindow().addFlags(mWindowFlags);
 
             // Create a solid color image to use as the content of the presentation.
diff --git a/core/tests/coretests/src/android/os/CombinedVibrationEffectTest.java b/core/tests/coretests/src/android/os/CombinedVibrationEffectTest.java
new file mode 100644
index 0000000..faa67a8
--- /dev/null
+++ b/core/tests/coretests/src/android/os/CombinedVibrationEffectTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.os;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.testng.Assert.assertThrows;
+
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@Presubmit
+@RunWith(JUnit4.class)
+public class CombinedVibrationEffectTest {
+
+    @Test
+    public void testValidateMono() {
+        CombinedVibrationEffect.createSynced(VibrationEffect.get(VibrationEffect.EFFECT_CLICK));
+
+        assertThrows(IllegalArgumentException.class,
+                () -> CombinedVibrationEffect.createSynced(new VibrationEffect.OneShot(-1, -1)));
+    }
+
+    @Test
+    public void testSerializationMono() {
+        CombinedVibrationEffect original = CombinedVibrationEffect.createSynced(
+                VibrationEffect.get(VibrationEffect.EFFECT_CLICK));
+
+        Parcel parcel = Parcel.obtain();
+        original.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        CombinedVibrationEffect restored = CombinedVibrationEffect.CREATOR.createFromParcel(parcel);
+        assertEquals(original, restored);
+    }
+}
diff --git a/core/tests/coretests/src/android/os/VibrationEffectTest.java b/core/tests/coretests/src/android/os/VibrationEffectTest.java
index e685292..c357414 100644
--- a/core/tests/coretests/src/android/os/VibrationEffectTest.java
+++ b/core/tests/coretests/src/android/os/VibrationEffectTest.java
@@ -28,6 +28,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertThrows;
 
 import android.content.ContentInterface;
 import android.content.ContentResolver;
@@ -100,6 +101,73 @@
     }
 
     @Test
+    public void testValidateOneShot() {
+        VibrationEffect.createOneShot(1, 255).validate();
+        VibrationEffect.createOneShot(1, VibrationEffect.DEFAULT_AMPLITUDE).validate();
+
+        assertThrows(IllegalArgumentException.class,
+                () -> VibrationEffect.createOneShot(-1, 255).validate());
+        assertThrows(IllegalArgumentException.class,
+                () -> VibrationEffect.createOneShot(0, 255).validate());
+        assertThrows(IllegalArgumentException.class,
+                () -> VibrationEffect.createOneShot(1, -2).validate());
+        assertThrows(IllegalArgumentException.class,
+                () -> VibrationEffect.createOneShot(1, 256).validate());
+    }
+
+    @Test
+    public void testValidatePrebaked() {
+        VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK).validate();
+        VibrationEffect.createPredefined(VibrationEffect.RINGTONES[1]).validate();
+
+        assertThrows(IllegalArgumentException.class,
+                () -> VibrationEffect.createPredefined(-1).validate());
+    }
+
+    @Test
+    public void testValidateWaveform() {
+        VibrationEffect.createWaveform(TEST_TIMINGS, TEST_AMPLITUDES, -1).validate();
+        VibrationEffect.createWaveform(TEST_TIMINGS, TEST_AMPLITUDES, 0).validate();
+
+        assertThrows(IllegalArgumentException.class,
+                () -> VibrationEffect.createWaveform(new long[0], new int[0], -1).validate());
+        assertThrows(IllegalArgumentException.class,
+                () -> VibrationEffect.createWaveform(TEST_TIMINGS, new int[0], -1).validate());
+        assertThrows(IllegalArgumentException.class,
+                () -> VibrationEffect.createWaveform(
+                        new long[]{0, 0, 0}, TEST_AMPLITUDES, -1).validate());
+        assertThrows(IllegalArgumentException.class,
+                () -> VibrationEffect.createWaveform(
+                        TEST_TIMINGS, new int[]{-1, -1, -2}, -1).validate());
+        assertThrows(IllegalArgumentException.class,
+                () -> VibrationEffect.createWaveform(
+                        TEST_TIMINGS, TEST_AMPLITUDES, TEST_TIMINGS.length).validate());
+    }
+
+    @Test
+    public void testValidateComposed() {
+        VibrationEffect.startComposition()
+                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
+                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 10)
+                .compose()
+                .validate();
+
+        assertThrows(IllegalArgumentException.class,
+                () -> VibrationEffect.startComposition().addPrimitive(-1).compose().validate());
+        assertThrows(IllegalArgumentException.class,
+                () -> VibrationEffect.startComposition()
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, -1, 10)
+                        .compose()
+                        .validate());
+        assertThrows(IllegalArgumentException.class,
+                () -> VibrationEffect.startComposition()
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, -1)
+                        .compose()
+                        .validate());
+    }
+
+    @Test
     public void testScalePrebaked_ignoresScaleAndReturnsSameEffect() {
         VibrationEffect initial = VibrationEffect.get(VibrationEffect.RINGTONES[1]);
         assertSame(initial, initial.scale(0.5f));
diff --git a/core/tests/coretests/src/android/os/VibratorTest.java b/core/tests/coretests/src/android/os/VibratorTest.java
new file mode 100644
index 0000000..c3d84ec
--- /dev/null
+++ b/core/tests/coretests/src/android/os/VibratorTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.os;
+
+import static junit.framework.TestCase.assertEquals;
+
+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.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.media.AudioAttributes;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.junit.MockitoJUnitRunner;
+
+/**
+ * Tests for {@link Vibrator}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:VibratorTest
+ */
+@Presubmit
+@RunWith(MockitoJUnitRunner.class)
+public class VibratorTest {
+
+    private Vibrator mVibratorSpy;
+
+    @Before
+    public void setUp() {
+        mVibratorSpy = spy(InstrumentationRegistry.getContext().getSystemService(Vibrator.class));
+    }
+
+    @Test
+    public void vibrate_withAudioAttributes_createsVibrationAttributesWithSameUsage() {
+        VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
+        AudioAttributes audioAttributes = new AudioAttributes.Builder().setUsage(
+                AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY).build();
+
+        mVibratorSpy.vibrate(effect, audioAttributes);
+
+        ArgumentCaptor<VibrationAttributes> captor = ArgumentCaptor.forClass(
+                VibrationAttributes.class);
+        verify(mVibratorSpy).vibrate(anyInt(), anyString(), eq(effect), isNull(), captor.capture());
+
+        VibrationAttributes vibrationAttributes = captor.getValue();
+        assertEquals(VibrationAttributes.USAGE_COMMUNICATION_REQUEST,
+                vibrationAttributes.getUsage());
+        // Keeps original AudioAttributes usage to be used by the VibratorService.
+        assertEquals(AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY,
+                vibrationAttributes.getAudioUsage());
+    }
+
+    @Test
+    public void vibrate_withUnknownAudioAttributes_hasTouchUsageFromEffect() {
+        VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
+        AudioAttributes audioAttributes = new AudioAttributes.Builder().setUsage(
+                AudioAttributes.USAGE_UNKNOWN).build();
+
+        mVibratorSpy.vibrate(effect, audioAttributes);
+
+        ArgumentCaptor<VibrationAttributes> captor = ArgumentCaptor.forClass(
+                VibrationAttributes.class);
+        verify(mVibratorSpy).vibrate(anyInt(), anyString(), eq(effect), isNull(), captor.capture());
+
+        VibrationAttributes vibrationAttributes = captor.getValue();
+        assertEquals(VibrationAttributes.USAGE_TOUCH,
+                vibrationAttributes.getUsage());
+        // Sets AudioAttributes usage based on effect.
+        assertEquals(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION,
+                vibrationAttributes.getAudioUsage());
+    }
+
+    @Test
+    public void vibrate_withoutAudioAttributes_hasTouchUsageFromEffect() {
+        mVibratorSpy.vibrate(VibrationEffect.get(VibrationEffect.EFFECT_CLICK));
+
+        ArgumentCaptor<VibrationAttributes> captor = ArgumentCaptor.forClass(
+                VibrationAttributes.class);
+        verify(mVibratorSpy).vibrate(anyInt(), anyString(), any(), isNull(), captor.capture());
+
+        VibrationAttributes vibrationAttributes = captor.getValue();
+        assertEquals(VibrationAttributes.USAGE_TOUCH, vibrationAttributes.getUsage());
+        // Sets AudioAttributes usage based on effect.
+        assertEquals(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION,
+                vibrationAttributes.getAudioUsage());
+    }
+
+    @Test
+    public void vibrate_withoutAudioAttributesAndLongEffect_hasUnknownUsage() {
+        mVibratorSpy.vibrate(VibrationEffect.createOneShot(10_000, 255));
+
+        ArgumentCaptor<VibrationAttributes> captor = ArgumentCaptor.forClass(
+                VibrationAttributes.class);
+        verify(mVibratorSpy).vibrate(anyInt(), anyString(), any(), isNull(), captor.capture());
+
+        VibrationAttributes vibrationAttributes = captor.getValue();
+        assertEquals(VibrationAttributes.USAGE_UNKNOWN, vibrationAttributes.getUsage());
+        assertEquals(AudioAttributes.USAGE_UNKNOWN, vibrationAttributes.getAudioUsage());
+    }
+}
diff --git a/core/tests/coretests/src/android/text/TextShaperTest.java b/core/tests/coretests/src/android/text/TextShaperTest.java
new file mode 100644
index 0000000..8237cb0
--- /dev/null
+++ b/core/tests/coretests/src/android/text/TextShaperTest.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.text;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TextShaperTest {
+
+    @Test
+    public void testFontWithPath() {
+        TextPaint p = new TextPaint();
+        p.setFontFeatureSettings("'wght' 900");
+        TextShaper.shapeText("a", 0, 1, TextDirectionHeuristics.LTR, p,
+                (start, end, glyphs, paint) -> {
+                // This test only passes if the font of the Latin font is variable font.
+                assertThat(glyphs.getFont(0).getFile()).isNotNull();
+            });
+    }
+}
diff --git a/core/tests/coretests/src/android/text/TextUtilsTest.java b/core/tests/coretests/src/android/text/TextUtilsTest.java
index be6ef19..a0fc349 100644
--- a/core/tests/coretests/src/android/text/TextUtilsTest.java
+++ b/core/tests/coretests/src/android/text/TextUtilsTest.java
@@ -16,6 +16,8 @@
 
 package android.text;
 
+import static android.text.TextUtils.formatSimple;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -792,5 +794,83 @@
         assertEquals("ABC...", TextUtils.trimToLengthWithEllipsis("ABCDEF", 3));
         assertEquals("ABC", TextUtils.trimToLengthWithEllipsis("ABC", 3));
         assertEquals("", TextUtils.trimToLengthWithEllipsis("", 3));
+        assertNull(TextUtils.trimToLengthWithEllipsis(null, 3));
+    }
+
+    @Test
+    public void testFormatSimple_Types() {
+        assertEquals("true", formatSimple("%b", true));
+        assertEquals("false", formatSimple("%b", false));
+        assertEquals("true", formatSimple("%b", this));
+        assertEquals("false", formatSimple("%b", new Object[] { null }));
+
+        assertEquals("!", formatSimple("%c", '!'));
+
+        assertEquals("42", formatSimple("%d", 42));
+        assertEquals("281474976710656", formatSimple("%d", 281474976710656L));
+
+        assertEquals("3.14159", formatSimple("%f", 3.14159));
+        assertEquals("NaN", formatSimple("%f", Float.NaN));
+
+        assertEquals("example", formatSimple("%s", "example"));
+        assertEquals("null", formatSimple("%s", new Object[] { null }));
+
+        assertEquals("2a", formatSimple("%x", 42));
+        assertEquals("1000000000000", formatSimple("%x", 281474976710656L));
+
+        assertEquals("%", formatSimple("%%"));
+    }
+
+    @Test
+    public void testFormatSimple_Width() {
+        assertEquals("42", formatSimple("%1d", 42));
+        assertEquals("42", formatSimple("%2d", 42));
+        assertEquals(" 42", formatSimple("%3d", 42));
+        assertEquals("  42", formatSimple("%4d", 42));
+        assertEquals("  42  42", formatSimple("%4d%4d", 42, 42));
+        assertEquals(" -42", formatSimple("%4d", -42));
+        assertEquals("        42", formatSimple("%10d", 42));
+
+        assertEquals("42", formatSimple("%01d", 42));
+        assertEquals("42", formatSimple("%02d", 42));
+        assertEquals("042", formatSimple("%03d", 42));
+        assertEquals("0042", formatSimple("%04d", 42));
+        assertEquals("00420042", formatSimple("%04d%04d", 42, 42));
+        assertEquals("-042", formatSimple("%04d", -42));
+        assertEquals("0000000042", formatSimple("%010d", 42));
+    }
+
+    @Test
+    public void testFormatSimple_Empty() {
+        assertEquals("", formatSimple(""));
+    }
+
+    @Test
+    public void testFormatSimple_Typical() {
+        assertEquals("String foobar and %% number -42 together",
+                formatSimple("String %s%s and %%%% number %d%d together", "foo", "bar", -4, 2));
+    }
+
+    @Test
+    public void testFormatSimple_Advanced() {
+        assertEquals("000000000000002a.ext",
+                formatSimple("%016x.%s", 42, "ext"));
+        assertEquals("crtcl=0x002a:intrsv=Y:grnk=0x0018:gsmry=A:example:rnk=0x0000",
+                formatSimple("crtcl=0x%04x:intrsv=%c:grnk=0x%04x:gsmry=%c:%s:rnk=0x%04x",
+                        42, 'Y', 24, 'A', "example", 0));
+    }
+
+    @Test
+    public void testFormatSimple_Mismatch() {
+        try {
+            formatSimple("%s");
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+        try {
+            formatSimple("%s", "foo", "bar");
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
     }
 }
diff --git a/core/tests/coretests/src/android/text/format/DateFormatTest.java b/core/tests/coretests/src/android/text/format/DateFormatTest.java
index a3434e8..212cc44 100644
--- a/core/tests/coretests/src/android/text/format/DateFormatTest.java
+++ b/core/tests/coretests/src/android/text/format/DateFormatTest.java
@@ -21,13 +21,19 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import android.compat.testing.PlatformCompatChangeRule;
 import android.icu.text.DateFormatSymbols;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
 
 import java.util.Arrays;
@@ -38,6 +44,9 @@
 @RunWith(AndroidJUnit4.class)
 public class DateFormatTest {
 
+    @Rule
+    public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
     @Test
     public void testHasDesignator() {
         assertTrue(DateFormat.hasDesignator("hh:mm:ss", DateFormat.MINUTE));
@@ -135,4 +144,29 @@
     private static String best(Locale l, String skeleton) {
         return DateFormat.getBestDateTimePattern(l, skeleton);
     }
+
+    @Test
+    @EnableCompatChanges({DateFormat.DISALLOW_DUPLICATE_FIELD_IN_SKELETON})
+    public void testGetBestDateTimePattern_disableDuplicateField() {
+        assertIllegalArgumentException(Locale.US, "jmma");
+        assertIllegalArgumentException(Locale.US, "ahmma");
+    }
+
+    @Test
+    @DisableCompatChanges({DateFormat.DISALLOW_DUPLICATE_FIELD_IN_SKELETON})
+    public void testGetBestDateTimePattern_enableDuplicateField() {
+        // en-US uses 12-hour format by default.
+        assertEquals("h:mm a", DateFormat.getBestDateTimePattern(Locale.US, "jmma"));
+        assertEquals("h:mm a", DateFormat.getBestDateTimePattern(Locale.US, "ahmma"));
+    }
+
+    private static void assertIllegalArgumentException(Locale l, String skeleton) {
+        try {
+            DateFormat.getBestDateTimePattern(l, skeleton);
+            fail("getBestDateTimePattern() does not fail with Locale: " + l
+                    + " skeleton: " + skeleton);
+        } catch (IllegalArgumentException expected) {
+            // ignored
+        }
+    }
 }
diff --git a/core/tests/coretests/src/android/text/format/OWNERS b/core/tests/coretests/src/android/text/format/OWNERS
new file mode 100644
index 0000000..32adc12
--- /dev/null
+++ b/core/tests/coretests/src/android/text/format/OWNERS
@@ -0,0 +1,3 @@
+# Inherits OWNERS from parent directory, plus the following
+
+vichang@google.com
diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
index 8412e53..873627e 100644
--- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
+++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
@@ -27,6 +27,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -89,6 +90,7 @@
         mInsetsState = new InsetsState();
         mInsetsState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 500, 100));
         mInsetsState.getSource(ITYPE_NAVIGATION_BAR).setFrame(new Rect(400, 0, 500, 500));
+        doNothing().when(mMockController).onRequestedVisibilityChanged(any());
         InsetsSourceConsumer topConsumer = new InsetsSourceConsumer(ITYPE_STATUS_BAR, mInsetsState,
                 () -> mMockTransaction, mMockController);
         topConsumer.setControl(
@@ -107,7 +109,7 @@
         mController = new InsetsAnimationControlImpl(controls,
                 new Rect(0, 0, 500, 500), mInsetsState, mMockListener, systemBars(),
                 mMockController, 10 /* durationMs */, new LinearInterpolator(),
-                0 /* animationType */);
+                0 /* animationType */, null /* translator */);
         mController.mReadyDispatched = true;
     }
 
@@ -130,7 +132,7 @@
     public void testChangeInsets() {
         mController.setInsetsAndAlpha(Insets.of(0, 30, 40, 0), 1f /* alpha */,
                 0f /* fraction */);
-        mController.applyChangeInsets(new InsetsState());
+        mController.applyChangeInsets(null /* outState */);
         assertEquals(Insets.of(0, 30, 40, 0), mController.getCurrentInsets());
         assertEquals(1f, mController.getCurrentAlpha(), 1f - mController.getCurrentAlpha());
 
@@ -150,7 +152,7 @@
     public void testChangeAlphaNoInsets() {
         Insets initialInsets = mController.getCurrentInsets();
         mController.setInsetsAndAlpha(null, 0.5f, 0f /* fraction*/);
-        mController.applyChangeInsets(new InsetsState());
+        mController.applyChangeInsets(null /* outState */);
         assertEquals(0.5f, mController.getCurrentAlpha(), 0.5f - mController.getCurrentAlpha());
         assertEquals(initialInsets, mController.getCurrentInsets());
     }
@@ -158,7 +160,7 @@
     @Test
     public void testChangeInsetsAndAlpha() {
         mController.setInsetsAndAlpha(Insets.of(0, 30, 40, 0), 0.5f, 1f);
-        mController.applyChangeInsets(new InsetsState());
+        mController.applyChangeInsets(null /* outState */);
         assertEquals(0.5f, mController.getCurrentAlpha(), 0.5f - mController.getCurrentAlpha());
         assertEquals(Insets.of(0, 30, 40, 0), mController.getCurrentInsets());
     }
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index cc97eb5..7b84f68c 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -683,71 +683,15 @@
     @Test
     public void testRequestedState() {
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            final InsetsState state = mTestHost.getRequestedState();
 
-            // The modified state can be controlled when we have control.
-            mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR));
-            mController.hide(statusBars());
-            assertFalse(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible());
+            mController.hide(statusBars() | navigationBars());
+            assertFalse(state.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR));
+            assertFalse(state.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR));
 
-            // The modified state won't be changed while losing control.
-            mController.onControlsChanged(null /* activeControls */);
-            assertFalse(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible());
-
-            // The modified state won't be changed while state changed while we don't have control.
-            InsetsState newState = new InsetsState(mController.getState(), true /* copySource */);
-            mController.onStateChanged(newState);
-            assertFalse(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible());
-
-            // The modified state won't be changed while controlling an insets without having the
-            // control.
-            mController.show(statusBars());
-            assertFalse(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible());
-
-            // The modified state can be updated while gaining control.
-            mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR));
-            assertTrue(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible());
-
-            // The modified state can still be updated if the local state and the requested state
-            // are the same.
-            mController.onControlsChanged(null /* activeControls */);
-            mController.hide(statusBars());
-            newState = new InsetsState(mController.getState(), true /* copySource */);
-            newState.getSource(ITYPE_STATUS_BAR).setVisible(false);
-            mController.onStateChanged(newState);
-            mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR));
-            assertFalse(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible());
-
-            // The modified state will always be updated while receiving IME control if IME is
-            // requested visible.
-            mController.getSourceConsumer(ITYPE_IME).show(false /* fromIme */);
-            newState = new InsetsState(mController.getState(), true /* copySource */);
-            newState.getSource(ITYPE_IME).setVisible(true);
-            newState.getSource(ITYPE_IME).setFrame(1, 2, 3, 4);
-            mController.onStateChanged(newState);
-            mController.onControlsChanged(createSingletonControl(ITYPE_IME));
-            assertEquals(newState.getSource(ITYPE_IME),
-                    mTestHost.getModifiedState().peekSource(ITYPE_IME));
-            newState = new InsetsState(mController.getState(), true /* copySource */);
-            newState.getSource(ITYPE_IME).setVisible(true);
-            newState.getSource(ITYPE_IME).setFrame(5, 6, 7, 8);
-            mController.onStateChanged(newState);
-            mController.onControlsChanged(createSingletonControl(ITYPE_IME));
-            assertEquals(newState.getSource(ITYPE_IME),
-                    mTestHost.getModifiedState().peekSource(ITYPE_IME));
-
-            // The modified frames cannot be updated if there is an animation.
-            mController.onControlsChanged(createSingletonControl(ITYPE_NAVIGATION_BAR));
-            mController.hide(navigationBars());
-            newState = new InsetsState(mController.getState(), true /* copySource */);
-            newState.getSource(ITYPE_NAVIGATION_BAR).getFrame().top--;
-            mController.onStateChanged(newState);
-            assertNotEquals(newState.getSource(ITYPE_NAVIGATION_BAR),
-                    mTestHost.getModifiedState().peekSource(ITYPE_NAVIGATION_BAR));
-
-            // The modified frames can be updated while the animation is done.
-            mController.cancelExistingAnimations();
-            assertEquals(newState.getSource(ITYPE_NAVIGATION_BAR),
-                    mTestHost.getModifiedState().peekSource(ITYPE_NAVIGATION_BAR));
+            mController.show(statusBars() | navigationBars());
+            assertTrue(state.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR));
+            assertTrue(state.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR));
         });
     }
 
@@ -878,7 +822,7 @@
 
     public static class TestHost extends ViewRootInsetsControllerHost {
 
-        private InsetsState mModifiedState = new InsetsState();
+        private final InsetsState mRequestedState = new InsetsState();
 
         TestHost(ViewRootImpl viewRoot) {
             super(viewRoot);
@@ -886,12 +830,12 @@
 
         @Override
         public void onInsetsModified(InsetsState insetsState) {
-            mModifiedState = new InsetsState(insetsState, true /* copySource */);
+            mRequestedState.set(insetsState, true);
             super.onInsetsModified(insetsState);
         }
 
-        public InsetsState getModifiedState() {
-            return mModifiedState;
+        public InsetsState getRequestedState() {
+            return mRequestedState;
         }
     }
 }
diff --git a/core/tests/coretests/src/android/view/ScrollCaptureClientTest.java b/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java
similarity index 73%
rename from core/tests/coretests/src/android/view/ScrollCaptureClientTest.java
rename to core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java
index e6ac2d6..b9cf1e4 100644
--- a/core/tests/coretests/src/android/view/ScrollCaptureClientTest.java
+++ b/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java
@@ -47,11 +47,11 @@
 import org.mockito.stubbing.Answer;
 
 /**
- * Tests of {@link ScrollCaptureClient}.
+ * Tests of {@link ScrollCaptureConnection}.
  */
 @SuppressWarnings("UnnecessaryLocalVariable")
 @RunWith(AndroidJUnit4.class)
-public class ScrollCaptureClientTest {
+public class ScrollCaptureConnectionTest {
 
     private final Point mPositionInWindow = new Point(1, 2);
     private final Rect mLocalVisibleRect = new Rect(2, 3, 4, 5);
@@ -63,7 +63,7 @@
     @Mock
     private Surface mSurface;
     @Mock
-    private IScrollCaptureController mClientCallbacks;
+    private IScrollCaptureCallbacks mConnectionCallbacks;
     @Mock
     private View mMockView1;
     @Mock
@@ -86,8 +86,8 @@
     @Test
     public void testDelayedAction() {
         Runnable action = Mockito.mock(Runnable.class);
-        ScrollCaptureClient.DelayedAction delayed =
-                new ScrollCaptureClient.DelayedAction(mHandler, 100, action);
+        ScrollCaptureConnection.DelayedAction delayed =
+                new ScrollCaptureConnection.DelayedAction(mHandler, 100, action);
         try {
             Thread.sleep(200);
         } catch (InterruptedException ex) {
@@ -103,8 +103,8 @@
     @Test
     public void testDelayedAction_cancel() {
         Runnable action = Mockito.mock(Runnable.class);
-        ScrollCaptureClient.DelayedAction delayed =
-                new ScrollCaptureClient.DelayedAction(mHandler, 100, action);
+        ScrollCaptureConnection.DelayedAction delayed =
+                new ScrollCaptureConnection.DelayedAction(mHandler, 100, action);
         try {
             Thread.sleep(50);
         } catch (InterruptedException ex) {
@@ -125,8 +125,8 @@
     @Test
     public void testDelayedAction_timeoutNow() {
         Runnable action = Mockito.mock(Runnable.class);
-        ScrollCaptureClient.DelayedAction delayed =
-                new ScrollCaptureClient.DelayedAction(mHandler, 100, action);
+        ScrollCaptureConnection.DelayedAction delayed =
+                new ScrollCaptureConnection.DelayedAction(mHandler, 100, action);
         try {
             Thread.sleep(50);
         } catch (InterruptedException ex) {
@@ -141,7 +141,7 @@
     /** Test creating a client with valid info */
     @Test
     public void testConstruction() {
-        new ScrollCaptureClient(mTarget1, mClientCallbacks);
+        new ScrollCaptureConnection(mTarget1, mConnectionCallbacks);
     }
 
     /** Test creating a client fails if arguments are not valid. */
@@ -149,7 +149,7 @@
     public void testConstruction_requiresScrollBounds() {
         try {
             mTarget1.setScrollBounds(null);
-            new ScrollCaptureClient(mTarget1, mClientCallbacks);
+            new ScrollCaptureConnection(mTarget1, mConnectionCallbacks);
             fail("An exception was expected.");
         } catch (RuntimeException ex) {
             // Ignore, expected.
@@ -174,48 +174,51 @@
         };
     }
 
-    /** @see ScrollCaptureClient#startCapture(Surface) */
+    /** @see ScrollCaptureConnection#startCapture(Surface) */
     @Test
     public void testStartCapture() throws Exception {
-        final ScrollCaptureClient client = new ScrollCaptureClient(mTarget1, mClientCallbacks);
+        final ScrollCaptureConnection connection = new ScrollCaptureConnection(mTarget1,
+                mConnectionCallbacks);
 
         // Have the session start accepted immediately
         doAnswer(runRunnable(1)).when(mCallback1)
                 .onScrollCaptureStart(any(ScrollCaptureSession.class), any(Runnable.class));
-        client.startCapture(mSurface);
+        connection.startCapture(mSurface);
         getInstrumentation().waitForIdleSync();
 
         verify(mCallback1, times(1))
                 .onScrollCaptureStart(any(ScrollCaptureSession.class), any(Runnable.class));
-        verify(mClientCallbacks, times(1)).onCaptureStarted();
-        verifyNoMoreInteractions(mClientCallbacks);
+        verify(mConnectionCallbacks, times(1)).onCaptureStarted();
+        verifyNoMoreInteractions(mConnectionCallbacks);
     }
 
     @Test
     public void testStartCaptureTimeout() throws Exception {
-        final ScrollCaptureClient client = new ScrollCaptureClient(mTarget1, mClientCallbacks);
-        client.startCapture(mSurface);
+        final ScrollCaptureConnection connection = new ScrollCaptureConnection(mTarget1,
+                mConnectionCallbacks);
+        connection.startCapture(mSurface);
 
         // Force timeout to fire
-        client.getTimeoutAction().timeoutNow();
+        connection.getTimeoutAction().timeoutNow();
 
         getInstrumentation().waitForIdleSync();
         verify(mCallback1, times(1)).onScrollCaptureEnd(any(Runnable.class));
     }
 
-    private void startClient(ScrollCaptureClient client) throws Exception {
+    private void startCapture(ScrollCaptureConnection connection) throws Exception {
         doAnswer(runRunnable(1)).when(mCallback1)
                 .onScrollCaptureStart(any(ScrollCaptureSession.class), any(Runnable.class));
-        client.startCapture(mSurface);
+        connection.startCapture(mSurface);
         getInstrumentation().waitForIdleSync();
-        reset(mCallback1, mClientCallbacks);
+        reset(mCallback1, mConnectionCallbacks);
     }
 
-    /** @see ScrollCaptureClient#requestImage(Rect) */
+    /** @see ScrollCaptureConnection#requestImage(Rect) */
     @Test
     public void testRequestImage() throws Exception {
-        final ScrollCaptureClient client = new ScrollCaptureClient(mTarget1, mClientCallbacks);
-        startClient(client);
+        final ScrollCaptureConnection connection = new ScrollCaptureConnection(mTarget1,
+                mConnectionCallbacks);
+        startCapture(connection);
 
         // Stub the callback to complete the request immediately
         doAnswer(reportBufferSent(/* sessionArg */ 0, /* frameNum */ 1L, new Rect(1, 2, 3, 4)))
@@ -223,7 +226,7 @@
                 .onScrollCaptureImageRequest(any(ScrollCaptureSession.class), any(Rect.class));
 
         // Make the inbound binder call
-        client.requestImage(new Rect(1, 2, 3, 4));
+        connection.requestImage(new Rect(1, 2, 3, 4));
 
         // Wait for handler thread dispatch
         getInstrumentation().waitForIdleSync();
@@ -232,18 +235,20 @@
 
         // Wait for binder thread dispatch
         getInstrumentation().waitForIdleSync();
-        verify(mClientCallbacks, times(1)).onCaptureBufferSent(eq(1L), eq(new Rect(1, 2, 3, 4)));
+        verify(mConnectionCallbacks, times(1))
+                .onCaptureBufferSent(eq(1L), eq(new Rect(1, 2, 3, 4)));
 
-        verifyNoMoreInteractions(mCallback1, mClientCallbacks);
+        verifyNoMoreInteractions(mCallback1, mConnectionCallbacks);
     }
 
     @Test
     public void testRequestImageTimeout() throws Exception {
-        final ScrollCaptureClient client = new ScrollCaptureClient(mTarget1, mClientCallbacks);
-        startClient(client);
+        final ScrollCaptureConnection connection = new ScrollCaptureConnection(mTarget1,
+                mConnectionCallbacks);
+        startCapture(connection);
 
         // Make the inbound binder call
-        client.requestImage(new Rect(1, 2, 3, 4));
+        connection.requestImage(new Rect(1, 2, 3, 4));
 
         // Wait for handler thread dispatch
         getInstrumentation().waitForIdleSync();
@@ -251,20 +256,21 @@
                 any(ScrollCaptureSession.class), eq(new Rect(1, 2, 3, 4)));
 
         // Force timeout to fire
-        client.getTimeoutAction().timeoutNow();
+        connection.getTimeoutAction().timeoutNow();
         getInstrumentation().waitForIdleSync();
 
         // (callback not stubbed, does nothing)
         // Timeout triggers request to end capture
         verify(mCallback1, times(1)).onScrollCaptureEnd(any(Runnable.class));
-        verifyNoMoreInteractions(mCallback1, mClientCallbacks);
+        verifyNoMoreInteractions(mCallback1, mConnectionCallbacks);
     }
 
-    /** @see ScrollCaptureClient#endCapture() */
+    /** @see ScrollCaptureConnection#endCapture() */
     @Test
     public void testEndCapture() throws Exception {
-        final ScrollCaptureClient client = new ScrollCaptureClient(mTarget1, mClientCallbacks);
-        startClient(client);
+        final ScrollCaptureConnection connection = new ScrollCaptureConnection(mTarget1,
+                mConnectionCallbacks);
+        startCapture(connection);
 
         // Stub the callback to complete the request immediately
         doAnswer(runRunnable(0))
@@ -272,7 +278,7 @@
                 .onScrollCaptureEnd(any(Runnable.class));
 
         // Make the inbound binder call
-        client.endCapture();
+        connection.endCapture();
 
         // Wait for handler thread dispatch
         getInstrumentation().waitForIdleSync();
@@ -280,30 +286,31 @@
 
         // Wait for binder thread dispatch
         getInstrumentation().waitForIdleSync();
-        verify(mClientCallbacks, times(1)).onConnectionClosed();
+        verify(mConnectionCallbacks, times(1)).onConnectionClosed();
 
-        verifyNoMoreInteractions(mCallback1, mClientCallbacks);
+        verifyNoMoreInteractions(mCallback1, mConnectionCallbacks);
     }
 
     @Test
     public void testEndCaptureTimeout() throws Exception {
-        final ScrollCaptureClient client = new ScrollCaptureClient(mTarget1, mClientCallbacks);
-        startClient(client);
+        final ScrollCaptureConnection connection = new ScrollCaptureConnection(mTarget1,
+                mConnectionCallbacks);
+        startCapture(connection);
 
         // Make the inbound binder call
-        client.endCapture();
+        connection.endCapture();
 
         // Wait for handler thread dispatch
         getInstrumentation().waitForIdleSync();
         verify(mCallback1, times(1)).onScrollCaptureEnd(any(Runnable.class));
 
         // Force timeout to fire
-        client.getTimeoutAction().timeoutNow();
+        connection.getTimeoutAction().timeoutNow();
 
         // Wait for binder thread dispatch
         getInstrumentation().waitForIdleSync();
-        verify(mClientCallbacks, times(1)).onConnectionClosed();
+        verify(mConnectionCallbacks, times(1)).onConnectionClosed();
 
-        verifyNoMoreInteractions(mCallback1, mClientCallbacks);
+        verifyNoMoreInteractions(mCallback1, mConnectionCallbacks);
     }
 }
diff --git a/core/tests/coretests/src/android/view/ViewInputConnectionTest.java b/core/tests/coretests/src/android/view/ViewInputConnectionTest.java
new file mode 100644
index 0000000..d667af3
--- /dev/null
+++ b/core/tests/coretests/src/android/view/ViewInputConnectionTest.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertTrue;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.os.Handler;
+import android.text.format.DateUtils;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.Button;
+import android.widget.EditText;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.MediumTest;
+import androidx.test.rule.ActivityTestRule;
+
+import com.android.compatibility.common.util.PollingCheck;
+import com.android.frameworks.coretests.R;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for internal APIs/behaviors of {@link View} and {@link InputConnection}.
+ */
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class ViewInputConnectionTest {
+    @Rule
+    public ActivityTestRule<ViewInputConnectionTestActivity> mActivityRule =
+            new ActivityTestRule<>(ViewInputConnectionTestActivity.class);
+
+    private Instrumentation mInstrumentation;
+    private ViewInputConnectionTestActivity mActivity;
+    private InputMethodManager mImm;
+
+    @Before
+    public void before() {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mActivity = mActivityRule.getActivity();
+        PollingCheck.waitFor(5 * DateUtils.SECOND_IN_MILLIS, mActivity::hasWindowFocus);
+        assertTrue(mActivity.hasWindowFocus());
+        mImm = mActivity.getSystemService(InputMethodManager.class);
+    }
+
+    @Test
+    public void testInputConnectionCallbacks() throws Throwable {
+        // Add two EditText inputs to the layout view.
+        final ViewGroup viewGroup = mActivity.findViewById(R.id.root);
+        final TestEditText editText1 = new TestEditText(mActivity, false);
+        final TestEditText editText2 = new TestEditText(mActivity, false);
+        mActivityRule.runOnUiThread(() -> {
+            viewGroup.addView(editText1);
+            viewGroup.addView(editText2);
+        });
+        mInstrumentation.waitForIdleSync();
+
+        // Focus into the first EditText.
+        mActivityRule.runOnUiThread(editText1::requestFocus);
+        mInstrumentation.waitForIdleSync();
+        assertThat(editText1.isFocused()).isTrue();
+        assertThat(editText2.isFocused()).isFalse();
+
+        // Show the IME for the first EditText. Assert that the appropriate opened/closed callbacks
+        // have been invoked (InputConnection opened for the first EditText).
+        mActivityRule.runOnUiThread(() -> mImm.showSoftInput(editText1, 0));
+        mInstrumentation.waitForIdleSync();
+        mActivityRule.runOnUiThread(() -> {
+            assertThat(editText1.mCalledOnCreateInputConnection).isTrue();
+            assertThat(editText1.mCalledOnInputConnectionOpened).isTrue();
+            assertThat(editText1.mCalledOnInputConnectionClosed).isFalse();
+
+            assertThat(editText2.mCalledOnCreateInputConnection).isFalse();
+            assertThat(editText2.mCalledOnInputConnectionOpened).isFalse();
+            assertThat(editText2.mCalledOnInputConnectionClosed).isFalse();
+        });
+
+        // Focus into the second EditText.
+        mActivityRule.runOnUiThread(editText2::requestFocus);
+        mInstrumentation.waitForIdleSync();
+        assertThat(editText1.isFocused()).isFalse();
+        assertThat(editText2.isFocused()).isTrue();
+
+        // Show the IME for the second EditText. Assert that the appropriate opened/closed callbacks
+        // have been invoked (InputConnection closed for the first EditText and opened for the
+        // second EditText).
+        mActivityRule.runOnUiThread(() -> mImm.showSoftInput(editText2, 0));
+        mInstrumentation.waitForIdleSync();
+        mActivityRule.runOnUiThread(() -> {
+            assertThat(editText1.mCalledOnCreateInputConnection).isTrue();
+            assertThat(editText1.mCalledOnInputConnectionOpened).isTrue();
+            assertThat(editText1.mCalledOnInputConnectionClosed).isTrue();
+
+            assertThat(editText2.mCalledOnCreateInputConnection).isTrue();
+            assertThat(editText2.mCalledOnInputConnectionOpened).isTrue();
+            assertThat(editText2.mCalledOnInputConnectionClosed).isFalse();
+        });
+    }
+
+    @Test
+    public void testInputConnectionCallbacks_nullInputConnection() throws Throwable {
+        // Add two EditText inputs to the layout view.
+        final ViewGroup viewGroup = mActivity.findViewById(R.id.root);
+        final TestEditText editText1 = new TestEditText(mActivity, true);
+        final TestEditText editText2 = new TestEditText(mActivity, true);
+        mActivityRule.runOnUiThread(() -> {
+            viewGroup.addView(editText1);
+            viewGroup.addView(editText2);
+        });
+        mInstrumentation.waitForIdleSync();
+
+        // Focus into the first EditText.
+        mActivityRule.runOnUiThread(editText1::requestFocus);
+        mInstrumentation.waitForIdleSync();
+        assertThat(editText1.isFocused()).isTrue();
+        assertThat(editText2.isFocused()).isFalse();
+
+        // Show the IME for the first EditText. Assert that the opened/closed callbacks are not
+        // invoked since there's no input connection.
+        mActivityRule.runOnUiThread(() -> mImm.showSoftInput(editText1, 0));
+        mInstrumentation.waitForIdleSync();
+        mActivityRule.runOnUiThread(() -> {
+            assertThat(editText1.mCalledOnCreateInputConnection).isTrue();
+            assertThat(editText1.mCalledOnInputConnectionOpened).isFalse();
+            assertThat(editText1.mCalledOnInputConnectionClosed).isFalse();
+
+            assertThat(editText2.mCalledOnCreateInputConnection).isFalse();
+            assertThat(editText2.mCalledOnInputConnectionOpened).isFalse();
+            assertThat(editText2.mCalledOnInputConnectionClosed).isFalse();
+        });
+
+        // Focus into the second EditText.
+        mActivityRule.runOnUiThread(editText2::requestFocus);
+        mInstrumentation.waitForIdleSync();
+        assertThat(editText1.isFocused()).isFalse();
+        assertThat(editText2.isFocused()).isTrue();
+
+        // Show the IME for the second EditText. Assert that the opened/closed callbacks are not
+        // invoked since there's no input connection.
+        mActivityRule.runOnUiThread(() -> mImm.showSoftInput(editText2, 0));
+        mInstrumentation.waitForIdleSync();
+        mActivityRule.runOnUiThread(() -> {
+            assertThat(editText1.mCalledOnCreateInputConnection).isTrue();
+            assertThat(editText1.mCalledOnInputConnectionOpened).isFalse();
+            assertThat(editText1.mCalledOnInputConnectionClosed).isFalse();
+
+            assertThat(editText2.mCalledOnCreateInputConnection).isTrue();
+            assertThat(editText2.mCalledOnInputConnectionOpened).isFalse();
+            assertThat(editText2.mCalledOnInputConnectionClosed).isFalse();
+        });
+    }
+
+    @Test
+    public void testInputConnectionCallbacks_nonEditableInput() throws Throwable {
+        final ViewGroup viewGroup = mActivity.findViewById(R.id.root);
+        final TestButton view1 = new TestButton(mActivity);
+        final TestButton view2 = new TestButton(mActivity);
+        mActivityRule.runOnUiThread(() -> {
+            viewGroup.addView(view1);
+            viewGroup.addView(view2);
+        });
+        mInstrumentation.waitForIdleSync();
+
+        // Request focus + IME on the first view.
+        mActivityRule.runOnUiThread(view1::requestFocus);
+        mInstrumentation.waitForIdleSync();
+        assertThat(view1.isFocused()).isTrue();
+        assertThat(view2.isFocused()).isFalse();
+        mActivityRule.runOnUiThread(() -> mImm.showSoftInput(view1, 0));
+        mInstrumentation.waitForIdleSync();
+
+        // Assert that the opened/closed callbacks are not invoked since there's no InputConnection.
+        mActivityRule.runOnUiThread(() -> {
+            assertThat(view1.mCalledOnCreateInputConnection).isTrue();
+            assertThat(view1.mCalledOnInputConnectionOpened).isFalse();
+            assertThat(view1.mCalledOnInputConnectionClosed).isFalse();
+
+            assertThat(view2.mCalledOnCreateInputConnection).isFalse();
+            assertThat(view2.mCalledOnInputConnectionOpened).isFalse();
+            assertThat(view2.mCalledOnInputConnectionClosed).isFalse();
+        });
+
+        // Request focus + IME on the second view.
+        mActivityRule.runOnUiThread(view2::requestFocus);
+        mInstrumentation.waitForIdleSync();
+        assertThat(view1.isFocused()).isFalse();
+        assertThat(view2.isFocused()).isTrue();
+        mActivityRule.runOnUiThread(() -> mImm.showSoftInput(view1, 0));
+        mInstrumentation.waitForIdleSync();
+
+        // Assert that the opened/closed callbacks are not invoked since there's no InputConnection.
+        mActivityRule.runOnUiThread(() -> {
+            assertThat(view1.mCalledOnCreateInputConnection).isTrue();
+            assertThat(view1.mCalledOnInputConnectionOpened).isFalse();
+            assertThat(view1.mCalledOnInputConnectionClosed).isFalse();
+
+            assertThat(view2.mCalledOnCreateInputConnection).isTrue();
+            assertThat(view2.mCalledOnInputConnectionOpened).isFalse();
+            assertThat(view2.mCalledOnInputConnectionClosed).isFalse();
+        });
+    }
+
+    private static class TestEditText extends EditText {
+        private final boolean mReturnNullInputConnection;
+
+        public boolean mCalledOnCreateInputConnection = false;
+        public boolean mCalledOnInputConnectionOpened = false;
+        public boolean mCalledOnInputConnectionClosed = false;
+
+        TestEditText(Context context, boolean returnNullInputConnection) {
+            super(context);
+            mReturnNullInputConnection = returnNullInputConnection;
+        }
+
+        @Override
+        public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+            mCalledOnCreateInputConnection = true;
+            if (mReturnNullInputConnection) {
+                return null;
+            } else {
+                return super.onCreateInputConnection(outAttrs);
+            }
+        }
+
+        @Override
+        public void onInputConnectionOpenedInternal(@NonNull InputConnection inputConnection,
+                @NonNull EditorInfo editorInfo, @Nullable Handler handler) {
+            mCalledOnInputConnectionOpened = true;
+            super.onInputConnectionOpenedInternal(inputConnection, editorInfo, handler);
+        }
+
+        @Override
+        public void onInputConnectionClosedInternal() {
+            mCalledOnInputConnectionClosed = true;
+            super.onInputConnectionClosedInternal();
+        }
+    }
+
+    private static class TestButton extends Button {
+        public boolean mCalledOnCreateInputConnection = false;
+        public boolean mCalledOnInputConnectionOpened = false;
+        public boolean mCalledOnInputConnectionClosed = false;
+
+        TestButton(Context context) {
+            super(context);
+        }
+
+        @Override
+        public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+            mCalledOnCreateInputConnection = true;
+            return super.onCreateInputConnection(outAttrs);
+        }
+
+        @Override
+        public void onInputConnectionOpenedInternal(@NonNull InputConnection inputConnection,
+                @NonNull EditorInfo editorInfo, @Nullable Handler handler) {
+            mCalledOnInputConnectionOpened = true;
+            super.onInputConnectionOpenedInternal(inputConnection, editorInfo, handler);
+        }
+
+        @Override
+        public void onInputConnectionClosedInternal() {
+            mCalledOnInputConnectionClosed = true;
+            super.onInputConnectionClosedInternal();
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Udfps.java b/core/tests/coretests/src/android/view/ViewInputConnectionTestActivity.java
similarity index 60%
copy from services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Udfps.java
copy to core/tests/coretests/src/android/view/ViewInputConnectionTestActivity.java
index 74cae02..55c812d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Udfps.java
+++ b/core/tests/coretests/src/android/view/ViewInputConnectionTestActivity.java
@@ -14,14 +14,18 @@
  * limitations under the License.
  */
 
-package com.android.server.biometrics.sensors.fingerprint.hidl;
+package android.view;
 
-/**
- * Interface for under-display fingerprint sensors.
- * {@link com.android.server.biometrics.sensors.ClientMonitor} subclass that require knowledge of
- * finger position (e.g. enroll, authenticate) should implement this.
- */
-public interface Udfps {
-    void onFingerDown(int x, int y, float minor, float major);
-    void onFingerUp();
+import android.app.Activity;
+import android.os.Bundle;
+
+import com.android.frameworks.coretests.R;
+
+public class ViewInputConnectionTestActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_view_ic_test);
+    }
 }
diff --git a/core/tests/coretests/src/android/view/inputmethod/SurroundingTextTest.java b/core/tests/coretests/src/android/view/inputmethod/SurroundingTextTest.java
new file mode 100644
index 0000000..dfbc39c
--- /dev/null
+++ b/core/tests/coretests/src/android/view/inputmethod/SurroundingTextTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.inputmethod;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import android.os.Parcel;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SurroundingTextTest {
+
+    @Test
+    public void testSurroundingTextBasicCreation() {
+        SurroundingText surroundingText1 = new SurroundingText("test", 0, 0, 0);
+        assertThat(surroundingText1.getText(), is("test"));
+        assertThat(surroundingText1.getSelectionStart(), is(0));
+        assertThat(surroundingText1.getSelectionEnd(), is(0));
+        assertThat(surroundingText1.getOffset(), is(0));
+
+        SurroundingText surroundingText2 = new SurroundingText("", -1, -1, -1);
+        assertThat(surroundingText2.getText(), is(""));
+        assertThat(surroundingText2.getSelectionStart(), is(-1));
+        assertThat(surroundingText2.getSelectionEnd(), is(-1));
+        assertThat(surroundingText2.getOffset(), is(-1));
+
+        SurroundingText surroundingText3 = new SurroundingText("hello", 0, 5, 0);
+        assertThat(surroundingText3.getText(), is("hello"));
+        assertThat(surroundingText3.getSelectionStart(), is(0));
+        assertThat(surroundingText3.getSelectionEnd(), is(5));
+        assertThat(surroundingText3.getOffset(), is(0));
+    }
+
+    @Test
+    public void testSurroundingTextWriteToParcel() {
+        SurroundingText surroundingText = new SurroundingText("text", 0, 1, 2);
+        Parcel parcel = Parcel.obtain();
+        surroundingText.writeToParcel(parcel, 0);
+
+        parcel.setDataPosition(0);
+        SurroundingText surroundingTextFromParcel =
+                SurroundingText.CREATOR.createFromParcel(parcel);
+        assertThat(surroundingText.getText(), is("text"));
+        assertThat(surroundingText.getSelectionStart(), is(0));
+        assertThat(surroundingText.getSelectionEnd(), is(1));
+        assertThat(surroundingText.getOffset(), is(2));
+        assertThat(surroundingTextFromParcel.getText(), is("text"));
+        assertThat(surroundingTextFromParcel.getSelectionStart(), is(0));
+        assertThat(surroundingTextFromParcel.getSelectionEnd(), is(1));
+        assertThat(surroundingTextFromParcel.getOffset(), is(2));
+    }
+}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
index 628252d..402b92a 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
@@ -52,7 +52,8 @@
 
     @Test
     public void testGetLocalTextClassifier() {
-        assertThat(mTcm.getTextClassifier(TextClassifier.LOCAL)).isSameAs(TextClassifier.NO_OP);
+        assertThat(mTcm.getTextClassifier(TextClassifier.LOCAL))
+                .isSameInstanceAs(TextClassifier.NO_OP);
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/widget/CustomInputConnectionEditText.java b/core/tests/coretests/src/android/widget/CustomInputConnectionEditText.java
new file mode 100644
index 0000000..2dfc53a
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/CustomInputConnectionEditText.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputConnectionWrapper;
+
+import java.util.Arrays;
+
+/**
+ * An {@link EditText} component that allows customizing its
+ * {@link android.view.inputmethod.InputConnection}.
+ */
+public class CustomInputConnectionEditText extends EditText {
+    private static final String LOG_TAG = "CustomInputConnectionEditText";
+
+    private String[] mContentMimeTypes;
+    private InputConnectionWrapper mInputConnectionWrapper;
+
+    public CustomInputConnectionEditText(Context context) {
+        super(context);
+    }
+
+    public CustomInputConnectionEditText(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public CustomInputConnectionEditText(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public CustomInputConnectionEditText(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    public void setContentMimeTypes(String[] contentMimeTypes) {
+        mContentMimeTypes = contentMimeTypes;
+    }
+
+    public void setInputConnectionWrapper(InputConnectionWrapper inputConnectionWrapper) {
+        mInputConnectionWrapper = inputConnectionWrapper;
+    }
+
+    @Override
+    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+        InputConnection ic = super.onCreateInputConnection(outAttrs);
+        if (ic == null) {
+            Log.d(LOG_TAG, "Not wrapping InputConnection, because super returned null");
+            return null;
+        }
+        if (mInputConnectionWrapper == null) {
+            Log.d(LOG_TAG, "Not wrapping InputConnection, because wrapper is null");
+            return ic;
+        }
+
+        Log.d(LOG_TAG, "Wrapping InputConnection");
+        mInputConnectionWrapper.setTarget(ic);
+
+        Log.d(LOG_TAG,
+                "Setting EditorInfo.contentMimeTypes: " + Arrays.toString(mContentMimeTypes));
+        outAttrs.contentMimeTypes = mContentMimeTypes;
+
+        return mInputConnectionWrapper;
+    }
+}
diff --git a/core/tests/coretests/src/android/widget/CustomInputConnectionEditTextActivity.java b/core/tests/coretests/src/android/widget/CustomInputConnectionEditTextActivity.java
new file mode 100644
index 0000000..9328a50
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/CustomInputConnectionEditTextActivity.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.widget;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+import com.android.frameworks.coretests.R;
+
+/**
+ * Activity that uses an {@link EditText} component that allows customizing its
+ * {@link android.view.inputmethod.InputConnection}.
+ */
+public class CustomInputConnectionEditTextActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_custom_input_connection_edit_text);
+    }
+}
diff --git a/core/tests/coretests/src/android/widget/TextViewOnReceiveContentCallbackTest.java b/core/tests/coretests/src/android/widget/TextViewOnReceiveContentCallbackTest.java
new file mode 100644
index 0000000..5112326
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/TextViewOnReceiveContentCallbackTest.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.widget;
+
+import static android.view.OnReceiveContentCallback.Payload.SOURCE_AUTOFILL;
+import static android.view.OnReceiveContentCallback.Payload.SOURCE_CLIPBOARD;
+import static android.view.OnReceiveContentCallback.Payload.SOURCE_DRAG_AND_DROP;
+import static android.view.OnReceiveContentCallback.Payload.SOURCE_INPUT_METHOD;
+import static android.view.OnReceiveContentCallback.Payload.SOURCE_PROCESS_TEXT;
+import static android.widget.TextViewOnReceiveContentCallback.canReuse;
+import static android.widget.espresso.TextViewActions.clickOnTextAtIndex;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.ArraySet;
+import android.view.OnReceiveContentCallback;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputConnectionWrapper;
+import android.view.inputmethod.InputContentInfo;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.MediumTest;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.frameworks.coretests.R;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+
+/**
+ * Tests for {@link TextViewOnReceiveContentCallback}. Most of the test cases are in the CTS test
+ * {@link android.widget.cts.TextViewOnReceiveContentCallbackTest}. This class tests some internal
+ * implementation details, e.g. fallback to the keyboard image API.
+ */
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class TextViewOnReceiveContentCallbackTest {
+    private static final Uri SAMPLE_CONTENT_URI = Uri.parse("content://com.example/path");
+
+    @Rule
+    public ActivityTestRule<CustomInputConnectionEditTextActivity> mActivityRule =
+            new ActivityTestRule<>(CustomInputConnectionEditTextActivity.class);
+
+    private Instrumentation mInstrumentation;
+    private Activity mActivity;
+    private CustomInputConnectionEditText mEditText;
+    private TextViewOnReceiveContentCallback mDefaultCallback;
+
+    @Before
+    public void before() {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mActivity = mActivityRule.getActivity();
+        mEditText = mActivity.findViewById(R.id.edittext2);
+        mDefaultCallback = mEditText.getEditorForTesting().getDefaultOnReceiveContentCallback();
+    }
+
+    @Test
+    public void testGetSupportedMimeTypes_fallbackToCommitContent() throws Throwable {
+        // Configure the EditText with an EditorInfo/InputConnection that supports some image MIME
+        // types.
+        mEditText.setContentMimeTypes(new String[] {"image/gif", "image/png"});
+        MyInputConnection ic = new MyInputConnection();
+        mEditText.setInputConnectionWrapper(ic);
+
+        // Focus into the EditText.
+        onView(withId(mEditText.getId())).perform(clickOnTextAtIndex(0));
+
+        // Assert that the callback returns the MIME types declared in the EditorInfo in addition to
+        // the default.
+        assertThat(mDefaultCallback.getSupportedMimeTypes(mEditText)).containsExactly(
+                "text/*", "image/gif", "image/png");
+    }
+
+    @Test
+    public void testGetSupportedMimeTypes_fallbackToCommitContent_noMimeTypesInEditorInfo()
+            throws Throwable {
+        // Configure the EditText with an EditorInfo/InputConnection that doesn't declare any MIME
+        // types.
+        mEditText.setContentMimeTypes(new String[0]);
+        MyInputConnection ic = new MyInputConnection();
+        mEditText.setInputConnectionWrapper(ic);
+
+        // Focus into the EditText.
+        onView(withId(mEditText.getId())).perform(clickOnTextAtIndex(0));
+
+        // Assert that the callback returns the default MIME types.
+        assertThat(mDefaultCallback.getSupportedMimeTypes(mEditText)).containsExactly("text/*");
+    }
+
+    @Test
+    public void testOnReceive_fallbackToCommitContent() throws Throwable {
+        // Configure the EditText with an EditorInfo/InputConnection that supports some image MIME
+        // types.
+        mEditText.setContentMimeTypes(new String[] {"image/gif", "image/png"});
+        MyInputConnection ic = new MyInputConnection();
+        mEditText.setInputConnectionWrapper(ic);
+
+        // Focus into the EditText.
+        onView(withId(mEditText.getId())).perform(clickOnTextAtIndex(0));
+
+        // Invoke the callback with SOURCE_AUTOFILL and assert that it triggers a call to
+        // InputConnection.commitContent.
+        ClipDescription description = new ClipDescription("", new String[] {"image/gif"});
+        ClipData clip = new ClipData(description, new ClipData.Item(SAMPLE_CONTENT_URI));
+        OnReceiveContentCallback.Payload payload =
+                new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_AUTOFILL).build();
+        mDefaultCallback.onReceiveContent(mEditText, payload);
+        verify(ic.mMock, times(1))
+                .commitContent(any(InputContentInfo.class), eq(0), eq(null));
+        verifyNoMoreInteractions(ic.mMock);
+    }
+
+    @Test
+    public void testOnReceive_fallbackToCommitContent_noMimeTypesInEditorInfo() throws Throwable {
+        // Configure the EditText with an EditorInfo/InputConnection that doesn't declare any MIME
+        // types.
+        mEditText.setContentMimeTypes(new String[0]);
+        MyInputConnection ic = new MyInputConnection();
+        mEditText.setInputConnectionWrapper(ic);
+
+        // Focus into the EditText.
+        onView(withId(mEditText.getId())).perform(clickOnTextAtIndex(0));
+
+        // Invoke the callback and assert that the InputConnection is not invoked.
+        ClipDescription description = new ClipDescription("", new String[] {"image/gif"});
+        ClipData clip = new ClipData(description, new ClipData.Item(SAMPLE_CONTENT_URI));
+        OnReceiveContentCallback.Payload payload =
+                new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_AUTOFILL).build();
+        mDefaultCallback.onReceiveContent(mEditText, payload);
+        verifyZeroInteractions(ic.mMock);
+    }
+
+    @Test
+    public void testOnReceive_fallbackToCommitContent_sourceOtherThanAutofill() throws Throwable {
+        // Configure the EditText with an EditorInfo/InputConnection that supports some image MIME
+        // types.
+        mEditText.setContentMimeTypes(new String[] {"image/gif", "image/png"});
+        MyInputConnection ic = new MyInputConnection();
+        mEditText.setInputConnectionWrapper(ic);
+
+        // Focus into the EditText.
+        onView(withId(mEditText.getId())).perform(clickOnTextAtIndex(0));
+
+        // Invoke the callback with sources other than SOURCE_AUTOFILL and assert that it does NOT
+        // trigger calls to InputConnection.commitContent.
+        ClipDescription description = new ClipDescription("", new String[] {"image/gif"});
+        ClipData clip = new ClipData(description, new ClipData.Item(SAMPLE_CONTENT_URI));
+        OnReceiveContentCallback.Payload payload =
+                new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_CLIPBOARD).build();
+        mDefaultCallback.onReceiveContent(mEditText, payload);
+        verifyZeroInteractions(ic.mMock);
+
+        payload = new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_INPUT_METHOD).build();
+        mDefaultCallback.onReceiveContent(mEditText, payload);
+        verifyZeroInteractions(ic.mMock);
+
+        payload = new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_DRAG_AND_DROP).build();
+        mDefaultCallback.onReceiveContent(mEditText, payload);
+        verifyZeroInteractions(ic.mMock);
+
+        payload = new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_PROCESS_TEXT).build();
+        mDefaultCallback.onReceiveContent(mEditText, payload);
+        verifyZeroInteractions(ic.mMock);
+    }
+
+    @Test
+    public void testCanReuse() throws Throwable {
+        ArraySet<String> mimeTypes = null;
+        String[] editorContentMimeTypes = new String[0];
+        assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isFalse();
+
+        mimeTypes = new ArraySet<>();
+        editorContentMimeTypes = new String[0];
+        assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isTrue();
+
+        mimeTypes = newArraySet("text/*");
+        editorContentMimeTypes = new String[0];
+        assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isTrue();
+
+        mimeTypes = newArraySet("text/*");
+        editorContentMimeTypes = new String[] {"text/*"};
+        assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isTrue();
+
+        mimeTypes = newArraySet("image/gif", "image/png", "text/*");
+        editorContentMimeTypes = new String[] {"image/gif", "image/png"};
+        assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isTrue();
+
+        mimeTypes = newArraySet("image/gif", "image/png", "text/*");
+        editorContentMimeTypes = new String[] {"image/gif", "image/png", "text/*"};
+        assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isTrue();
+
+        mimeTypes = newArraySet("image/gif", "image/png", "text/*");
+        editorContentMimeTypes = new String[] {"image/gif"};
+        assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isFalse();
+
+        mimeTypes = newArraySet("image/gif", "image/png", "text/*");
+        editorContentMimeTypes = new String[] {"image/gif", "image/png", "image/jpg"};
+        assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isFalse();
+
+        mimeTypes = newArraySet("image/gif", "image/png", "text/*");
+        editorContentMimeTypes = new String[] {"image/gif", "image/jpg"};
+        assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isFalse();
+
+        mimeTypes = newArraySet("image/gif", "image/png", "text/*");
+        editorContentMimeTypes = new String[] {"image/gif", "image/jpg", "text/*"};
+        assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isFalse();
+    }
+
+    private static class MyInputConnection extends InputConnectionWrapper {
+        public final InputConnection mMock;
+
+        MyInputConnection() {
+            super(null, true);
+            mMock = Mockito.mock(InputConnection.class);
+        }
+
+        @Override
+        public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) {
+            mMock.commitContent(inputContentInfo, flags, opts);
+            return true;
+        }
+    }
+
+    @SafeVarargs
+    private static <T> ArraySet<T> newArraySet(T ... elements) {
+        return new ArraySet<>(elements);
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java b/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java
index f108eb8..a2bc77a 100644
--- a/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java
+++ b/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java
@@ -81,7 +81,7 @@
         future.completeExceptionally(origException);
         ExecutionException executionException =
                 expectThrows(ExecutionException.class, future::get);
-        assertThat(executionException.getCause()).isSameAs(origException);
+        assertThat(executionException.getCause()).isSameInstanceAs(origException);
     }
 
     @Test
@@ -92,7 +92,7 @@
         CountDownLatch latch = new CountDownLatch(1);
         future.whenComplete((obj, err) -> {
             assertThat(obj).isNull();
-            assertThat(err).isSameAs(origException);
+            assertThat(err).isSameInstanceAs(origException);
             latch.countDown();
         });
         latch.await();
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
index 0eb34a9..3117935 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -497,9 +497,9 @@
         bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
 
         PrintWriter pw = new PrintWriter(new StringWriter());
-        bcs.dump(pw, new AppIdToPackageMap(new HashMap<>()), Process.INVALID_UID, true);
+        bcs.dump(pw, new AppIdToPackageMap(new SparseArray<>()), Process.INVALID_UID, true);
 
-        bcs.dump(pw, new AppIdToPackageMap(new HashMap<>()), WORKSOURCE_UID, true);
+        bcs.dump(pw, new AppIdToPackageMap(new SparseArray<>()), WORKSOURCE_UID, true);
     }
 
     @Test
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
index 5914887..942045c 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
@@ -107,7 +107,7 @@
             if (!isAlive) {
                 return false;
             }
-            assertThat(mRecipient).isSameAs(recipient);
+            assertThat(mRecipient).isSameInstanceAs(recipient);
             mRecipient = null;
             return true;
         }
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java
index 403c1c2..b5720a2 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java
@@ -38,8 +38,6 @@
 import java.io.OutputStream;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.util.Comparator;
-import java.util.List;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -59,31 +57,21 @@
     }
 
     @Test
-    public void getThreadCpuUsage() throws IOException {
+    public void getProcessCpuUsage() throws IOException {
         setupDirectory(42,
                 new int[] {42, 1, 2, 3},
                 new int[] {1000, 2000},
                 // Units are 10ms aka 10000Us
-                new int[][] {{100, 200}, {0, 200}, {100, 300}, {0, 400}},
-                new int[] {1400, 1500});
+                new int[][] {{100, 200}, {0, 200}, {100, 300}, {0, 600}},
+                new int[] {4500, 500});
 
         KernelSingleProcessCpuThreadReader reader = new KernelSingleProcessCpuThreadReader(42,
                 mProcDirectory.toPath());
         KernelSingleProcessCpuThreadReader.ProcessCpuUsage processCpuUsage =
-                reader.getProcessCpuUsage();
-        assertThat(processCpuUsage.cpuTimeMillis).isEqualTo(29000);
-        List<KernelSingleProcessCpuThreadReader.ThreadCpuUsage> threadCpuUsage =
-                processCpuUsage.threadCpuUsages;
-        threadCpuUsage.sort(Comparator.comparingInt(o -> o.threadId));
-        assertThat(threadCpuUsage).hasSize(4);
-        assertThat(threadCpuUsage.get(0).threadId).isEqualTo(1);
-        assertThat(threadCpuUsage.get(0).usageTimesMillis).isEqualTo(new long[]{0, 2000});
-        assertThat(threadCpuUsage.get(1).threadId).isEqualTo(2);
-        assertThat(threadCpuUsage.get(1).usageTimesMillis).isEqualTo(new long[]{1000, 3000});
-        assertThat(threadCpuUsage.get(2).threadId).isEqualTo(3);
-        assertThat(threadCpuUsage.get(2).usageTimesMillis).isEqualTo(new long[]{0, 4000});
-        assertThat(threadCpuUsage.get(3).threadId).isEqualTo(42);
-        assertThat(threadCpuUsage.get(3).usageTimesMillis).isEqualTo(new long[]{1000, 2000});
+                reader.getProcessCpuUsage(new int[] {2, 3});
+        assertThat(processCpuUsage.threadCpuTimesMillis).isEqualTo(new long[] {2000, 13000});
+        assertThat(processCpuUsage.selectedThreadCpuTimesMillis).isEqualTo(new long[] {1000, 9000});
+        assertThat(processCpuUsage.processCpuTimesMillis).isEqualTo(new long[] {6666, 43333});
     }
 
     @Test
diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
index 37e1efd..bd0bbe9 100644
--- a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
@@ -66,7 +66,7 @@
     public void testCalculateApp() {
         // Test Power Profile has two CPU clusters with 3 and 4 speeds, thus 7 freq times total
         mMockSystemServerCpuThreadReader.setCpuTimes(
-                210000,
+                new long[] {10000, 15000, 20000, 25000, 30000, 35000, 40000},
                 new long[] {30000, 40000, 50000, 60000, 70000, 80000, 90000},
                 new long[] {20000, 30000, 40000, 50000, 60000, 70000, 80000});
 
@@ -107,13 +107,13 @@
                 mMockBatteryStats.getUidStatsLocked(workSourceUid1), 0);
         mSystemServicePowerCalculator.calculateApp(app1, app1.uidObj, 0, 0,
                 BatteryStats.STATS_SINCE_CHARGED);
-        assertEquals(0.00018958, app1.systemServiceCpuPowerMah, 0.0000001);
+        assertEquals(0.00016269, app1.systemServiceCpuPowerMah, 0.0000001);
 
         BatterySipper app2 = new BatterySipper(BatterySipper.DrainType.APP,
                 mMockBatteryStats.getUidStatsLocked(workSourceUid2), 0);
         mSystemServicePowerCalculator.calculateApp(app2, app2.uidObj, 0, 0,
                 BatteryStats.STATS_SINCE_CHARGED);
-        assertEquals(0.00170625, app2.systemServiceCpuPowerMah, 0.0000001);
+        assertEquals(0.00146426, app2.systemServiceCpuPowerMah, 0.0000001);
     }
 
     private static class MockKernelCpuUidFreqTimeReader extends
@@ -148,9 +148,9 @@
             super(null);
         }
 
-        public void setCpuTimes(long processCpuTimeUs, long[] threadCpuTimesUs,
+        public void setCpuTimes(long[] processCpuTimesUs, long[] threadCpuTimesUs,
                 long[] binderThreadCpuTimesUs) {
-            mThreadTimes.processCpuTimeUs = processCpuTimeUs;
+            mThreadTimes.processCpuTimesUs = processCpuTimesUs;
             mThreadTimes.threadCpuTimesUs = threadCpuTimesUs;
             mThreadTimes.binderThreadCpuTimesUs = binderThreadCpuTimesUs;
         }
diff --git a/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java b/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
index 9f68ef3..7eca320 100644
--- a/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
+++ b/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
@@ -74,7 +74,7 @@
         assertThat(copy.mImeBackDisposition).isEqualTo(original.mImeBackDisposition);
         assertThat(copy.mShowImeSwitcher).isEqualTo(original.mShowImeSwitcher);
         assertThat(copy.mDisabledFlags2).isEqualTo(original.mDisabledFlags2);
-        assertThat(copy.mImeToken).isSameAs(original.mImeToken);
+        assertThat(copy.mImeToken).isSameInstanceAs(original.mImeToken);
         assertThat(copy.mNavbarColorManagedByIme).isEqualTo(original.mNavbarColorManagedByIme);
         assertThat(copy.mAppFullscreen).isEqualTo(original.mAppFullscreen);
         assertThat(copy.mAppImmersive).isEqualTo(original.mAppImmersive);
diff --git a/core/tests/coretests/src/com/android/internal/view/RecyclerViewCaptureHelperTest.java b/core/tests/coretests/src/com/android/internal/view/RecyclerViewCaptureHelperTest.java
new file mode 100644
index 0000000..88bbcc2
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/view/RecyclerViewCaptureHelperTest.java
@@ -0,0 +1,343 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.view;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.internal.view.ScrollCaptureViewHelper.ScrollResult;
+import com.android.internal.widget.LinearLayoutManager;
+import com.android.internal.widget.RecyclerView;
+
+import com.google.common.truth.Truth;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Random;
+
+@RunWith(AndroidJUnit4.class)
+public class RecyclerViewCaptureHelperTest {
+    private static final int CHILD_VIEWS = 12;
+    private static final int CHILD_VIEW_HEIGHT = 300;
+    private static final int WINDOW_WIDTH = 800;
+    private static final int WINDOW_HEIGHT = 1200;
+    private static final int CAPTURE_HEIGHT = 600;
+
+    private FrameLayout mParent;
+    private RecyclerView mTarget;
+    private WindowManager mWm;
+
+    private WindowManager.LayoutParams mWindowLayoutParams;
+
+    private Context mContext;
+    private float mDensity;
+    private LinearLayoutManager mLinearLayoutManager;
+    private Instrumentation mInstrumentation;
+
+    @Before
+    @UiThreadTest
+    public void setUp() {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mContext = mInstrumentation.getContext();
+        mDensity = mContext.getResources().getDisplayMetrics().density;
+
+        mParent = new FrameLayout(mContext);
+
+        mTarget = new RecyclerView(mContext);
+        mParent.addView(mTarget, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
+
+        mTarget.setAdapter(new TestAdapter());
+        mLinearLayoutManager =
+                new LinearLayoutManager(mContext, LinearLayoutManager.VERTICAL, false);
+        mTarget.setLayoutManager(mLinearLayoutManager);
+        mWm = mContext.getSystemService(WindowManager.class);
+
+        // Setup the window that we are going to use
+        mWindowLayoutParams = new WindowManager.LayoutParams(WINDOW_WIDTH, WINDOW_HEIGHT,
+                TYPE_APPLICATION_OVERLAY, FLAG_NOT_TOUCHABLE, PixelFormat.OPAQUE);
+        mWindowLayoutParams.setTitle("ScrollViewCaptureHelper");
+        mWindowLayoutParams.gravity = Gravity.CENTER;
+        mWm.addView(mParent, mWindowLayoutParams);
+    }
+
+    @After
+    @UiThreadTest
+    public void tearDown() {
+        mWm.removeViewImmediate(mParent);
+    }
+
+    @Test
+    @UiThreadTest
+    public void onScrollRequested_up_fromTop() {
+        mTarget.scrollBy(0, -(WINDOW_HEIGHT * 3));
+        // mTarget.createSnapshot(new ViewDebug.HardwareCanvasProvider(), false);
+
+        RecyclerViewCaptureHelper rvc = new RecyclerViewCaptureHelper();
+        Rect scrollBounds = rvc.onComputeScrollBounds(mTarget);
+        rvc.onPrepareForStart(mTarget, scrollBounds);
+
+        assertThat(scrollBounds.height()).isGreaterThan(CAPTURE_HEIGHT);
+
+        Rect request = new Rect(0, -CAPTURE_HEIGHT, scrollBounds.width(), 0);
+
+        ScrollResult scrollResult = rvc.onScrollRequested(mTarget,
+                scrollBounds, request);
+
+        // The result is an empty rectangle and no scrolling, since it
+        // is not possible to physically scroll further up to make the
+        // requested area visible at all (it doesn't exist).
+        assertEmpty(scrollResult.availableArea);
+    }
+
+    @Test
+    @UiThreadTest
+    public void onScrollRequested_down_fromTop() {
+        mTarget.scrollBy(0, -(WINDOW_HEIGHT * 3));
+
+        RecyclerViewCaptureHelper rvc = new RecyclerViewCaptureHelper();
+        Rect scrollBounds = rvc.onComputeScrollBounds(mTarget);
+        rvc.onPrepareForStart(mTarget, scrollBounds);
+
+        assertThat(scrollBounds.height()).isGreaterThan(CAPTURE_HEIGHT);
+
+        // Capture between y = +1200 to +1800 pixels BELOW current top
+        Rect request = new Rect(0, WINDOW_HEIGHT, scrollBounds.width(),
+                WINDOW_HEIGHT + CAPTURE_HEIGHT);
+
+        ScrollResult scrollResult = rvc.onScrollRequested(mTarget, scrollBounds, request);
+        assertThat(request).isEqualTo(scrollResult.requestedArea);
+        assertThat(request).isEqualTo(scrollResult.availableArea);
+        assertThat(scrollResult.scrollDelta).isEqualTo(CAPTURE_HEIGHT);
+        assertAvailableAreaCompletelyVisible(scrollResult, mTarget);
+    }
+
+    @Test
+    @UiThreadTest
+    public void onScrollRequested_up_fromMiddle() {
+        mTarget.scrollBy(0, WINDOW_HEIGHT);
+
+        RecyclerViewCaptureHelper helper = new RecyclerViewCaptureHelper();
+        Rect scrollBounds = helper.onComputeScrollBounds(mTarget);
+        helper.onPrepareForStart(mTarget, scrollBounds);
+
+        Rect request = new Rect(0, -CAPTURE_HEIGHT, scrollBounds.width(), 0);
+
+        ScrollResult scrollResult = helper.onScrollRequested(mTarget, scrollBounds, request);
+        assertThat(request).isEqualTo(scrollResult.requestedArea);
+        assertThat(request).isEqualTo(scrollResult.availableArea);
+        assertThat(scrollResult.scrollDelta).isEqualTo(-CAPTURE_HEIGHT);
+        assertAvailableAreaCompletelyVisible(scrollResult, mTarget);
+    }
+
+    @Test
+    @UiThreadTest
+    public void onScrollRequested_down_fromMiddle() {
+        mTarget.scrollBy(0, WINDOW_HEIGHT);
+
+        RecyclerViewCaptureHelper helper = new RecyclerViewCaptureHelper();
+        Rect scrollBounds = helper.onComputeScrollBounds(mTarget);
+        helper.onPrepareForStart(mTarget, scrollBounds);
+
+        Rect request = new Rect(0, WINDOW_HEIGHT, scrollBounds.width(),
+                WINDOW_HEIGHT + CAPTURE_HEIGHT);
+
+        ScrollResult scrollResult = helper.onScrollRequested(mTarget, scrollBounds, request);
+        assertThat(request).isEqualTo(scrollResult.requestedArea);
+        assertThat(request).isEqualTo(scrollResult.availableArea);
+        assertThat(scrollResult.scrollDelta).isEqualTo(CAPTURE_HEIGHT);
+        assertAvailableAreaCompletelyVisible(scrollResult, mTarget);
+    }
+
+    @Test
+    @UiThreadTest
+    public void onScrollRequested_up_fromBottom() {
+        mTarget.scrollBy(0, WINDOW_HEIGHT * 2);
+
+        RecyclerViewCaptureHelper helper = new RecyclerViewCaptureHelper();
+        Rect scrollBounds = helper.onComputeScrollBounds(mTarget);
+        helper.onPrepareForStart(mTarget, scrollBounds);
+
+        Rect request = new Rect(0, -CAPTURE_HEIGHT, scrollBounds.width(), 0);
+
+        ScrollResult scrollResult = helper.onScrollRequested(mTarget, scrollBounds, request);
+        assertThat(request).isEqualTo(scrollResult.requestedArea);
+        assertThat(request).isEqualTo(scrollResult.availableArea);
+        assertThat(scrollResult.scrollDelta).isEqualTo(-CAPTURE_HEIGHT);
+        assertAvailableAreaCompletelyVisible(scrollResult, mTarget);
+    }
+
+    @Test
+    @UiThreadTest
+    public void onScrollRequested_down_fromBottom() {
+        mTarget.scrollBy(0, WINDOW_HEIGHT * 3);
+
+        RecyclerViewCaptureHelper rvc = new RecyclerViewCaptureHelper();
+        Rect scrollBounds = rvc.onComputeScrollBounds(mTarget);
+        rvc.onPrepareForStart(mTarget, scrollBounds);
+
+        Rect request = new Rect(0, WINDOW_HEIGHT, scrollBounds.width(),
+                WINDOW_HEIGHT + CAPTURE_HEIGHT);
+
+        ScrollResult scrollResult = rvc.onScrollRequested(mTarget,
+                scrollBounds, request);
+        Truth.assertThat(request).isEqualTo(scrollResult.requestedArea);
+
+        // The result is an empty rectangle and no scrolling, since it
+        // is not possible to physically scroll further down to make the
+        // requested area visible at all (it doesn't exist).
+        assertEmpty(scrollResult.availableArea);
+    }
+
+    @Test
+    @UiThreadTest
+    public void onScrollRequested_offTopEdge() {
+        mTarget.scrollBy(0, -(WINDOW_HEIGHT * 3));
+
+        RecyclerViewCaptureHelper helper = new RecyclerViewCaptureHelper();
+        Rect scrollBounds = helper.onComputeScrollBounds(mTarget);
+        helper.onPrepareForStart(mTarget, scrollBounds);
+
+        // Create a request which lands halfway off the top of the content
+        //from -1500 to -900, (starting at 1200 = -300 to +300 within the content)
+        int top = 0;
+        Rect request = new Rect(
+                0, top - (CAPTURE_HEIGHT / 2),
+                scrollBounds.width(), top + (CAPTURE_HEIGHT / 2));
+
+        ScrollResult scrollResult = helper.onScrollRequested(mTarget, scrollBounds, request);
+        assertThat(request).isEqualTo(scrollResult.requestedArea);
+
+        ScrollResult result = helper.onScrollRequested(mTarget, scrollBounds, request);
+        // The result is a partial result
+        Rect expectedResult = new Rect(request);
+        expectedResult.top += (CAPTURE_HEIGHT / 2); // top half clipped
+        assertThat(expectedResult).isEqualTo(result.availableArea);
+        assertThat(scrollResult.scrollDelta).isEqualTo(0);
+        assertAvailableAreaPartiallyVisible(scrollResult, mTarget);
+    }
+
+    @Test
+    @UiThreadTest
+    public void onScrollRequested_offBottomEdge() {
+        mTarget.scrollBy(0, WINDOW_HEIGHT * 2);
+
+        RecyclerViewCaptureHelper helper = new RecyclerViewCaptureHelper();
+        Rect scrollBounds = helper.onComputeScrollBounds(mTarget);
+        helper.onPrepareForStart(mTarget, scrollBounds);
+
+        // Create a request which lands halfway off the bottom of the content
+        //from 600 to to 1200, (starting at 2400 = 3000 to  3600 within the content)
+
+        int bottom = WINDOW_HEIGHT;
+        Rect request = new Rect(
+                0, bottom - (CAPTURE_HEIGHT / 2),
+                scrollBounds.width(), bottom + (CAPTURE_HEIGHT / 2));
+
+        ScrollResult result = helper.onScrollRequested(mTarget, scrollBounds, request);
+
+        Rect expectedResult = new Rect(request);
+        expectedResult.bottom -= 300; // bottom half clipped
+        assertThat(expectedResult).isEqualTo(result.availableArea);
+        assertThat(result.scrollDelta).isEqualTo(0);
+        assertAvailableAreaPartiallyVisible(result, mTarget);
+    }
+
+    static final class TestViewHolder extends RecyclerView.ViewHolder {
+        TestViewHolder(View itemView) {
+            super(itemView);
+        }
+    }
+
+    static final class TestAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
+        private final Random mRandom = new Random();
+
+        @Override
+        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+            return new TestViewHolder(new TextView(parent.getContext()));
+        }
+
+        @Override
+        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
+            TextView view = (TextView) holder.itemView;
+            view.setText("Child #" + position);
+            view.setTextColor(Color.WHITE);
+            view.setTextSize(30f);
+            view.setBackgroundColor(Color.rgb(mRandom.nextFloat(), mRandom.nextFloat(),
+                    mRandom.nextFloat()));
+            view.setMinHeight(CHILD_VIEW_HEIGHT);
+        }
+
+        @Override
+        public int getItemCount() {
+            return CHILD_VIEWS;
+        }
+    }
+
+    static void assertEmpty(Rect r) {
+        if (r != null && !r.isEmpty()) {
+            fail("Not true that " + r + " is empty");
+        }
+    }
+
+    static Rect getVisibleRect(View v) {
+        Rect r = new Rect(0, 0, v.getWidth(), v.getHeight());
+        v.getLocalVisibleRect(r);
+        return r;
+    }
+
+    static void assertAvailableAreaCompletelyVisible(ScrollResult result, View container) {
+        Rect requested = new Rect(result.availableArea);
+        requested.offset(0, -result.scrollDelta); // make relative
+        Rect localVisible = getVisibleRect(container);
+        if (!localVisible.contains(requested)) {
+            fail("Not true that all of " + requested + " is contained by " + localVisible);
+        }
+    }
+
+    static void assertAvailableAreaPartiallyVisible(ScrollResult result, View container) {
+        Rect requested = new Rect(result.availableArea);
+        requested.offset(0, -result.scrollDelta); // make relative
+        Rect localVisible = getVisibleRect(container);
+        if (!Rect.intersects(localVisible, requested)) {
+            fail("Not true that any of " + requested + " is contained by " + localVisible);
+        }
+    }
+}
diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
index a4f2065..cc68bb6 100644
--- a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
+++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
@@ -28,6 +28,7 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -373,6 +374,25 @@
         public void removeHdmiCecVolumeControlFeatureListener(
                 IHdmiCecVolumeControlFeatureListener listener) {
         }
+
+        @Override
+        public List<String> getUserCecSettings() {
+            return new ArrayList<>();
+        }
+
+        @Override
+        public List<String> getAllowedCecSettingValues(String name) {
+            return new ArrayList<>();
+        }
+
+        @Override
+        public String getCecSettingValue(String name) {
+            return "";
+        }
+
+        @Override
+        public void setCecSettingValue(String name, String value) {
+        }
     }
 
 }
diff --git a/core/tests/powertests/PowerStatsViewer/Android.bp b/core/tests/powertests/PowerStatsViewer/Android.bp
new file mode 100644
index 0000000..a3dc4fb
--- /dev/null
+++ b/core/tests/powertests/PowerStatsViewer/Android.bp
@@ -0,0 +1,13 @@
+android_test {
+    name: "PowerStatsViewer",
+    srcs: ["src/**/*.java"],
+    defaults: ["SettingsLibDefaults"],
+    static_libs: [
+        "androidx.appcompat_appcompat",
+        "androidx.cardview_cardview",
+        "androidx.recyclerview_recyclerview",
+        "com.google.android.material_material",
+    ],
+    platform_apis: true,
+    certificate: "platform",
+}
diff --git a/core/tests/powertests/PowerStatsViewer/AndroidManifest.xml b/core/tests/powertests/PowerStatsViewer/AndroidManifest.xml
new file mode 100644
index 0000000..378d035
--- /dev/null
+++ b/core/tests/powertests/PowerStatsViewer/AndroidManifest.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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.frameworks.core.powerstatsviewer"
+          android:sharedUserId="android.uid.system">
+
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+    <uses-permission android:name="android.permission.BATTERY_STATS"/>
+
+    <application
+        android:theme="@style/Theme"
+        android:label="Power Stats Viewer">
+        <activity android:name=".PowerStatsViewerActivity"
+                  android:label="Power Stats Viewer"
+                  android:launchMode="singleTop"
+                  android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".AppPickerActivity"
+                  android:label="Power Stats - Select an App"/>
+
+    </application>
+</manifest>
diff --git a/core/tests/powertests/PowerStatsViewer/res/layout/app_info_layout.xml b/core/tests/powertests/PowerStatsViewer/res/layout/app_info_layout.xml
new file mode 100644
index 0000000..fe6fe2d
--- /dev/null
+++ b/core/tests/powertests/PowerStatsViewer/res/layout/app_info_layout.xml
@@ -0,0 +1,85 @@
+<?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.
+  -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal"
+    android:background="?android:attr/selectableItemBackground"
+    android:gravity="center_vertical"
+    android:minHeight="?android:attr/listPreferredItemHeightSmall"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+    <ImageView
+        android:id="@android:id/icon"
+        android:layout_width="@dimen/secondary_app_icon_size"
+        android:layout_height="@dimen/secondary_app_icon_size"
+        android:layout_marginEnd="12dp"
+        android:layout_marginTop="4dp"
+        android:layout_marginBottom="4dp"
+        android:gravity="start|center_vertical"/>
+
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="vertical"
+        android:paddingTop="16dp"
+        android:paddingBottom="16dp">
+
+        <TextView
+            android:id="@android:id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:ellipsize="marquee"
+            android:fadingEdge="horizontal"
+            android:singleLine="true"
+            android:textAppearance="?android:attr/textAppearanceListItem"/>
+
+        <TextView
+            android:id="@+id/uid"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:ellipsize="marquee"
+            android:fadingEdge="horizontal"
+            android:singleLine="true"
+            android:textAppearance="?android:attr/textAppearanceSmall"/>
+
+        <TextView
+            android:id="@+id/packages"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textDirection="locale"
+            android:maxLines="3"
+            android:ellipsize="end"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textColor="?android:attr/textColorSecondary"/>
+
+    </LinearLayout>
+
+    <TextView
+        android:id="@+id/power_mah"
+        android:layout_width="100dp"
+        android:layout_height="wrap_content"
+        android:layout_gravity="end|center_vertical"
+        android:layout_marginStart="16dp"
+        android:gravity="right"
+        android:textAppearance="?android:attr/textAppearanceListItem"
+        android:visibility="gone"/>
+
+</LinearLayout>
diff --git a/core/tests/powertests/PowerStatsViewer/res/layout/app_picker_layout.xml b/core/tests/powertests/PowerStatsViewer/res/layout/app_picker_layout.xml
new file mode 100644
index 0000000..6f28999
--- /dev/null
+++ b/core/tests/powertests/PowerStatsViewer/res/layout/app_picker_layout.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="match_parent">
+
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/app_list_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:visibility="gone"/>
+
+    <ProgressBar
+        style="?android:attr/progressBarStyleLarge"
+        android:id="@+id/loading_view"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:indeterminate="true"/>
+</FrameLayout>
\ No newline at end of file
diff --git a/core/tests/powertests/PowerStatsViewer/res/layout/power_stats_entry_layout.xml b/core/tests/powertests/PowerStatsViewer/res/layout/power_stats_entry_layout.xml
new file mode 100644
index 0000000..1ced825
--- /dev/null
+++ b/core/tests/powertests/PowerStatsViewer/res/layout/power_stats_entry_layout.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal"
+    android:minHeight="?android:attr/listPreferredItemHeightSmall"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:paddingTop="8dp"
+    android:paddingBottom="8dp">
+
+    <TextView
+        android:id="@+id/title"
+        android:layout_width="0dp"
+        android:layout_weight="1"
+        android:layout_height="wrap_content"
+        android:textAppearance="@style/TextAppearanceBody"/>
+
+    <TextView
+        android:id="@+id/amount"
+        android:layout_width="0dp"
+        android:layout_weight="0.7"
+        android:layout_height="wrap_content"
+        android:gravity="right"
+        android:textAppearance="@style/TextAppearanceBody"/>
+
+    <TextView
+        android:id="@+id/percent"
+        android:layout_width="64dp"
+        android:layout_height="wrap_content"
+        android:gravity="right"
+        android:textAppearance="@style/TextAppearanceBody"/>
+</LinearLayout>
diff --git a/core/tests/powertests/PowerStatsViewer/res/layout/power_stats_viewer_layout.xml b/core/tests/powertests/PowerStatsViewer/res/layout/power_stats_viewer_layout.xml
new file mode 100644
index 0000000..9949418
--- /dev/null
+++ b/core/tests/powertests/PowerStatsViewer/res/layout/power_stats_viewer_layout.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="match_parent">
+    <LinearLayout
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <androidx.cardview.widget.CardView
+            style="@style/LoadTestCardView"
+            android:id="@+id/app_card"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="10dp"
+            android:layout_marginEnd="10dp"
+            android:layout_marginBottom="10dp"
+            android:layout_marginStart="10dp"
+            android:padding="20dp">
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical"
+                android:minHeight="80dp"
+                android:paddingStart="10dp"
+                android:paddingEnd="10dp">
+
+                <include layout="@layout/app_info_layout"/>
+
+            </LinearLayout>
+        </androidx.cardview.widget.CardView>
+
+        <androidx.recyclerview.widget.RecyclerView
+            android:id="@+id/power_stats_data_view"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:layout_weight="1"/>
+
+        <TextView
+            android:id="@+id/empty_view"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:gravity="center"
+            android:visibility="gone"
+            android:text="No power stats available"/>
+    </LinearLayout>
+
+    <FrameLayout
+        android:id="@+id/loading_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="#AAFFFFFF">
+        <ProgressBar
+            style="?android:attr/progressBarStyleLarge"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:indeterminate="true"/>
+    </FrameLayout>
+</FrameLayout>
diff --git a/core/tests/powertests/PowerStatsViewer/res/values/styles.xml b/core/tests/powertests/PowerStatsViewer/res/values/styles.xml
new file mode 100644
index 0000000..629d729
--- /dev/null
+++ b/core/tests/powertests/PowerStatsViewer/res/values/styles.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="Theme" parent="Theme.MaterialComponents.Light">
+        <item name="colorPrimary">#34a853</item>
+        <item name="android:windowActionBar">true</item>
+        <item name="android:windowNoTitle">false</item>
+    </style>
+
+    <style name="LoadTestCardView" parent="Widget.MaterialComponents.CardView">
+        <item name="cardBackgroundColor">#ceead6</item>
+    </style>
+
+    <style name="TextAppearanceBody" parent="android:TextAppearance.DeviceDefault">
+        <item name="android:textColor">#000000</item>
+        <item name="android:textSize">18sp</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/AppInfoHelper.java b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/AppInfoHelper.java
new file mode 100644
index 0000000..8526561
--- /dev/null
+++ b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/AppInfoHelper.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.frameworks.core.powerstatsviewer;
+
+import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Process;
+
+import com.android.internal.os.BatterySipper;
+
+class AppInfoHelper {
+
+    private static final String SYSTEM_SERVER_PACKAGE_NAME = "android";
+
+    public static class AppInfo {
+        public int uid;
+        public CharSequence label;
+        public double powerMah;
+        public ApplicationInfo iconInfo;
+        public CharSequence packages;
+    }
+
+    @Nullable
+    public static AppInfo makeApplicationInfo(PackageManager packageManager, int uid,
+            @Nullable BatterySipper sipper) {
+        if (sipper != null && sipper.drainType != BatterySipper.DrainType.APP) {
+            return null;
+        }
+
+        String packageWithHighestDrain = null;
+
+        AppInfo info = new AppInfo();
+        info.uid = uid;
+        if (sipper != null) {
+            sipper.sumPower();
+            info.powerMah = sipper.totalSmearedPowerMah;
+            packageWithHighestDrain = sipper.packageWithHighestDrain;
+        }
+        if (info.uid == Process.ROOT_UID) {
+            info.label = "<root>";
+        } else {
+            String[] packages = packageManager.getPackagesForUid(info.uid);
+            String primaryPackageName = null;
+            if (info.uid == Process.SYSTEM_UID) {
+                primaryPackageName = SYSTEM_SERVER_PACKAGE_NAME;
+            } else if (packages != null) {
+                for (String name : packages) {
+                    primaryPackageName = name;
+                    if (name.equals(packageWithHighestDrain)) {
+                        break;
+                    }
+                }
+            }
+
+            if (primaryPackageName != null) {
+                try {
+                    ApplicationInfo applicationInfo =
+                            packageManager.getApplicationInfo(primaryPackageName, 0);
+                    info.label = applicationInfo.loadLabel(packageManager);
+                    info.iconInfo = applicationInfo;
+                } catch (PackageManager.NameNotFoundException e) {
+                    info.label = primaryPackageName;
+                }
+            } else if (packageWithHighestDrain != null) {
+                info.label = packageWithHighestDrain;
+            }
+
+            if (packages != null && packages.length > 0) {
+                StringBuilder sb = new StringBuilder();
+                if (primaryPackageName != null) {
+                    sb.append(primaryPackageName);
+                }
+                for (String packageName : packages) {
+                    if (packageName.equals(primaryPackageName)) {
+                        continue;
+                    }
+
+                    if (sb.length() != 0) {
+                        sb.append(", ");
+                    }
+                    sb.append(packageName);
+                }
+
+                info.packages = sb;
+            }
+        }
+
+        // Default the app icon to System Server. This includes root, dex2oat and other UIDs.
+        if (info.iconInfo == null) {
+            try {
+                info.iconInfo =
+                        packageManager.getApplicationInfo(SYSTEM_SERVER_PACKAGE_NAME, 0);
+            } catch (PackageManager.NameNotFoundException nameNotFoundException) {
+                // Won't happen
+            }
+        }
+        return info;
+    }
+}
diff --git a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/AppPickerActivity.java b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/AppPickerActivity.java
new file mode 100644
index 0000000..b4fc73c
--- /dev/null
+++ b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/AppPickerActivity.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.frameworks.core.powerstatsviewer;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.BatteryStats;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.UserManager;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.activity.ComponentActivity;
+import androidx.activity.result.contract.ActivityResultContract;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.loader.app.LoaderManager;
+import androidx.loader.content.Loader;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.frameworks.core.powerstatsviewer.AppInfoHelper.AppInfo;
+import com.android.internal.os.BatterySipper;
+import com.android.internal.os.BatteryStatsHelper;
+import com.android.settingslib.utils.AsyncLoaderCompat;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Picker, showing a sorted list of applications consuming power.  Returns the selected
+ * application UID or Process.INVALID_UID.
+ */
+public class AppPickerActivity extends ComponentActivity {
+    private static final String TAG = "AppPicker";
+
+    public static final ActivityResultContract<Void, Integer> CONTRACT =
+            new ActivityResultContract<Void, Integer>() {
+                @NonNull
+                @Override
+                public Intent createIntent(@NonNull Context context, Void aVoid) {
+                    return new Intent(context, AppPickerActivity.class);
+                }
+
+                @Override
+                public Integer parseResult(int resultCode, @Nullable Intent intent) {
+                    if (resultCode != RESULT_OK || intent == null) {
+                        return Process.INVALID_UID;
+                    }
+                    return intent.getIntExtra(Intent.EXTRA_UID, Process.INVALID_UID);
+                }
+            };
+
+    private AppListAdapter mAppListAdapter;
+    private RecyclerView mAppList;
+    private View mLoadingView;
+
+    private interface OnAppSelectedListener {
+        void onAppSelected(int uid);
+    }
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        getActionBar().setDisplayHomeAsUpEnabled(true);
+
+        setContentView(R.layout.app_picker_layout);
+
+        mLoadingView = findViewById(R.id.loading_view);
+
+        mAppList = findViewById(R.id.app_list_view);
+        mAppList.setLayoutManager(new LinearLayoutManager(this));
+        mAppListAdapter = new AppListAdapter(AppPickerActivity.this::setSelectedUid);
+        mAppList.setAdapter(mAppListAdapter);
+
+        LoaderManager.getInstance(this).initLoader(0, null,
+                new AppListLoaderCallbacks());
+    }
+
+    protected void setSelectedUid(int uid) {
+        Intent intent = new Intent();
+        intent.putExtra(Intent.EXTRA_UID, uid);
+        setResult(RESULT_OK, intent);
+        finish();
+    }
+
+    @Override
+    public boolean onNavigateUp() {
+        onBackPressed();
+        return true;
+    }
+
+    private static class AppListLoader extends AsyncLoaderCompat<List<AppInfo>> {
+        private final BatteryStatsHelper mStatsHelper;
+        private final UserManager mUserManager;
+        private final PackageManager mPackageManager;
+
+        AppListLoader(Context context) {
+            super(context);
+            mUserManager = context.getSystemService(UserManager.class);
+            mStatsHelper = new BatteryStatsHelper(context, false /* collectBatteryBroadcast */);
+            mStatsHelper.create((Bundle) null);
+            mStatsHelper.clearStats();
+            mPackageManager = context.getPackageManager();
+        }
+
+        @Override
+        public List<AppInfo> loadInBackground() {
+            List<AppInfo> applicationList = new ArrayList<>();
+
+            mStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED,
+                    mUserManager.getUserProfiles());
+
+            final List<BatterySipper> usageList = mStatsHelper.getUsageList();
+            for (BatterySipper sipper : usageList) {
+                AppInfo info =
+                        AppInfoHelper.makeApplicationInfo(mPackageManager, sipper.getUid(), sipper);
+                if (info != null) {
+                    applicationList.add(info);
+                }
+            }
+
+            applicationList.sort(
+                    Comparator.comparing((AppInfo a) -> a.powerMah).reversed());
+            return applicationList;
+        }
+
+        @Override
+        protected void onDiscardResult(List<AppInfo> result) {
+        }
+    }
+
+    private class AppListLoaderCallbacks implements
+            LoaderManager.LoaderCallbacks<List<AppInfo>> {
+
+        @NonNull
+        @Override
+        public Loader<List<AppInfo>> onCreateLoader(int id, Bundle args) {
+            return new AppListLoader(AppPickerActivity.this);
+        }
+
+        @Override
+        public void onLoadFinished(@NonNull Loader<List<AppInfo>> loader,
+                List<AppInfo> applicationList) {
+            mAppListAdapter.setApplicationList(applicationList);
+            mAppList.setVisibility(View.VISIBLE);
+            mLoadingView.setVisibility(View.GONE);
+        }
+
+        @Override
+        public void onLoaderReset(@NonNull Loader<List<AppInfo>> loader) {
+        }
+    }
+
+    public class AppListAdapter extends RecyclerView.Adapter<AppViewHolder> {
+        private final OnAppSelectedListener mListener;
+        private List<AppInfo> mApplicationList;
+
+        public AppListAdapter(OnAppSelectedListener listener) {
+            mListener = listener;
+        }
+
+        void setApplicationList(List<AppInfo> applicationList) {
+            mApplicationList = applicationList;
+            notifyDataSetChanged();
+        }
+
+        @Override
+        public int getItemCount() {
+            return mApplicationList.size();
+        }
+
+        @NonNull
+        @Override
+        public AppViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int position) {
+            LayoutInflater layoutInflater = LayoutInflater.from(viewGroup.getContext());
+            View view = layoutInflater.inflate(R.layout.app_info_layout, viewGroup, false);
+            return new AppViewHolder(view, mListener);
+        }
+
+        @Override
+        public void onBindViewHolder(@NonNull AppViewHolder appViewHolder, int position) {
+            AppInfo item = mApplicationList.get(position);
+            appViewHolder.uid = item.uid;
+            appViewHolder.titleView.setText(item.label);
+            appViewHolder.uidView.setText(
+                    String.format(Locale.getDefault(), "UID: %d", item.uid));
+            appViewHolder.powerView.setText(
+                    String.format(Locale.getDefault(), "%.1f mAh", item.powerMah));
+            appViewHolder.iconView.setImageDrawable(
+                    item.iconInfo.loadIcon(getPackageManager()));
+            if (item.packages != null) {
+                appViewHolder.packagesView.setText(item.packages);
+                appViewHolder.packagesView.setVisibility(View.VISIBLE);
+            } else {
+                appViewHolder.packagesView.setVisibility(View.GONE);
+            }
+        }
+    }
+
+    // View Holder used when displaying apps
+    public static class AppViewHolder extends RecyclerView.ViewHolder
+            implements View.OnClickListener {
+        private final OnAppSelectedListener mListener;
+
+        public int uid;
+        public TextView titleView;
+        public TextView uidView;
+        public ImageView iconView;
+        public TextView packagesView;
+        public TextView powerView;
+
+        AppViewHolder(View view, OnAppSelectedListener listener) {
+            super(view);
+            mListener = listener;
+            view.setOnClickListener(this);
+            titleView = view.findViewById(android.R.id.title);
+            uidView = view.findViewById(R.id.uid);
+            iconView = view.findViewById(android.R.id.icon);
+            packagesView = view.findViewById(R.id.packages);
+            powerView = view.findViewById(R.id.power_mah);
+            powerView.setVisibility(View.VISIBLE);
+        }
+
+        @Override
+        public void onClick(View v) {
+            mListener.onAppSelected(uid);
+        }
+    }
+}
diff --git a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsData.java b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsData.java
new file mode 100644
index 0000000..09f20ba
--- /dev/null
+++ b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsData.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.frameworks.core.powerstatsviewer;
+
+import android.content.Context;
+import android.os.Process;
+
+import com.android.internal.os.BatterySipper;
+import com.android.internal.os.BatteryStatsHelper;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class PowerStatsData {
+    private static final String PACKAGE_CALENDAR_PROVIDER = "com.android.providers.calendar";
+    private static final String PACKAGE_MEDIA_PROVIDER = "com.android.providers.media";
+    private static final String PACKAGE_SYSTEMUI = "com.android.systemui";
+    private static final String[] PACKAGES_SYSTEM = {PACKAGE_MEDIA_PROVIDER,
+            PACKAGE_CALENDAR_PROVIDER, PACKAGE_SYSTEMUI};
+
+    enum EntryType {
+        POWER,
+        DURATION,
+    }
+
+    public static class Entry {
+        public String title;
+        public EntryType entryType;
+        public double value;
+        public double total;
+    }
+
+    private final AppInfoHelper.AppInfo mAppInfo;
+    private final List<Entry> mEntries = new ArrayList<>();
+
+    public PowerStatsData(Context context, BatteryStatsHelper batteryStatsHelper,
+            int uid) {
+        List<BatterySipper> usageList = batteryStatsHelper.getUsageList();
+
+        double totalPowerMah = 0;
+        double totalSmearedPowerMah = 0;
+        double totalPowerExcludeSystemMah = 0;
+        double totalScreenPower = 0;
+        double totalProportionalSmearMah = 0;
+        double totalCpuPowerMah = 0;
+        double totalSystemServiceCpuPowerMah = 0;
+        double totalUsagePowerMah = 0;
+        double totalWakeLockPowerMah = 0;
+        double totalMobileRadioPowerMah = 0;
+        double totalWifiPowerMah = 0;
+        double totalBluetoothPowerMah = 0;
+        double totalGpsPowerMah = 0;
+        double totalCameraPowerMah = 0;
+        double totalFlashlightPowerMah = 0;
+        double totalSensorPowerMah = 0;
+        double totalAudioPowerMah = 0;
+        double totalVideoPowerMah = 0;
+
+        long totalCpuTimeMs = 0;
+        long totalCpuFgTimeMs = 0;
+        long totalWakeLockTimeMs = 0;
+        long totalWifiRunningTimeMs = 0;
+        long totalBluetoothRunningTimeMs = 0;
+        long totalGpsTimeMs = 0;
+        long totalCameraTimeMs = 0;
+        long totalFlashlightTimeMs = 0;
+        long totalAudioTimeMs = 0;
+        long totalVideoTimeMs = 0;
+
+        BatterySipper uidSipper = null;
+        for (BatterySipper sipper : usageList) {
+            if (sipper.drainType == BatterySipper.DrainType.SCREEN) {
+                totalScreenPower = sipper.sumPower();
+            }
+
+            if (isHiddenDrainType(sipper.drainType)) {
+                continue;
+            }
+
+            if (sipper.drainType == BatterySipper.DrainType.APP && sipper.getUid() == uid) {
+                uidSipper = sipper;
+            }
+
+            totalPowerMah += sipper.sumPower();
+            totalSmearedPowerMah += sipper.totalSmearedPowerMah;
+            totalProportionalSmearMah += sipper.proportionalSmearMah;
+
+            if (!isSystemSipper(sipper)) {
+                totalPowerExcludeSystemMah += sipper.totalSmearedPowerMah;
+            }
+
+            totalCpuPowerMah += sipper.cpuPowerMah;
+            totalSystemServiceCpuPowerMah += sipper.systemServiceCpuPowerMah;
+            totalUsagePowerMah += sipper.usagePowerMah;
+            totalWakeLockPowerMah += sipper.wakeLockPowerMah;
+            totalMobileRadioPowerMah += sipper.mobileRadioPowerMah;
+            totalWifiPowerMah += sipper.wifiPowerMah;
+            totalBluetoothPowerMah += sipper.bluetoothPowerMah;
+            totalGpsPowerMah += sipper.gpsPowerMah;
+            totalCameraPowerMah += sipper.cameraPowerMah;
+            totalFlashlightPowerMah += sipper.flashlightPowerMah;
+            totalSensorPowerMah += sipper.sensorPowerMah;
+            totalAudioPowerMah += sipper.audioPowerMah;
+            totalVideoPowerMah += sipper.videoPowerMah;
+
+            totalCpuTimeMs += sipper.cpuTimeMs;
+            totalCpuFgTimeMs += sipper.cpuFgTimeMs;
+            totalWakeLockTimeMs += sipper.wakeLockTimeMs;
+            totalWifiRunningTimeMs += sipper.wifiRunningTimeMs;
+            totalBluetoothRunningTimeMs += sipper.bluetoothRunningTimeMs;
+            totalGpsTimeMs += sipper.gpsTimeMs;
+            totalCameraTimeMs += sipper.cameraTimeMs;
+            totalFlashlightTimeMs += sipper.flashlightTimeMs;
+            totalAudioTimeMs += sipper.audioTimeMs;
+            totalVideoTimeMs += sipper.videoTimeMs;
+        }
+
+        mAppInfo = AppInfoHelper.makeApplicationInfo(context.getPackageManager(), uid, uidSipper);
+
+        if (uidSipper == null) {
+            return;
+        }
+
+        addEntry("Total power", EntryType.POWER,
+                uidSipper.totalSmearedPowerMah, totalSmearedPowerMah);
+        addEntry("... excluding system", EntryType.POWER,
+                uidSipper.totalSmearedPowerMah, totalPowerExcludeSystemMah);
+        addEntry("Screen, smeared", EntryType.POWER,
+                uidSipper.screenPowerMah, totalScreenPower);
+        addEntry("Other, smeared", EntryType.POWER,
+                uidSipper.proportionalSmearMah, totalProportionalSmearMah);
+        addEntry("Excluding smeared", EntryType.POWER,
+                uidSipper.totalPowerMah, totalPowerMah);
+        addEntry("CPU", EntryType.POWER,
+                uidSipper.cpuPowerMah, totalCpuPowerMah);
+        addEntry("System services", EntryType.POWER,
+                uidSipper.systemServiceCpuPowerMah, totalSystemServiceCpuPowerMah);
+        addEntry("RAM", EntryType.POWER,
+                uidSipper.usagePowerMah, totalUsagePowerMah);
+        addEntry("Wake lock", EntryType.POWER,
+                uidSipper.wakeLockPowerMah, totalWakeLockPowerMah);
+        addEntry("Mobile radio", EntryType.POWER,
+                uidSipper.mobileRadioPowerMah, totalMobileRadioPowerMah);
+        addEntry("WiFi", EntryType.POWER,
+                uidSipper.wifiPowerMah, totalWifiPowerMah);
+        addEntry("Bluetooth", EntryType.POWER,
+                uidSipper.bluetoothPowerMah, totalBluetoothPowerMah);
+        addEntry("GPS", EntryType.POWER,
+                uidSipper.gpsPowerMah, totalGpsPowerMah);
+        addEntry("Camera", EntryType.POWER,
+                uidSipper.cameraPowerMah, totalCameraPowerMah);
+        addEntry("Flashlight", EntryType.POWER,
+                uidSipper.flashlightPowerMah, totalFlashlightPowerMah);
+        addEntry("Sensors", EntryType.POWER,
+                uidSipper.sensorPowerMah, totalSensorPowerMah);
+        addEntry("Audio", EntryType.POWER,
+                uidSipper.audioPowerMah, totalAudioPowerMah);
+        addEntry("Video", EntryType.POWER,
+                uidSipper.videoPowerMah, totalVideoPowerMah);
+
+        addEntry("CPU time", EntryType.DURATION,
+                uidSipper.cpuTimeMs, totalCpuTimeMs);
+        addEntry("CPU foreground time", EntryType.DURATION,
+                uidSipper.cpuFgTimeMs, totalCpuFgTimeMs);
+        addEntry("Wake lock time", EntryType.DURATION,
+                uidSipper.wakeLockTimeMs, totalWakeLockTimeMs);
+        addEntry("WiFi running time", EntryType.DURATION,
+                uidSipper.wifiRunningTimeMs, totalWifiRunningTimeMs);
+        addEntry("Bluetooth time", EntryType.DURATION,
+                uidSipper.bluetoothRunningTimeMs, totalBluetoothRunningTimeMs);
+        addEntry("GPS time", EntryType.DURATION,
+                uidSipper.gpsTimeMs, totalGpsTimeMs);
+        addEntry("Camera time", EntryType.DURATION,
+                uidSipper.cameraTimeMs, totalCameraTimeMs);
+        addEntry("Flashlight time", EntryType.DURATION,
+                uidSipper.flashlightTimeMs, totalFlashlightTimeMs);
+        addEntry("Audio time", EntryType.DURATION,
+                uidSipper.audioTimeMs, totalAudioTimeMs);
+        addEntry("Video time", EntryType.DURATION,
+                uidSipper.videoTimeMs, totalVideoTimeMs);
+    }
+
+    protected boolean isHiddenDrainType(BatterySipper.DrainType drainType) {
+        return drainType == BatterySipper.DrainType.IDLE
+                || drainType == BatterySipper.DrainType.CELL
+                || drainType == BatterySipper.DrainType.SCREEN
+                || drainType == BatterySipper.DrainType.UNACCOUNTED
+                || drainType == BatterySipper.DrainType.OVERCOUNTED
+                || drainType == BatterySipper.DrainType.BLUETOOTH
+                || drainType == BatterySipper.DrainType.WIFI;
+    }
+
+    private boolean isSystemSipper(BatterySipper sipper) {
+        final int uid = sipper.uidObj == null ? -1 : sipper.getUid();
+        if (uid >= Process.ROOT_UID && uid < Process.FIRST_APPLICATION_UID) {
+            return true;
+        } else if (sipper.mPackages != null) {
+            for (final String packageName : sipper.mPackages) {
+                for (final String systemPackage : PACKAGES_SYSTEM) {
+                    if (systemPackage.equals(packageName)) {
+                        return true;
+                    }
+                }
+            }
+        }
+
+        return false;
+    }
+
+    private void addEntry(String title, EntryType entryType, double amount, double totalAmount) {
+        Entry entry = new Entry();
+        entry.title = title;
+        entry.entryType = entryType;
+        entry.value = amount;
+        entry.total = totalAmount;
+        mEntries.add(entry);
+    }
+
+    public AppInfoHelper.AppInfo getAppInfo() {
+        return mAppInfo;
+    }
+
+    public List<Entry> getEntries() {
+        return mEntries;
+    }
+}
diff --git a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsViewerActivity.java b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsViewerActivity.java
new file mode 100644
index 0000000..1605e9c
--- /dev/null
+++ b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsViewerActivity.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.frameworks.core.powerstatsviewer;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.BatteryStats;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.UserManager;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.activity.ComponentActivity;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.loader.app.LoaderManager;
+import androidx.loader.app.LoaderManager.LoaderCallbacks;
+import androidx.loader.content.Loader;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.internal.os.BatteryStatsHelper;
+import com.android.settingslib.utils.AsyncLoaderCompat;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+
+public class PowerStatsViewerActivity extends ComponentActivity {
+    private static final int POWER_STATS_REFRESH_RATE_MILLIS = 60 * 1000;
+    public static final String PREF_SELECTED_UID = "selectedUid";
+    private static final String LOADER_ARG_UID = "uid";
+
+    private PowerStatsDataAdapter mPowerStatsDataAdapter;
+    private Runnable mPowerStatsRefresh = this::periodicPowerStatsRefresh;
+    private SharedPreferences mSharedPref;
+    private int mUid = Process.INVALID_UID;
+    private TextView mTitleView;
+    private TextView mUidView;
+    private ImageView mIconView;
+    private TextView mPackagesView;
+    private RecyclerView mPowerStatsDataView;
+    private View mLoadingView;
+    private View mEmptyView;
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mSharedPref = getPreferences(Context.MODE_PRIVATE);
+
+        setContentView(R.layout.power_stats_viewer_layout);
+
+        View appCard = findViewById(R.id.app_card);
+        appCard.setOnClickListener((e) -> startAppPicker());
+
+        mTitleView = findViewById(android.R.id.title);
+        mUidView = findViewById(R.id.uid);
+        mIconView = findViewById(android.R.id.icon);
+        mPackagesView = findViewById(R.id.packages);
+
+        mPowerStatsDataView = findViewById(R.id.power_stats_data_view);
+        mPowerStatsDataView.setLayoutManager(new LinearLayoutManager(this));
+        mPowerStatsDataAdapter = new PowerStatsDataAdapter();
+        mPowerStatsDataView.setAdapter(mPowerStatsDataAdapter);
+
+        mLoadingView = findViewById(R.id.loading_view);
+        mEmptyView = findViewById(R.id.empty_view);
+
+        mUid = mSharedPref.getInt(PREF_SELECTED_UID, Process.INVALID_UID);
+        loadPowerStats();
+        if (mUid == Process.INVALID_UID) {
+            startAppPicker();
+        }
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        periodicPowerStatsRefresh();
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        getMainThreadHandler().removeCallbacks(mPowerStatsRefresh);
+    }
+
+    private void startAppPicker() {
+        registerForActivityResult(AppPickerActivity.CONTRACT, this::onApplicationSelected)
+                .launch(null);
+    }
+
+    private void onApplicationSelected(int uid) {
+        if (uid == -1) {
+            if (mUid == Process.INVALID_UID) {
+                finish();
+            }
+        } else {
+            mUid = uid;
+            mSharedPref.edit().putInt(PREF_SELECTED_UID, mUid).apply();
+            mLoadingView.setVisibility(View.VISIBLE);
+            loadPowerStats();
+        }
+    }
+
+    private void periodicPowerStatsRefresh() {
+        loadPowerStats();
+        getMainThreadHandler().postDelayed(mPowerStatsRefresh, POWER_STATS_REFRESH_RATE_MILLIS);
+    }
+
+    private void loadPowerStats() {
+        Bundle args = new Bundle();
+        args.putInt(LOADER_ARG_UID, mUid);
+        LoaderManager.getInstance(this).restartLoader(0, args, new PowerStatsDataLoaderCallbacks());
+    }
+
+    private static class PowerStatsDataLoader extends AsyncLoaderCompat<PowerStatsData> {
+        private final int mUid;
+        private final BatteryStatsHelper mBatteryStatsHelper;
+        private final UserManager mUserManager;
+
+        PowerStatsDataLoader(Context context, int uid) {
+            super(context);
+            mUid = uid;
+            mUserManager = context.getSystemService(UserManager.class);
+            mBatteryStatsHelper = new BatteryStatsHelper(context,
+                    false /* collectBatteryBroadcast */);
+            mBatteryStatsHelper.create((Bundle) null);
+            mBatteryStatsHelper.clearStats();
+        }
+
+        @Override
+        public PowerStatsData loadInBackground() {
+            mBatteryStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED,
+                    mUserManager.getUserProfiles());
+            return new PowerStatsData(getContext(), mBatteryStatsHelper, mUid);
+        }
+
+        @Override
+        protected void onDiscardResult(PowerStatsData result) {
+        }
+    }
+
+    private class PowerStatsDataLoaderCallbacks implements LoaderCallbacks<PowerStatsData> {
+        @NonNull
+        @Override
+        public Loader<PowerStatsData> onCreateLoader(int id, Bundle args) {
+            return new PowerStatsDataLoader(PowerStatsViewerActivity.this,
+                    args.getInt(LOADER_ARG_UID, Process.INVALID_UID));
+        }
+
+        @Override
+        public void onLoadFinished(@NonNull Loader<PowerStatsData> loader,
+                PowerStatsData powerStatsData) {
+
+            AppInfoHelper.AppInfo appInfo = powerStatsData.getAppInfo();
+            mTitleView.setText(appInfo.label);
+            mUidView.setText(String.format(Locale.getDefault(), "UID: %d", appInfo.uid));
+            mIconView.setImageDrawable(appInfo.iconInfo.loadIcon(getPackageManager()));
+
+            if (appInfo.packages != null) {
+                mPackagesView.setText(appInfo.packages);
+                mPackagesView.setVisibility(View.VISIBLE);
+            } else {
+                mPackagesView.setVisibility(View.GONE);
+            }
+
+            mPowerStatsDataAdapter.setEntries(powerStatsData.getEntries());
+
+            if (powerStatsData.getEntries().isEmpty()) {
+                mEmptyView.setVisibility(View.VISIBLE);
+                mPowerStatsDataView.setVisibility(View.GONE);
+            } else {
+                mEmptyView.setVisibility(View.GONE);
+                mPowerStatsDataView.setVisibility(View.VISIBLE);
+            }
+
+            mLoadingView.setVisibility(View.GONE);
+        }
+
+        @Override
+        public void onLoaderReset(@NonNull Loader<PowerStatsData> loader) {
+        }
+    }
+
+    private static class PowerStatsDataAdapter extends
+            RecyclerView.Adapter<PowerStatsDataAdapter.ViewHolder> {
+        public static class ViewHolder extends RecyclerView.ViewHolder {
+            public TextView titleTextView;
+            public TextView amountTextView;
+            public TextView percentTextView;
+
+            ViewHolder(View itemView) {
+                super(itemView);
+
+                titleTextView = itemView.findViewById(R.id.title);
+                amountTextView = itemView.findViewById(R.id.amount);
+                percentTextView = itemView.findViewById(R.id.percent);
+            }
+        }
+
+        private List<PowerStatsData.Entry> mEntries = Collections.emptyList();
+
+        public void setEntries(List<PowerStatsData.Entry> entries) {
+            mEntries = entries;
+            notifyDataSetChanged();
+        }
+
+        @Override
+        public int getItemCount() {
+            return mEntries.size();
+        }
+
+        @NonNull
+        @Override
+        public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int position) {
+            LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
+            View itemView = layoutInflater.inflate(R.layout.power_stats_entry_layout, parent,
+                    false);
+            return new ViewHolder(itemView);
+        }
+
+        @Override
+        public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) {
+            PowerStatsData.Entry entry = mEntries.get(position);
+            switch (entry.entryType) {
+                case POWER:
+                    viewHolder.titleTextView.setText(entry.title);
+                    viewHolder.amountTextView.setText(
+                            String.format(Locale.getDefault(), "%.1f mAh", entry.value));
+                    break;
+                case DURATION:
+                    viewHolder.titleTextView.setText(entry.title);
+                    viewHolder.amountTextView.setText(
+                            String.format(Locale.getDefault(), "%,d ms", (long) entry.value));
+                    break;
+            }
+
+            double proportion = entry.total != 0 ? entry.value * 100 / entry.total : 0;
+            viewHolder.percentTextView.setText(String.format(Locale.getDefault(), "%.1f%%",
+                    proportion));
+        }
+    }
+}
diff --git a/core/xsd/vts/Android.bp b/core/xsd/vts/Android.bp
index 4b43b41..ca655f1 100644
--- a/core/xsd/vts/Android.bp
+++ b/core/xsd/vts/Android.bp
@@ -40,7 +40,3 @@
     ],
     test_config: "vts_permission_validate_test.xml",
 }
-
-vts_config {
-    name: "VtsValidatePermission",
-}
diff --git a/core/xsd/vts/AndroidTest.xml b/core/xsd/vts/AndroidTest.xml
deleted file mode 100644
index e5cc9a0..0000000
--- a/core/xsd/vts/AndroidTest.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?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="Config for VTS VtsValidatePermission.">
-    <option name="config-descriptor:metadata" key="plan" value="vts-treble" />
-    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
-        <option name="abort-on-push-failure" value="false"/>
-        <option name="push-group" value="HostDrivenTest.push"/>
-        <option name="push" value="DATA/etc/permission.xsd->/data/local/tmp/permission.xsd"/>
-    </target_preparer>
-    <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
-        <option name="test-module-name" value="VtsValidatePermission"/>
-        <option name="binary-test-source" value="_32bit::DATA/nativetest/vts_permission_validate_test/vts_permission_validate_test" />
-        <option name="binary-test-source" value="_64bit::DATA/nativetest64/vts_permission_validate_test/vts_permission_validate_test" />
-        <option name="binary-test-type" value="gtest"/>
-        <option name="test-timeout" value="30s"/>
-    </test>
-</configuration>
diff --git a/data/etc/car/Android.bp b/data/etc/car/Android.bp
index e122e00..745de84 100644
--- a/data/etc/car/Android.bp
+++ b/data/etc/car/Android.bp
@@ -149,3 +149,11 @@
     src: "com.android.car.ui.paintbooth.xml",
     filename_from_src: true,
 }
+
+prebuilt_etc {
+    name: "allowed_privapp_com.android.car.provision",
+    system_ext_specific: true,
+    sub_dir: "permissions",
+    src: "com.android.car.provision.xml",
+    filename_from_src: true,
+}
\ No newline at end of file
diff --git a/data/etc/car/com.android.car.provision.xml b/data/etc/car/com.android.car.provision.xml
new file mode 100644
index 0000000..37f64b5
--- /dev/null
+++ b/data/etc/car/com.android.car.provision.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<permissions>
+    <privapp-permissions package="com.android.car.provision">
+        <permission name="android.car.permission.CAR_POWERTRAIN"/>
+        <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+        <permission name="android.permission.QUERY_ALL_PACKAGES"/>
+        <permission name="android.permission.SEND_CATEGORY_CAR_NOTIFICATIONS"/>
+        <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+        <permission name="android.permission.WRITE_SETTINGS"/>
+    </privapp-permissions>
+</permissions>
diff --git a/data/etc/car/com.google.android.car.kitchensink.xml b/data/etc/car/com.google.android.car.kitchensink.xml
index 59aa45e..3a20a9c 100644
--- a/data/etc/car/com.google.android.car.kitchensink.xml
+++ b/data/etc/car/com.google.android.car.kitchensink.xml
@@ -44,5 +44,8 @@
         <!-- use for CarServiceTest -->
         <permission name="android.permission.SET_ACTIVITY_WATCHER"/>
         <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+
+        <!-- use for rotary fragment to enable/disable packages related to rotary -->
+        <permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/>
     </privapp-permissions>
 </permissions>
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 977703d..301b491 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -248,8 +248,7 @@
     <library name="android.hidl.base-V1.0-java"
             file="/system/framework/android.hidl.base-V1.0-java.jar" />
     <library name="android.hidl.manager-V1.0-java"
-            file="/system/framework/android.hidl.manager-V1.0-java.jar"
-            dependency="android.hidl.base-V1.0-java" />
+            file="/system/framework/android.hidl.manager-V1.0-java.jar" />
 
     <!-- These are the standard packages that are white-listed to always have internet
          access while in power save mode, even if they aren't in the foreground. -->
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 81da5c8..4c3b36f 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -437,6 +437,9 @@
         <permission name="android.permission.MANAGE_DEBUGGING" />
         <!-- Permissions required for CTS test - TimeManagerTest -->
         <permission name="android.permission.MANAGE_TIME_AND_ZONE_DETECTION" />
+        <!-- Permissions required for CTS test - android.server.biometrics -->
+        <permission name="android.permission.USE_BIOMETRIC" />
+        <permission name="android.permission.TEST_BIOMETRIC" />
     </privapp-permissions>
 
     <privapp-permissions package="com.android.statementservice">
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 29c8de6..98c3370 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -115,18 +115,18 @@
       "group": "WM_DEBUG_WINDOW_ORGANIZER",
       "at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java"
     },
-    "-1977793524": {
-      "message": "moveStackToDisplay: moving stackId=%d to displayId=%d",
-      "level": "DEBUG",
-      "group": "WM_DEBUG_TASKS",
-      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
-    },
     "-1976930686": {
       "message": "Attempted to add Accessibility overlay window with bad token %s.  Aborting.",
       "level": "WARN",
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-1973119651": {
+      "message": "SyncGroup %d: Adding to group: %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_SYNC_ENGINE",
+      "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
+    },
     "-1963461591": {
       "message": "Removing %s from %s",
       "level": "VERBOSE",
@@ -163,14 +163,20 @@
       "group": "WM_DEBUG_STARTING_WINDOW",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "-1918702467": {
+      "message": "onSyncFinishedDrawing %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_SYNC_ENGINE",
+      "at": "com\/android\/server\/wm\/WindowContainer.java"
+    },
     "-1915280162": {
       "message": "Attempted to add wallpaper window with bad token %s.  Aborting.",
       "level": "WARN",
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
-    "-1910833551": {
-      "message": "SyncSet{%x:%d} Start for %s",
+    "-1905191109": {
+      "message": "SyncGroup %d: Finished!",
       "level": "VERBOSE",
       "group": "WM_DEBUG_SYNC_ENGINE",
       "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
@@ -307,12 +313,6 @@
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/WindowState.java"
     },
-    "-1741065110": {
-      "message": "No app is requesting an orientation, return %d for display id=%d",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_ORIENTATION",
-      "at": "com\/android\/server\/wm\/DisplayContent.java"
-    },
     "-1730156332": {
       "message": "Display id=%d rotation changed to %d from %d, lastOrientation=%d",
       "level": "VERBOSE",
@@ -535,6 +535,12 @@
       "group": "WM_DEBUG_STATES",
       "at": "com\/android\/server\/wm\/Task.java"
     },
+    "-1480772131": {
+      "message": "No app or window is requesting an orientation, return %d for display id=%d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_ORIENTATION",
+      "at": "com\/android\/server\/wm\/DisplayContent.java"
+    },
     "-1474292612": {
       "message": "Could not find task for id: %d",
       "level": "DEBUG",
@@ -583,6 +589,12 @@
       "group": "WM_DEBUG_ADD_REMOVE",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-1419762046": {
+      "message": "moveRootTaskToDisplay: moving taskId=%d to displayId=%d",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_TASKS",
+      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+    },
     "-1413901262": {
       "message": "startRecentsActivity(): intent=%s",
       "level": "DEBUG",
@@ -607,12 +619,6 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
-    "-1387080937": {
-      "message": "SyncSet{%x:%d} Child ready, now ready=%b and waiting on %d transactions",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_SYNC_ENGINE",
-      "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
-    },
     "-1376035390": {
       "message": "No task found",
       "level": "DEBUG",
@@ -643,12 +649,6 @@
       "group": "WM_DEBUG_BOOT",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
-    "-1340783230": {
-      "message": "SyncSet{%x:%d} Added %s. now waiting on %d transactions",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_SYNC_ENGINE",
-      "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
-    },
     "-1340540100": {
       "message": "Creating SnapshotStartingData",
       "level": "VERBOSE",
@@ -1177,11 +1177,11 @@
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/DragState.java"
     },
-    "-678300709": {
-      "message": "SyncSet{%x:%d} Trying to add %s",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_SYNC_ENGINE",
-      "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
+    "-677449371": {
+      "message": "moveTaskToRootTask: moving task=%d to rootTaskId=%d toTop=%b",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_TASKS",
+      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
     },
     "-672228342": {
       "message": "resumeTopActivityLocked: Top activity resumed %s",
@@ -1399,12 +1399,6 @@
       "group": "WM_DEBUG_RECENTS_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RecentsAnimationController.java"
     },
-    "-444624452": {
-      "message": "REPARENT from: %s to: %s",
-      "level": "INFO",
-      "group": "WM_SHOW_TRANSACTIONS",
-      "at": "com\/android\/server\/wm\/WindowSurfaceController.java"
-    },
     "-443173857": {
       "message": "Moving pending starting from %s to %s",
       "level": "VERBOSE",
@@ -1513,12 +1507,6 @@
       "group": "WM_DEBUG_STATES",
       "at": "com\/android\/server\/wm\/Task.java"
     },
-    "-324085783": {
-      "message": "SURFACE CROP %s: %s",
-      "level": "INFO",
-      "group": "WM_SHOW_TRANSACTIONS",
-      "at": "com\/android\/server\/wm\/WindowSurfaceController.java"
-    },
     "-322743468": {
       "message": "setInputMethodInputTarget %s",
       "level": "INFO",
@@ -1555,12 +1543,6 @@
       "group": "WM_DEBUG_WINDOW_TRANSITIONS",
       "at": "com\/android\/server\/wm\/Transition.java"
     },
-    "-300896109": {
-      "message": "moveTaskToStack: moving task=%d to stackId=%d toTop=%b",
-      "level": "DEBUG",
-      "group": "WM_DEBUG_TASKS",
-      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
-    },
     "-279436615": {
       "message": "Moving to PAUSING: %s",
       "level": "VERBOSE",
@@ -1603,6 +1585,12 @@
       "group": "WM_DEBUG_STATES",
       "at": "com\/android\/server\/wm\/Task.java"
     },
+    "-230587670": {
+      "message": "SyncGroup %d:  Unfinished container: %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_SYNC_ENGINE",
+      "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
+    },
     "-198463978": {
       "message": "updateRotationUnchecked: alwaysSendConfiguration=%b forceRelayout=%b",
       "level": "VERBOSE",
@@ -1711,12 +1699,6 @@
       "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
       "at": "com\/android\/server\/wm\/WindowContainer.java"
     },
-    "-29233992": {
-      "message": "SURFACE CLEAR CROP: %s",
-      "level": "INFO",
-      "group": "WM_SHOW_TRANSACTIONS",
-      "at": "com\/android\/server\/wm\/WindowSurfaceController.java"
-    },
     "-21399771": {
       "message": "activity %s already destroying, skipping request with reason:%s",
       "level": "VERBOSE",
@@ -1999,6 +1981,12 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "255339989": {
+      "message": "setFocusedRootTask: taskId=%d",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_FOCUS",
+      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+    },
     "255692476": {
       "message": "**** GOOD TO GO",
       "level": "VERBOSE",
@@ -2101,6 +2089,12 @@
       "group": "WM_DEBUG_CONFIGURATION",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "371173718": {
+      "message": "finishSync cancel=%b for %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_SYNC_ENGINE",
+      "at": "com\/android\/server\/wm\/WindowContainer.java"
+    },
     "371641947": {
       "message": "Window Manager Crash %s",
       "level": "WTF",
@@ -2245,6 +2239,12 @@
       "group": "WM_DEBUG_BOOT",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "550717438": {
+      "message": "SyncGroup %d: Started for listener: %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_SYNC_ENGINE",
+      "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
+    },
     "556758086": {
       "message": "Applying new update lock state '%s' for %s",
       "level": "DEBUG",
@@ -2281,12 +2281,6 @@
       "group": "WM_DEBUG_IME",
       "at": "com\/android\/server\/wm\/ImeInsetsSourceProvider.java"
     },
-    "590184240": {
-      "message": "- NOT adding to sync: visible=%b hasListener=%b",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_SYNC_ENGINE",
-      "at": "com\/android\/server\/wm\/WindowContainer.java"
-    },
     "594260577": {
       "message": "createWallpaperAnimations()",
       "level": "DEBUG",
@@ -2461,6 +2455,12 @@
       "group": "WM_DEBUG_RECENTS_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RecentsAnimation.java"
     },
+    "781471998": {
+      "message": "moveWindowTokenToDisplay: Cannot move to the original display for token: %s",
+      "level": "WARN",
+      "group": "WM_ERROR",
+      "at": "com\/android\/server\/wm\/WindowManagerService.java"
+    },
     "791468751": {
       "message": "Pausing rotation during re-position",
       "level": "DEBUG",
@@ -2503,12 +2503,6 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowToken.java"
     },
-    "845234215": {
-      "message": "App is requesting an orientation, return %d for display id=%d",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_ORIENTATION",
-      "at": "com\/android\/server\/wm\/DisplayContent.java"
-    },
     "849147756": {
       "message": "Finish collecting in transition %d",
       "level": "VERBOSE",
@@ -2611,6 +2605,18 @@
       "group": "WM_DEBUG_LOCKTASK",
       "at": "com\/android\/server\/wm\/LockTaskController.java"
     },
+    "959486822": {
+      "message": "setSyncGroup #%d on %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_SYNC_ENGINE",
+      "at": "com\/android\/server\/wm\/WindowContainer.java"
+    },
+    "966569777": {
+      "message": "SyncGroup %d: onSurfacePlacement checking %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_SYNC_ENGINE",
+      "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
+    },
     "969323241": {
       "message": "Sending new config to %s, config: %s",
       "level": "VERBOSE",
@@ -2635,12 +2641,6 @@
       "group": "WM_DEBUG_WINDOW_TRANSITIONS",
       "at": "com\/android\/server\/wm\/Transition.java"
     },
-    "1000601037": {
-      "message": "SyncSet{%x:%d} Set ready",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_SYNC_ENGINE",
-      "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
-    },
     "1001509841": {
       "message": "Auto-PIP allowed, entering PIP mode directly: %s",
       "level": "DEBUG",
@@ -2665,6 +2665,18 @@
       "group": "WM_DEBUG_STATES",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "1033274509": {
+      "message": "moveWindowTokenToDisplay: Attempted to move non-existing token: %s",
+      "level": "WARN",
+      "group": "WM_ERROR",
+      "at": "com\/android\/server\/wm\/WindowManagerService.java"
+    },
+    "1035154109": {
+      "message": "SURFACE backgroundBlur=%o: %s",
+      "level": "INFO",
+      "group": "WM_SHOW_TRANSACTIONS",
+      "at": "com\/android\/server\/wm\/WindowSurfaceController.java"
+    },
     "1040675582": {
       "message": "Can't report activity configuration update - client not running, activityRecord=%s",
       "level": "WARN",
@@ -2797,12 +2809,6 @@
       "group": "WM_DEBUG_FOCUS",
       "at": "com\/android\/server\/wm\/WindowToken.java"
     },
-    "1220075598": {
-      "message": "SURFACE SIZE %dx%d: %s",
-      "level": "INFO",
-      "group": "WM_SHOW_TRANSACTIONS",
-      "at": "com\/android\/server\/wm\/WindowSurfaceController.java"
-    },
     "1224184681": {
       "message": "No longer Stopped: %s",
       "level": "VERBOSE",
@@ -2905,6 +2911,12 @@
       "group": "WM_DEBUG_IME",
       "at": "com\/android\/server\/wm\/ImeInsetsSourceProvider.java"
     },
+    "1381227466": {
+      "message": "App is requesting an orientation, return %d for display id=%d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_ORIENTATION",
+      "at": "com\/android\/server\/wm\/TaskDisplayArea.java"
+    },
     "1401295262": {
       "message": "Mode default, asking user",
       "level": "WARN",
@@ -3115,6 +3127,18 @@
       "group": "WM_DEBUG_RESIZE",
       "at": "com\/android\/server\/wm\/WindowState.java"
     },
+    "1640436199": {
+      "message": "No app is requesting an orientation, return %d for display id=%d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_ORIENTATION",
+      "at": "com\/android\/server\/wm\/TaskDisplayArea.java"
+    },
+    "1648338379": {
+      "message": "Display id=%d is ignoring all orientation requests, return %d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_ORIENTATION",
+      "at": "com\/android\/server\/wm\/DisplayContent.java"
+    },
     "1653210583": {
       "message": "Removing app %s delayed=%b animation=%s animating=%b",
       "level": "VERBOSE",
@@ -3151,6 +3175,12 @@
       "group": "WM_DEBUG_CONFIGURATION",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "1689989893": {
+      "message": "SyncGroup %d: Set ready",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_SYNC_ENGINE",
+      "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
+    },
     "1696210756": {
       "message": "Launch on display check: allow launch on public display",
       "level": "DEBUG",
@@ -3361,12 +3391,6 @@
       "group": "WM_DEBUG_APP_TRANSITIONS",
       "at": "com\/android\/server\/wm\/AppTransitionController.java"
     },
-    "1975793405": {
-      "message": "setFocusedStack: stackId=%d",
-      "level": "DEBUG",
-      "group": "WM_DEBUG_FOCUS",
-      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
-    },
     "1984470582": {
       "message": "Creating TaskScreenshotAnimatable: task: %s width: %d height: %d",
       "level": "DEBUG",
@@ -3385,12 +3409,6 @@
       "group": "WM_DEBUG_CONFIGURATION",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
-    "2001924866": {
-      "message": "SyncSet{%x:%d} Finished. Reporting %d containers to %s",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_SYNC_ENGINE",
-      "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
-    },
     "2016061474": {
       "message": "Prepare app transition: transit=%s %s alwaysKeepCurrent=%b displayId=%d Callers=%s",
       "level": "VERBOSE",
@@ -3457,6 +3475,12 @@
       "group": "WM_DEBUG_REMOTE_ANIMATIONS",
       "at": "com\/android\/server\/wm\/WallpaperAnimationAdapter.java"
     },
+    "2060978050": {
+      "message": "moveWindowTokenToDisplay: Attempted to move token: %s to non-exiting displayId=%d",
+      "level": "WARN",
+      "group": "WM_ERROR",
+      "at": "com\/android\/server\/wm\/WindowManagerService.java"
+    },
     "2081291430": {
       "message": "Focus not requested for window=%s because it has no surface",
       "level": "DEBUG",
diff --git a/drm/jni/Android.bp b/drm/jni/Android.bp
index 1e33f0e..68757d8 100644
--- a/drm/jni/Android.bp
+++ b/drm/jni/Android.bp
@@ -21,6 +21,7 @@
 
     shared_libs: [
         "libdrmframework",
+        "libdrmframeworkcommon",
         "liblog",
         "libutils",
         "libandroid_runtime",
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/BinderIdentityChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/BinderIdentityChecker.java
new file mode 100644
index 0000000..68477ed
--- /dev/null
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/BinderIdentityChecker.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.google.errorprone.bugpatterns.android;
+
+import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
+import static com.google.errorprone.matchers.Matchers.contains;
+import static com.google.errorprone.matchers.Matchers.methodInvocation;
+import static com.google.errorprone.matchers.Matchers.staticMethod;
+
+import com.google.auto.service.AutoService;
+import com.google.errorprone.BugPattern;
+import com.google.errorprone.VisitorState;
+import com.google.errorprone.bugpatterns.BugChecker;
+import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
+import com.google.errorprone.matchers.Description;
+import com.google.errorprone.matchers.Matcher;
+import com.sun.source.tree.BlockTree;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.MethodInvocationTree;
+import com.sun.source.tree.StatementTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.Tree.Kind;
+import com.sun.source.tree.TryTree;
+import com.sun.source.tree.VariableTree;
+
+import java.util.List;
+
+import javax.lang.model.element.Modifier;
+
+/**
+ * Binder maintains thread-local identity information about any remote caller,
+ * which can be temporarily cleared while performing operations that need to be
+ * handled as the current process. However, it's important to restore the
+ * original remote calling identity after carefully scoping this work inside a
+ * try/finally block, to avoid obscure security vulnerabilities.
+ */
+@AutoService(BugChecker.class)
+@BugPattern(
+    name = "AndroidFrameworkBinderIdentity",
+    summary = "Verifies that Binder.clearCallingIdentity() is always restored",
+    severity = WARNING)
+public final class BinderIdentityChecker extends BugChecker implements MethodInvocationTreeMatcher {
+    private static final Matcher<ExpressionTree> CLEAR_CALL = methodInvocation(staticMethod()
+            .onClass("android.os.Binder").withSignature("clearCallingIdentity()"));
+    private static final Matcher<ExpressionTree> RESTORE_CALL = methodInvocation(staticMethod()
+            .onClass("android.os.Binder").withSignature("restoreCallingIdentity(long)"));
+
+    @Override
+    public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
+        if (CLEAR_CALL.matches(tree, state)) {
+            // First, make sure we're recording the token for later
+            final VariableTree token = state.findEnclosing(VariableTree.class);
+            if (token == null || !token.getModifiers().getFlags().contains(Modifier.FINAL)) {
+                return buildDescription(tree)
+                        .setMessage("Must store Binder.clearCallingIdentity() token as final"
+                                + " variable to support safe restore")
+                        .build();
+            }
+
+            // Next, verify the very next block is try-finally; any other calls
+            // between the clearing and try risk throwing an exception without
+            // doing a safe restore
+            final Tree next = nextStatement(token, state);
+            if (next == null || next.getKind() != Kind.TRY) {
+                return buildDescription(tree)
+                        .setMessage("Must immediately define a try-finally block after"
+                                + " Binder.clearCallingIdentity() to support safe restore")
+                        .build();
+            }
+
+            // Finally, verify that we restore inside the finally block
+            final TryTree tryTree = (TryTree) next;
+            final BlockTree finallyTree = tryTree.getFinallyBlock();
+            if (finallyTree == null
+                    || !contains(ExpressionTree.class, RESTORE_CALL).matches(finallyTree, state)) {
+                return buildDescription(tree)
+                        .setMessage("Must call Binder.restoreCallingIdentity() in finally"
+                                + "  block to support safe restore")
+                        .build();
+            }
+        }
+        return Description.NO_MATCH;
+    }
+
+    private static Tree nextStatement(Tree tree, VisitorState state) {
+        final BlockTree block = state.findEnclosing(BlockTree.class);
+        if (block == null) return null;
+        final List<? extends StatementTree> siblings = block.getStatements();
+        if (siblings == null) return null;
+        final int index = siblings.indexOf(tree);
+        if (index == -1 || index + 1 >= siblings.size()) return null;
+        return siblings.get(index + 1);
+    }
+}
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientCollectionsChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientCollectionsChecker.java
new file mode 100644
index 0000000..c4c1ab6
--- /dev/null
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientCollectionsChecker.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.google.errorprone.bugpatterns.android;
+
+import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
+import static com.google.errorprone.matchers.Matchers.isSubtypeOf;
+
+import com.google.auto.service.AutoService;
+import com.google.errorprone.BugPattern;
+import com.google.errorprone.VisitorState;
+import com.google.errorprone.bugpatterns.BugChecker;
+import com.google.errorprone.bugpatterns.BugChecker.NewClassTreeMatcher;
+import com.google.errorprone.matchers.Description;
+import com.google.errorprone.matchers.Matcher;
+import com.google.errorprone.util.ASTHelpers;
+import com.sun.source.tree.NewClassTree;
+import com.sun.source.tree.Tree;
+import com.sun.tools.javac.code.Type;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Android offers several efficient alternatives to some upstream
+ * {@link Collections} containers, such as {@code SparseIntArray} instead of
+ * {@code Map<Integer, Integer>}.
+ */
+@AutoService(BugChecker.class)
+@BugPattern(
+    name = "AndroidFrameworkEfficientCollections",
+    summary = "Verifies efficient collections best-practices",
+    severity = WARNING)
+public final class EfficientCollectionsChecker extends BugChecker implements NewClassTreeMatcher {
+    private static final Matcher<Tree> IS_LIST = isSubtypeOf("java.util.List");
+    private static final Matcher<Tree> IS_MAP = isSubtypeOf("java.util.Map");
+
+    private static final String INTEGER = "java.lang.Integer";
+    private static final String LONG = "java.lang.Long";
+    private static final String BOOLEAN = "java.lang.Boolean";
+
+    @Override
+    public Description matchNewClass(NewClassTree tree, VisitorState state) {
+        final List<Type> types = ASTHelpers.getType(tree).getTypeArguments();
+        if (IS_LIST.matches(tree, state) && types != null && types.size() == 1) {
+            final Type first = types.get(0);
+            if (ASTHelpers.isSameType(first, state.getTypeFromString(INTEGER), state)) {
+                return buildDescription(tree)
+                        .setMessage("Consider replacing with IntArray for efficiency")
+                        .build();
+            } else if (ASTHelpers.isSameType(first, state.getTypeFromString(LONG), state))  {
+                return buildDescription(tree)
+                        .setMessage("Consider replacing with LongArray for efficiency")
+                        .build();
+            }
+        } else if (IS_MAP.matches(tree, state) && types != null && types.size() == 2) {
+            final Type first = types.get(0);
+            final Type second = types.get(1);
+            if (ASTHelpers.isSameType(first, state.getTypeFromString(INTEGER), state)) {
+                if (ASTHelpers.isSameType(second, state.getTypeFromString(INTEGER), state)) {
+                    return buildDescription(tree)
+                            .setMessage("Consider replacing with SparseIntArray for efficiency")
+                            .build();
+                } else if (ASTHelpers.isSameType(second, state.getTypeFromString(LONG), state)) {
+                    return buildDescription(tree)
+                            .setMessage("Consider replacing with SparseLongArray for efficiency")
+                            .build();
+                } else if (ASTHelpers.isSameType(second, state.getTypeFromString(BOOLEAN), state)) {
+                    return buildDescription(tree)
+                            .setMessage("Consider replacing with SparseBooleanArray for efficiency")
+                            .build();
+                } else {
+                    return buildDescription(tree)
+                            .setMessage("Consider replacing with SparseArray for efficiency")
+                            .build();
+                }
+            } else if (ASTHelpers.isSameType(first, state.getTypeFromString(LONG), state)) {
+                return buildDescription(tree)
+                        .setMessage("Consider replacing with LongSparseArray for efficiency")
+                        .build();
+            }
+        }
+        return Description.NO_MATCH;
+    }
+}
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/ParcelablePerformanceChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientParcelableChecker.java
similarity index 97%
rename from errorprone/java/com/google/errorprone/bugpatterns/android/ParcelablePerformanceChecker.java
rename to errorprone/java/com/google/errorprone/bugpatterns/android/EfficientParcelableChecker.java
index d524316..c29a095 100644
--- a/errorprone/java/com/google/errorprone/bugpatterns/android/ParcelablePerformanceChecker.java
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientParcelableChecker.java
@@ -43,10 +43,10 @@
  */
 @AutoService(BugChecker.class)
 @BugPattern(
-    name = "AndroidFrameworkParcelablePerformance",
+    name = "AndroidFrameworkEfficientParcelable",
     summary = "Verifies Parcelable performance best-practices",
     severity = WARNING)
-public final class ParcelablePerformanceChecker extends BugChecker
+public final class EfficientParcelableChecker extends BugChecker
         implements MethodInvocationTreeMatcher {
     private static final Matcher<Tree> INSIDE_WRITE_TO_PARCEL = allOf(
             enclosingClass(isSubtypeOf("android.os.Parcelable")),
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientStringsChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientStringsChecker.java
new file mode 100644
index 0000000..3a0fbd3
--- /dev/null
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientStringsChecker.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.google.errorprone.bugpatterns.android;
+
+import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
+import static com.google.errorprone.matchers.Matchers.allOf;
+import static com.google.errorprone.matchers.Matchers.anyOf;
+import static com.google.errorprone.matchers.Matchers.contains;
+import static com.google.errorprone.matchers.Matchers.hasModifier;
+import static com.google.errorprone.matchers.Matchers.instanceMethod;
+import static com.google.errorprone.matchers.Matchers.isSubtypeOf;
+import static com.google.errorprone.matchers.Matchers.kindIs;
+import static com.google.errorprone.matchers.Matchers.methodInvocation;
+import static com.google.errorprone.matchers.Matchers.not;
+import static com.google.errorprone.matchers.Matchers.staticMethod;
+
+import com.google.auto.service.AutoService;
+import com.google.errorprone.BugPattern;
+import com.google.errorprone.VisitorState;
+import com.google.errorprone.bugpatterns.BugChecker;
+import com.google.errorprone.bugpatterns.BugChecker.CompoundAssignmentTreeMatcher;
+import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
+import com.google.errorprone.bugpatterns.BugChecker.NewClassTreeMatcher;
+import com.google.errorprone.matchers.Description;
+import com.google.errorprone.matchers.Matcher;
+import com.google.errorprone.predicates.TypePredicate;
+import com.google.errorprone.util.ASTHelpers;
+import com.sun.source.tree.BinaryTree;
+import com.sun.source.tree.CompoundAssignmentTree;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.LiteralTree;
+import com.sun.source.tree.MethodInvocationTree;
+import com.sun.source.tree.NewClassTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.Tree.Kind;
+import com.sun.tools.javac.code.Symbol.VarSymbol;
+import com.sun.tools.javac.code.Type;
+
+import java.util.List;
+import java.util.Objects;
+
+import javax.lang.model.element.Modifier;
+
+/**
+ * Android offers several efficient alternatives to some upstream {@link String}
+ * operations.
+ */
+@AutoService(BugChecker.class)
+@BugPattern(
+    name = "AndroidFrameworkEfficientStrings",
+    summary = "Verifies efficient Strings best-practices",
+    severity = WARNING)
+public final class EfficientStringsChecker extends BugChecker
+        implements MethodInvocationTreeMatcher, NewClassTreeMatcher, CompoundAssignmentTreeMatcher {
+
+    private static final Matcher<ExpressionTree> FORMAT_CALL = methodInvocation(
+            staticMethod().onClass("java.lang.String").named("format"));
+    private static final Matcher<ExpressionTree> PRECONDITIONS_CALL = methodInvocation(
+            staticMethod().onClass(withSimpleName("Preconditions")).withAnyName());
+    private static final Matcher<ExpressionTree> OBJECTS_CALL = methodInvocation(
+            staticMethod().onClass("java.util.Objects").named("requireNonNull"));
+    private static final Matcher<ExpressionTree> APPEND_CALL = methodInvocation(
+            instanceMethod().onExactClass("java.lang.StringBuilder")
+                    .withSignature("append(java.lang.String)"));
+
+    /**
+     * Identify any dynamic values that will likely cause us to allocate a
+     * transparent StringBuilder.
+     */
+    private static final Matcher<ExpressionTree> DYNAMIC_VALUE = anyOf(
+            allOf(kindIs(Kind.MEMBER_SELECT),
+                    not(allOf(hasModifier(Modifier.STATIC), hasModifier(Modifier.FINAL)))),
+            allOf(kindIs(Kind.IDENTIFIER),
+                    not(allOf(hasModifier(Modifier.STATIC), hasModifier(Modifier.FINAL)))),
+            kindIs(Kind.METHOD_INVOCATION));
+
+    /**
+     * Identify an expression that is either a direct "+" binary operator, or
+     * that contains a "+" binary operator nested deep inside.
+     */
+    private static final Matcher<Tree> PLUS = anyOf(kindIs(Kind.PLUS),
+            contains(BinaryTree.class, kindIs(Kind.PLUS)));
+
+    /**
+     * Identify an expression that is using a "+" binary operator to combine
+     * dynamic values, which will likely end up allocating a transparent
+     * {@link StringBuilder}.
+     */
+    private static final Matcher<Tree> PLUS_DYNAMIC_VALUE = allOf(
+            PLUS, contains(ExpressionTree.class, DYNAMIC_VALUE));
+
+    private static final Matcher<Tree> IS_STRING_BUFFER = isSubtypeOf("java.lang.StringBuffer");
+
+    @Override
+    public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
+        if (FORMAT_CALL.matches(tree, state)) {
+            // Skip over possible locale to find format string
+            final List<? extends ExpressionTree> args = tree.getArguments();
+            final ExpressionTree formatArg;
+            final List<VarSymbol> vars = ASTHelpers.getSymbol(tree).params();
+            if (vars.get(0).type.toString().equals("java.util.Locale")) {
+                formatArg = args.get(1);
+            } else {
+                formatArg = args.get(0);
+            }
+
+            // Determine if format string is "simple" enough to replace
+            if (formatArg.getKind() == Kind.STRING_LITERAL) {
+                final String format = String.valueOf(((LiteralTree) formatArg).getValue());
+                if (isSimple(format)) {
+                    return buildDescription(formatArg)
+                            .setMessage("Simple format strings can be replaced with "
+                                    + "TextUtils.formatSimple() for a 6x performance improvement")
+                            .build();
+                }
+            }
+        } else if (PRECONDITIONS_CALL.matches(tree, state)
+                || OBJECTS_CALL.matches(tree, state)) {
+            final List<? extends ExpressionTree> args = tree.getArguments();
+            if (args.size() > 1) {
+                final ExpressionTree arg = args.get(1);
+                if (PLUS_DYNAMIC_VALUE.matches(arg, state)) {
+                    return buildDescription(arg)
+                            .setMessage("Building dynamic messages is discouraged, since they "
+                                    + "always allocate a transparent StringBuilder, even in "
+                                    + "the successful case")
+                            .build();
+                }
+            }
+        } else if (APPEND_CALL.matches(tree, state)) {
+            final ExpressionTree arg = tree.getArguments().get(0);
+            if (PLUS_DYNAMIC_VALUE.matches(arg, state)) {
+                return buildDescription(arg)
+                        .setMessage("Call append() directly for each argument instead of "
+                                + "allocating a transparent StringBuilder")
+                        .build();
+            }
+        }
+        return Description.NO_MATCH;
+    }
+
+    @Override
+    public Description matchNewClass(NewClassTree tree, VisitorState state) {
+        if (IS_STRING_BUFFER.matches(tree, state)) {
+            return buildDescription(tree)
+                    .setMessage("Strongly encouraged to replace with StringBuilder "
+                            + "which avoids synchronization overhead")
+                    .build();
+        }
+        return Description.NO_MATCH;
+    }
+
+    @Override
+    public Description matchCompoundAssignment(CompoundAssignmentTree tree, VisitorState state) {
+        if (tree.getKind() == Kind.PLUS_ASSIGNMENT && "java.lang.String"
+                .equals(String.valueOf(ASTHelpers.getType(tree.getVariable())))) {
+            return buildDescription(tree)
+                    .setMessage("Strongly encouraged to replace with StringBuilder "
+                            + "which avoids transparent StringBuilder allocations")
+                    .build();
+        }
+        return Description.NO_MATCH;
+    }
+
+    static boolean isSimple(String format) {
+        for (int i = 0; i < format.length(); i++) {
+            char c = format.charAt(i);
+            if (c == '%') {
+                c = format.charAt(++i);
+                while ('0' <= c && c <= '9') {
+                    c = format.charAt(++i);
+                }
+                switch (c) {
+                    case 'b':
+                    case 'c':
+                    case 'd':
+                    case 'f':
+                    case 's':
+                    case 'x':
+                    case '%':
+                        break;
+                    default:
+                        return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    static TypePredicate withSimpleName(final String filter) {
+        return new TypePredicate() {
+            @Override
+            public boolean apply(Type type, VisitorState state) {
+                return type.tsym.getSimpleName().toString().equals(filter);
+            }
+        };
+    }
+}
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/PendingIntentMutabilityChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/PendingIntentMutabilityChecker.java
new file mode 100644
index 0000000..2561b41
--- /dev/null
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/PendingIntentMutabilityChecker.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.google.errorprone.bugpatterns.android;
+
+import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
+import static com.google.errorprone.matchers.FieldMatchers.staticField;
+import static com.google.errorprone.matchers.Matchers.anyOf;
+import static com.google.errorprone.matchers.Matchers.contains;
+import static com.google.errorprone.matchers.Matchers.methodInvocation;
+import static com.google.errorprone.matchers.Matchers.staticMethod;
+
+
+import com.google.auto.service.AutoService;
+import com.google.errorprone.BugPattern;
+import com.google.errorprone.VisitorState;
+import com.google.errorprone.bugpatterns.BugChecker;
+import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
+import com.google.errorprone.matchers.Description;
+import com.google.errorprone.matchers.Matcher;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.MethodInvocationTree;
+
+import java.util.regex.Pattern;
+
+/**
+ * Any method calls to create a PendingIntent require that one of the
+ * mutability flags, FLAG_MUTABLE or FLAG_IMMUTABLE, be explicitly specified.
+ * This checker verifies that one of these mutability flags are used when
+ * creating PendingIntents.
+ */
+@AutoService(BugChecker.class)
+@BugPattern(
+        name = "AndroidFrameworkPendingIntentMutability",
+        summary = "Verifies that FLAG_MUTABLE or FLAG_IMMUTABLE is always set",
+        severity = WARNING)
+public final class PendingIntentMutabilityChecker extends BugChecker
+        implements MethodInvocationTreeMatcher {
+
+    private static final Matcher<ExpressionTree> PENDING_INTENT_METHOD = methodInvocation(
+            staticMethod()
+            .onClass("android.app.PendingIntent")
+            .withNameMatching(Pattern.compile(
+                    "^(getActivity|getActivityAsUser|getActivities|getActivitiesAsUser|"
+                    + "getBroadcast|getBroadcastAsUser|getService|getForegroundService).*")));
+
+    private static final Matcher<ExpressionTree> VALID_FLAGS = anyOf(
+            staticField("android.app.PendingIntent", "FLAG_MUTABLE"),
+            staticField("android.app.PendingIntent", "FLAG_IMMUTABLE"));
+
+    private static final Matcher<ExpressionTree> CONTAINS_VALID_FLAGS = contains(
+            ExpressionTree.class, VALID_FLAGS);
+
+    @Override
+    public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
+        if (PENDING_INTENT_METHOD.matches(tree, state)) {
+            final ExpressionTree arg = tree.getArguments().get(3);
+            if (!(VALID_FLAGS.matches(arg, state) || CONTAINS_VALID_FLAGS.matches(arg, state))) {
+                return buildDescription(arg)
+                        .setMessage("To improve security, PendingIntents must declare one of"
+                                + " FLAG_MUTABLE or FLAG_IMMUTABLE explicitly; see"
+                                + " go/immutable-pendingintents for more details")
+                        .build();
+            }
+        }
+        return Description.NO_MATCH;
+    }
+}
diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/BinderIdentityCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/BinderIdentityCheckerTest.java
new file mode 100644
index 0000000..9448344
--- /dev/null
+++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/BinderIdentityCheckerTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.google.errorprone.bugpatterns.android;
+
+import com.google.errorprone.CompilationTestHelper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class BinderIdentityCheckerTest {
+    private CompilationTestHelper compilationHelper;
+
+    @Before
+    public void setUp() {
+        compilationHelper = CompilationTestHelper.newInstance(
+                BinderIdentityChecker.class, getClass());
+    }
+
+    @Test
+    public void testValid() {
+        compilationHelper
+                .addSourceFile("/android/os/Binder.java")
+                .addSourceLines("FooService.java",
+                        "import android.os.Binder;",
+                        "public class FooService {",
+                        "  void bar() {",
+                        "    final long token = Binder.clearCallingIdentity();",
+                        "    try {",
+                        "      FooService.class.toString();",
+                        "    } finally {",
+                        "      Binder.restoreCallingIdentity(token);",
+                        "    }",
+                        "  }",
+                        "}")
+                .doTest();
+    }
+
+    @Test
+    public void testInvalid() {
+        compilationHelper
+                .addSourceFile("/android/os/Binder.java")
+                .addSourceLines("FooService.java",
+                        "import android.os.Binder;",
+                        "public class FooService {",
+                        "  void noRestore() {",
+                        "    // BUG: Diagnostic contains:",
+                        "    final long token = Binder.clearCallingIdentity();",
+                        "    FooService.class.toString();",
+                        "  }",
+                        "  void noTry() {",
+                        "    // BUG: Diagnostic contains:",
+                        "    final long token = Binder.clearCallingIdentity();",
+                        "    FooService.class.toString();",
+                        "    Binder.restoreCallingIdentity(token);",
+                        "  }",
+                        "  void noImmediateTry() {",
+                        "    // BUG: Diagnostic contains:",
+                        "    final long token = Binder.clearCallingIdentity();",
+                        "    FooService.class.toString();",
+                        "    try {",
+                        "      FooService.class.toString();",
+                        "    } finally {",
+                        "      Binder.restoreCallingIdentity(token);",
+                        "    }",
+                        "  }",
+                        "  void noFinally() {",
+                        "    // BUG: Diagnostic contains:",
+                        "    final long token = Binder.clearCallingIdentity();",
+                        "    try {",
+                        "      FooService.class.toString();",
+                        "    } catch (Exception ignored) { }",
+                        "  }",
+                        "  void noFinal() {",
+                        "    // BUG: Diagnostic contains:",
+                        "    long token = Binder.clearCallingIdentity();",
+                        "    try {",
+                        "      FooService.class.toString();",
+                        "    } finally {",
+                        "      Binder.restoreCallingIdentity(token);",
+                        "    }",
+                        "  }",
+                        "  void noRecording() {",
+                        "    // BUG: Diagnostic contains:",
+                        "    Binder.clearCallingIdentity();",
+                        "    FooService.class.toString();",
+                        "  }",
+                        "  void noWork() {",
+                        "    // BUG: Diagnostic contains:",
+                        "    final long token = Binder.clearCallingIdentity();",
+                        "  }",
+                        "}")
+                .doTest();
+    }
+}
diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/EfficientCollectionsCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/EfficientCollectionsCheckerTest.java
new file mode 100644
index 0000000..e128b6a
--- /dev/null
+++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/EfficientCollectionsCheckerTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.google.errorprone.bugpatterns.android;
+
+import com.google.errorprone.CompilationTestHelper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class EfficientCollectionsCheckerTest {
+    private CompilationTestHelper compilationHelper;
+
+    @Before
+    public void setUp() {
+        compilationHelper = CompilationTestHelper.newInstance(
+                EfficientCollectionsChecker.class, getClass());
+    }
+
+    @Test
+    public void testMap() {
+        compilationHelper
+                .addSourceLines("Example.java",
+                        "import java.util.HashMap;",
+                        "public class Example {",
+                        "  public void exampleInteger() {",
+                        "    // BUG: Diagnostic contains:",
+                        "    HashMap<Integer, Integer> a = new HashMap<>();",
+                        "    // BUG: Diagnostic contains:",
+                        "    HashMap<Integer, Long> b = new HashMap<>();",
+                        "    // BUG: Diagnostic contains:",
+                        "    HashMap<Integer, Boolean> c = new HashMap<>();",
+                        "    // BUG: Diagnostic contains:",
+                        "    HashMap<Integer, String> d = new HashMap<>();",
+                        "  }",
+                        "  public void exampleLong() {",
+                        "    // BUG: Diagnostic contains:",
+                        "    HashMap<Long, String> res = new HashMap<>();",
+                        "  }",
+                        "  public void exampleOther() {",
+                        "    HashMap<String, String> res = new HashMap<>();",
+                        "  }",
+                        "}")
+                .doTest();
+    }
+
+    @Test
+    public void testList() {
+        compilationHelper
+                .addSourceLines("Example.java",
+                        "import java.util.ArrayList;",
+                        "public class Example {",
+                        "  public void exampleInteger() {",
+                        "    // BUG: Diagnostic contains:",
+                        "    ArrayList<Integer> res = new ArrayList<>();",
+                        "  }",
+                        "  public void exampleLong() {",
+                        "    // BUG: Diagnostic contains:",
+                        "    ArrayList<Long> res = new ArrayList<>();",
+                        "  }",
+                        "  public void exampleOther() {",
+                        "    ArrayList<String> res = new ArrayList<>();",
+                        "  }",
+                        "}")
+                .doTest();
+    }
+
+    @Test
+    public void testErasure() {
+        compilationHelper
+                .addSourceLines("Example.java",
+                        "import java.util.HashMap;",
+                        "public class Example {",
+                        "  public void example() {",
+                        "    HashMap a = new HashMap();",
+                        "  }",
+                        "}")
+                .doTest();
+    }
+}
diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/ParcelablePerformanceCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/EfficientParcelableCheckerTest.java
similarity index 97%
rename from errorprone/tests/java/com/google/errorprone/bugpatterns/android/ParcelablePerformanceCheckerTest.java
rename to errorprone/tests/java/com/google/errorprone/bugpatterns/android/EfficientParcelableCheckerTest.java
index 75c76e3..a40414b 100644
--- a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/ParcelablePerformanceCheckerTest.java
+++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/EfficientParcelableCheckerTest.java
@@ -24,13 +24,13 @@
 import org.junit.runners.JUnit4;
 
 @RunWith(JUnit4.class)
-public class ParcelablePerformanceCheckerTest {
+public class EfficientParcelableCheckerTest {
     private CompilationTestHelper compilationHelper;
 
     @Before
     public void setUp() {
         compilationHelper = CompilationTestHelper.newInstance(
-                ParcelablePerformanceChecker.class, getClass());
+                EfficientParcelableChecker.class, getClass());
     }
 
     @Test
diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/EfficientStringsCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/EfficientStringsCheckerTest.java
new file mode 100644
index 0000000..48e4ad1
--- /dev/null
+++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/EfficientStringsCheckerTest.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.google.errorprone.bugpatterns.android;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import com.google.errorprone.CompilationTestHelper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class EfficientStringsCheckerTest {
+    private CompilationTestHelper compilationHelper;
+
+    @Before
+    public void setUp() {
+        compilationHelper = CompilationTestHelper.newInstance(
+                EfficientStringsChecker.class, getClass());
+    }
+
+    @Test
+    public void testSimple() {
+        assertTrue(EfficientStringsChecker.isSimple(""));
+        assertTrue(EfficientStringsChecker.isSimple("%s"));
+        assertTrue(EfficientStringsChecker.isSimple("String %s%s and %%%% number %d%d together"));
+        assertTrue(EfficientStringsChecker.isSimple("%04d"));
+        assertTrue(EfficientStringsChecker.isSimple("%02x:%02x:%02x"));
+        assertTrue(EfficientStringsChecker.isSimple("%10d"));
+
+        assertFalse(EfficientStringsChecker.isSimple("%0.4f"));
+        assertFalse(EfficientStringsChecker.isSimple("%t"));
+        assertFalse(EfficientStringsChecker.isSimple("%1$s"));
+    }
+
+    @Test
+    public void testFormat() {
+        compilationHelper
+                .addSourceLines("Example.java",
+                        "import java.util.Locale;",
+                        "public class Example {",
+                        "  public void example(String str) {",
+                        "    String.format(str, str);",
+                        "    // BUG: Diagnostic contains:",
+                        "    String.format(\"foo %s bar\", str);",
+                        "    // BUG: Diagnostic contains:",
+                        "    String.format(\"foo %d bar\", 42);",
+                        "    // BUG: Diagnostic contains:",
+                        "    String.format(\"foo %04d bar\", 42);",
+                        "  }",
+                        "  public void exampleLocale(String str) {",
+                        "    String.format(Locale.US, str, str);",
+                        "    // BUG: Diagnostic contains:",
+                        "    String.format(Locale.US, \"foo %s bar\", str);",
+                        "    // BUG: Diagnostic contains:",
+                        "    String.format(Locale.US, \"foo %d bar\", 42);",
+                        "    // BUG: Diagnostic contains:",
+                        "    String.format(Locale.US, \"foo %04d bar\", 42);",
+                        "  }",
+                        "}")
+                .doTest();
+    }
+
+    @Test
+    public void testPreconditions() {
+        compilationHelper
+                .addSourceFile("/android/util/Preconditions.java")
+                .addSourceLines("Example.java",
+                        "import android.util.Preconditions;",
+                        "import java.util.Objects;",
+                        "public class Example {",
+                        "  String str;",
+                        "  public void checkState(boolean val) {",
+                        "    Preconditions.checkState(val);",
+                        "    Preconditions.checkState(val, str);",
+                        "    Preconditions.checkState(val, \"foo\");",
+                        "    Preconditions.checkState(val, \"foo\" + \"bar\");",
+                        "    // BUG: Diagnostic contains:",
+                        "    Preconditions.checkState(val, \"foo \" + val);",
+                        "  }",
+                        "  public void checkArgument(boolean val) {",
+                        "    Preconditions.checkArgument(val);",
+                        "    Preconditions.checkArgument(val, str);",
+                        "    Preconditions.checkArgument(val, \"foo\");",
+                        "    Preconditions.checkArgument(val, \"foo\" + \"bar\");",
+                        "    // BUG: Diagnostic contains:",
+                        "    Preconditions.checkArgument(val, \"foo \" + val);",
+                        "  }",
+                        "  public void checkNotNull(Object val) {",
+                        "    Preconditions.checkNotNull(val);",
+                        "    Preconditions.checkNotNull(val, str);",
+                        "    Preconditions.checkNotNull(val, \"foo\");",
+                        "    Preconditions.checkNotNull(val, \"foo\" + \"bar\");",
+                        "    // BUG: Diagnostic contains:",
+                        "    Preconditions.checkNotNull(val, \"foo \" + val);",
+                        "  }",
+                        "  public void requireNonNull(Object val) {",
+                        "    Objects.requireNonNull(val);",
+                        "    Objects.requireNonNull(val, str);",
+                        "    Objects.requireNonNull(val, \"foo\");",
+                        "    Objects.requireNonNull(val, \"foo\" + \"bar\");",
+                        "    // BUG: Diagnostic contains:",
+                        "    Objects.requireNonNull(val, \"foo \" + val);",
+                        "  }",
+                        "}")
+                .doTest();
+    }
+
+    @Test
+    public void testPreconditions_Complex() {
+        compilationHelper
+                .addSourceFile("/android/util/Preconditions.java")
+                .addSourceLines("Example.java",
+                        "import android.util.Preconditions;",
+                        "public class Example {",
+                        "  String[] classArray = new String[] { null };",
+                        "  String classVar;",
+                        "  static final String CONST_VAR = \"baz\";",
+                        "  public String classMethod() { return \"baz\"; }",
+                        "  public static final String CONST_METHOD() { return \"baz\"; }",
+                        "  public void checkNotNull(Example example, Object val) {",
+                        "    String methodVar = \"baz\";",
+                        "    Preconditions.checkNotNull(val, \"foo\");",
+                        "    Preconditions.checkNotNull(val, (\"foo\"));",
+                        "    Preconditions.checkNotNull(val, classArray[0]);",
+                        "    Preconditions.checkNotNull(val, classVar);",
+                        "    Preconditions.checkNotNull(val, CONST_VAR);",
+                        "    Preconditions.checkNotNull(val, example.classVar);",
+                        "    Preconditions.checkNotNull(val, Example.CONST_VAR);",
+                        "    Preconditions.checkNotNull(val, methodVar);",
+                        "    Preconditions.checkNotNull(val, classMethod());",
+                        "    Preconditions.checkNotNull(val, CONST_METHOD());",
+                        "    Preconditions.checkNotNull(val, \"foo\" + \"bar\");",
+                        "    Preconditions.checkNotNull(val, (\"foo\" + \"bar\"));",
+                        "    // BUG: Diagnostic contains:",
+                        "    Preconditions.checkNotNull(val, \"foo\" + classArray[0]);",
+                        "    // BUG: Diagnostic contains:",
+                        "    Preconditions.checkNotNull(val, \"foo\" + classVar);",
+                        "    Preconditions.checkNotNull(val, \"foo\" + CONST_VAR);",
+                        "    // BUG: Diagnostic contains:",
+                        "    Preconditions.checkNotNull(val, \"foo\" + methodVar);",
+                        "    // BUG: Diagnostic contains:",
+                        "    Preconditions.checkNotNull(val, \"foo\" + classMethod());",
+                        "    // BUG: Diagnostic contains:",
+                        "    Preconditions.checkNotNull(val, \"foo\" + CONST_METHOD());",
+                        "  }",
+                        "}")
+                .doTest();
+    }
+
+    @Test
+    public void testStringBuffer() {
+        compilationHelper
+                .addSourceLines("Example.java",
+                        "public class Example {",
+                        "  public void example() {",
+                        "    // BUG: Diagnostic contains:",
+                        "    StringBuffer sb = new StringBuffer();",
+                        "  }",
+                        "}")
+                .doTest();
+    }
+
+    @Test
+    public void testStringBuilder() {
+        compilationHelper
+                .addSourceLines("Example.java",
+                        "public class Example {",
+                        "  StringBuilder sb = new StringBuilder();",
+                        "  String[] classArray = new String[] { null };",
+                        "  String classVar;",
+                        "  static final String CONST_VAR = \"baz\";",
+                        "  public String classMethod() { return \"baz\"; }",
+                        "  public static final String CONST_METHOD() { return \"baz\"; }",
+                        "  public void generic(Example example) {",
+                        "    sb.append(\"foo\");",
+                        "    sb.append(\"foo\" + \"bar\");",
+                        "    sb.append(classArray[0]);",
+                        "    sb.append(example.classArray[0]);",
+                        "    sb.append(classVar);",
+                        "    sb.append(CONST_VAR);",
+                        "    sb.append(example.classVar);",
+                        "    sb.append(Example.CONST_VAR);",
+                        "    sb.append(classMethod());",
+                        "    sb.append(CONST_METHOD());",
+                        "  }",
+                        "  public void string(String val) {",
+                        "    sb.append(\"foo\").append(val);",
+                        "    sb.append(\"foo\").append(val != null ? \"bar\" : \"baz\");",
+                        "    // BUG: Diagnostic contains:",
+                        "    sb.append(\"foo\" + val);",
+                        "  }",
+                        "  public void number(int val) {",
+                        "    sb.append(\"foo\").append(val);",
+                        "    sb.append(\"foo\").append(val + val);",
+                        "    sb.append(\"foo\").append(val > 0 ? \"bar\" : \"baz\");",
+                        "    // BUG: Diagnostic contains:",
+                        "    sb.append(\"foo\" + val);",
+                        "    // BUG: Diagnostic contains:",
+                        "    sb.append(\"foo\" + String.valueOf(val));",
+                        "    // BUG: Diagnostic contains:",
+                        "    sb.append(\"foo\" + Integer.toString(val));",
+                        "  }",
+                        "}")
+                .doTest();
+    }
+
+    @Test
+    public void testPlusAssignment() {
+        compilationHelper
+                .addSourceLines("Example.java",
+                        "public class Example {",
+                        "  public void string(String val) {",
+                        "    String s = \"foo\";",
+                        "    // BUG: Diagnostic contains:",
+                        "    s += \"bar\";",
+                        "    // BUG: Diagnostic contains:",
+                        "    s += val;",
+                        "    // BUG: Diagnostic contains:",
+                        "    s += (\"bar\" + \"baz\");",
+                        "  }",
+                        "  public void number(int val) {",
+                        "    int other = 42;",
+                        "    other += 24;",
+                        "    other += val;",
+                        "  }",
+                        "}")
+                .doTest();
+    }
+}
diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/PendingIntentMutabilityCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/PendingIntentMutabilityCheckerTest.java
new file mode 100644
index 0000000..a8badf6
--- /dev/null
+++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/PendingIntentMutabilityCheckerTest.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.google.errorprone.bugpatterns.android;
+
+import com.google.errorprone.CompilationTestHelper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class PendingIntentMutabilityCheckerTest {
+    private CompilationTestHelper mCompilationHelper;
+
+    @Before
+    public void setUp() {
+        mCompilationHelper = CompilationTestHelper.newInstance(
+                PendingIntentMutabilityChecker.class, getClass());
+    }
+
+    @Test
+    public void testGetActivity() {
+        mCompilationHelper
+                .addSourceFile("/android/app/PendingIntent.java")
+                .addSourceFile("/android/content/Context.java")
+                .addSourceFile("/android/content/Intent.java")
+                .addSourceFile("/android/os/UserHandle.java")
+                .addSourceFile("/android/os/Bundle.java")
+                .addSourceLines("Example.java",
+                        "import android.app.PendingIntent;",
+                        "import android.content.Context;",
+                        "import android.content.Intent;",
+                        "public class Example {",
+                        "  Context context;",
+                        "  Intent intent;",
+                        "  void example() {",
+                        "    PendingIntent.getActivity(context, 42, intent, PendingIntent.FLAG_MUTABLE);",
+                        "    PendingIntent.getActivity(context, 42, intent, PendingIntent.FLAG_IMMUTABLE);",
+                        "    PendingIntent.getActivity(context, 42, intent, PendingIntent.FLAG_NO_CREATE | PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT);",
+                        "    PendingIntent.getActivity(context, 42, intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT);",
+                        "    PendingIntent.getActivity(context, 42, intent, 0 | PendingIntent.FLAG_MUTABLE);",
+                        "    // BUG: Diagnostic contains:",
+                        "    PendingIntent.getActivity(context, 42, intent, PendingIntent.FLAG_ONE_SHOT);",
+                        "    // BUG: Diagnostic contains:",
+                        "    PendingIntent.getActivity(context, 42, intent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_NO_CREATE);",
+                        "    // BUG: Diagnostic contains:",
+                        "    PendingIntent.getActivity(context, 42, intent, 0);",
+                        "  }",
+                        "}")
+                .doTest();
+    }
+
+    @Test
+    public void testGetActivityAsUser() {
+        mCompilationHelper
+                .addSourceFile("/android/app/PendingIntent.java")
+                .addSourceFile("/android/content/Context.java")
+                .addSourceFile("/android/content/Intent.java")
+                .addSourceFile("/android/os/UserHandle.java")
+                .addSourceFile("/android/os/Bundle.java")
+                .addSourceLines("Example.java",
+                        "import android.app.PendingIntent;",
+                        "import android.content.Context;",
+                        "import android.content.Intent;",
+                        "public class Example {",
+                        "  Context context;",
+                        "  Intent intent;",
+                        "  void example() {",
+                        "    PendingIntent.getActivityAsUser(context, 42, intent, PendingIntent.FLAG_MUTABLE, null, null);",
+                        "    PendingIntent.getActivityAsUser(context, 42, intent, PendingIntent.FLAG_IMMUTABLE, null, null);",
+                        "    PendingIntent.getActivityAsUser(context, 42, intent, PendingIntent.FLAG_NO_CREATE | PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT, null, null);",
+                        "    PendingIntent.getActivityAsUser(context, 42, intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT, null, null);",
+                        "    PendingIntent.getActivityAsUser(context, 42, intent, 0 | PendingIntent.FLAG_MUTABLE, null, null);",
+                        "    // BUG: Diagnostic contains:",
+                        "    PendingIntent.getActivityAsUser(context, 42, intent, PendingIntent.FLAG_ONE_SHOT, null, null);",
+                        "    // BUG: Diagnostic contains:",
+                        "    PendingIntent.getActivityAsUser(context, 42, intent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_NO_CREATE, null, null);",
+                        "    // BUG: Diagnostic contains:",
+                        "    PendingIntent.getActivityAsUser(context, 42, intent, 0, null, null);",
+                        "  }",
+                        "}")
+                .doTest();
+    }
+
+    @Test
+    public void testGetActivities() {
+        mCompilationHelper
+                .addSourceFile("/android/app/PendingIntent.java")
+                .addSourceFile("/android/content/Context.java")
+                .addSourceFile("/android/content/Intent.java")
+                .addSourceFile("/android/os/UserHandle.java")
+                .addSourceFile("/android/os/Bundle.java")
+                .addSourceLines("Example.java",
+                        "import android.app.PendingIntent;",
+                        "import android.content.Context;",
+                        "import android.content.Intent;",
+                        "public class Example {",
+                        "  Context context;",
+                        "  Intent[] intents;",
+                        "  void example() {",
+                        "    PendingIntent.getActivities(context, 42, intents, PendingIntent.FLAG_MUTABLE, null);",
+                        "    PendingIntent.getActivities(context, 42, intents, PendingIntent.FLAG_IMMUTABLE, null);",
+                        "    PendingIntent.getActivities(context, 42, intents, PendingIntent.FLAG_NO_CREATE | PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT, null);",
+                        "    PendingIntent.getActivities(context, 42, intents, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT, null);",
+                        "    PendingIntent.getActivities(context, 42, intents, 0 | PendingIntent.FLAG_MUTABLE, null);",
+                        "    // BUG: Diagnostic contains:",
+                        "    PendingIntent.getActivities(context, 42, intents, PendingIntent.FLAG_ONE_SHOT, null);",
+                        "    // BUG: Diagnostic contains:",
+                        "    PendingIntent.getActivities(context, 42, intents, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_NO_CREATE, null);",
+                        "    // BUG: Diagnostic contains:",
+                        "    PendingIntent.getActivities(context, 42, intents, 0, null);",
+                        "  }",
+                        "}")
+                .doTest();
+    }
+
+    @Test
+    public void testGetActivitiesAsUser() {
+        mCompilationHelper
+                .addSourceFile("/android/app/PendingIntent.java")
+                .addSourceFile("/android/content/Context.java")
+                .addSourceFile("/android/content/Intent.java")
+                .addSourceFile("/android/os/UserHandle.java")
+                .addSourceFile("/android/os/Bundle.java")
+                .addSourceLines("Example.java",
+                        "import android.app.PendingIntent;",
+                        "import android.content.Context;",
+                        "import android.content.Intent;",
+                        "public class Example {",
+                        "  Context context;",
+                        "  Intent[] intents;",
+                        "  void example() {",
+                        "    PendingIntent.getActivitiesAsUser(context, 42, intents, PendingIntent.FLAG_MUTABLE, null, null);",
+                        "    PendingIntent.getActivitiesAsUser(context, 42, intents, PendingIntent.FLAG_IMMUTABLE, null, null);",
+                        "    PendingIntent.getActivitiesAsUser(context, 42, intents, PendingIntent.FLAG_NO_CREATE | PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT, null, null);",
+                        "    PendingIntent.getActivitiesAsUser(context, 42, intents, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT, null, null);",
+                        "    PendingIntent.getActivitiesAsUser(context, 42, intents, 0 | PendingIntent.FLAG_MUTABLE, null, null);",
+                        "    // BUG: Diagnostic contains:",
+                        "    PendingIntent.getActivitiesAsUser(context, 42, intents, PendingIntent.FLAG_ONE_SHOT, null, null);",
+                        "    // BUG: Diagnostic contains:",
+                        "    PendingIntent.getActivitiesAsUser(context, 42, intents, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_NO_CREATE, null, null);",
+                        "    // BUG: Diagnostic contains:",
+                        "    PendingIntent.getActivitiesAsUser(context, 42, intents, 0, null, null);",
+                        "  }",
+                        "}")
+                .doTest();
+    }
+
+
+    @Test
+    public void testGetBroadcast() {
+        mCompilationHelper
+                .addSourceFile("/android/app/PendingIntent.java")
+                .addSourceFile("/android/content/Context.java")
+                .addSourceFile("/android/content/Intent.java")
+                .addSourceFile("/android/os/UserHandle.java")
+                .addSourceFile("/android/os/Bundle.java")
+                .addSourceLines("Example.java",
+                        "import android.app.PendingIntent;",
+                        "import android.content.Context;",
+                        "import android.content.Intent;",
+                        "public class Example {",
+                        "  Context context;",
+                        "  Intent intent;",
+                        "  void example() {",
+                        "    PendingIntent.getBroadcast(context, 42, intent, PendingIntent.FLAG_MUTABLE);",
+                        "    PendingIntent.getBroadcast(context, 42, intent, PendingIntent.FLAG_IMMUTABLE);",
+                        "    PendingIntent.getBroadcast(context, 42, intent, PendingIntent.FLAG_NO_CREATE | PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT);",
+                        "    PendingIntent.getBroadcast(context, 42, intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT);",
+                        "    PendingIntent.getBroadcast(context, 42, intent, 0 | PendingIntent.FLAG_MUTABLE);",
+                        "    // BUG: Diagnostic contains:",
+                        "    PendingIntent.getBroadcast(context, 42, intent, PendingIntent.FLAG_ONE_SHOT);",
+                        "    // BUG: Diagnostic contains:",
+                        "    PendingIntent.getBroadcast(context, 42, intent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_NO_CREATE);",
+                        "    // BUG: Diagnostic contains:",
+                        "    PendingIntent.getBroadcast(context, 42, intent, 0);",
+                        "  }",
+                        "}")
+                .doTest();
+    }
+
+    @Test
+    public void testGetBroadcastAsUser() {
+        mCompilationHelper
+                .addSourceFile("/android/app/PendingIntent.java")
+                .addSourceFile("/android/content/Context.java")
+                .addSourceFile("/android/content/Intent.java")
+                .addSourceFile("/android/os/UserHandle.java")
+                .addSourceFile("/android/os/Bundle.java")
+                .addSourceLines("Example.java",
+                        "import android.app.PendingIntent;",
+                        "import android.content.Context;",
+                        "import android.content.Intent;",
+                        "public class Example {",
+                        "  Context context;",
+                        "  Intent intent;",
+                        "  void example() {",
+                        "    PendingIntent.getBroadcastAsUser(context, 42, intent, PendingIntent.FLAG_MUTABLE, null);",
+                        "    PendingIntent.getBroadcastAsUser(context, 42, intent, PendingIntent.FLAG_IMMUTABLE, null);",
+                        "    PendingIntent.getBroadcastAsUser(context, 42, intent, PendingIntent.FLAG_NO_CREATE | PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT, null);",
+                        "    PendingIntent.getBroadcastAsUser(context, 42, intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT, null);",
+                        "    PendingIntent.getBroadcastAsUser(context, 42, intent, 0 | PendingIntent.FLAG_MUTABLE, null);",
+                        "    // BUG: Diagnostic contains:",
+                        "    PendingIntent.getBroadcastAsUser(context, 42, intent, PendingIntent.FLAG_ONE_SHOT, null);",
+                        "    // BUG: Diagnostic contains:",
+                        "    PendingIntent.getBroadcastAsUser(context, 42, intent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_NO_CREATE, null);",
+                        "    // BUG: Diagnostic contains:",
+                        "    PendingIntent.getBroadcastAsUser(context, 42, intent, 0, null);",
+                        "  }",
+                        "}")
+                .doTest();
+    }
+
+    @Test
+    public void testGetService() {
+        mCompilationHelper
+                .addSourceFile("/android/app/PendingIntent.java")
+                .addSourceFile("/android/content/Context.java")
+                .addSourceFile("/android/content/Intent.java")
+                .addSourceFile("/android/os/UserHandle.java")
+                .addSourceFile("/android/os/Bundle.java")
+                .addSourceLines("Example.java",
+                        "import android.app.PendingIntent;",
+                        "import android.content.Context;",
+                        "import android.content.Intent;",
+                        "public class Example {",
+                        "  Context context;",
+                        "  Intent intent;",
+                        "  void example() {",
+                        "    PendingIntent.getService(context, 42, intent, PendingIntent.FLAG_MUTABLE);",
+                        "    PendingIntent.getService(context, 42, intent, PendingIntent.FLAG_IMMUTABLE);",
+                        "    PendingIntent.getService(context, 42, intent, PendingIntent.FLAG_NO_CREATE | PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT);",
+                        "    PendingIntent.getService(context, 42, intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT);",
+                        "    PendingIntent.getService(context, 42, intent, 0 | PendingIntent.FLAG_MUTABLE);",
+                        "    // BUG: Diagnostic contains:",
+                        "    PendingIntent.getService(context, 42, intent, PendingIntent.FLAG_ONE_SHOT);",
+                        "    // BUG: Diagnostic contains:",
+                        "    PendingIntent.getService(context, 42, intent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_NO_CREATE);",
+                        "    // BUG: Diagnostic contains:",
+                        "    PendingIntent.getService(context, 42, intent, 0);",
+                        "  }",
+                        "}")
+                .doTest();
+    }
+
+    @Test
+    public void testGetForegroundService() {
+        mCompilationHelper
+                .addSourceFile("/android/app/PendingIntent.java")
+                .addSourceFile("/android/content/Context.java")
+                .addSourceFile("/android/content/Intent.java")
+                .addSourceFile("/android/os/UserHandle.java")
+                .addSourceFile("/android/os/Bundle.java")
+                .addSourceLines("Example.java",
+                        "import android.app.PendingIntent;",
+                        "import android.content.Context;",
+                        "import android.content.Intent;",
+                        "public class Example {",
+                        "  Context context;",
+                        "  Intent intent;",
+                        "  void example() {",
+                        "    PendingIntent.getForegroundService(context, 42, intent, PendingIntent.FLAG_MUTABLE);",
+                        "    PendingIntent.getForegroundService(context, 42, intent, PendingIntent.FLAG_IMMUTABLE);",
+                        "    PendingIntent.getForegroundService(context, 42, intent, PendingIntent.FLAG_NO_CREATE | PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT);",
+                        "    PendingIntent.getForegroundService(context, 42, intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT);",
+                        "    PendingIntent.getForegroundService(context, 42, intent, 0 | PendingIntent.FLAG_MUTABLE);",
+                        "    // BUG: Diagnostic contains:",
+                        "    PendingIntent.getForegroundService(context, 42, intent, PendingIntent.FLAG_ONE_SHOT);",
+                        "    // BUG: Diagnostic contains:",
+                        "    PendingIntent.getForegroundService(context, 42, intent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_NO_CREATE);",
+                        "    // BUG: Diagnostic contains:",
+                        "    PendingIntent.getForegroundService(context, 42, intent, 0);",
+                        "  }",
+                        "}")
+                .doTest();
+    }
+}
diff --git a/errorprone/tests/res/android/app/PendingIntent.java b/errorprone/tests/res/android/app/PendingIntent.java
new file mode 100644
index 0000000..a0cdedf
--- /dev/null
+++ b/errorprone/tests/res/android/app/PendingIntent.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.UserHandle;
+
+public class PendingIntent {
+    public static final int FLAG_ONE_SHOT = 1<<30;
+    public static final int FLAG_IMMUTABLE = 1<<26;
+    public static final int FLAG_MUTABLE = 1<<25;
+    public static final int FLAG_NO_CREATE = 1<<29;
+
+    public static PendingIntent getActivity(Context context, int requestCode,
+            Intent intent, int flags) {
+        throw new UnsupportedOperationException();
+    }
+
+    public static PendingIntent getActivityAsUser(Context context, int requestCode,
+            Intent intent, int flags, Bundle options, UserHandle user) {
+        throw new UnsupportedOperationException();
+    }
+
+    public static PendingIntent getActivities(Context context, int requestCode,
+            Intent[] intents, int flags, Bundle options) {
+        throw new UnsupportedOperationException();
+    }
+
+    public static PendingIntent getActivitiesAsUser(Context context, int requestCode,
+            Intent[] intents, int flags, Bundle options, UserHandle user) {
+        throw new UnsupportedOperationException();
+    }
+
+    public static PendingIntent getBroadcast(Context context, int requestCode,
+            Intent intent, int flags) {
+        throw new UnsupportedOperationException();
+    }
+
+    public static PendingIntent getBroadcastAsUser(Context context, int requestCode,
+            Intent intent, int flags, UserHandle userHandle) {
+        throw new UnsupportedOperationException();
+    }
+
+    public static PendingIntent getService(Context context, int requestCode,
+            Intent intent, int flags) {
+        throw new UnsupportedOperationException();
+    }
+
+    public static PendingIntent getForegroundService(Context context, int requestCode,
+            Intent intent, int flags) {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/media/java/android/media/tv/TvChannelInfo.aidl b/errorprone/tests/res/android/content/Intent.java
similarity index 83%
copy from media/java/android/media/tv/TvChannelInfo.aidl
copy to errorprone/tests/res/android/content/Intent.java
index 71cd0a7..9d22d04 100644
--- a/media/java/android/media/tv/TvChannelInfo.aidl
+++ b/errorprone/tests/res/android/content/Intent.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open 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,6 +14,7 @@
  * limitations under the License.
  */
 
-package android.media.tv;
+package android.content;
 
-parcelable TvChannelInfo;
+public class Intent {
+}
diff --git a/errorprone/tests/res/android/os/Binder.java b/errorprone/tests/res/android/os/Binder.java
index d388587..c969108 100644
--- a/errorprone/tests/res/android/os/Binder.java
+++ b/errorprone/tests/res/android/os/Binder.java
@@ -20,4 +20,12 @@
     public static int getCallingUid() {
         throw new UnsupportedOperationException();
     }
+
+    public static long clearCallingIdentity() {
+        throw new UnsupportedOperationException();
+    }
+
+    public static void restoreCallingIdentity(long token) {
+        throw new UnsupportedOperationException();
+    }
 }
diff --git a/media/java/android/media/tv/TvChannelInfo.aidl b/errorprone/tests/res/android/os/Bundle.java
similarity index 83%
copy from media/java/android/media/tv/TvChannelInfo.aidl
copy to errorprone/tests/res/android/os/Bundle.java
index 71cd0a7..6d2f7b8 100644
--- a/media/java/android/media/tv/TvChannelInfo.aidl
+++ b/errorprone/tests/res/android/os/Bundle.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open 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,6 +14,7 @@
  * limitations under the License.
  */
 
-package android.media.tv;
+package android.os;
 
-parcelable TvChannelInfo;
+public class Bundle {
+}
diff --git a/errorprone/tests/res/android/util/Preconditions.java b/errorprone/tests/res/android/util/Preconditions.java
new file mode 100644
index 0000000..558cdaf
--- /dev/null
+++ b/errorprone/tests/res/android/util/Preconditions.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.util;
+
+public class Preconditions {
+    public static void checkArgument(boolean expression) {
+        throw new UnsupportedOperationException();
+    }
+
+    public static void checkArgument(boolean expression, Object errorMessage) {
+        throw new UnsupportedOperationException();
+    }
+
+    public static <T> T checkNotNull(T reference) {
+        throw new UnsupportedOperationException();
+    }
+
+    public static <T> T checkNotNull(T reference, Object errorMessage) {
+        throw new UnsupportedOperationException();
+    }
+
+    public static void checkState(boolean expression) {
+        throw new UnsupportedOperationException();
+    }
+
+    public static void checkState(boolean expression, String message) {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java
index 4c0f890..94bfdc9 100644
--- a/graphics/java/android/graphics/BLASTBufferQueue.java
+++ b/graphics/java/android/graphics/BLASTBufferQueue.java
@@ -29,9 +29,10 @@
     private static native long nativeCreate(String name, long surfaceControl, long width,
                                             long height, boolean tripleBufferingEnabled);
     private static native void nativeDestroy(long ptr);
-    private static native Surface nativeGetSurface(long ptr);
+    private static native Surface nativeGetSurface(long ptr, boolean includeSurfaceControlHandle);
     private static native void nativeSetNextTransaction(long ptr, long transactionPtr);
     private static native void nativeUpdate(long ptr, long surfaceControl, long width, long height);
+    private static native void nativeFlushShadowQueue(long ptr);
 
     /** Create a new connection with the surface flinger. */
     public BLASTBufferQueue(String name, SurfaceControl sc, int width, int height,
@@ -48,7 +49,15 @@
      * @return a new Surface instance from the IGraphicsBufferProducer of the adapter.
      */
     public Surface createSurface() {
-        return nativeGetSurface(mNativeObject);
+        return nativeGetSurface(mNativeObject, false /* includeSurfaceControlHandle */);
+    }
+
+    /**
+     * @return a new Surface instance from the IGraphicsBufferProducer of the adapter and
+     * the SurfaceControl handle.
+     */
+    public Surface createSurfaceWithHandle() {
+        return nativeGetSurface(mNativeObject, true /* includeSurfaceControlHandle */);
     }
 
     public void setNextTransaction(SurfaceControl.Transaction t) {
@@ -69,4 +78,8 @@
             super.finalize();
         }
     }
+
+    public void flushShadowQueue() {
+        nativeFlushShadowQueue(mNativeObject);
+    }
 }
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index bee8d5e..54f9fa8 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -18,19 +18,27 @@
 
 import android.annotation.ColorInt;
 import android.annotation.ColorLong;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.Size;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.Canvas.VertexMode;
+import android.graphics.fonts.Font;
 import android.graphics.text.MeasuredText;
+import android.graphics.text.TextRunShaper;
 import android.text.GraphicsOperations;
 import android.text.MeasuredParagraph;
 import android.text.PrecomputedText;
 import android.text.SpannableString;
 import android.text.SpannedString;
+import android.text.TextShaper;
 import android.text.TextUtils;
 
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
 /**
  * This class is a base class for Canvas's drawing operations. Any modifications here
  * should be accompanied by a similar modification to {@link BaseRecordingCanvas}.
@@ -443,6 +451,58 @@
                 paint.getNativeInstance());
     }
 
+    /**
+     * Draw array of glyphs with specified font.
+     *
+     * @param glyphIds Array of glyph IDs. The length of array must be greater than or equal to
+     *                 {@code glyphStart + glyphCount}.
+     * @param glyphIdOffset Number of elements to skip before drawing in <code>glyphIds</code>
+     *                     array.
+     * @param positions A flattened X and Y position array. The first glyph X position must be
+     *                  stored at {@code positionOffset}. The first glyph Y position must be stored
+     *                  at {@code positionOffset + 1}, then the second glyph X position must be
+     *                  stored at {@code positionOffset + 2}.
+     *                 The length of array must be greater than or equal to
+     *                 {@code positionOffset + glyphCount * 2}.
+     * @param positionOffset Number of elements to skip before drawing in {@code positions}.
+     *                       The first glyph X position must be stored at {@code positionOffset}.
+     *                       The first glyph Y position must be stored at
+     *                       {@code positionOffset + 1}, then the second glyph X position must be
+     *                       stored at {@code positionOffset + 2}.
+     * @param glyphCount Number of glyphs to be drawn.
+     * @param font Font used for drawing.
+     * @param paint Paint used for drawing. The typeface set to this paint is ignored.
+     *
+     * @see TextRunShaper
+     * @see TextShaper
+     */
+    public void drawGlyphs(
+            @NonNull int[] glyphIds,
+            @IntRange(from = 0) int glyphIdOffset,
+            @NonNull float[] positions,
+            @IntRange(from = 0) int positionOffset,
+            @IntRange(from = 0) int glyphCount,
+            @NonNull Font font,
+            @NonNull Paint paint) {
+        Objects.requireNonNull(glyphIds, "glyphIds must not be null.");
+        Objects.requireNonNull(positions, "positions must not be null.");
+        Objects.requireNonNull(font, "font must not be null.");
+        Objects.requireNonNull(paint, "paint must not be null.");
+        Preconditions.checkArgumentNonnegative(glyphCount);
+
+        if (glyphIdOffset < 0 || glyphIdOffset + glyphCount > glyphIds.length) {
+            throw new IndexOutOfBoundsException(
+                    "glyphIds must have at least " + (glyphIdOffset + glyphCount) + " of elements");
+        }
+        if (positionOffset < 0 || positionOffset + glyphCount * 2 > positions.length) {
+            throw new IndexOutOfBoundsException(
+                    "positions must have at least " + (positionOffset + glyphCount * 2)
+                            + " of elements");
+        }
+        nDrawGlyphs(mNativeCanvasWrapper, glyphIds, positions, glyphIdOffset, positionOffset,
+                glyphCount, font.getNativePtr(), paint.getNativeInstance());
+    }
+
     public void drawText(@NonNull char[] text, int index, int count, float x, float y,
             @NonNull Paint paint) {
         if ((index | count | (index + count) |
@@ -734,6 +794,9 @@
             int vertOffset, float[] texs, int texOffset, int[] colors, int colorOffset,
             short[] indices, int indexOffset, int indexCount, long nativePaint);
 
+    private static native void nDrawGlyphs(long nativeCanvas, int[] glyphIds, float[] positions,
+            int glyphIdStart, int positionStart, int glyphCount, long nativeFont, long nativePaint);
+
     private static native void nDrawText(long nativeCanvas, char[] text, int index, int count,
             float x, float y, int flags, long nativePaint);
 
diff --git a/graphics/java/android/graphics/BaseRecordingCanvas.java b/graphics/java/android/graphics/BaseRecordingCanvas.java
index 2f5214cb1..a8922e8 100644
--- a/graphics/java/android/graphics/BaseRecordingCanvas.java
+++ b/graphics/java/android/graphics/BaseRecordingCanvas.java
@@ -18,9 +18,11 @@
 
 import android.annotation.ColorInt;
 import android.annotation.ColorLong;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.Size;
+import android.graphics.fonts.Font;
 import android.graphics.text.MeasuredText;
 import android.text.GraphicsOperations;
 import android.text.MeasuredParagraph;
@@ -29,8 +31,12 @@
 import android.text.SpannedString;
 import android.text.TextUtils;
 
+import com.android.internal.util.Preconditions;
+
 import dalvik.annotation.optimization.FastNative;
 
+import java.util.Objects;
+
 /**
  * This class is a base class for canvases that defer drawing operations, so all
  * the draw operations can be marked @FastNative. It contains a re-implementation of
@@ -409,6 +415,34 @@
     }
 
     @Override
+    public void drawGlyphs(
+            @NonNull int[] glyphIds,
+            @IntRange(from = 0) int glyphIdOffset,
+            @NonNull float[] positions,
+            @IntRange(from = 0) int positionOffset,
+            @IntRange(from = 0) int glyphCount,
+            @NonNull Font font,
+            @NonNull Paint paint) {
+        Objects.requireNonNull(glyphIds, "glyphIds must not be null.");
+        Objects.requireNonNull(positions, "positions must not be null.");
+        Objects.requireNonNull(font, "font must not be null.");
+        Objects.requireNonNull(paint, "paint must not be null.");
+        Preconditions.checkArgumentNonnegative(glyphCount);
+
+        if (glyphIdOffset < 0 || glyphIdOffset + glyphCount > glyphIds.length) {
+            throw new IndexOutOfBoundsException(
+                    "glyphIds must have at least " + (glyphIdOffset + glyphCount) + " of elements");
+        }
+        if (positionOffset < 0 || positionOffset + glyphCount * 2 > positions.length) {
+            throw new IndexOutOfBoundsException(
+                    "positions must have at least " + (positionOffset + glyphCount * 2)
+                            + " of elements");
+        }
+        nDrawGlyphs(mNativeCanvasWrapper, glyphIds, positions, glyphIdOffset, positionOffset,
+                glyphCount, font.getNativePtr(), paint.getNativeInstance());
+    }
+
+    @Override
     public final void drawText(@NonNull char[] text, int index, int count, float x, float y,
             @NonNull Paint paint) {
         if ((index | count | (index + count)
@@ -674,6 +708,10 @@
             short[] indices, int indexOffset, int indexCount, long nativePaint);
 
     @FastNative
+    private static native void nDrawGlyphs(long nativeCanvas, int[] glyphIds, float[] positions,
+            int glyphIdStart, int positionStart, int glyphCount, long nativeFont, long nativePaint);
+
+    @FastNative
     private static native void nDrawText(long nativeCanvas, char[] text, int index, int count,
             float x, float y, int flags, long nativePaint);
 
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 559571c..829d0f4 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -19,12 +19,16 @@
 import android.annotation.ColorInt;
 import android.annotation.ColorLong;
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.Size;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.graphics.fonts.Font;
 import android.graphics.text.MeasuredText;
+import android.graphics.text.TextRunShaper;
 import android.os.Build;
+import android.text.TextShaper;
 
 import dalvik.annotation.optimization.CriticalNative;
 import dalvik.annotation.optimization.FastNative;
@@ -2048,6 +2052,43 @@
     }
 
     /**
+     * Draw array of glyphs with specified font.
+     *
+     * @param glyphIds Array of glyph IDs. The length of array must be greater than or equal to
+     *                 {@code glyphIdOffset + glyphCount}.
+     * @param glyphIdOffset Number of elements to skip before drawing in <code>glyphIds</code>
+     *                     array.
+     * @param positions A flattened X and Y position array. The first glyph X position must be
+     *                  stored at {@code positionOffset}. The first glyph Y position must be stored
+     *                  at {@code positionOffset + 1}, then the second glyph X position must be
+     *                  stored at {@code positionOffset + 2}.
+     *                 The length of array must be greater than or equal to
+     *                 {@code positionOffset + glyphCount * 2}.
+     * @param positionOffset Number of elements to skip before drawing in {@code positions}.
+     *                       The first glyph X position must be stored at {@code positionOffset}.
+     *                       The first glyph Y position must be stored at
+     *                       {@code positionOffset + 1}, then the second glyph X position must be
+     *                       stored at {@code positionOffset + 2}.
+     * @param glyphCount Number of glyphs to be drawn.
+     * @param font Font used for drawing.
+     * @param paint Paint used for drawing. The typeface set to this paint is ignored.
+     *
+     * @see TextRunShaper
+     * @see TextShaper
+     */
+    public void drawGlyphs(
+            @NonNull int[] glyphIds,
+            @IntRange(from = 0) int glyphIdOffset,
+            @NonNull float[] positions,
+            @IntRange(from = 0) int positionOffset,
+            @IntRange(from = 0) int glyphCount,
+            @NonNull Font font,
+            @NonNull Paint paint) {
+        super.drawGlyphs(glyphIds, glyphIdOffset, positions, positionOffset, glyphCount, font,
+                paint);
+    }
+
+    /**
      * Draw the text, with origin at (x,y), using the specified paint. The origin is interpreted
      * based on the Align setting in the paint.
      *
diff --git a/graphics/java/android/graphics/FrameInfo.java b/graphics/java/android/graphics/FrameInfo.java
index f768bc7..0061ea1 100644
--- a/graphics/java/android/graphics/FrameInfo.java
+++ b/graphics/java/android/graphics/FrameInfo.java
@@ -40,7 +40,7 @@
  */
 public final class FrameInfo {
 
-    public long[] frameInfo = new long[10];
+    public long[] frameInfo = new long[FRAME_INFO_SIZE];
 
     // Various flags set to provide extra metadata about the current frame
     private static final int FLAGS = 0;
@@ -87,14 +87,22 @@
     // When View:draw() started
     private static final int DRAW_START = 9;
 
+    // When the frame needs to be ready by
+    private static final int FRAME_DEADLINE = 10;
+
+    // Must be the last one
+    private static final int FRAME_INFO_SIZE = FRAME_DEADLINE + 1;
+
     /** checkstyle */
-    public void setVsync(long intendedVsync, long usedVsync, long frameTimelineVsyncId) {
+    public void setVsync(long intendedVsync, long usedVsync, long frameTimelineVsyncId,
+            long frameDeadline) {
         frameInfo[FRAME_TIMELINE_VSYNC_ID] = frameTimelineVsyncId;
         frameInfo[INTENDED_VSYNC] = intendedVsync;
         frameInfo[VSYNC] = usedVsync;
         frameInfo[OLDEST_INPUT_EVENT] = Long.MAX_VALUE;
         frameInfo[NEWEST_INPUT_EVENT] = 0;
         frameInfo[FLAGS] = 0;
+        frameInfo[FRAME_DEADLINE] = frameDeadline;
     }
 
     /** checkstyle */
diff --git a/graphics/java/android/graphics/GraphicsStatsService.java b/graphics/java/android/graphics/GraphicsStatsService.java
index 2d6848b..dc785c5 100644
--- a/graphics/java/android/graphics/GraphicsStatsService.java
+++ b/graphics/java/android/graphics/GraphicsStatsService.java
@@ -175,7 +175,7 @@
         int uid = Binder.getCallingUid();
         int pid = Binder.getCallingPid();
         ParcelFileDescriptor pfd = null;
-        long callingIdentity = Binder.clearCallingIdentity();
+        final long callingIdentity = Binder.clearCallingIdentity();
         try {
             mAppOps.checkPackage(uid, packageName);
             PackageInfo info = mContext.getPackageManager().getPackageInfoAsUser(
@@ -214,7 +214,7 @@
             }
         }
 
-        long callingIdentity = Binder.clearCallingIdentity();
+        final long callingIdentity = Binder.clearCallingIdentity();
         try {
             pullGraphicsStatsImpl(lastFullDay, pulledData);
         } finally {
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index fd5916c..a7f2739 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -355,7 +355,7 @@
          */
         public @NonNull FrameRenderRequest setVsyncTime(long vsyncTime) {
             // TODO(b/168552873): populate vsync Id once available to Choreographer public API
-            mFrameInfo.setVsync(vsyncTime, vsyncTime, FrameInfo.INVALID_VSYNC_ID);
+            mFrameInfo.setVsync(vsyncTime, vsyncTime, FrameInfo.INVALID_VSYNC_ID, Long.MAX_VALUE);
             mFrameInfo.addFlags(FrameInfo.FLAG_SURFACE_CANVAS);
             return this;
         }
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 28d7911..a191fe5 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -115,6 +115,21 @@
         Align.LEFT, Align.CENTER, Align.RIGHT
     };
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, value = {
+            ANTI_ALIAS_FLAG,
+            FILTER_BITMAP_FLAG,
+            DITHER_FLAG,
+            UNDERLINE_TEXT_FLAG,
+            STRIKE_THRU_TEXT_FLAG,
+            FAKE_BOLD_TEXT_FLAG,
+            LINEAR_TEXT_FLAG,
+            SUBPIXEL_TEXT_FLAG,
+            EMBEDDED_BITMAP_TEXT_FLAG
+    })
+    public @interface PaintFlag{}
+
     /**
      * Paint flag that enables antialiasing when drawing.
      *
@@ -724,7 +739,7 @@
      *
      * @return the paint's flags (see enums ending in _Flag for bit masks)
      */
-    public int getFlags() {
+    public @PaintFlag int getFlags() {
         return nGetFlags(mNativePaint);
     }
 
@@ -733,7 +748,7 @@
      *
      * @param flags The new flag bits for the paint
      */
-    public void setFlags(int flags) {
+    public void setFlags(@PaintFlag int flags) {
         nSetFlags(mNativePaint, flags);
     }
 
diff --git a/graphics/java/android/graphics/ParcelableColorSpace.java b/graphics/java/android/graphics/ParcelableColorSpace.java
index d408ac3..3260849 100644
--- a/graphics/java/android/graphics/ParcelableColorSpace.java
+++ b/graphics/java/android/graphics/ParcelableColorSpace.java
@@ -73,6 +73,9 @@
         }
     }
 
+    /**
+     * @return the backing ColorSpace that this ParcelableColorSpace is wrapping.
+     */
     public @NonNull ColorSpace getColorSpace() {
         return mColorSpace;
     }
diff --git a/graphics/java/android/graphics/RuntimeShader.java b/graphics/java/android/graphics/RuntimeShader.java
index 5a0b4a9..63089e2 100644
--- a/graphics/java/android/graphics/RuntimeShader.java
+++ b/graphics/java/android/graphics/RuntimeShader.java
@@ -34,6 +34,7 @@
     }
 
     private byte[] mUniforms;
+    private Shader[] mInputShaders;
     private boolean mIsOpaque;
 
     /**
@@ -50,13 +51,30 @@
      * @param isOpaque True if all pixels have alpha 1.0f.
      */
     public RuntimeShader(@NonNull String sksl, @Nullable byte[] uniforms, boolean isOpaque) {
-        this(sksl, uniforms, isOpaque, ColorSpace.get(ColorSpace.Named.SRGB));
+        this(sksl, uniforms, null, isOpaque, ColorSpace.get(ColorSpace.Named.SRGB));
     }
 
-    private RuntimeShader(@NonNull String sksl, @Nullable byte[] uniforms, boolean isOpaque,
-            ColorSpace colorSpace) {
+    /**
+     * Creates a new RuntimeShader.
+     *
+     * @param sksl The text of SKSL program to run on the GPU.
+     * @param uniforms Array of parameters passed by the SKSL shader. Array size depends
+     *                 on number of uniforms declared by sksl.
+     * @param shaderInputs Array of shaders passed to the SKSL shader. Array size depends
+     *                     on the number of input shaders declared in the sksl
+     * @param isOpaque True if all pixels have alpha 1.0f.
+     */
+    public  RuntimeShader(@NonNull String sksl, @Nullable byte[] uniforms,
+                          @Nullable Shader[] shaderInputs, boolean isOpaque) {
+        this(sksl, uniforms, shaderInputs, isOpaque, ColorSpace.get(ColorSpace.Named.SRGB));
+    }
+
+    private RuntimeShader(@NonNull String sksl, @Nullable byte[] uniforms,
+                          @Nullable Shader[] shaderInputs, boolean isOpaque,
+                          ColorSpace colorSpace) {
         super(colorSpace);
         mUniforms = uniforms;
+        mInputShaders = shaderInputs;
         mIsOpaque = isOpaque;
         mNativeInstanceRuntimeShaderFactory = nativeCreateShaderFactory(sksl);
         NoImagePreloadHolder.sRegistry.registerNativeAllocation(this,
@@ -74,15 +92,31 @@
         discardNativeInstance();
     }
 
+    /**
+     * Sets new values for the shaders that serve as inputs to this shader.
+     *
+     * @param shaderInputs Array of Shaders passed into the SKSL shader. Array size depends
+     *                     on number of input shaders declared by sksl.
+     */
+    public void updateInputShaders(@Nullable Shader[] shaderInputs) {
+        mInputShaders = shaderInputs;
+        discardNativeInstance();
+    }
+
     /** @hide */
     @Override
     protected long createNativeInstance(long nativeMatrix) {
+        long[] nativeShaders = mInputShaders.length > 0 ? new long[mInputShaders.length] : null;
+        for (int i = 0; i < mInputShaders.length; i++) {
+            nativeShaders[i] = mInputShaders[i].getNativeInstance();
+        }
+
         return nativeCreate(mNativeInstanceRuntimeShaderFactory, nativeMatrix, mUniforms,
-                colorSpace().getNativeInstance(), mIsOpaque);
+                nativeShaders, colorSpace().getNativeInstance(), mIsOpaque);
     }
 
     private static native long nativeCreate(long shaderFactory, long matrix, byte[] inputs,
-            long colorSpaceHandle, boolean isOpaque);
+            long[] shaderInputs, long colorSpaceHandle, boolean isOpaque);
 
     private static native long nativeCreateShaderFactory(String sksl);
 
diff --git a/graphics/java/android/graphics/Shader.java b/graphics/java/android/graphics/Shader.java
index d71ff11..7651d01 100644
--- a/graphics/java/android/graphics/Shader.java
+++ b/graphics/java/android/graphics/Shader.java
@@ -83,21 +83,22 @@
 
     public enum TileMode {
         /**
-         * replicate the edge color if the shader draws outside of its
-         * original bounds
+         * Replicate the edge color if the shader draws outside of its
+         * original bounds.
          */
         CLAMP   (0),
         /**
-         * repeat the shader's image horizontally and vertically
+         * Repeat the shader's image horizontally and vertically.
          */
         REPEAT  (1),
         /**
-         * repeat the shader's image horizontally and vertically, alternating
-         * mirror images so that adjacent images always seam
+         * Repeat the shader's image horizontally and vertically, alternating
+         * mirror images so that adjacent images always seam.
          */
         MIRROR(2),
         /**
-         * Only draw within the original domain, return transparent-black everywhere else
+         * Render the shader's image pixels only within its original bounds. If the shader
+         * draws outside of its original bounds, transparent black is drawn instead.
          */
         DECAL(3);
 
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index a2dd9a8..ecb0ff4 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -768,7 +768,7 @@
         public @NonNull CustomFallbackBuilder addCustomFallback(@NonNull FontFamily family) {
             Preconditions.checkNotNull(family);
             Preconditions.checkArgument(mFamilies.size() < getMaxCustomFallbackCount(),
-                    "Custom fallback limit exceeded(" + getMaxCustomFallbackCount() + ")");
+                    "Custom fallback limit exceeded(%d)", getMaxCustomFallbackCount());
             mFamilies.add(family);
             return this;
         }
diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java
index 90412f4..abf0e8a 100644
--- a/graphics/java/android/graphics/drawable/Icon.java
+++ b/graphics/java/android/graphics/drawable/Icon.java
@@ -39,6 +39,7 @@
 import android.os.Message;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -441,10 +442,11 @@
                 resPackage = context.getPackageName();
             }
             if (getResources() == null && !(getResPackage().equals("android"))) {
-                final PackageManager pm = context.getPackageManager();
+                final PackageManager pm = context.createContextAsUser(
+                        UserHandle.of(userId), /* flags */ 0).getPackageManager();
                 try {
                     // assign getResources() as the correct user
-                    mObj1 = pm.getResourcesForApplicationAsUser(resPackage, userId);
+                    mObj1 = pm.getResourcesForApplication(resPackage);
                 } catch (PackageManager.NameNotFoundException e) {
                     Log.e(TAG, String.format("Unable to find pkg=%s user=%d",
                                     getResPackage(),
diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java
index 21b8fc6..586c512 100644
--- a/graphics/java/android/graphics/fonts/Font.java
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -523,6 +523,9 @@
     /**
      * Returns a font file buffer.
      *
+     * Duplicate before reading values by {@link ByteBuffer#duplicate()} for avoiding unexpected
+     * reading position sharing.
+     *
      * @return a font buffer
      */
     public @NonNull ByteBuffer getBuffer() {
@@ -628,18 +631,49 @@
         if (o == this) {
             return true;
         }
-        if (o == null || !(o instanceof Font)) {
+        if (!(o instanceof Font)) {
             return false;
         }
         Font f = (Font) o;
-        return mFontStyle.equals(f.mFontStyle) && f.mTtcIndex == mTtcIndex
-                && Arrays.equals(f.mAxes, mAxes) && f.mBuffer.equals(mBuffer)
-                && Objects.equals(f.mLocaleList, mLocaleList);
+        boolean paramEqual = mFontStyle.equals(f.mFontStyle) && f.mTtcIndex == mTtcIndex
+                && Arrays.equals(f.mAxes, mAxes) && Objects.equals(f.mLocaleList, mLocaleList)
+                && Objects.equals(mFile, f.mFile);
+
+        if (!paramEqual) {
+            return false;
+        }
+
+        // Shortcut for different font buffer check by comparing size.
+        if (mBuffer.capacity() != f.mBuffer.capacity()) {
+            return false;
+        }
+
+        // ByteBuffer#equals compares all bytes which is not performant for e.g HashMap. Since
+        // underlying native font object holds buffer address, check if this buffer points exactly
+        // the same address as a shortcut of equality. For being compatible with of API30 or before,
+        // check buffer position even if the buffer points the same address.
+        if (nIsSameBufferAddress(mNativePtr, f.mNativePtr)
+                && mBuffer.position() == f.mBuffer.position()) {
+            return true;
+        }
+
+        // Unfortunately, need to compare bytes one-by-one since the buffer may be different font
+        // file but has the same file size, or two font has same content but they are allocated
+        // differently. For being compatible with API30 ore before, compare with ByteBuffer#equals.
+        return mBuffer.equals(f.mBuffer);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mFontStyle, mTtcIndex, Arrays.hashCode(mAxes), mBuffer, mLocaleList);
+        return Objects.hash(
+                mFontStyle,
+                mTtcIndex,
+                Arrays.hashCode(mAxes),
+                // Use Buffer size instead of ByteBuffer#hashCode since ByteBuffer#hashCode traverse
+                // data which is not performant e.g. for HashMap. The hash collision are less likely
+                // happens because it is unlikely happens the different font files has exactly the
+                // same size.
+                mLocaleList);
     }
 
     @Override
@@ -687,7 +721,9 @@
                 charBuffer[3] = (char) ((packedAxis & 0x0000_00FF_0000_0000L) >> 32);
                 axes[i] = new FontVariationAxis(new String(charBuffer), value);
             }
-            Font.Builder builder = new Font.Builder(buffer)
+            String path = nGetFontPath(ptr);
+            File file = (path == null) ? null : new File(path);
+            Font.Builder builder = new Font.Builder(buffer, file, "")
                     .setWeight(weight)
                     .setSlant(italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT)
                     .setTtcIndex(ttcIndex)
@@ -712,6 +748,9 @@
     private static native long nGetAxisInfo(long ptr, int i);
 
     @FastNative
+    private static native String nGetFontPath(long ptr);
+
+    @FastNative
     private static native float nGetGlyphBounds(long font, int glyphId, long paint, RectF rect);
 
     @FastNative
@@ -719,4 +758,7 @@
 
     @CriticalNative
     private static native long nGetNativeFontPtr(long ptr);
+
+    @CriticalNative
+    private static native boolean nIsSameBufferAddress(long lFontPtr, long rFontPtr);
 }
diff --git a/graphics/java/android/graphics/text/GlyphStyle.java b/graphics/java/android/graphics/text/GlyphStyle.java
deleted file mode 100644
index cc8c4d2..0000000
--- a/graphics/java/android/graphics/text/GlyphStyle.java
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics.text;
-
-import android.annotation.ColorInt;
-import android.annotation.FloatRange;
-import android.annotation.NonNull;
-import android.graphics.Paint;
-
-import java.util.Objects;
-
-/**
- * Represents subset of Paint parameters such as font size, scaleX that is used to draw a glyph.
- *
- * Glyph is a most primitive unit of text drawing.
- *
- */
-public class GlyphStyle {
-    private @ColorInt int mColor;
-    private float mFontSize;
-    private float mScaleX;
-    private float mSkewX;
-    private int mFlags;
-
-    /**
-     * @param color a color.
-     * @param fontSize a font size in pixels.
-     * @param scaleX a horizontal scale factor.
-     * @param skewX a horizontal skew factor
-     * @param flags paint flags
-     *
-     * @see Paint#getFlags()
-     * @see Paint#setFlags(int)
-     */
-    public GlyphStyle(
-            @ColorInt int color,
-            @FloatRange(from = 0) float fontSize,
-            @FloatRange(from = 0) float scaleX,
-            @FloatRange(from = 0) float skewX,
-            int flags) {
-        mColor = color;
-        mFontSize = fontSize;
-        mScaleX = scaleX;
-        mSkewX = skewX;
-        mFlags = flags;
-    }
-
-    /**
-     * Create glyph style from Paint
-     *
-     * @param paint a paint
-     */
-    public GlyphStyle(@NonNull Paint paint) {
-        setFromPaint(paint);
-    }
-
-    /**
-     * Gets the color.
-     *
-     * @return a color
-     * @see Paint#getColor()
-     * @see Paint#setColor(int)
-     */
-    public @ColorInt int getColor() {
-        return mColor;
-    }
-
-    /**
-     * Sets the color.
-     *
-     * @param color a color
-     * @see Paint#getColor()
-     * @see Paint#setColor(int)
-     */
-    public void setColor(@ColorInt int color) {
-        mColor = color;
-    }
-
-    /**
-     * Gets the font size in pixels.
-     *
-     * @return font size
-     * @see Paint#getTextSize()
-     * @see Paint#setTextSize(float)
-     */
-    public @FloatRange(from = 0) float getFontSize() {
-        return mFontSize;
-    }
-
-    /**
-     * Sets the font size in pixels.
-     *
-     * @param fontSize font size in pixel
-     * @see Paint#getTextSize()
-     * @see Paint#setTextSize(float)
-     */
-    public void setFontSize(@FloatRange(from = 0) float fontSize) {
-        mFontSize = fontSize;
-    }
-
-    /**
-     * Return the horizontal scale factor
-     *
-     * @return a horizontal scale factor
-     * @see Paint#getTextScaleX()
-     * @see Paint#setTextScaleX(float)
-     */
-    public @FloatRange(from = 0) float getScaleX() {
-        return mScaleX;
-    }
-
-    /**
-     * Set the horizontal scale factor
-     *
-     * @param scaleX a horizontal scale factor
-     * @see Paint#getTextScaleX()
-     * @see Paint#setTextScaleX(float)
-     */
-    public void setScaleX(@FloatRange(from = 0) float scaleX) {
-        mScaleX = scaleX;
-    }
-
-    /**
-     * Return the horizontal skew factor
-     *
-     * @return a horizontal skew factor
-     * @see Paint#getTextSkewX()
-     * @see Paint#setTextSkewX(float)
-     */
-    public @FloatRange(from = 0) float getSkewX() {
-        return mSkewX;
-    }
-
-    /**
-     * Set the horizontal skew factor
-     *
-     * @param skewX a horizontal skew factor
-     * @see Paint#getTextSkewX()
-     * @see Paint#setTextSkewX(float)
-     */
-    public void setSkewX(@FloatRange(from = 0) float skewX) {
-        mSkewX = skewX;
-    }
-
-    /**
-     * Returns the Paint flags.
-     *
-     * @return a paint flags
-     * @see Paint#getFlags()
-     * @see Paint#setFlags(int)
-     */
-    public int getFlags() {
-        return mFlags;
-    }
-
-    /**
-     * Set the Paint flags.
-     *
-     * @param flags a paint flags
-     * @see Paint#getFlags()
-     * @see Paint#setFlags(int)
-     */
-    public void setFlags(int flags) {
-        mFlags = flags;
-    }
-
-    /**
-     * Applies glyph style to the paint object.
-     *
-     * @param paint a paint object
-     */
-    public void applyToPaint(@NonNull Paint paint) {
-        paint.setColor(mColor);
-        paint.setTextSize(mFontSize);
-        paint.setTextScaleX(mScaleX);
-        paint.setTextSkewX(mSkewX);
-        paint.setFlags(mFlags);
-    }
-
-    /**
-     * Copy parameters from a Paint object.
-     *
-     * @param paint a paint object
-     */
-    public void setFromPaint(@NonNull Paint paint) {
-        mColor = paint.getColor();
-        mFontSize = paint.getTextSize();
-        mScaleX = paint.getTextScaleX();
-        mSkewX = paint.getTextSkewX();
-        mFlags = paint.getFlags();
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (!(o instanceof GlyphStyle)) return false;
-        GlyphStyle that = (GlyphStyle) o;
-        return that.mColor == mColor
-                && Float.compare(that.mFontSize, mFontSize) == 0
-                && Float.compare(that.mScaleX, mScaleX) == 0
-                && Float.compare(that.mSkewX, mSkewX) == 0
-                && mFlags == that.mFlags;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(mColor, mFontSize, mScaleX, mSkewX, mFlags);
-    }
-
-    @Override
-    public String toString() {
-        return "GlyphStyle{"
-                + "mColor=" + mColor
-                + ", mFontSize=" + mFontSize
-                + ", mScaleX=" + mScaleX
-                + ", mSkewX=" + mSkewX
-                + ", mFlags=" + mFlags
-                + '}';
-    }
-}
diff --git a/graphics/java/android/graphics/text/MeasuredText.java b/graphics/java/android/graphics/text/MeasuredText.java
index b6d8fa1..31c3d09 100644
--- a/graphics/java/android/graphics/text/MeasuredText.java
+++ b/graphics/java/android/graphics/text/MeasuredText.java
@@ -80,11 +80,11 @@
     public @FloatRange(from = 0.0) @Px float getWidth(
             @IntRange(from = 0) int start, @IntRange(from = 0) int end) {
         Preconditions.checkArgument(0 <= start && start <= mChars.length,
-                "start(" + start + ") must be 0 <= start <= " + mChars.length);
+                "start(%d) must be 0 <= start <= %d", start, mChars.length);
         Preconditions.checkArgument(0 <= end && end <= mChars.length,
-                "end(" + end + ") must be 0 <= end <= " + mChars.length);
+                "end(%d) must be 0 <= end <= %d", end, mChars.length);
         Preconditions.checkArgument(start <= end,
-                "start(" + start + ") is larger than end(" + end + ")");
+                "start(%d) is larger than end(%d)", start, end);
         return nGetWidth(mNativePtr, start, end);
     }
 
@@ -107,11 +107,11 @@
     public void getBounds(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
             @NonNull Rect rect) {
         Preconditions.checkArgument(0 <= start && start <= mChars.length,
-                "start(" + start + ") must be 0 <= start <= " + mChars.length);
+                "start(%d) must be 0 <= start <= %d", start, mChars.length);
         Preconditions.checkArgument(0 <= end && end <= mChars.length,
-                "end(" + end + ") must be 0 <= end <= " + mChars.length);
+                "end(%d) must be 0 <= end <= %d", end, mChars.length);
         Preconditions.checkArgument(start <= end,
-                "start(" + start + ") is larger than end(" + end + ")");
+                "start(%d) is larger than end(%d)", start, end);
         Preconditions.checkNotNull(rect);
         nGetBounds(mNativePtr, mChars, start, end, rect);
     }
@@ -123,7 +123,7 @@
      */
     public @FloatRange(from = 0.0f) @Px float getCharWidthAt(@IntRange(from = 0) int offset) {
         Preconditions.checkArgument(0 <= offset && offset < mChars.length,
-                "offset(" + offset + ") is larger than text length: " + mChars.length);
+                "offset(%d) is larger than text length %d" + offset, mChars.length);
         return nGetCharWidthAt(mNativePtr, offset);
     }
 
diff --git a/graphics/java/android/graphics/text/PositionedGlyphs.java b/graphics/java/android/graphics/text/PositionedGlyphs.java
index 7364d54..c2de0ac 100644
--- a/graphics/java/android/graphics/text/PositionedGlyphs.java
+++ b/graphics/java/android/graphics/text/PositionedGlyphs.java
@@ -35,11 +35,12 @@
  * Text shaping result object for single style text.
  *
  * You can get text shaping result by
- * {@link TextShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint)} and
- * {@link TextShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)}.
+ * {@link TextRunShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint)} and
+ * {@link TextRunShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean,
+ * Paint)}.
  *
- * @see TextShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint)
- * @see TextShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)
+ * @see TextRunShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint)
+ * @see TextRunShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)
  */
 public final class PositionedGlyphs {
     private static final NativeAllocationRegistry REGISTRY =
@@ -49,7 +50,6 @@
     private final long mLayoutPtr;
     private final float mXOffset;
     private final float mYOffset;
-    private final GlyphStyle mGlyphStyle;
     private final ArrayList<Font> mFonts;
 
     /**
@@ -62,7 +62,7 @@
      *
      * @return total amount of advance
      */
-    public float getTotalAdvance() {
+    public float getAdvance() {
         return nGetTotalAdvance(mLayoutPtr);
     }
 
@@ -91,21 +91,11 @@
     }
 
     /**
-     * Returns the glyph style used for drawing the glyph at the given index.
-     *
-     * @return A glyph style
-     */
-    @NonNull
-    public GlyphStyle getStyle() {
-        return mGlyphStyle;
-    }
-
-    /**
      * Returns the amount of X offset added to glyph position.
      *
      * @return The X offset added to glyph position.
      */
-    public float getOriginX() {
+    public float getOffsetX() {
         return mXOffset;
     }
 
@@ -114,7 +104,7 @@
      *
      * @return The Y offset added to glyph position.
      */
-    public float getOriginY() {
+    public float getOffsetY() {
         return mYOffset;
     }
 
@@ -144,7 +134,7 @@
      * Returns the glyph ID used for drawing the glyph at the given index.
      *
      * @param index the glyph index
-     * @return A font object
+     * @return An glyph ID of the font.
      */
     @IntRange(from = 0)
     public int getGlyphId(@IntRange(from = 0) int index) {
@@ -158,7 +148,7 @@
      * @param index the glyph index
      * @return A X offset in pixels
      */
-    public float getPositionX(@IntRange(from = 0) int index) {
+    public float getGlyphX(@IntRange(from = 0) int index) {
         Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index");
         return nGetX(mLayoutPtr, index) + mXOffset;
     }
@@ -169,7 +159,7 @@
      * @param index the glyph index
      * @return A Y offset in pixels.
      */
-    public float getPositionY(@IntRange(from = 0) int index) {
+    public float getGlyphY(@IntRange(from = 0) int index) {
         Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index");
         return nGetY(mLayoutPtr, index) + mYOffset;
     }
@@ -180,11 +170,9 @@
      * @hide
      *
      * @param layoutPtr the address of native layout object.
-     * @param paint a paint object
      */
-    public PositionedGlyphs(long layoutPtr, @NonNull Paint paint, float xOffset, float yOffset) {
+    public PositionedGlyphs(long layoutPtr, float xOffset, float yOffset) {
         mLayoutPtr = layoutPtr;
-        mGlyphStyle = new GlyphStyle(paint);
         int glyphCount = nGetGlyphCount(layoutPtr);
         mFonts = new ArrayList<>(glyphCount);
         mXOffset = xOffset;
@@ -229,14 +217,13 @@
         if (!(o instanceof PositionedGlyphs)) return false;
         PositionedGlyphs that = (PositionedGlyphs) o;
 
-        if (!mGlyphStyle.equals(that.mGlyphStyle)) return false;
         if (mXOffset != that.mXOffset || mYOffset != that.mYOffset) return false;
         if (glyphCount() != that.glyphCount()) return false;
 
         for (int i = 0; i < glyphCount(); ++i) {
             if (getGlyphId(i) != that.getGlyphId(i)) return false;
-            if (getPositionX(i) != that.getPositionX(i)) return false;
-            if (getPositionY(i) != that.getPositionY(i)) return false;
+            if (getGlyphX(i) != that.getGlyphX(i)) return false;
+            if (getGlyphY(i) != that.getGlyphY(i)) return false;
             // Intentionally using reference equality since font equality is heavy due to buffer
             // compare.
             if (getFont(i) != that.getFont(i)) return false;
@@ -247,10 +234,10 @@
 
     @Override
     public int hashCode() {
-        int hashCode = Objects.hash(mXOffset, mYOffset, mGlyphStyle);
+        int hashCode = Objects.hash(mXOffset, mYOffset);
         for (int i = 0; i < glyphCount(); ++i) {
             hashCode = Objects.hash(hashCode,
-                    getGlyphId(i), getPositionX(i), getPositionY(i), getFont(i));
+                    getGlyphId(i), getGlyphX(i), getGlyphY(i), getFont(i));
         }
         return hashCode;
     }
@@ -263,7 +250,7 @@
                 sb.append(", ");
             }
             sb.append("[ ID = " + getGlyphId(i) + ","
-                    + " pos = (" + getPositionX(i) + "," + getPositionY(i) + ")"
+                    + " pos = (" + getGlyphX(i) + "," + getGlyphY(i) + ")"
                     + " font = " + getFont(i) + " ]");
         }
         sb.append("]");
@@ -271,7 +258,6 @@
                 + "glyphs = " + sb.toString()
                 + ", mXOffset=" + mXOffset
                 + ", mYOffset=" + mYOffset
-                + ", mGlyphStyle=" + mGlyphStyle
                 + '}';
     }
 }
diff --git a/graphics/java/android/graphics/text/TextShaper.java b/graphics/java/android/graphics/text/TextRunShaper.java
similarity index 86%
rename from graphics/java/android/graphics/text/TextShaper.java
rename to graphics/java/android/graphics/text/TextRunShaper.java
index f40ed8f..8459e7b 100644
--- a/graphics/java/android/graphics/text/TextShaper.java
+++ b/graphics/java/android/graphics/text/TextRunShaper.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.graphics.Paint;
 import android.text.TextDirectionHeuristic;
+import android.text.TextPaint;
 import android.text.TextUtils;
 
 import com.android.internal.util.Preconditions;
@@ -31,15 +32,18 @@
  * Text shaping is a preprocess for drawing text into canvas with glyphs. The glyph is a most
  * primitive unit of the text drawing, consist of glyph identifier in the font file and its position
  * and style. You can draw the shape result to Canvas by calling Canvas#drawGlyphs.
-
  *
- * @see TextShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)
- * @see TextShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint)
- * @see android.text.StyledTextShaper#shapeText(CharSequence, int, int, TextDirectionHeuristic,
- * TextPaint)
+ * For most of the use cases, {@link android.text.TextShaper} will provide text shaping
+ * functionalities needed. {@link TextRunShaper} is a lower level API that is used by
+ * {@link android.text.TextShaper}.
+ *
+ * @see TextRunShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)
+ * @see TextRunShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint)
+ * @see android.text.TextShaper#shapeText(CharSequence, int, int, TextDirectionHeuristic, TextPaint,
+ * TextShaper.GlyphsConsumer)
  */
-public class TextShaper {
-    private TextShaper() {}  // Do not instantiate
+public class TextRunShaper {
+    private TextRunShaper() {}  // Do not instantiate
 
     /**
      * Shape non-styled text.
@@ -68,7 +72,7 @@
         return new PositionedGlyphs(
                 nativeShapeTextRun(text, start, count, contextStart, contextCount, isRtl,
                         paint.getNativeInstance()),
-                paint, xOffset, yOffset);
+                xOffset, yOffset);
     }
 
     /**
@@ -100,7 +104,7 @@
                     nativeShapeTextRun(
                             (String) text, start, count, contextStart, contextCount, isRtl,
                             paint.getNativeInstance()),
-                    paint, xOffset, yOffset);
+                    xOffset, yOffset);
         } else {
             char[] buf = new char[contextCount];
             TextUtils.getChars(text, contextStart, contextStart + contextCount, buf, 0);
@@ -108,7 +112,7 @@
                     nativeShapeTextRun(
                             buf, start - contextStart, count,
                             0, contextCount, isRtl, paint.getNativeInstance()),
-                    paint, xOffset, yOffset);
+                    xOffset, yOffset);
         }
     }
 
diff --git a/keystore/java/android/security/keystore/AttestationUtils.java b/keystore/java/android/security/keystore/AttestationUtils.java
index c8d4be3..f48da74 100644
--- a/keystore/java/android/security/keystore/AttestationUtils.java
+++ b/keystore/java/android/security/keystore/AttestationUtils.java
@@ -20,7 +20,6 @@
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.content.Context;
 import android.os.Build;
 import android.security.KeyStore;
@@ -44,7 +43,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public abstract class AttestationUtils {
     private AttestationUtils() {
     }
diff --git a/keystore/java/android/security/keystore/DeviceIdAttestationException.java b/keystore/java/android/security/keystore/DeviceIdAttestationException.java
index 8ba0317..4f9f9e6 100644
--- a/keystore/java/android/security/keystore/DeviceIdAttestationException.java
+++ b/keystore/java/android/security/keystore/DeviceIdAttestationException.java
@@ -18,7 +18,6 @@
 
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 
 /**
  * Thrown when {@link AttestationUtils} is unable to attest the given device ids.
@@ -26,7 +25,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public class DeviceIdAttestationException extends Exception {
     /**
      * Constructs a new {@code DeviceIdAttestationException} with the current stack trace and the
diff --git a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
index 227eec2..3f6ca0f 100644
--- a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
+++ b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
@@ -1,6 +1,12 @@
 {
   "version": "1.0.0",
   "messages": {
+    "-1683614271": {
+      "message": "Existing task: id=%d component=%s",
+      "level": "VERBOSE",
+      "group": "WM_SHELL_TASK_ORG",
+      "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
+    },
     "-1534364071": {
       "message": "onTransitionReady %s: %s",
       "level": "VERBOSE",
@@ -37,12 +43,6 @@
       "group": "WM_SHELL_TASK_ORG",
       "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
     },
-    "-242812822": {
-      "message": "Add listener for modes=%s listener=%s",
-      "level": "VERBOSE",
-      "group": "WM_SHELL_TASK_ORG",
-      "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
-    },
     "-191422040": {
       "message": "Transition animations finished, notifying core %s",
       "level": "VERBOSE",
@@ -55,17 +55,35 @@
       "group": "WM_SHELL_TASK_ORG",
       "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
     },
+    "481673835": {
+      "message": "addListenerForTaskId taskId=%s",
+      "level": "VERBOSE",
+      "group": "WM_SHELL_TASK_ORG",
+      "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
+    },
     "564235578": {
       "message": "Fullscreen Task Vanished: #%d",
       "level": "VERBOSE",
       "group": "WM_SHELL_TASK_ORG",
       "at": "com\/android\/wm\/shell\/FullscreenTaskListener.java"
     },
+    "580605218": {
+      "message": "Registering organizer",
+      "level": "VERBOSE",
+      "group": "WM_SHELL_TASK_ORG",
+      "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
+    },
     "980952660": {
       "message": "Task root back pressed taskId=%d",
       "level": "VERBOSE",
       "group": "WM_SHELL_TASK_ORG",
       "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
+    },
+    "1990759023": {
+      "message": "addListenerForType types=%s listener=%s",
+      "level": "VERBOSE",
+      "group": "WM_SHELL_TASK_ORG",
+      "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
     }
   },
   "groups": {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java
index 9d6271b..5bd693a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java
@@ -16,21 +16,30 @@
 
 package com.android.wm.shell;
 
+import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN;
+import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString;
+
 import android.app.ActivityManager;
-import android.util.ArraySet;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.util.ArrayMap;
 import android.util.Slog;
 import android.view.SurfaceControl;
 
+import androidx.annotation.NonNull;
+
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 
+import java.io.PrintWriter;
+
 class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
-    private static final String TAG = "FullscreenTaskOrg";
+    private static final String TAG = "FullscreenTaskListener";
 
     private final SyncTransactionQueue mSyncQueue;
 
-    private final ArraySet<Integer> mTasks = new ArraySet<>();
+    private final ArrayMap<Integer, SurfaceControl> mTasks = new ArrayMap<>();
 
     FullscreenTaskListener(SyncTransactionQueue syncQueue) {
         mSyncQueue = syncQueue;
@@ -39,16 +48,16 @@
     @Override
     public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
         synchronized (mTasks) {
-            if (mTasks.contains(taskInfo.taskId)) {
+            if (mTasks.containsKey(taskInfo.taskId)) {
                 throw new RuntimeException("Task appeared more than once: #" + taskInfo.taskId);
             }
             ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Appeared: #%d",
                     taskInfo.taskId);
-            mTasks.add(taskInfo.taskId);
+            mTasks.put(taskInfo.taskId, leash);
             mSyncQueue.runInSync(t -> {
                 // Reset several properties back to fullscreen (PiP, for example, leaves all these
                 // properties in a bad state).
-                t.setPosition(leash, 0, 0);
+                updateSurfacePosition(t, taskInfo, leash);
                 t.setWindowCrop(leash, null);
                 // TODO(shell-transitions): Eventually set everything in transition so there's no
                 //                          SF Transaction here.
@@ -64,7 +73,7 @@
     @Override
     public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
         synchronized (mTasks) {
-            if (!mTasks.remove(taskInfo.taskId)) {
+            if (mTasks.remove(taskInfo.taskId) == null) {
                 Slog.e(TAG, "Task already vanished: #" + taskInfo.taskId);
                 return;
             }
@@ -75,5 +84,40 @@
 
     @Override
     public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
+        synchronized (mTasks) {
+            if (!mTasks.containsKey(taskInfo.taskId)) {
+                Slog.e(TAG, "Changed Task wasn't appeared or already vanished: #"
+                        + taskInfo.taskId);
+                return;
+            }
+            final SurfaceControl leash = mTasks.get(taskInfo.taskId);
+            mSyncQueue.runInSync(t -> {
+                // Reposition the task in case the bounds has been changed (such as Task level
+                // letterboxing).
+                updateSurfacePosition(t, taskInfo, leash);
+            });
+        }
+    }
+
+    @Override
+    public void dump(@NonNull PrintWriter pw, String prefix) {
+        final String innerPrefix = prefix + "  ";
+        final String childPrefix = innerPrefix + "  ";
+        pw.println(prefix + this);
+        pw.println(innerPrefix + mTasks.size() + " Tasks");
+    }
+
+    @Override
+    public String toString() {
+        return TAG + ":" + taskListenerTypeToString(TASK_LISTENER_TYPE_FULLSCREEN);
+    }
+
+    /** Places the Task surface to the latest position. */
+    private static void updateSurfacePosition(SurfaceControl.Transaction t,
+            ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
+        // TODO(170725334) drop this after ag/12876439
+        final Configuration config = taskInfo.getConfiguration();
+        final Rect bounds = config.windowConfiguration.getBounds();
+        t.setPosition(leash, bounds.left, bounds.top);
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index 8f496d0..cbc1c8d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -16,16 +16,30 @@
 
 package com.android.wm.shell;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
+
+import android.annotation.IntDef;
 import android.app.ActivityManager.RunningTaskInfo;
+import android.app.WindowConfiguration.WindowingMode;
+import android.os.IBinder;
+import android.util.ArrayMap;
 import android.util.Log;
-import android.util.Pair;
 import android.util.SparseArray;
 import android.view.SurfaceControl;
 import android.window.ITaskOrganizerController;
+import android.window.TaskAppearedInfo;
 import android.window.TaskOrganizer;
 
+import androidx.annotation.NonNull;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.wm.shell.common.ShellExecutor;
@@ -33,8 +47,10 @@
 import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 
 /**
  * Unified task organizer for all components in the shell.
@@ -42,6 +58,23 @@
  */
 public class ShellTaskOrganizer extends TaskOrganizer {
 
+    // Intentionally using negative numbers here so the positive numbers can be used
+    // for task id specific listeners that will be added later.
+    public static final int TASK_LISTENER_TYPE_UNDEFINED = -1;
+    public static final int TASK_LISTENER_TYPE_FULLSCREEN = -2;
+    public static final int TASK_LISTENER_TYPE_MULTI_WINDOW = -3;
+    public static final int TASK_LISTENER_TYPE_PIP = -4;
+    public static final int TASK_LISTENER_TYPE_SPLIT_SCREEN = -5;
+
+    @IntDef(prefix = {"TASK_LISTENER_TYPE_"}, value = {
+            TASK_LISTENER_TYPE_UNDEFINED,
+            TASK_LISTENER_TYPE_FULLSCREEN,
+            TASK_LISTENER_TYPE_MULTI_WINDOW,
+            TASK_LISTENER_TYPE_PIP,
+            TASK_LISTENER_TYPE_SPLIT_SCREEN,
+    })
+    public @interface TaskListenerType {}
+
     private static final String TAG = "ShellTaskOrganizer";
 
     /**
@@ -52,60 +85,100 @@
         default void onTaskInfoChanged(RunningTaskInfo taskInfo) {}
         default void onTaskVanished(RunningTaskInfo taskInfo) {}
         default void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {}
+        default void dump(@NonNull PrintWriter pw, String prefix) {};
     }
 
-    private final SparseArray<ArrayList<TaskListener>> mListenersByWindowingMode =
-            new SparseArray<>();
+    /**
+     * Keys map from either a task id or {@link TaskListenerType}.
+     * @see #addListenerForTaskId
+     * @see #addListenerForType
+     */
+    private final SparseArray<TaskListener> mTaskListeners = new SparseArray<>();
 
     // Keeps track of all the tasks reported to this organizer (changes in windowing mode will
     // require us to report to both old and new listeners)
-    private final SparseArray<Pair<RunningTaskInfo, SurfaceControl>> mTasks = new SparseArray<>();
+    private final SparseArray<TaskAppearedInfo> mTasks = new SparseArray<>();
+
+    /** @see #setPendingLaunchCookieListener */
+    private final ArrayMap<IBinder, TaskListener> mLaunchCookieToListener = new ArrayMap<>();
 
     // TODO(shell-transitions): move to a more "global" Shell location as this isn't only for Tasks
     private final Transitions mTransitions;
 
+    private final Object mLock = new Object();
+
     public ShellTaskOrganizer(SyncTransactionQueue syncQueue, TransactionPool transactionPool,
             ShellExecutor mainExecutor, ShellExecutor animExecutor) {
-        super();
-        addListener(new FullscreenTaskListener(syncQueue), WINDOWING_MODE_FULLSCREEN);
-        mTransitions = new Transitions(this, transactionPool, mainExecutor, animExecutor);
-        if (Transitions.ENABLE_SHELL_TRANSITIONS) registerTransitionPlayer(mTransitions);
+        this(null, syncQueue, transactionPool, mainExecutor, animExecutor);
     }
 
     @VisibleForTesting
     ShellTaskOrganizer(ITaskOrganizerController taskOrganizerController,
             SyncTransactionQueue syncQueue, TransactionPool transactionPool,
             ShellExecutor mainExecutor, ShellExecutor animExecutor) {
-        super(taskOrganizerController);
-        addListener(new FullscreenTaskListener(syncQueue), WINDOWING_MODE_FULLSCREEN);
+        super(taskOrganizerController, mainExecutor);
+        addListenerForType(new FullscreenTaskListener(syncQueue), TASK_LISTENER_TYPE_FULLSCREEN);
         mTransitions = new Transitions(this, transactionPool, mainExecutor, animExecutor);
         if (Transitions.ENABLE_SHELL_TRANSITIONS) registerTransitionPlayer(mTransitions);
     }
 
-    /**
-     * Adds a listener for tasks in a specific windowing mode.
-     */
-    public void addListener(TaskListener listener, int... windowingModes) {
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Add listener for modes=%s listener=%s",
-                Arrays.toString(windowingModes), listener);
-        for (int winMode : windowingModes) {
-            ArrayList<TaskListener> listeners = mListenersByWindowingMode.get(winMode);
-            if (listeners == null) {
-                listeners = new ArrayList<>();
-                mListenersByWindowingMode.put(winMode, listeners);
+    @Override
+    public List<TaskAppearedInfo> registerOrganizer() {
+        synchronized (mLock) {
+            ProtoLog.v(WM_SHELL_TASK_ORG, "Registering organizer");
+            final List<TaskAppearedInfo> taskInfos = super.registerOrganizer();
+            for (int i = 0; i < taskInfos.size(); i++) {
+                final TaskAppearedInfo info = taskInfos.get(i);
+                ProtoLog.v(WM_SHELL_TASK_ORG, "Existing task: id=%d component=%s",
+                        info.getTaskInfo().taskId, info.getTaskInfo().baseIntent);
+                onTaskAppeared(info);
             }
-            if (listeners.contains(listener)) {
-                Log.w(TAG, "Listener already exists");
-                return;
-            }
-            listeners.add(listener);
+            return taskInfos;
+        }
+    }
 
-            // Notify the listener of all existing tasks in that windowing mode
-            for (int i = mTasks.size() - 1; i >= 0; i--) {
-                Pair<RunningTaskInfo, SurfaceControl> data = mTasks.valueAt(i);
-                int taskWinMode = data.first.configuration.windowConfiguration.getWindowingMode();
-                if (taskWinMode == winMode) {
-                    listener.onTaskAppeared(data.first, data.second);
+    /**
+     * Adds a listener for a specific task id.
+     */
+    public void addListenerForTaskId(TaskListener listener, int taskId) {
+        synchronized (mLock) {
+            ProtoLog.v(WM_SHELL_TASK_ORG, "addListenerForTaskId taskId=%s", taskId);
+            if (mTaskListeners.get(taskId) != null) {
+                throw new IllegalArgumentException(
+                        "Listener for taskId=" + taskId + " already exists");
+            }
+
+            final TaskAppearedInfo info = mTasks.get(taskId);
+            if (info == null) {
+                throw new IllegalArgumentException("addListenerForTaskId unknown taskId=" + taskId);
+            }
+
+            final TaskListener oldListener = getTaskListener(info.getTaskInfo());
+            mTaskListeners.put(taskId, listener);
+            updateTaskListenerIfNeeded(info.getTaskInfo(), info.getLeash(), oldListener, listener);
+        }
+    }
+
+    /**
+     * Adds a listener for tasks with given types.
+     */
+    public void addListenerForType(TaskListener listener, @TaskListenerType int... listenerTypes) {
+        synchronized (mLock) {
+            ProtoLog.v(WM_SHELL_TASK_ORG, "addListenerForType types=%s listener=%s",
+                    Arrays.toString(listenerTypes), listener);
+            for (int listenerType : listenerTypes) {
+                if (mTaskListeners.get(listenerType) != null) {
+                    throw new IllegalArgumentException("Listener for listenerType=" + listenerType
+                            + " already exists");
+                }
+                mTaskListeners.put(listenerType, listener);
+
+                // Notify the listener of all existing tasks with the given type.
+                for (int i = mTasks.size() - 1; i >= 0; --i) {
+                    final TaskAppearedInfo data = mTasks.valueAt(i);
+                    final TaskListener taskListener = getTaskListener(data.getTaskInfo());
+                    if (taskListener != listener) continue;
+                    listener.onTaskAppeared(data.getTaskInfo(), data.getLeash());
                 }
             }
         }
@@ -115,88 +188,222 @@
      * Removes a registered listener.
      */
     public void removeListener(TaskListener listener) {
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Remove listener=%s", listener);
-        for (int i = 0; i < mListenersByWindowingMode.size(); i++) {
-            mListenersByWindowingMode.valueAt(i).remove(listener);
+        synchronized (mLock) {
+            ProtoLog.v(WM_SHELL_TASK_ORG, "Remove listener=%s", listener);
+            final int index = mTaskListeners.indexOfValue(listener);
+            if (index == -1) {
+                Log.w(TAG, "No registered listener found");
+                return;
+            }
+
+            // Collect tasks associated with the listener we are about to remove.
+            final ArrayList<TaskAppearedInfo> tasks = new ArrayList<>();
+            for (int i = mTasks.size() - 1; i >= 0; --i) {
+                final TaskAppearedInfo data = mTasks.valueAt(i);
+                final TaskListener taskListener = getTaskListener(data.getTaskInfo());
+                if (taskListener != listener) continue;
+                tasks.add(data);
+            }
+
+            // Remove listener
+            mTaskListeners.removeAt(index);
+
+            // Associate tasks with new listeners if needed.
+            for (int i = tasks.size() - 1; i >= 0; --i) {
+                final TaskAppearedInfo data = tasks.get(i);
+                updateTaskListenerIfNeeded(data.getTaskInfo(), data.getLeash(),
+                        null /* oldListener already removed*/, getTaskListener(data.getTaskInfo()));
+            }
+        }
+    }
+
+    /**
+     * Associated a listener to a pending launch cookie so we can route the task later once it
+     * appears.
+     */
+    public void setPendingLaunchCookieListener(IBinder cookie, TaskListener listener) {
+        synchronized (mLock) {
+            mLaunchCookieToListener.put(cookie, listener);
         }
     }
 
     @Override
     public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Task appeared taskId=%d",
-                taskInfo.taskId);
-        mTasks.put(taskInfo.taskId, new Pair<>(taskInfo, leash));
-        ArrayList<TaskListener> listeners = mListenersByWindowingMode.get(
-                getWindowingMode(taskInfo));
-        if (listeners != null) {
-            for (int i = listeners.size() - 1; i >= 0; i--) {
-                listeners.get(i).onTaskAppeared(taskInfo, leash);
-            }
+        synchronized (mLock) {
+            onTaskAppeared(new TaskAppearedInfo(taskInfo, leash));
+        }
+    }
+
+    private void onTaskAppeared(TaskAppearedInfo info) {
+        final int taskId = info.getTaskInfo().taskId;
+        ProtoLog.v(WM_SHELL_TASK_ORG, "Task appeared taskId=%d", taskId);
+        mTasks.put(taskId, info);
+        final TaskListener listener =
+                getTaskListener(info.getTaskInfo(), true /*removeLaunchCookieIfNeeded*/);
+        if (listener != null) {
+            listener.onTaskAppeared(info.getTaskInfo(), info.getLeash());
         }
     }
 
     @Override
     public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Task info changed taskId=%d",
-                taskInfo.taskId);
-        Pair<RunningTaskInfo, SurfaceControl> data = mTasks.get(taskInfo.taskId);
-        int winMode = getWindowingMode(taskInfo);
-        int prevWinMode = getWindowingMode(data.first);
-        mTasks.put(taskInfo.taskId, new Pair<>(taskInfo, data.second));
-        if (prevWinMode != -1 && prevWinMode != winMode) {
-            // TODO: We currently send vanished/appeared as the task moves between win modes, but
-            //       we should consider adding a different mode-changed callback
-            ArrayList<TaskListener> listeners = mListenersByWindowingMode.get(prevWinMode);
-            if (listeners != null) {
-                for (int i = listeners.size() - 1; i >= 0; i--) {
-                    listeners.get(i).onTaskVanished(taskInfo);
-                }
-            }
-            listeners = mListenersByWindowingMode.get(winMode);
-            if (listeners != null) {
-                SurfaceControl leash = data.second;
-                for (int i = listeners.size() - 1; i >= 0; i--) {
-                    listeners.get(i).onTaskAppeared(taskInfo, leash);
-                }
-            }
-        } else {
-            ArrayList<TaskListener> listeners = mListenersByWindowingMode.get(winMode);
-            if (listeners != null) {
-                for (int i = listeners.size() - 1; i >= 0; i--) {
-                    listeners.get(i).onTaskInfoChanged(taskInfo);
-                }
+        synchronized (mLock) {
+            ProtoLog.v(WM_SHELL_TASK_ORG, "Task info changed taskId=%d", taskInfo.taskId);
+            final TaskAppearedInfo data = mTasks.get(taskInfo.taskId);
+            final TaskListener oldListener = getTaskListener(data.getTaskInfo());
+            final TaskListener newListener = getTaskListener(taskInfo);
+            mTasks.put(taskInfo.taskId, new TaskAppearedInfo(taskInfo, data.getLeash()));
+            final boolean updated = updateTaskListenerIfNeeded(
+                    taskInfo, data.getLeash(), oldListener, newListener);
+            if (!updated && newListener != null) {
+                newListener.onTaskInfoChanged(taskInfo);
             }
         }
     }
 
     @Override
     public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Task root back pressed taskId=%d",
-                taskInfo.taskId);
-        ArrayList<TaskListener> listeners = mListenersByWindowingMode.get(
-                getWindowingMode(taskInfo));
-        if (listeners != null) {
-            for (int i = listeners.size() - 1; i >= 0; i--) {
-                listeners.get(i).onBackPressedOnTaskRoot(taskInfo);
+        synchronized (mLock) {
+            ProtoLog.v(WM_SHELL_TASK_ORG, "Task root back pressed taskId=%d", taskInfo.taskId);
+            final TaskListener listener = getTaskListener(taskInfo);
+            if (listener != null) {
+                listener.onBackPressedOnTaskRoot(taskInfo);
             }
         }
     }
 
     @Override
     public void onTaskVanished(RunningTaskInfo taskInfo) {
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Task vanished taskId=%d",
-                taskInfo.taskId);
-        int prevWinMode = getWindowingMode(mTasks.get(taskInfo.taskId).first);
-        mTasks.remove(taskInfo.taskId);
-        ArrayList<TaskListener> listeners = mListenersByWindowingMode.get(prevWinMode);
-        if (listeners != null) {
-            for (int i = listeners.size() - 1; i >= 0; i--) {
-                listeners.get(i).onTaskVanished(taskInfo);
+        synchronized (mLock) {
+            ProtoLog.v(WM_SHELL_TASK_ORG, "Task vanished taskId=%d", taskInfo.taskId);
+            final int taskId = taskInfo.taskId;
+            final TaskListener listener = getTaskListener(mTasks.get(taskId).getTaskInfo());
+            mTasks.remove(taskId);
+            if (listener != null) {
+                listener.onTaskVanished(taskInfo);
             }
         }
     }
 
-    private int getWindowingMode(RunningTaskInfo taskInfo) {
+    private boolean updateTaskListenerIfNeeded(RunningTaskInfo taskInfo, SurfaceControl leash,
+            TaskListener oldListener, TaskListener newListener) {
+        if (oldListener == newListener) return false;
+        // TODO: We currently send vanished/appeared as the task moves between types, but
+        //       we should consider adding a different mode-changed callback
+        if (oldListener != null) {
+            oldListener.onTaskVanished(taskInfo);
+        }
+        if (newListener != null) {
+            newListener.onTaskAppeared(taskInfo, leash);
+        }
+        return true;
+    }
+
+    private TaskListener getTaskListener(RunningTaskInfo runningTaskInfo) {
+        return getTaskListener(runningTaskInfo, false /*removeLaunchCookieIfNeeded*/);
+    }
+
+    private TaskListener getTaskListener(RunningTaskInfo runningTaskInfo,
+            boolean removeLaunchCookieIfNeeded) {
+
+        final int taskId = runningTaskInfo.taskId;
+        TaskListener listener;
+
+        // First priority goes to listener that might be pending for this task.
+        final ArrayList<IBinder> launchCookies = runningTaskInfo.launchCookies;
+        for (int i = launchCookies.size() - 1; i >= 0; --i) {
+            final IBinder cookie = launchCookies.get(i);
+            listener = mLaunchCookieToListener.get(cookie);
+            if (listener == null) continue;
+
+            if (removeLaunchCookieIfNeeded) {
+                // Remove the cookie and add the listener.
+                mLaunchCookieToListener.remove(cookie);
+                mTaskListeners.put(taskId, listener);
+            }
+            return listener;
+        }
+
+        // Next priority goes to taskId specific listeners.
+        listener = mTaskListeners.get(taskId);
+        if (listener != null) return listener;
+
+        // Next we try type specific listeners.
+        final int windowingMode = getWindowingMode(runningTaskInfo);
+        final int taskListenerType = windowingModeToTaskListenerType(windowingMode);
+        return mTaskListeners.get(taskListenerType);
+    }
+
+    @WindowingMode
+    private static int getWindowingMode(RunningTaskInfo taskInfo) {
         return taskInfo.configuration.windowConfiguration.getWindowingMode();
     }
+
+    private static @TaskListenerType int windowingModeToTaskListenerType(
+            @WindowingMode int windowingMode) {
+        switch (windowingMode) {
+            case WINDOWING_MODE_FULLSCREEN:
+                return TASK_LISTENER_TYPE_FULLSCREEN;
+            case WINDOWING_MODE_MULTI_WINDOW:
+                return TASK_LISTENER_TYPE_MULTI_WINDOW;
+            case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
+            case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY:
+                return TASK_LISTENER_TYPE_SPLIT_SCREEN;
+            case WINDOWING_MODE_PINNED:
+                return TASK_LISTENER_TYPE_PIP;
+            case WINDOWING_MODE_FREEFORM:
+            case WINDOWING_MODE_UNDEFINED:
+            default:
+                return TASK_LISTENER_TYPE_UNDEFINED;
+        }
+    }
+
+    public static String taskListenerTypeToString(@TaskListenerType int type) {
+        switch (type) {
+            case TASK_LISTENER_TYPE_FULLSCREEN:
+                return "TASK_LISTENER_TYPE_FULLSCREEN";
+            case TASK_LISTENER_TYPE_MULTI_WINDOW:
+                return "TASK_LISTENER_TYPE_MULTI_WINDOW";
+            case TASK_LISTENER_TYPE_SPLIT_SCREEN:
+                return "TASK_LISTENER_TYPE_SPLIT_SCREEN";
+            case TASK_LISTENER_TYPE_PIP:
+                return "TASK_LISTENER_TYPE_PIP";
+            case TASK_LISTENER_TYPE_UNDEFINED:
+                return "TASK_LISTENER_TYPE_UNDEFINED";
+            default:
+                return "taskId#" + type;
+        }
+    }
+
+    public void dump(@NonNull PrintWriter pw, String prefix) {
+        synchronized (mLock) {
+            final String innerPrefix = prefix + "  ";
+            final String childPrefix = innerPrefix + "  ";
+            pw.println(prefix + TAG);
+            pw.println(innerPrefix + mTaskListeners.size() + " Listeners");
+            for (int i = mTaskListeners.size() - 1; i >= 0; --i) {
+                final int key = mTaskListeners.keyAt(i);
+                final TaskListener listener = mTaskListeners.valueAt(i);
+                pw.println(innerPrefix + "#" + i + " " + taskListenerTypeToString(key));
+                listener.dump(pw, childPrefix);
+            }
+
+            pw.println();
+            pw.println(innerPrefix + mTasks.size() + " Tasks");
+            for (int i = mTasks.size() - 1; i >= 0; --i) {
+                final int key = mTasks.keyAt(i);
+                final TaskAppearedInfo info = mTasks.valueAt(i);
+                final TaskListener listener = getTaskListener(info.getTaskInfo());
+                pw.println(innerPrefix + "#" + i + " task=" + key + " listener=" + listener);
+            }
+
+            pw.println();
+            pw.println(innerPrefix + mLaunchCookieToListener.size() + " Launch Cookies");
+            for (int i = mLaunchCookieToListener.size() - 1; i >= 0; --i) {
+                final IBinder key = mLaunchCookieToListener.keyAt(i);
+                final TaskListener listener = mLaunchCookieToListener.valueAt(i);
+                pw.println(innerPrefix + "#" + i + " cookie=" + key + " listener=" + listener);
+            }
+        }
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index d810fb8..ea18a19 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -69,11 +69,6 @@
     private final SparseArray<PerDisplay> mImePerDisplay = new SparseArray<>();
     private final ArrayList<ImePositionProcessor> mPositionProcessors = new ArrayList<>();
 
-    @Deprecated
-    public DisplayImeController(IWindowManager wmService, DisplayController displayController,
-            Handler mainHandler, TransactionPool transactionPool) {
-        this(wmService, displayController, mainHandler::post, transactionPool);
-    }
 
     public DisplayImeController(IWindowManager wmService, DisplayController displayController,
             Executor mainExecutor, TransactionPool transactionPool) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
index 84b98f9..24381d9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
@@ -32,7 +32,7 @@
 import android.util.SparseArray;
 import android.view.Display;
 import android.view.DragEvent;
-import android.view.IScrollCaptureController;
+import android.view.IScrollCaptureCallbacks;
 import android.view.IWindow;
 import android.view.IWindowManager;
 import android.view.IWindowSession;
@@ -270,26 +270,6 @@
             mDisplayId = displayId;
         }
 
-        @Override
-        public int relayout(IWindow window, WindowManager.LayoutParams attrs,
-                int requestedWidth, int requestedHeight, int viewVisibility, int flags,
-                long frameNumber, ClientWindowFrames outFrames,
-                MergedConfiguration mergedConfiguration,
-                SurfaceControl outSurfaceControl, InsetsState outInsetsState,
-                InsetsSourceControl[] outActiveControls, Point outSurfaceSize,
-                SurfaceControl outBLASTSurfaceControl) {
-            int res = super.relayout(window, attrs, requestedWidth, requestedHeight,
-                    viewVisibility, flags, frameNumber, outFrames,
-                    mergedConfiguration, outSurfaceControl, outInsetsState,
-                    outActiveControls, outSurfaceSize, outBLASTSurfaceControl);
-            if (res != 0) {
-                return res;
-            }
-            DisplayLayout dl = mDisplayController.getDisplayLayout(mDisplayId);
-            outFrames.stableInsets.set(dl.stableInsets());
-            return 0;
-        }
-
         void updateConfiguration(Configuration configuration) {
             setConfiguration(configuration);
         }
@@ -374,9 +354,9 @@
         public void dispatchPointerCaptureChanged(boolean hasCapture) {}
 
         @Override
-        public void requestScrollCapture(IScrollCaptureController controller) {
+        public void requestScrollCapture(IScrollCaptureCallbacks callbacks) {
             try {
-                controller.onClientUnavailable();
+                callbacks.onUnavailable();
             } catch (RemoteException ex) {
                 // ignore
             }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
index 9c78fc5..9bb709f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
@@ -27,11 +27,6 @@
  */
 public interface OneHanded {
     /**
-     * Return whether the device has one handed feature or not.
-     */
-    boolean hasOneHandedFeature();
-
-    /**
      * Return one handed settings enabled or not.
      */
     boolean isOneHandedEnabled();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index d060f64..7d039d4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -30,9 +30,10 @@
 import android.os.ServiceManager;
 import android.os.SystemProperties;
 import android.provider.Settings;
-import android.util.Log;
+import android.util.Slog;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
 import com.android.wm.shell.common.DisplayChangeController;
@@ -54,7 +55,6 @@
 
     static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
 
-    private final boolean mHasOneHandedFeature;
     private boolean mIsOneHandedEnabled;
     private boolean mIsSwipeToNotificationEnabled;
     private boolean mTaskChangeToExit;
@@ -77,7 +77,7 @@
     private final DisplayChangeController.OnDisplayChangingListener mRotationController =
             (display, fromRotation, toRotation, wct) -> {
                 if (mDisplayAreaOrganizer != null) {
-                    mDisplayAreaOrganizer.onRotateDisplay(fromRotation, toRotation);
+                    mDisplayAreaOrganizer.onRotateDisplay(fromRotation, toRotation, wct);
                 }
             };
 
@@ -160,10 +160,16 @@
             };
 
     /**
-     * The static constructor method to create OneHnadedController.
+     * Creates {@link OneHandedController}, returns {@code null} if the feature is not supported.
      */
+    @Nullable
     public static OneHandedController create(
             Context context, DisplayController displayController) {
+        if (!SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) {
+            Slog.w(TAG, "Device doesn't support OneHanded feature");
+            return null;
+        }
+
         OneHandedTutorialHandler tutorialHandler = new OneHandedTutorialHandler(context);
         OneHandedAnimationController animationController =
                 new OneHandedAnimationController(context);
@@ -186,43 +192,30 @@
             OneHandedTutorialHandler tutorialHandler,
             OneHandedGestureHandler gestureHandler,
             IOverlayManager overlayManager) {
-        mHasOneHandedFeature = SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false);
-        if (!mHasOneHandedFeature) {
-            Log.i(TAG, "Device config SUPPORT_ONE_HANDED_MODE off");
-            mContext = null;
-            mDisplayAreaOrganizer = null;
-            mDisplayController = null;
-            mTouchHandler = null;
-            mTutorialHandler = null;
-            mGestureHandler = null;
-            mTimeoutHandler = null;
-            mOverlayManager = null;
-        } else {
-            mContext = context;
-            mDisplayAreaOrganizer = displayAreaOrganizer;
-            mDisplayController = displayController;
-            mTouchHandler = touchHandler;
-            mTutorialHandler = tutorialHandler;
-            mGestureHandler = gestureHandler;
-            mOverlayManager = overlayManager;
+        mContext = context;
+        mDisplayAreaOrganizer = displayAreaOrganizer;
+        mDisplayController = displayController;
+        mTouchHandler = touchHandler;
+        mTutorialHandler = tutorialHandler;
+        mGestureHandler = gestureHandler;
+        mOverlayManager = overlayManager;
 
-            mOffSetFraction = SystemProperties.getInt(ONE_HANDED_MODE_OFFSET_PERCENTAGE, 50)
-                    / 100.0f;
-            mIsOneHandedEnabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
-                    context.getContentResolver());
-            mIsSwipeToNotificationEnabled =
-                    OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
-                            context.getContentResolver());
-            mTimeoutHandler = OneHandedTimeoutHandler.get();
+        mOffSetFraction = SystemProperties.getInt(ONE_HANDED_MODE_OFFSET_PERCENTAGE, 50)
+                / 100.0f;
+        mIsOneHandedEnabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
+                context.getContentResolver());
+        mIsSwipeToNotificationEnabled =
+                OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
+                        context.getContentResolver());
+        mTimeoutHandler = OneHandedTimeoutHandler.get();
 
-            mDisplayController.addDisplayChangingController(mRotationController);
+        mDisplayController.addDisplayChangingController(mRotationController);
 
-            setupCallback();
-            setupSettingObservers();
-            setupTimeoutListener();
-            setupGesturalOverlay();
-            updateSettings();
-        }
+        setupCallback();
+        setupSettingObservers();
+        setupTimeoutListener();
+        setupGesturalOverlay();
+        updateSettings();
     }
 
     /**
@@ -249,11 +242,6 @@
     }
 
     @Override
-    public boolean hasOneHandedFeature() {
-        return mHasOneHandedFeature;
-    }
-
-    @Override
     public boolean isOneHandedEnabled() {
         return mIsOneHandedEnabled;
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
index 6bc838f..17418f9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -27,6 +27,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.SystemProperties;
+import android.util.ArrayMap;
 import android.util.Log;
 import android.view.SurfaceControl;
 import android.window.DisplayAreaInfo;
@@ -42,7 +43,6 @@
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Objects;
 
@@ -76,7 +76,7 @@
     private int mEnterExitAnimationDurationMs;
 
     @VisibleForTesting
-    HashMap<DisplayAreaInfo, SurfaceControl> mDisplayAreaMap = new HashMap();
+    ArrayMap<DisplayAreaInfo, SurfaceControl> mDisplayAreaMap = new ArrayMap();
     private DisplayController mDisplayController;
     private OneHandedAnimationController mAnimationController;
     private OneHandedSurfaceTransactionHelper.SurfaceControlTransactionFactory
@@ -117,12 +117,13 @@
     private Handler.Callback mUpdateCallback = (msg) -> {
         SomeArgs args = (SomeArgs) msg.obj;
         final Rect currentBounds = args.arg1 != null ? (Rect) args.arg1 : mDefaultDisplayBounds;
+        final WindowContainerTransaction wctFromRotate = (WindowContainerTransaction) args.arg2;
         final int yOffset = args.argi2;
         final int direction = args.argi3;
 
         switch (msg.what) {
             case MSG_RESET_IMMEDIATE:
-                resetWindowsOffset();
+                resetWindowsOffset(wctFromRotate);
                 mDefaultDisplayBounds.set(currentBounds);
                 mLastVisualDisplayBounds.set(currentBounds);
                 finishOffset(0, TRANSITION_DIRECTION_EXIT);
@@ -165,47 +166,44 @@
             @NonNull SurfaceControl leash) {
         Objects.requireNonNull(displayAreaInfo, "displayAreaInfo must not be null");
         Objects.requireNonNull(leash, "leash must not be null");
-
-        if (displayAreaInfo.featureId != FEATURE_ONE_HANDED) {
-            Log.w(TAG, "Bypass onDisplayAreaAppeared()! displayAreaInfo=" + displayAreaInfo);
-            return;
+        synchronized (this) {
+            if (mDisplayAreaMap.get(displayAreaInfo) == null) {
+                // mDefaultDisplayBounds may out of date after removeDisplayChangingController()
+                mDefaultDisplayBounds.set(getDisplayBounds());
+                mDisplayAreaMap.put(displayAreaInfo, leash);
+            }
         }
-        // mDefaultDisplayBounds may out of date after removeDisplayChangingController()
-        mDefaultDisplayBounds.set(getDisplayBounds());
-        mDisplayAreaMap.put(displayAreaInfo, leash);
     }
 
     @Override
     public void onDisplayAreaVanished(@NonNull DisplayAreaInfo displayAreaInfo) {
         Objects.requireNonNull(displayAreaInfo,
                 "Requires valid displayArea, and displayArea must not be null");
-
-        if (!mDisplayAreaMap.containsKey(displayAreaInfo)) {
-            Log.w(TAG, "Unrecognized token: " + displayAreaInfo.token);
-            return;
+        synchronized (this) {
+            if (!mDisplayAreaMap.containsKey(displayAreaInfo)) {
+                Log.w(TAG, "Unrecognized token: " + displayAreaInfo.token);
+                return;
+            }
+            mDisplayAreaMap.remove(displayAreaInfo);
         }
-        mDisplayAreaMap.remove(displayAreaInfo);
     }
 
     @Override
     public void unregisterOrganizer() {
         super.unregisterOrganizer();
-        resetWindowsOffset();
-
-        // Ensure all cached instance are cleared after resetWindowsOffset
-        mUpdateHandler.post(() -> {
-            if (mDisplayAreaMap != null && !mDisplayAreaMap.isEmpty()) {
-                mDisplayAreaMap.clear();
-            }
-        });
+        mUpdateHandler.post(() -> resetWindowsOffset(null));
     }
 
     /**
      * Handler for display rotation changes by below policy which
-     * handles 90 degree display rotation changes {@link Surface.Rotation}
+     * handles 90 degree display rotation changes {@link Surface.Rotation}.
      *
+     * @param fromRotation starting rotation of the display.
+     * @param toRotation target rotation of the display (after rotating).
+     * @param wct A task transaction {@link WindowContainerTransaction} from
+     *        {@link DisplayChangeController} to populate.
      */
-    public void onRotateDisplay(int fromRotation, int toRotation) {
+    public void onRotateDisplay(int fromRotation, int toRotation, WindowContainerTransaction wct) {
         // Stop one handed without animation and reset cropped size immediately
         final Rect newBounds = new Rect(mDefaultDisplayBounds);
         final boolean isOrientationDiff = Math.abs(fromRotation - toRotation) % 2 == 1;
@@ -214,6 +212,7 @@
             newBounds.set(newBounds.left, newBounds.top, newBounds.bottom, newBounds.right);
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = newBounds;
+            args.arg2 = wct;
             args.argi1 = 0 /* xOffset */;
             args.argi2 = 0 /* yOffset */;
             args.argi3 = TRANSITION_DIRECTION_EXIT;
@@ -239,18 +238,19 @@
             throw new RuntimeException("Callers should call scheduleOffset() instead of this "
                     + "directly");
         }
-        final WindowContainerTransaction wct = new WindowContainerTransaction();
-        mDisplayAreaMap.forEach(
-                (key, leash) -> {
-                    animateWindows(leash, fromBounds, toBounds, direction,
-                            durationMs);
-                    wct.setBounds(key.token, toBounds);
-                });
-        applyTransaction(wct);
+        synchronized (this) {
+            final WindowContainerTransaction wct = new WindowContainerTransaction();
+            mDisplayAreaMap.forEach(
+                    (key, leash) -> {
+                        animateWindows(leash, fromBounds, toBounds, direction, durationMs);
+                        wct.setBounds(key.token, toBounds);
+                    });
+            applyTransaction(wct);
+        }
     }
 
-    private void resetWindowsOffset() {
-        mUpdateHandler.post(() -> {
+    private void resetWindowsOffset(WindowContainerTransaction wct) {
+        synchronized (this) {
             final SurfaceControl.Transaction tx =
                     mSurfaceControlTransactionFactory.getTransaction();
             mDisplayAreaMap.forEach(
@@ -262,9 +262,13 @@
                         }
                         tx.setPosition(leash, 0, 0)
                                 .setWindowCrop(leash, -1/* reset */, -1/* reset */);
+                        // DisplayRotationController will applyTransaction() after finish rotating
+                        if (wct != null) {
+                            wct.setBounds(key.token, null/* reset */);
+                        }
                     });
             tx.apply();
-        });
+        }
     }
 
     private void animateWindows(SurfaceControl leash, Rect fromBounds, Rect toBounds,
@@ -335,8 +339,8 @@
     }
 
     @VisibleForTesting
-    Handler getUpdateHandler() {
-        return mUpdateHandler;
+    void setUpdateHandler(Handler updateHandler) {
+        mUpdateHandler = updateHandler;
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
index 7c26251..3ded409 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
@@ -18,7 +18,10 @@
 
 import android.annotation.Nullable;
 import android.app.ActivityManager;
+import android.app.PictureInPictureParams;
 import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
+import android.graphics.Rect;
 import android.media.session.MediaController;
 
 import com.android.wm.shell.pip.phone.PipTouchHandler;
@@ -95,7 +98,7 @@
     /**
      * Hides the PIP menu.
      */
-    void hidePipMenu(Runnable onStartCallback, Runnable onEndCallback);
+    default void hidePipMenu(Runnable onStartCallback, Runnable onEndCallback) {}
 
     /**
      * Returns {@code true} if PIP is shown.
@@ -223,7 +226,7 @@
     /**
      * Called when showing Pip menu.
      */
-    void showPictureInPictureMenu();
+    default void showPictureInPictureMenu() {}
 
     /**
      * Suspends resizing operation on the Pip until {@link #resumePipResizing} is called.
@@ -232,4 +235,28 @@
      */
     default void suspendPipResizing(int reason) {
     }
+
+    /**
+     * Called by Launcher when swiping an auto-pip enabled Activity to home starts
+     * @param componentName {@link ComponentName} represents the Activity entering PiP
+     * @param activityInfo {@link ActivityInfo} tied to the Activity
+     * @param pictureInPictureParams {@link PictureInPictureParams} tied to the Activity
+     * @param launcherRotation Rotation Launcher is in
+     * @param shelfHeight Shelf height when landing PiP window onto Launcher
+     * @return Destination bounds of PiP window based on the parameters passed in
+     */
+    default Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
+            PictureInPictureParams pictureInPictureParams,
+            int launcherRotation, int shelfHeight) {
+        return null;
+    }
+
+    /**
+     * Called by Launcher when swiping an auto-pip enable Activity to home finishes
+     * @param componentName {@link ComponentName} represents the Activity entering PiP
+     * @param destinationBounds Destination bounds of PiP window
+     */
+    default void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) {
+        return;
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsHandler.java
index de3261b..0831818 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsHandler.java
@@ -22,9 +22,9 @@
 import static android.view.Surface.ROTATION_0;
 import static android.view.Surface.ROTATION_180;
 
+import android.annotation.NonNull;
 import android.app.ActivityTaskManager;
 import android.app.ActivityTaskManager.RootTaskInfo;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Point;
@@ -52,18 +52,14 @@
     private static final String TAG = PipBoundsHandler.class.getSimpleName();
     private static final float INVALID_SNAP_FRACTION = -1f;
 
+    private final @NonNull PipBoundsState mPipBoundsState;
     private final PipSnapAlgorithm mSnapAlgorithm;
     private final DisplayInfo mDisplayInfo = new DisplayInfo();
     private DisplayLayout mDisplayLayout;
 
-    private ComponentName mLastPipComponentName;
-    private float mReentrySnapFraction = INVALID_SNAP_FRACTION;
-    private Size mReentrySize;
-
     private float mDefaultAspectRatio;
     private float mMinAspectRatio;
     private float mMaxAspectRatio;
-    private float mAspectRatio;
     private int mDefaultStackGravity;
     private int mDefaultMinSize;
     private Point mScreenEdgeInsets;
@@ -75,14 +71,15 @@
     private boolean mIsShelfShowing;
     private int mShelfHeight;
 
-    public PipBoundsHandler(Context context) {
+    public PipBoundsHandler(Context context, @NonNull PipBoundsState pipBoundsState) {
+        mPipBoundsState = pipBoundsState;
         mSnapAlgorithm = new PipSnapAlgorithm(context);
         mDisplayLayout = new DisplayLayout();
         reloadResources(context);
         // Initialize the aspect ratio to the default aspect ratio.  Don't do this in reload
         // resources as it would clobber mAspectRatio when entering PiP from fullscreen which
         // triggers a configuration change and the resources to be reloaded.
-        mAspectRatio = mDefaultAspectRatio;
+        mPipBoundsState.setAspectRatio(mDefaultAspectRatio);
     }
 
     /**
@@ -121,6 +118,13 @@
     }
 
     /**
+     * Get the current saved display info.
+     */
+    public DisplayInfo getDisplayInfo() {
+        return mDisplayInfo;
+    }
+
+    /**
      * Update the Min edge size for {@link PipSnapAlgorithm} to calculate corresponding bounds
      * @param minEdgeSize
      */
@@ -128,10 +132,6 @@
         mCurrentMinSize = minEdgeSize;
     }
 
-    protected float getAspectRatio() {
-        return mAspectRatio;
-    }
-
     /**
      * Sets both shelf visibility and its height if applicable.
      * @return {@code true} if the internal shelf state is changed, {@code false} otherwise.
@@ -167,48 +167,14 @@
         if (animatingBounds.isEmpty()) {
             animatingBounds.set(defaultBounds);
         }
-        if (isValidPictureInPictureAspectRatio(mAspectRatio)) {
-            transformBoundsToAspectRatio(normalBounds, mAspectRatio,
+        if (isValidPictureInPictureAspectRatio(mPipBoundsState.getAspectRatio())) {
+            transformBoundsToAspectRatio(normalBounds, mPipBoundsState.getAspectRatio(),
                     false /* useCurrentMinEdgeSize */, false /* useCurrentSize */);
         }
         displayInfo.copyFrom(mDisplayInfo);
     }
 
     /**
-     * Responds to IPinnedStackListener on saving reentry snap fraction and size
-     * for a given {@link ComponentName}.
-     */
-    public void onSaveReentryBounds(ComponentName componentName, Rect bounds) {
-        mReentrySnapFraction = getSnapFraction(bounds);
-        mReentrySize = new Size(bounds.width(), bounds.height());
-        mLastPipComponentName = componentName;
-    }
-
-    /**
-     * Responds to IPinnedStackListener on resetting reentry snap fraction and size
-     * for a given {@link ComponentName}.
-     */
-    public void onResetReentryBounds(ComponentName componentName) {
-        if (componentName.equals(mLastPipComponentName)) {
-            onResetReentryBoundsUnchecked();
-        }
-    }
-
-    private void onResetReentryBoundsUnchecked() {
-        mReentrySnapFraction = INVALID_SNAP_FRACTION;
-        mReentrySize = null;
-        mLastPipComponentName = null;
-    }
-
-    /**
-     * Returns ture if there's a valid snap fraction. This is used with {@link EXTRA_IS_FIRST_ENTRY}
-     * to see if this is the first time user has entered PIP for the component.
-     */
-    public boolean hasSaveReentryBounds() {
-        return mReentrySnapFraction != INVALID_SNAP_FRACTION;
-    }
-
-    /**
      * The {@link PipSnapAlgorithm} is couple on display bounds
      * @return {@link PipSnapAlgorithm}.
      */
@@ -241,48 +207,42 @@
     }
 
     /**
-     * Responds to IPinnedStackListener on resetting aspect ratio for the pinned window.
-     * It will normally follow up with a
-     * {@link #onMovementBoundsChanged(Rect, Rect, Rect, DisplayInfo)} callback.
+     * See {@link #getDestinationBounds(Rect, Size, boolean)}
      */
-    public void onAspectRatioChanged(float aspectRatio) {
-        mAspectRatio = aspectRatio;
-    }
-
-    /**
-     * See {@link #getDestinationBounds(ComponentName, float, Rect, Size, boolean)}
-     */
-    public Rect getDestinationBounds(ComponentName componentName, float aspectRatio, Rect bounds,
-            Size minimalSize) {
-        return getDestinationBounds(componentName, aspectRatio, bounds, minimalSize,
-                false /* useCurrentMinEdgeSize */);
+    public Rect getDestinationBounds(Rect bounds, Size minimalSize) {
+        return getDestinationBounds(bounds, minimalSize, false /* useCurrentMinEdgeSize */);
     }
 
     /**
      * @return {@link Rect} of the destination PiP window bounds.
      */
-    public Rect getDestinationBounds(ComponentName componentName, float aspectRatio, Rect bounds,
-            Size minimalSize, boolean useCurrentMinEdgeSize) {
-        if (!componentName.equals(mLastPipComponentName)) {
-            onResetReentryBoundsUnchecked();
-            mLastPipComponentName = componentName;
-        }
+    public Rect getDestinationBounds(Rect bounds, Size minimalSize, boolean useCurrentMinEdgeSize) {
+        boolean isReentryBounds = false;
         final Rect destinationBounds;
         if (bounds == null) {
-            final Rect defaultBounds = getDefaultBounds(mReentrySnapFraction, mReentrySize);
-            destinationBounds = new Rect(defaultBounds);
-            if (mReentrySnapFraction == INVALID_SNAP_FRACTION && mReentrySize == null) {
+            // Calculating initial entry bounds
+            final PipBoundsState.PipReentryState state = mPipBoundsState.getReentryState();
+
+            final Rect defaultBounds;
+            if (state != null) {
+                // Restore to reentry bounds.
+                defaultBounds = getDefaultBounds(state.getSnapFraction(), state.getSize());
+                isReentryBounds = true;
+            } else {
+                // Get actual default bounds.
+                defaultBounds = getDefaultBounds(INVALID_SNAP_FRACTION, null /* size */);
                 mOverrideMinimalSize = minimalSize;
             }
+
+            destinationBounds = new Rect(defaultBounds);
         } else {
+            // Just adjusting bounds (e.g. on aspect ratio changed).
             destinationBounds = new Rect(bounds);
         }
-        if (isValidPictureInPictureAspectRatio(aspectRatio)) {
-            boolean useCurrentSize = bounds == null && mReentrySize != null;
-            transformBoundsToAspectRatio(destinationBounds, aspectRatio, useCurrentMinEdgeSize,
-                    useCurrentSize);
+        if (isValidPictureInPictureAspectRatio(mPipBoundsState.getAspectRatio())) {
+            transformBoundsToAspectRatio(destinationBounds, mPipBoundsState.getAspectRatio(),
+                    useCurrentMinEdgeSize, isReentryBounds);
         }
-        mAspectRatio = aspectRatio;
         return destinationBounds;
     }
 
@@ -391,8 +351,8 @@
      * @param stackBounds
      */
     public void transformBoundsToAspectRatio(Rect stackBounds) {
-        transformBoundsToAspectRatio(stackBounds, mAspectRatio, true /* useCurrentMinEdgeSize */,
-                true /* useCurrentSize */);
+        transformBoundsToAspectRatio(stackBounds, mPipBoundsState.getAspectRatio(),
+                true /* useCurrentMinEdgeSize */, true /* useCurrentSize */);
     }
 
     /**
@@ -533,14 +493,10 @@
     public void dump(PrintWriter pw, String prefix) {
         final String innerPrefix = prefix + "  ";
         pw.println(prefix + TAG);
-        pw.println(innerPrefix + "mLastPipComponentName=" + mLastPipComponentName);
-        pw.println(innerPrefix + "mReentrySnapFraction=" + mReentrySnapFraction);
-        pw.println(innerPrefix + "mReentrySize=" + mReentrySize);
         pw.println(innerPrefix + "mDisplayInfo=" + mDisplayInfo);
         pw.println(innerPrefix + "mDefaultAspectRatio=" + mDefaultAspectRatio);
         pw.println(innerPrefix + "mMinAspectRatio=" + mMinAspectRatio);
         pw.println(innerPrefix + "mMaxAspectRatio=" + mMaxAspectRatio);
-        pw.println(innerPrefix + "mAspectRatio=" + mAspectRatio);
         pw.println(innerPrefix + "mDefaultStackGravity=" + mDefaultStackGravity);
         pw.println(innerPrefix + "mIsImeShowing=" + mIsImeShowing);
         pw.println(innerPrefix + "mImeHeight=" + mImeHeight);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
new file mode 100644
index 0000000..aba2a3a
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.wm.shell.pip;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.graphics.Rect;
+import android.util.Size;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.PrintWriter;
+import java.util.Objects;
+
+/**
+ * Singleton source of truth for the current state of PIP bounds.
+ */
+public final class PipBoundsState {
+    private static final String TAG = PipBoundsState.class.getSimpleName();
+
+    private final @NonNull Rect mBounds = new Rect();
+    private float mAspectRatio;
+    private PipReentryState mPipReentryState;
+    private ComponentName mLastPipComponentName;
+
+    void setBounds(@NonNull Rect bounds) {
+        mBounds.set(bounds);
+    }
+
+    @NonNull
+    public Rect getBounds() {
+        return new Rect(mBounds);
+    }
+
+    public void setAspectRatio(float aspectRatio) {
+        mAspectRatio = aspectRatio;
+    }
+
+    public float getAspectRatio() {
+        return mAspectRatio;
+    }
+
+    /**
+     * Save the reentry state to restore to when re-entering PIP mode.
+     *
+     * TODO(b/169373982): consider refactoring this so that this class alone can use mBounds and
+     * calculate the snap fraction to save for re-entry.
+     */
+    public void saveReentryState(@NonNull Rect bounds, float fraction) {
+        mPipReentryState = new PipReentryState(new Size(bounds.width(), bounds.height()), fraction);
+    }
+
+    /**
+     * Returns the saved reentry state.
+     */
+    @Nullable
+    public PipReentryState getReentryState() {
+        return mPipReentryState;
+    }
+
+    /**
+     * Set the last {@link ComponentName} to enter PIP mode.
+     */
+    public void setLastPipComponentName(ComponentName lastPipComponentName) {
+        final boolean changed = !Objects.equals(mLastPipComponentName, lastPipComponentName);
+        mLastPipComponentName = lastPipComponentName;
+        if (changed) {
+            clearReentryState();
+        }
+    }
+
+    public ComponentName getLastPipComponentName() {
+        return mLastPipComponentName;
+    }
+
+    @VisibleForTesting
+    void clearReentryState() {
+        mPipReentryState = null;
+    }
+
+    static final class PipReentryState {
+        private static final String TAG = PipReentryState.class.getSimpleName();
+
+        private final @NonNull Size mSize;
+        private final float mSnapFraction;
+
+        PipReentryState(@NonNull Size size, float snapFraction) {
+            mSize = size;
+            mSnapFraction = snapFraction;
+        }
+
+        @NonNull
+        Size getSize() {
+            return mSize;
+        }
+
+        float getSnapFraction() {
+            return mSnapFraction;
+        }
+
+        void dump(PrintWriter pw, String prefix) {
+            final String innerPrefix = prefix + "  ";
+            pw.println(prefix + TAG);
+            pw.println(innerPrefix + "mSize=" + mSize);
+            pw.println(innerPrefix + "mSnapFraction=" + mSnapFraction);
+        }
+    }
+
+    /**
+     * Dumps internal state.
+     */
+    public void dump(PrintWriter pw, String prefix) {
+        final String innerPrefix = prefix + "  ";
+        pw.println(prefix + TAG);
+        pw.println(innerPrefix + "mBounds=" + mBounds);
+        pw.println(innerPrefix + "mLastPipComponentName=" + mLastPipComponentName);
+        pw.println(innerPrefix + "mAspectRatio=" + mAspectRatio);
+        if (mPipReentryState == null) {
+            pw.println(innerPrefix + "mPipReentryState=null");
+        } else {
+            mPipReentryState.dump(pw, innerPrefix);
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index bb501fb..a7bf9ae 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -21,6 +21,8 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 
+import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_PIP;
+import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString;
 import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
 import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_BOUNDS;
 import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP;
@@ -58,6 +60,7 @@
 import android.window.WindowContainerTransaction;
 import android.window.WindowContainerTransactionCallback;
 
+import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.os.SomeArgs;
 import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTaskOrganizer;
@@ -76,6 +79,7 @@
 import java.util.Objects;
 import java.util.Optional;
 import java.util.function.Consumer;
+import java.util.function.IntConsumer;
 
 /**
  * Manages PiP tasks such as resize and offset.
@@ -88,7 +92,7 @@
  * This class is also responsible for general resize/offset PiP operations within SysUI component,
  * see also {@link PipMotionHelper}.
  */
-public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganizer.TaskListener,
+public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
         DisplayController.OnDisplaysChangedListener {
     private static final String TAG = PipTaskOrganizer.class.getSimpleName();
     private static final boolean DEBUG = false;
@@ -104,7 +108,8 @@
         UNDEFINED(0),
         TASK_APPEARED(1),
         ENTERING_PIP(2),
-        EXITING_PIP(3);
+        ENTERED_PIP(3),
+        EXITING_PIP(4);
 
         private final int mStateValue;
 
@@ -131,11 +136,11 @@
 
     private final Handler mMainHandler;
     private final Handler mUpdateHandler;
+    private final PipBoundsState mPipBoundsState;
     private final PipBoundsHandler mPipBoundsHandler;
     private final PipAnimationController mPipAnimationController;
     private final PipUiEventLogger mPipUiEventLoggerLogger;
     private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>();
-    private final Rect mLastReportedBounds = new Rect();
     private final int mEnterExitAnimationDuration;
     private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
     private final Map<IBinder, Configuration> mInitialState = new HashMap<>();
@@ -149,15 +154,25 @@
             new PipAnimationController.PipAnimationCallback() {
         @Override
         public void onPipAnimationStart(PipAnimationController.PipTransitionAnimator animator) {
-            sendOnPipTransitionStarted(animator.getTransitionDirection());
+            final int direction = animator.getTransitionDirection();
+            if (direction == TRANSITION_DIRECTION_TO_PIP) {
+                InteractionJankMonitor.getInstance().begin(
+                        InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_PIP, 2000);
+            }
+            sendOnPipTransitionStarted(direction);
         }
 
         @Override
         public void onPipAnimationEnd(SurfaceControl.Transaction tx,
                 PipAnimationController.PipTransitionAnimator animator) {
-            finishResize(tx, animator.getDestinationBounds(), animator.getTransitionDirection(),
+            final int direction = animator.getTransitionDirection();
+            finishResize(tx, animator.getDestinationBounds(), direction,
                     animator.getAnimationType());
-            sendOnPipTransitionFinished(animator.getTransitionDirection());
+            sendOnPipTransitionFinished(direction);
+            if (direction == TRANSITION_DIRECTION_TO_PIP) {
+                InteractionJankMonitor.getInstance().end(
+                        InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_PIP);
+            }
         }
 
         @Override
@@ -234,6 +249,7 @@
     private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
             mSurfaceControlTransactionFactory;
     private PictureInPictureParams mPictureInPictureParams;
+    private IntConsumer mOnDisplayIdChangeCallback;
 
     /**
      * If set to {@code true}, the entering animation will be skipped and we will wait for
@@ -241,7 +257,16 @@
      */
     private boolean mShouldDeferEnteringPip;
 
-    public PipTaskOrganizer(Context context, @NonNull PipBoundsHandler boundsHandler,
+    /**
+     * If set to {@code true}, no entering PiP transition would be kicked off and most likely
+     * it's due to the fact that Launcher is handling the transition directly when swiping
+     * auto PiP-able Activity to home.
+     * See also {@link #startSwipePipToHome(ComponentName, ActivityInfo, PictureInPictureParams)}.
+     */
+    private boolean mShouldIgnoreEnteringPipTransition;
+
+    public PipTaskOrganizer(Context context, @NonNull PipBoundsState pipBoundsState,
+            @NonNull PipBoundsHandler boundsHandler,
             @NonNull PipSurfaceTransactionHelper surfaceTransactionHelper,
             Optional<SplitScreen> splitScreenOptional,
             @NonNull DisplayController displayController,
@@ -249,6 +274,7 @@
             @NonNull ShellTaskOrganizer shellTaskOrganizer) {
         mMainHandler = new Handler(Looper.getMainLooper());
         mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks);
+        mPipBoundsState = pipBoundsState;
         mPipBoundsHandler = boundsHandler;
         mEnterExitAnimationDuration = context.getResources()
                 .getInteger(R.integer.config_pipResizeAnimationDuration);
@@ -258,30 +284,21 @@
         mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
         mSplitScreenOptional = splitScreenOptional;
         mTaskOrganizer = shellTaskOrganizer;
-
-        if (!PipUtils.hasSystemFeature(context)) {
-            Log.w(TAG, "Device not support PIP feature");
-        } else {
-            mTaskOrganizer.addListener(this, WINDOWING_MODE_PINNED);
-            displayController.addDisplayWindowListener(this);
-        }
+        mTaskOrganizer.addListenerForType(this, TASK_LISTENER_TYPE_PIP);
+        displayController.addDisplayWindowListener(this);
     }
 
     public Handler getUpdateHandler() {
         return mUpdateHandler;
     }
 
-    public Rect getLastReportedBounds() {
-        return new Rect(mLastReportedBounds);
-    }
-
     public Rect getCurrentOrAnimatingBounds() {
         PipAnimationController.PipTransitionAnimator animator =
                 mPipAnimationController.getCurrentAnimator();
         if (animator != null && animator.isRunning()) {
             return new Rect(animator.getDestinationBounds());
         }
-        return getLastReportedBounds();
+        return mPipBoundsState.getBounds();
     }
 
     public boolean isInPip() {
@@ -300,6 +317,13 @@
     }
 
     /**
+     * Registers a callback when a display change has been detected when we enter PiP.
+     */
+    public void registerOnDisplayIdChangeCallback(IntConsumer onDisplayIdChangeCallback) {
+        mOnDisplayIdChangeCallback = onDisplayIdChangeCallback;
+    }
+
+    /**
      * Sets the preferred animation type for one time.
      * This is typically used to set the animation type to
      * {@link PipAnimationController#ANIM_TYPE_ALPHA}.
@@ -309,6 +333,28 @@
     }
 
     /**
+     * Callback when Launcher starts swipe-pip-to-home operation.
+     * @return {@link Rect} for destination bounds.
+     */
+    public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
+            PictureInPictureParams pictureInPictureParams) {
+        mShouldIgnoreEnteringPipTransition = true;
+        mState = State.ENTERING_PIP;
+        mPipBoundsState.setLastPipComponentName(componentName);
+        mPipBoundsState.setAspectRatio(getAspectRatioOrDefault(pictureInPictureParams));
+        return mPipBoundsHandler.getDestinationBounds(null /* bounds */,
+                getMinimalSize(activityInfo));
+    }
+
+    /**
+     * Callback when launcher finishes swipe-pip-to-home operation.
+     * Expect {@link #onTaskAppeared(ActivityManager.RunningTaskInfo, SurfaceControl)} afterwards.
+     */
+    public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) {
+        mPipBoundsState.setBounds(destinationBounds);
+    }
+
+    /**
      * Expands PiP to the previous bounds, this is done in two phases using
      * {@link WindowContainerTransaction}
      * - setActivityWindowingMode to either fullscreen or split-secondary at beginning of the
@@ -352,7 +398,7 @@
             final SurfaceControl.Transaction tx =
                     mSurfaceControlTransactionFactory.getTransaction();
             mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds,
-                    mLastReportedBounds);
+                    mPipBoundsState.getBounds());
             tx.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height());
             // We set to fullscreen here for now, but later it will be set to UNDEFINED for
             // the proper windowing mode to take place. See #applyWindowingModeChangeOnExit.
@@ -366,9 +412,13 @@
                 @Override
                 public void onTransactionReady(int id, SurfaceControl.Transaction t) {
                     t.apply();
-                    scheduleAnimateResizePip(mLastReportedBounds, destinationBounds,
-                            getValidSourceHintRect(mTaskInfo, destinationBounds), direction,
-                            animationDurationMs, null /* updateBoundsCallback */);
+                    // Make sure to grab the latest source hint rect as it could have been updated
+                    // right after applying the windowing mode change.
+                    final Rect sourceHintRect = getValidSourceHintRect(mPictureInPictureParams,
+                            destinationBounds);
+                    scheduleAnimateResizePip(mPipBoundsState.getBounds(), destinationBounds,
+                            sourceHintRect, direction, animationDurationMs,
+                            null /* updateBoundsCallback */);
                     mState = State.EXITING_PIP;
                 }
             });
@@ -399,7 +449,7 @@
 
         // removePipImmediately is expected when the following animation finishes.
         mUpdateHandler.post(() -> mPipAnimationController
-                .getAnimator(mLeash, mLastReportedBounds, 1f, 0f)
+                .getAnimator(mLeash, mPipBoundsState.getBounds(), 1f, 0f)
                 .setTransitionDirection(TRANSITION_DIRECTION_REMOVE_STACK)
                 .setPipAnimationCallback(mPipAnimationCallback)
                 .setDuration(mEnterExitAnimationDuration)
@@ -415,7 +465,7 @@
             wct.setBounds(mToken, null);
             mTaskOrganizer.applyTransaction(wct);
 
-            ActivityTaskManager.getService().removeStacksInWindowingModes(
+            ActivityTaskManager.getService().removeRootTasksInWindowingModes(
                     new int[]{ WINDOWING_MODE_PINNED });
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to remove PiP", e);
@@ -431,10 +481,28 @@
         mLeash = leash;
         mInitialState.put(mToken.asBinder(), new Configuration(mTaskInfo.configuration));
         mPictureInPictureParams = mTaskInfo.pictureInPictureParams;
+        mPipBoundsState.setLastPipComponentName(mTaskInfo.topActivity);
 
         mPipUiEventLoggerLogger.setTaskInfo(mTaskInfo);
         mPipUiEventLoggerLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_ENTER);
 
+        // If the displayId of the task is different than what PipBoundsHandler has, then update
+        // it. This is possible if we entered PiP on an external display.
+        if (info.displayId != mPipBoundsHandler.getDisplayInfo().displayId
+                && mOnDisplayIdChangeCallback != null) {
+            mOnDisplayIdChangeCallback.accept(info.displayId);
+        }
+
+        if (mShouldIgnoreEnteringPipTransition) {
+            // Animation has been finished together with Recents, directly apply the sync
+            // transaction to PiP here.
+            applyEnterPipSyncTransaction(mPipBoundsState.getBounds(), () -> {
+                mState = State.ENTERED_PIP;
+            });
+            mShouldIgnoreEnteringPipTransition = false;
+            return;
+        }
+
         if (mShouldDeferEnteringPip) {
             if (DEBUG) Log.d(TAG, "Defer entering PiP animation, fixed rotation is ongoing");
             // if deferred, hide the surface till fixed rotation is completed
@@ -446,14 +514,15 @@
             return;
         }
 
-        final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
-                mTaskInfo.topActivity, getAspectRatioOrDefault(mPictureInPictureParams),
-                null /* bounds */, getMinimalSize(mTaskInfo.topActivityInfo));
+        mPipBoundsState.setAspectRatio(getAspectRatioOrDefault(mPictureInPictureParams));
+        final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(null /* bounds */,
+                getMinimalSize(mTaskInfo.topActivityInfo));
         Objects.requireNonNull(destinationBounds, "Missing destination bounds");
         final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds();
 
         if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
-            final Rect sourceHintRect = getValidSourceHintRect(info, currentBounds);
+            final Rect sourceHintRect = getValidSourceHintRect(info.pictureInPictureParams,
+                    currentBounds);
             scheduleAnimateResizePip(currentBounds, destinationBounds, sourceHintRect,
                     TRANSITION_DIRECTION_TO_PIP, mEnterExitAnimationDuration,
                     null /* updateBoundsCallback */);
@@ -470,10 +539,10 @@
      * Returns the source hint rect if it is valid (if provided and is contained by the current
      * task bounds).
      */
-    private Rect getValidSourceHintRect(ActivityManager.RunningTaskInfo info, Rect sourceBounds) {
-        final Rect sourceHintRect = info.pictureInPictureParams != null
-                && info.pictureInPictureParams.hasSourceBoundsHint()
-                ? info.pictureInPictureParams.getSourceRectHint()
+    private Rect getValidSourceHintRect(PictureInPictureParams params, Rect sourceBounds) {
+        final Rect sourceHintRect = params != null
+                && params.hasSourceBoundsHint()
+                ? params.getSourceRectHint()
                 : null;
         if (sourceHintRect != null && sourceBounds.contains(sourceHintRect)) {
             return sourceHintRect;
@@ -489,6 +558,20 @@
                 mSurfaceControlTransactionFactory.getTransaction();
         tx.setAlpha(mLeash, 0f);
         tx.apply();
+        applyEnterPipSyncTransaction(destinationBounds, () -> {
+            mUpdateHandler.post(() -> mPipAnimationController
+                    .getAnimator(mLeash, destinationBounds, 0f, 1f)
+                    .setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
+                    .setPipAnimationCallback(mPipAnimationCallback)
+                    .setDuration(durationMs)
+                    .start());
+            // mState is set right after the animation is kicked off to block any resize
+            // requests such as offsetPip that may have been called prior to the transition.
+            mState = State.ENTERING_PIP;
+        });
+    }
+
+    private void applyEnterPipSyncTransaction(Rect destinationBounds, Runnable runnable) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
         wct.setBounds(mToken, destinationBounds);
@@ -497,22 +580,16 @@
             @Override
             public void onTransactionReady(int id, SurfaceControl.Transaction t) {
                 t.apply();
-                mUpdateHandler.post(() -> mPipAnimationController
-                        .getAnimator(mLeash, destinationBounds, 0f, 1f)
-                        .setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
-                        .setPipAnimationCallback(mPipAnimationCallback)
-                        .setDuration(durationMs)
-                        .start());
-                // mState is set right after the animation is kicked off to block any resize
-                // requests such as offsetPip that may have been called prior to the transition.
-                mState = State.ENTERING_PIP;
+                if (runnable != null) {
+                    runnable.run();
+                }
             }
         });
     }
 
     private void sendOnPipTransitionStarted(
             @PipAnimationController.TransitionDirection int direction) {
-        final Rect pipBounds = new Rect(mLastReportedBounds);
+        final Rect pipBounds = mPipBoundsState.getBounds();
         runOnMainHandler(() -> {
             for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
                 final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
@@ -523,6 +600,9 @@
 
     private void sendOnPipTransitionFinished(
             @PipAnimationController.TransitionDirection int direction) {
+        if (direction == TRANSITION_DIRECTION_TO_PIP) {
+            mState = State.ENTERED_PIP;
+        }
         runOnMainHandler(() -> {
             for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
                 final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
@@ -606,7 +686,7 @@
     /**
      * Note that dismissing PiP is now originated from SystemUI, see {@link #exitPip(int)}.
      * Meanwhile this callback is invoked whenever the task is removed. For instance:
-     *   - as a result of removeStacksInWindowingModes from WM
+     *   - as a result of removeRootTasksInWindowingModes from WM
      *   - activity itself is died
      * Nevertheless, we simply update the internal state here as all the heavy lifting should
      * have been done in WM.
@@ -631,14 +711,15 @@
     @Override
     public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
         Objects.requireNonNull(mToken, "onTaskInfoChanged requires valid existing mToken");
+        mPipBoundsState.setLastPipComponentName(info.topActivity);
         final PictureInPictureParams newParams = info.pictureInPictureParams;
         if (newParams == null || !applyPictureInPictureParams(newParams)) {
             Log.d(TAG, "Ignored onTaskInfoChanged with PiP param: " + newParams);
             return;
         }
+        // Aspect ratio changed, re-calculate destination bounds.
         final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
-                info.topActivity, getAspectRatioOrDefault(newParams),
-                mLastReportedBounds, getMinimalSize(info.topActivityInfo),
+                mPipBoundsState.getBounds(), getMinimalSize(info.topActivityInfo),
                 true /* userCurrentMinEdgeSize */);
         Objects.requireNonNull(destinationBounds, "Missing destination bounds");
         scheduleAnimateResizePip(destinationBounds, mEnterExitAnimationDuration,
@@ -654,7 +735,6 @@
     public void onFixedRotationFinished(int displayId) {
         if (mShouldDeferEnteringPip && mState.isInPip()) {
             final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
-                    mTaskInfo.topActivity, getAspectRatioOrDefault(mPictureInPictureParams),
                     null /* bounds */, getMinimalSize(mTaskInfo.topActivityInfo));
             // schedule a regular animation to ensure all the callbacks are still being sent
             enterPipWithAlphaAnimation(destinationBounds, 0 /* durationMs */);
@@ -696,7 +776,7 @@
                     sendOnPipTransitionCancelled(direction);
                     sendOnPipTransitionFinished(direction);
                 }
-                mLastReportedBounds.set(destinationBoundsOut);
+                mPipBoundsState.setBounds(destinationBoundsOut);
 
                 // Create a reset surface transaction for the new bounds and update the window
                 // container transaction
@@ -711,8 +791,8 @@
                         destinationBoundsOut.set(animator.getDestinationBounds());
                     }
                 } else {
-                    if (!mLastReportedBounds.isEmpty()) {
-                        destinationBoundsOut.set(mLastReportedBounds);
+                    if (!mPipBoundsState.getBounds().isEmpty()) {
+                        destinationBoundsOut.set(mPipBoundsState.getBounds());
                     }
                 }
             }
@@ -727,9 +807,8 @@
             return;
         }
 
-        final Rect newDestinationBounds = mPipBoundsHandler.getDestinationBounds(
-                mTaskInfo.topActivity, getAspectRatioOrDefault(mPictureInPictureParams),
-                null /* bounds */, getMinimalSize(mTaskInfo.topActivityInfo));
+        final Rect newDestinationBounds = mPipBoundsHandler.getDestinationBounds(null /* bounds */,
+                getMinimalSize(mTaskInfo.topActivityInfo));
         if (newDestinationBounds.equals(currentDestinationBounds)) return;
         if (animator.getAnimationType() == ANIM_TYPE_BOUNDS) {
             animator.updateEndValue(newDestinationBounds);
@@ -750,7 +829,7 @@
                 params.getAspectRatioRational());
         mPictureInPictureParams = params;
         if (aspectRatioChanged) {
-            mPipBoundsHandler.onAspectRatioChanged(params.getAspectRatio());
+            mPipBoundsState.setAspectRatio(params.getAspectRatio());
         }
         return aspectRatioChanged;
     }
@@ -764,7 +843,7 @@
             Log.d(TAG, "skip scheduleAnimateResizePip, entering pip deferred");
             return;
         }
-        scheduleAnimateResizePip(mLastReportedBounds, toBounds, null /* sourceHintRect */,
+        scheduleAnimateResizePip(mPipBoundsState.getBounds(), toBounds, null /* sourceHintRect */,
                 TRANSITION_DIRECTION_NONE, duration, updateBoundsCallback);
     }
 
@@ -900,7 +979,7 @@
             Log.w(TAG, "Abort animation, invalid leash");
             return;
         }
-        mLastReportedBounds.set(destinationBounds);
+        mPipBoundsState.setBounds(destinationBounds);
         final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
         mSurfaceTransactionHelper
                 .crop(tx, mLeash, destinationBounds)
@@ -936,7 +1015,7 @@
             throw new RuntimeException("Callers should call scheduleResizePip() instead of this "
                     + "directly");
         }
-        mLastReportedBounds.set(destinationBounds);
+        mPipBoundsState.setBounds(destinationBounds);
         if (direction == TRANSITION_DIRECTION_REMOVE_STACK) {
             removePipImmediately();
             return;
@@ -1068,6 +1147,7 @@
     /**
      * Dumps internal states.
      */
+    @Override
     public void dump(PrintWriter pw, String prefix) {
         final String innerPrefix = prefix + "  ";
         pw.println(prefix + TAG);
@@ -1078,7 +1158,6 @@
         pw.println(innerPrefix + "mState=" + mState);
         pw.println(innerPrefix + "mOneShotAnimationType=" + mOneShotAnimationType);
         pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams);
-        pw.println(innerPrefix + "mLastReportedBounds=" + mLastReportedBounds);
         pw.println(innerPrefix + "mInitialState:");
         for (Map.Entry<IBinder, Configuration> e : mInitialState.entrySet()) {
             pw.println(innerPrefix + "  binder=" + e.getKey()
@@ -1086,6 +1165,11 @@
         }
     }
 
+    @Override
+    public String toString() {
+        return TAG + ":" + taskListenerTypeToString(TASK_LISTENER_TYPE_PIP);
+    }
+
     /**
      * Callback interface for PiP transitions (both from and to PiP mode)
      */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
index fddd547..18b6922 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
@@ -15,6 +15,7 @@
  */
 package com.android.wm.shell.pip.phone;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.graphics.Rect;
 import android.graphics.Region;
@@ -28,6 +29,7 @@
 import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
 
 import com.android.wm.shell.R;
+import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipSnapAlgorithm;
 import com.android.wm.shell.pip.PipTaskOrganizer;
 
@@ -50,6 +52,7 @@
 
     private Context mContext;
     private Handler mHandler;
+    private final @NonNull PipBoundsState mPipBoundsState;
     private PipMotionHelper mMotionHelper;
     private PipTaskOrganizer mTaskOrganizer;
     private PipSnapAlgorithm mSnapAlgorithm;
@@ -62,12 +65,14 @@
     private final Rect mExpandedMovementBounds = new Rect();
     private Rect mTmpBounds = new Rect();
 
-    public PipAccessibilityInteractionConnection(Context context, PipMotionHelper motionHelper,
+    public PipAccessibilityInteractionConnection(Context context,
+            @NonNull PipBoundsState pipBoundsState, PipMotionHelper motionHelper,
             PipTaskOrganizer taskOrganizer, PipSnapAlgorithm snapAlgorithm,
             AccessibilityCallbacks callbacks, Runnable updateMovementBoundCallback,
             Handler handler) {
         mContext = context;
         mHandler = handler;
+        mPipBoundsState = pipBoundsState;
         mMotionHelper = motionHelper;
         mTaskOrganizer = taskOrganizer;
         mSnapAlgorithm = snapAlgorithm;
@@ -148,7 +153,7 @@
 
     private void setToExpandedBounds() {
         float savedSnapFraction = mSnapAlgorithm.getSnapFraction(
-                new Rect(mTaskOrganizer.getLastReportedBounds()), mNormalMovementBounds);
+                mPipBoundsState.getBounds(), mNormalMovementBounds);
         mSnapAlgorithm.applySnapFraction(mExpandedBounds, mExpandedMovementBounds,
                 savedSnapFraction);
         mTaskOrganizer.scheduleFinishResizePip(mExpandedBounds, (Rect bounds) -> {
@@ -159,7 +164,7 @@
 
     private void setToNormalBounds() {
         float savedSnapFraction = mSnapAlgorithm.getSnapFraction(
-                new Rect(mTaskOrganizer.getLastReportedBounds()), mExpandedMovementBounds);
+                mPipBoundsState.getBounds(), mExpandedMovementBounds);
         mSnapAlgorithm.applySnapFraction(mNormalBounds, mNormalMovementBounds, savedSnapFraction);
         mTaskOrganizer.scheduleFinishResizePip(mNormalBounds, (Rect bounds) -> {
             mMotionHelper.synchronizePinnedStackBounds();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 5193656..edc68e5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -17,31 +17,37 @@
 package com.android.wm.shell.pip.phone;
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
 
 import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection;
 
-import android.annotation.Nullable;
 import android.app.ActivityManager;
+import android.app.PictureInPictureParams;
 import android.app.RemoteAction;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.pm.ActivityInfo;
 import android.content.pm.ParceledListSlice;
 import android.graphics.Rect;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.util.Log;
+import android.util.Slog;
 import android.view.DisplayInfo;
 import android.view.IPinnedStackController;
 import android.window.WindowContainerTransaction;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 import com.android.wm.shell.WindowManagerShellWrapper;
 import com.android.wm.shell.common.DisplayChangeController;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.pip.PinnedStackListenerForwarder;
 import com.android.wm.shell.pip.Pip;
 import com.android.wm.shell.pip.PipBoundsHandler;
+import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipTaskOrganizer;
 
 import java.io.PrintWriter;
@@ -64,6 +70,7 @@
     private DisplayController mDisplayController;
     private PipAppOpsListener mAppOpsListener;
     private PipBoundsHandler mPipBoundsHandler;
+    private @NonNull PipBoundsState mPipBoundsState;
     private PipMediaController mMediaController;
     private PipTouchHandler mTouchHandler;
     private Consumer<Boolean> mPinnedStackAnimationRecentsCallback;
@@ -87,7 +94,7 @@
         }
         // If there is an animation running (ie. from a shelf offset), then ensure that we calculate
         // the bounds for the next orientation using the destination bounds of the animation
-        // TODO: Techincally this should account for movement animation bounds as well
+        // TODO: Technically this should account for movement animation bounds as well
         Rect currentBounds = mPipTaskOrganizer.getCurrentOrAnimatingBounds();
         final boolean changed = mPipBoundsHandler.onDisplayRotationChanged(mContext,
                 mTmpNormalBounds, currentBounds, mTmpInsetBounds, displayId, fromRotation,
@@ -96,7 +103,7 @@
             // If the pip was in the offset zone earlier, adjust the new bounds to the bottom of the
             // movement bounds
             mTouchHandler.adjustBoundsForRotation(mTmpNormalBounds,
-                    mPipTaskOrganizer.getLastReportedBounds(), mTmpInsetBounds);
+                    mPipBoundsState.getBounds(), mTmpInsetBounds);
 
             // The bounds are being applied to a specific snap fraction, so reset any known offsets
             // for the previous orientation before updating the movement bounds.
@@ -166,7 +173,13 @@
 
         @Override
         public void onActivityHidden(ComponentName componentName) {
-            mHandler.post(() -> mPipBoundsHandler.onResetReentryBounds(componentName));
+            mHandler.post(() -> {
+                if (componentName.equals(mPipBoundsState.getLastPipComponentName())) {
+                    // The activity was removed, we don't want to restore to the reentry state
+                    // saved for this component anymore.
+                    mPipBoundsState.setLastPipComponentName(null);
+                }
+            });
         }
 
         @Override
@@ -184,55 +197,47 @@
 
         @Override
         public void onAspectRatioChanged(float aspectRatio) {
+            // TODO(b/169373982): Remove this callback as it is redundant with PipTaskOrg params
+            // change.
             mHandler.post(() -> {
-                mPipBoundsHandler.onAspectRatioChanged(aspectRatio);
+                mPipBoundsState.setAspectRatio(aspectRatio);
                 mTouchHandler.onAspectRatioChanged();
             });
         }
     }
 
-    public PipController(Context context,
+    protected PipController(Context context,
             DisplayController displayController,
             PipAppOpsListener pipAppOpsListener,
             PipBoundsHandler pipBoundsHandler,
+            @NonNull PipBoundsState pipBoundsState,
             PipMediaController pipMediaController,
             PipMenuActivityController pipMenuActivityController,
             PipTaskOrganizer pipTaskOrganizer,
             PipTouchHandler pipTouchHandler,
             WindowManagerShellWrapper windowManagerShellWrapper
     ) {
-        mContext = context;
-
-        if (PipUtils.hasSystemFeature(mContext)) {
-            initController(context, displayController, pipAppOpsListener, pipBoundsHandler,
-                    pipMediaController, pipMenuActivityController, pipTaskOrganizer,
-                    pipTouchHandler, windowManagerShellWrapper);
-        } else {
-            Log.w(TAG, "Device not support PIP feature");
-        }
-    }
-
-    private void initController(Context context,
-            DisplayController displayController,
-            PipAppOpsListener pipAppOpsListener,
-            PipBoundsHandler pipBoundsHandler,
-            PipMediaController pipMediaController,
-            PipMenuActivityController pipMenuActivityController,
-            PipTaskOrganizer pipTaskOrganizer,
-            PipTouchHandler pipTouchHandler,
-            WindowManagerShellWrapper windowManagerShellWrapper) {
-
         // Ensure that we are the primary user's SystemUI.
         final int processUser = UserManager.get(context).getUserHandle();
         if (processUser != UserHandle.USER_SYSTEM) {
             throw new IllegalStateException("Non-primary Pip component not currently supported.");
         }
 
+        mContext = context;
         mWindowManagerShellWrapper = windowManagerShellWrapper;
         mDisplayController = displayController;
         mPipBoundsHandler = pipBoundsHandler;
+        mPipBoundsState = pipBoundsState;
         mPipTaskOrganizer = pipTaskOrganizer;
         mPipTaskOrganizer.registerPipTransitionCallback(this);
+        mPipTaskOrganizer.registerOnDisplayIdChangeCallback((int displayId) -> {
+            final DisplayInfo newDisplayInfo = new DisplayInfo();
+            displayController.getDisplay(displayId).getDisplayInfo(newDisplayInfo);
+            mPipBoundsHandler.onDisplayInfoChanged(newDisplayInfo);
+            updateMovementBounds(null /* toBounds */, false /* fromRotation */,
+                    false /* fromImeAdjustment */, false /* fromShelfAdustment */,
+                    null /* wct */);
+        });
         mMediaController = pipMediaController;
         mMenuController = pipMenuActivityController;
         mTouchHandler = pipTouchHandler;
@@ -250,7 +255,7 @@
             mWindowManagerShellWrapper.addPinnedStackListener(
                     new PipControllerPinnedStackListener());
         } catch (RemoteException e) {
-            Log.e(TAG, "Failed to register pinned stack listener", e);
+            Slog.e(TAG, "Failed to register pinned stack listener", e);
         }
     }
 
@@ -351,16 +356,18 @@
      */
     @Override
     public void setShelfHeight(boolean visible, int height) {
-        mHandler.post(() -> {
-            final int shelfHeight = visible ? height : 0;
-            final boolean changed = mPipBoundsHandler.setShelfHeight(visible, shelfHeight);
-            if (changed) {
-                mTouchHandler.onShelfVisibilityChanged(visible, shelfHeight);
-                updateMovementBounds(mPipTaskOrganizer.getLastReportedBounds(),
-                        false /* fromRotation */, false /* fromImeAdjustment */,
-                        true /* fromShelfAdjustment */, null /* windowContainerTransaction */);
-            }
-        });
+        mHandler.post(() -> setShelfHeightLocked(visible, height));
+    }
+
+    private void setShelfHeightLocked(boolean visible, int height) {
+        final int shelfHeight = visible ? height : 0;
+        final boolean changed = mPipBoundsHandler.setShelfHeight(visible, shelfHeight);
+        if (changed) {
+            mTouchHandler.onShelfVisibilityChanged(visible, shelfHeight);
+            updateMovementBounds(mPipBoundsState.getBounds(),
+                    false /* fromRotation */, false /* fromImeAdjustment */,
+                    true /* fromShelfAdjustment */, null /* windowContainerTransaction */);
+        }
     }
 
     @Override
@@ -374,11 +381,27 @@
     }
 
     @Override
+    public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
+            PictureInPictureParams pictureInPictureParams,
+            int launcherRotation, int shelfHeight) {
+        setShelfHeightLocked(shelfHeight > 0 /* visible */, shelfHeight);
+        mPipBoundsHandler.onDisplayRotationChangedNotInPip(mContext, launcherRotation);
+        return mPipTaskOrganizer.startSwipePipToHome(componentName, activityInfo,
+                pictureInPictureParams);
+    }
+
+    @Override
+    public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) {
+        mPipTaskOrganizer.stopSwipePipToHome(componentName, destinationBounds);
+    }
+
+    @Override
     public void onPipTransitionStarted(ComponentName activity, int direction, Rect pipBounds) {
         if (isOutPipDirection(direction)) {
             // Exiting PIP, save the reentry bounds to restore to when re-entering.
             updateReentryBounds(pipBounds);
-            mPipBoundsHandler.onSaveReentryBounds(activity, mReentryBounds);
+            final float snapFraction = mPipBoundsHandler.getSnapFraction(mReentryBounds);
+            mPipBoundsState.saveReentryState(mReentryBounds, snapFraction);
         }
         // Disable touches while the animation is running
         mTouchHandler.setTouchEnabled(false);
@@ -438,5 +461,26 @@
         mTouchHandler.dump(pw, innerPrefix);
         mPipBoundsHandler.dump(pw, innerPrefix);
         mPipTaskOrganizer.dump(pw, innerPrefix);
+        mPipBoundsState.dump(pw, innerPrefix);
+    }
+
+    /**
+     * Instantiates {@link PipController}, returns {@code null} if the feature not supported.
+     */
+    @Nullable
+    public static PipController create(Context context, DisplayController displayController,
+            PipAppOpsListener pipAppOpsListener, PipBoundsHandler pipBoundsHandler,
+            PipBoundsState pipBoundsState, PipMediaController pipMediaController,
+            PipMenuActivityController pipMenuActivityController,
+            PipTaskOrganizer pipTaskOrganizer, PipTouchHandler pipTouchHandler,
+            WindowManagerShellWrapper windowManagerShellWrapper) {
+        if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
+            Slog.w(TAG, "Device doesn't support Pip feature");
+            return null;
+        }
+
+        return new PipController(context, displayController, pipAppOpsListener, pipBoundsHandler,
+                pipBoundsState, pipMediaController, pipMenuActivityController,
+                pipTaskOrganizer, pipTouchHandler, windowManagerShellWrapper);
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
new file mode 100644
index 0000000..bebe5f9
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.wm.shell.pip.phone;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.drawable.TransitionDrawable;
+import android.os.Handler;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+
+import androidx.annotation.NonNull;
+import androidx.dynamicanimation.animation.DynamicAnimation;
+import androidx.dynamicanimation.animation.SpringForce;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.animation.PhysicsAnimator;
+import com.android.wm.shell.common.DismissCircleView;
+import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
+import com.android.wm.shell.pip.PipUiEventLogger;
+
+import kotlin.Unit;
+
+/**
+ * Handler of all Magnetized Object related code for PiP.
+ */
+public class PipDismissTargetHandler {
+
+    /* The multiplier to apply scale the target size by when applying the magnetic field radius */
+    private static final float MAGNETIC_FIELD_RADIUS_MULTIPLIER = 1.25f;
+
+    /** Duration of the dismiss scrim fading in/out. */
+    private static final int DISMISS_TRANSITION_DURATION_MS = 200;
+
+    /**
+     * MagnetizedObject wrapper for PIP. This allows the magnetic target library to locate and move
+     * PIP.
+     */
+    private final MagnetizedObject<Rect> mMagnetizedPip;
+
+    /**
+     * Container for the dismiss circle, so that it can be animated within the container via
+     * translation rather than within the WindowManager via slow layout animations.
+     */
+    private final ViewGroup mTargetViewContainer;
+
+    /** Circle view used to render the dismiss target. */
+    private final DismissCircleView mTargetView;
+
+    /**
+     * MagneticTarget instance wrapping the target view and allowing us to set its magnetic radius.
+     */
+    private final MagnetizedObject.MagneticTarget mMagneticTarget;
+
+    /** PhysicsAnimator instance for animating the dismiss target in/out. */
+    private final PhysicsAnimator<View> mMagneticTargetAnimator;
+
+    /** Default configuration to use for springing the dismiss target in/out. */
+    private final PhysicsAnimator.SpringConfig mTargetSpringConfig =
+            new PhysicsAnimator.SpringConfig(
+                    SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
+
+    /**
+     * Runnable that can be posted delayed to show the target. This needs to be saved as a member
+     * variable so we can pass it to removeCallbacks.
+     */
+    private Runnable mShowTargetAction = this::showDismissTargetMaybe;
+
+    // Allow dragging the PIP to a location to close it
+    private final boolean mEnableDismissDragToEdge;
+
+    private int mDismissAreaHeight;
+
+    private final Context mContext;
+    private final PipMotionHelper mMotionHelper;
+    private final PipUiEventLogger mPipUiEventLogger;
+    private final WindowManager mWindowManager;
+    private final Handler mHandler;
+
+    public PipDismissTargetHandler(Context context, PipUiEventLogger pipUiEventLogger,
+            PipMotionHelper motionHelper, Handler handler) {
+        mContext = context;
+        mPipUiEventLogger = pipUiEventLogger;
+        mMotionHelper = motionHelper;
+        mHandler = handler;
+        mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+
+        Resources res = context.getResources();
+        mEnableDismissDragToEdge = res.getBoolean(R.bool.config_pipEnableDismissDragToEdge);
+        mDismissAreaHeight = res.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height);
+
+        mTargetView = new DismissCircleView(context);
+        mTargetViewContainer = new FrameLayout(context);
+        mTargetViewContainer.setBackgroundDrawable(
+                context.getDrawable(R.drawable.floating_dismiss_gradient_transition));
+        mTargetViewContainer.setClipChildren(false);
+        mTargetViewContainer.addView(mTargetView);
+
+        mMagnetizedPip = mMotionHelper.getMagnetizedPip();
+        mMagneticTarget = mMagnetizedPip.addTarget(mTargetView, 0);
+        updateMagneticTargetSize();
+
+        mMagnetizedPip.setAnimateStuckToTarget(
+                (target, velX, velY, flung, after) -> {
+                    if (mEnableDismissDragToEdge) {
+                        mMotionHelper.animateIntoDismissTarget(target, velX, velY, flung, after);
+                    }
+                    return Unit.INSTANCE;
+                });
+        mMagnetizedPip.setMagnetListener(new MagnetizedObject.MagnetListener() {
+            @Override
+            public void onStuckToTarget(@NonNull MagnetizedObject.MagneticTarget target) {
+                // Show the dismiss target, in case the initial touch event occurred within the
+                // magnetic field radius.
+                if (mEnableDismissDragToEdge) {
+                    showDismissTargetMaybe();
+                }
+            }
+
+            @Override
+            public void onUnstuckFromTarget(@NonNull MagnetizedObject.MagneticTarget target,
+                    float velX, float velY, boolean wasFlungOut) {
+                if (wasFlungOut) {
+                    mMotionHelper.flingToSnapTarget(velX, velY, null /* endAction */);
+                    hideDismissTargetMaybe();
+                } else {
+                    mMotionHelper.setSpringingToTouch(true);
+                }
+            }
+
+            @Override
+            public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
+                mMotionHelper.notifyDismissalPending();
+
+                handler.post(() -> {
+                    mMotionHelper.animateDismiss();
+                    hideDismissTargetMaybe();
+                });
+
+                mPipUiEventLogger.log(
+                        PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_DRAG_TO_REMOVE);
+            }
+        });
+
+        mMagneticTargetAnimator = PhysicsAnimator.getInstance(mTargetView);
+    }
+
+    /**
+     * Potentially start consuming future motion events if PiP is currently near the magnetized
+     * object.
+     */
+    public boolean maybeConsumeMotionEvent(MotionEvent ev) {
+        return mMagnetizedPip.maybeConsumeMotionEvent(ev);
+    }
+
+    /**
+     * Update the magnet size.
+     */
+    public void updateMagneticTargetSize() {
+        if (mTargetView == null) {
+            return;
+        }
+
+        final Resources res = mContext.getResources();
+        final int targetSize = res.getDimensionPixelSize(R.dimen.dismiss_circle_size);
+        mDismissAreaHeight = res.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height);
+        final FrameLayout.LayoutParams newParams =
+                new FrameLayout.LayoutParams(targetSize, targetSize);
+        newParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
+        newParams.bottomMargin = mContext.getResources().getDimensionPixelSize(
+                R.dimen.floating_dismiss_bottom_margin);
+        mTargetView.setLayoutParams(newParams);
+
+        // Set the magnetic field radius equal to the target size from the center of the target
+        mMagneticTarget.setMagneticFieldRadiusPx(
+                (int) (targetSize * MAGNETIC_FIELD_RADIUS_MULTIPLIER));
+    }
+
+    /** Adds the magnetic target view to the WindowManager so it's ready to be animated in. */
+    public void createOrUpdateDismissTarget() {
+        if (!mTargetViewContainer.isAttachedToWindow()) {
+            mHandler.removeCallbacks(mShowTargetAction);
+            mMagneticTargetAnimator.cancel();
+
+            mTargetViewContainer.setVisibility(View.INVISIBLE);
+
+            try {
+                mWindowManager.addView(mTargetViewContainer, getDismissTargetLayoutParams());
+            } catch (IllegalStateException e) {
+                // This shouldn't happen, but if the target is already added, just update its layout
+                // params.
+                mWindowManager.updateViewLayout(
+                        mTargetViewContainer, getDismissTargetLayoutParams());
+            }
+        } else {
+            mWindowManager.updateViewLayout(mTargetViewContainer, getDismissTargetLayoutParams());
+        }
+    }
+
+    /** Returns layout params for the dismiss target, using the latest display metrics. */
+    private WindowManager.LayoutParams getDismissTargetLayoutParams() {
+        final Point windowSize = new Point();
+        mWindowManager.getDefaultDisplay().getRealSize(windowSize);
+
+        final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+                WindowManager.LayoutParams.MATCH_PARENT,
+                mDismissAreaHeight,
+                0, windowSize.y - mDismissAreaHeight,
+                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                        | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+                        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+                PixelFormat.TRANSLUCENT);
+
+        lp.setTitle("pip-dismiss-overlay");
+        lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+        lp.setFitInsetsTypes(0 /* types */);
+
+        return lp;
+    }
+
+    /** Makes the dismiss target visible and animates it in, if it isn't already visible. */
+    public void showDismissTargetMaybe() {
+        if (!mEnableDismissDragToEdge) {
+            return;
+        }
+
+        createOrUpdateDismissTarget();
+
+        if (mTargetViewContainer.getVisibility() != View.VISIBLE) {
+
+            mTargetView.setTranslationY(mTargetViewContainer.getHeight());
+            mTargetViewContainer.setVisibility(View.VISIBLE);
+
+            // Cancel in case we were in the middle of animating it out.
+            mMagneticTargetAnimator.cancel();
+            mMagneticTargetAnimator
+                    .spring(DynamicAnimation.TRANSLATION_Y, 0f, mTargetSpringConfig)
+                    .start();
+
+            ((TransitionDrawable) mTargetViewContainer.getBackground()).startTransition(
+                    DISMISS_TRANSITION_DURATION_MS);
+        }
+    }
+
+    /** Animates the magnetic dismiss target out and then sets it to GONE. */
+    public void hideDismissTargetMaybe() {
+        if (!mEnableDismissDragToEdge) {
+            return;
+        }
+
+        mHandler.removeCallbacks(mShowTargetAction);
+        mMagneticTargetAnimator
+                .spring(DynamicAnimation.TRANSLATION_Y,
+                        mTargetViewContainer.getHeight(),
+                        mTargetSpringConfig)
+                .withEndActions(() -> mTargetViewContainer.setVisibility(View.GONE))
+                .start();
+
+        ((TransitionDrawable) mTargetViewContainer.getBackground()).reverseTransition(
+                DISMISS_TRANSITION_DURATION_MS);
+    }
+
+    /**
+     * Removes the dismiss target and cancels any pending callbacks to show it.
+     */
+    public void cleanUpDismissTarget() {
+        mHandler.removeCallbacks(mShowTargetAction);
+
+        if (mTargetViewContainer.isAttachedToWindow()) {
+            mWindowManager.removeViewImmediate(mTargetViewContainer);
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMediaController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMediaController.java
index 4a8db6b..64e3758 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMediaController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMediaController.java
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.pip.phone;
 
+import static android.app.PendingIntent.FLAG_IMMUTABLE;
 import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
 
 import android.app.IActivityManager;
@@ -28,7 +29,6 @@
 import android.content.IntentFilter;
 import android.graphics.drawable.Icon;
 import android.media.session.MediaController;
-import android.media.session.MediaSession;
 import android.media.session.MediaSessionManager;
 import android.media.session.PlaybackState;
 import android.os.UserHandle;
@@ -153,8 +153,7 @@
         }
 
         ArrayList<RemoteAction> mediaActions = new ArrayList<>();
-        int state = mMediaController.getPlaybackState().getState();
-        boolean isPlaying = MediaSession.isActiveState(state);
+        boolean isPlaying = mMediaController.getPlaybackState().isActiveState();
         long actions = mMediaController.getPlaybackState().getActions();
 
         // Prev action
@@ -182,25 +181,25 @@
         mPauseAction = new RemoteAction(Icon.createWithResource(mContext,
                 R.drawable.pip_ic_pause_white), pauseDescription, pauseDescription,
                 PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_PAUSE),
-                        FLAG_UPDATE_CURRENT));
+                        FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE));
 
         String playDescription = mContext.getString(R.string.pip_play);
         mPlayAction = new RemoteAction(Icon.createWithResource(mContext,
                 R.drawable.pip_ic_play_arrow_white), playDescription, playDescription,
                 PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_PLAY),
-                        FLAG_UPDATE_CURRENT));
+                        FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE));
 
         String nextDescription = mContext.getString(R.string.pip_skip_to_next);
         mNextAction = new RemoteAction(Icon.createWithResource(mContext,
                 R.drawable.pip_ic_skip_next_white), nextDescription, nextDescription,
                 PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_NEXT),
-                        FLAG_UPDATE_CURRENT));
+                        FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE));
 
         String prevDescription = mContext.getString(R.string.pip_skip_to_prev);
         mPrevAction = new RemoteAction(Icon.createWithResource(mContext,
                 R.drawable.pip_ic_skip_previous_white), prevDescription, prevDescription,
                 PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_PREV),
-                        FLAG_UPDATE_CURRENT));
+                        FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE));
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActivityController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActivityController.java
index c53803a7..cd47d55 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActivityController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActivityController.java
@@ -156,20 +156,6 @@
     }
 
     /**
-     * Updates the appearance of the menu and scrim on top of the PiP while dismissing.
-     */
-    public void setDismissFraction(float fraction) {
-        final boolean isMenuVisible = isMenuVisible();
-        if (DEBUG) {
-            Log.d(TAG, "setDismissFraction() isMenuVisible=" + isMenuVisible
-                    + " fraction=" + fraction);
-        }
-        if (isMenuVisible) {
-            mPipMenuView.updateDismissFraction(fraction);
-        }
-    }
-
-    /**
      * Similar to {@link #showMenu(int, Rect, boolean, boolean, boolean)} but only show the menu
      * upon PiP window transition is finished.
      */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index 24e49f8..5195140 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -429,25 +429,6 @@
         }
     }
 
-    void updateDismissFraction(float fraction) {
-        int alpha;
-        final float menuAlpha = 1 - fraction;
-        if (mMenuState == MENU_STATE_FULL) {
-            mMenuContainer.setAlpha(menuAlpha);
-            mSettingsButton.setAlpha(menuAlpha);
-            mDismissButton.setAlpha(menuAlpha);
-            final float interpolatedAlpha =
-                    MENU_BACKGROUND_ALPHA * menuAlpha + DISMISS_BACKGROUND_ALPHA * fraction;
-            alpha = (int) (interpolatedAlpha * 255);
-        } else {
-            if (mMenuState == MENU_STATE_CLOSE) {
-                mDismissButton.setAlpha(menuAlpha);
-            }
-            alpha = (int) (fraction * DISMISS_BACKGROUND_ALPHA * 255);
-        }
-        mBackgroundDrawable.setAlpha(alpha);
-    }
-
     private void notifyMenuStateChange(int menuState, boolean resize, Runnable callback) {
         mMenuState = menuState;
         mController.onMenuStateChanged(menuState, resize, callback);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index cc86cf9..b5fa030 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -37,6 +37,7 @@
 import com.android.wm.shell.animation.PhysicsAnimator;
 import com.android.wm.shell.common.FloatingContentCoordinator;
 import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
+import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipSnapAlgorithm;
 import com.android.wm.shell.pip.PipTaskOrganizer;
 
@@ -66,6 +67,7 @@
 
     private final Context mContext;
     private final PipTaskOrganizer mPipTaskOrganizer;
+    private final @NonNull PipBoundsState mPipBoundsState;
 
     private PipMenuActivityController mMenuController;
     private PipSnapAlgorithm mSnapAlgorithm;
@@ -178,11 +180,12 @@
         public void onPipTransitionCanceled(ComponentName activity, int direction) {}
     };
 
-    public PipMotionHelper(Context context, PipTaskOrganizer pipTaskOrganizer,
-            PipMenuActivityController menuController, PipSnapAlgorithm snapAlgorithm,
-            FloatingContentCoordinator floatingContentCoordinator) {
+    public PipMotionHelper(Context context, @NonNull PipBoundsState pipBoundsState,
+            PipTaskOrganizer pipTaskOrganizer, PipMenuActivityController menuController,
+            PipSnapAlgorithm snapAlgorithm, FloatingContentCoordinator floatingContentCoordinator) {
         mContext = context;
         mPipTaskOrganizer = pipTaskOrganizer;
+        mPipBoundsState = pipBoundsState;
         mMenuController = menuController;
         mSnapAlgorithm = snapAlgorithm;
         mFloatingContentCoordinator = floatingContentCoordinator;
@@ -220,7 +223,7 @@
      */
     void synchronizePinnedStackBounds() {
         cancelAnimations();
-        mBounds.set(mPipTaskOrganizer.getLastReportedBounds());
+        mBounds.set(mPipBoundsState.getBounds());
         mTemporaryBounds.setEmpty();
 
         if (mPipTaskOrganizer.isInPip()) {
@@ -385,23 +388,20 @@
      * Flings the PiP to the closest snap target.
      */
     void flingToSnapTarget(
-            float velocityX, float velocityY,
-            @Nullable Runnable updateAction, @Nullable Runnable endAction) {
-        movetoTarget(velocityX, velocityY, updateAction, endAction, false /* isStash */);
+            float velocityX, float velocityY, @Nullable Runnable endAction) {
+        movetoTarget(velocityX, velocityY, endAction, false /* isStash */);
     }
 
     /**
      * Stash PiP to the closest edge.
      */
     void stashToEdge(
-            float velocityX, float velocityY,
-            @Nullable Runnable updateAction, @Nullable Runnable endAction) {
-        movetoTarget(velocityX, velocityY, updateAction, endAction, true /* isStash */);
+            float velocityX, float velocityY, @Nullable Runnable endAction) {
+        movetoTarget(velocityX, velocityY, endAction, true /* isStash */);
     }
 
     private void movetoTarget(
-            float velocityX, float velocityY,
-            @Nullable Runnable updateAction, @Nullable Runnable endAction, boolean isStash) {
+            float velocityX, float velocityY, @Nullable Runnable endAction, boolean isStash) {
         // If we're flinging to a snap target now, we're not springing to catch up to the touch
         // location now.
         mSpringingToTouch = false;
@@ -416,11 +416,6 @@
                         FloatProperties.RECT_Y, velocityY, mFlingConfigY, mSpringConfig)
                 .withEndActions(endAction);
 
-        if (updateAction != null) {
-            mTemporaryBoundsPhysicsAnimator.addUpdateListener(
-                    (target, values) -> updateAction.run());
-        }
-
         final float offset = ((float) mBounds.width()) * (1.0f - STASH_RATIO);
         final float leftEdge = isStash ? mMovementBounds.left - offset : mMovementBounds.left;
         final float rightEdge = isStash ?  mMovementBounds.right + offset : mMovementBounds.right;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 6b31772..a2233e5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -22,53 +22,39 @@
 import static com.android.wm.shell.pip.phone.PipMenuActivityController.MENU_STATE_FULL;
 import static com.android.wm.shell.pip.phone.PipMenuActivityController.MENU_STATE_NONE;
 
+import android.annotation.NonNull;
 import android.annotation.SuppressLint;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Resources;
-import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
-import android.graphics.drawable.TransitionDrawable;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.provider.DeviceConfig;
 import android.util.Log;
 import android.util.Size;
-import android.view.Gravity;
 import android.view.IPinnedStackController;
 import android.view.InputEvent;
 import android.view.MotionEvent;
-import android.view.View;
 import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityWindowInfo;
-import android.widget.FrameLayout;
-
-import androidx.annotation.NonNull;
-import androidx.dynamicanimation.animation.DynamicAnimation;
-import androidx.dynamicanimation.animation.SpringForce;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.wm.shell.R;
-import com.android.wm.shell.animation.PhysicsAnimator;
-import com.android.wm.shell.common.DismissCircleView;
 import com.android.wm.shell.common.FloatingContentCoordinator;
-import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
 import com.android.wm.shell.pip.PipAnimationController;
 import com.android.wm.shell.pip.PipBoundsHandler;
+import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipTaskOrganizer;
 import com.android.wm.shell.pip.PipUiEventLogger;
 
 import java.io.PrintWriter;
 
-import kotlin.Unit;
-
 /**
  * Manages all the touch handling for PIP on the Phone, including moving, dismissing and expanding
  * the PIP.
@@ -82,14 +68,13 @@
     /* The multiplier to apply scale the target size by when applying the magnetic field radius */
     private static final float MAGNETIC_FIELD_RADIUS_MULTIPLIER = 1.25f;
 
-    // Allow dragging the PIP to a location to close it
-    private final boolean mEnableDismissDragToEdge;
     // Allow PIP to resize to a slightly bigger state upon touch
     private final boolean mEnableResize;
     private final Context mContext;
-    private final WindowManager mWindowManager;
     private final PipBoundsHandler mPipBoundsHandler;
+    private final @NonNull PipBoundsState mPipBoundsState;
     private final PipUiEventLogger mPipUiEventLogger;
+    private final PipDismissTargetHandler mPipDismissTargetHandler;
 
     private PipResizeGestureHandler mPipResizeGestureHandler;
     private IPinnedStackController mPinnedStackController;
@@ -105,34 +90,6 @@
      */
     private boolean mEnableStash = false;
 
-    /**
-     * MagnetizedObject wrapper for PIP. This allows the magnetic target library to locate and move
-     * PIP.
-     */
-    private MagnetizedObject<Rect> mMagnetizedPip;
-
-    /**
-     * Container for the dismiss circle, so that it can be animated within the container via
-     * translation rather than within the WindowManager via slow layout animations.
-     */
-    private ViewGroup mTargetViewContainer;
-
-    /** Circle view used to render the dismiss target. */
-    private DismissCircleView mTargetView;
-
-    /**
-     * MagneticTarget instance wrapping the target view and allowing us to set its magnetic radius.
-     */
-    private MagnetizedObject.MagneticTarget mMagneticTarget;
-
-    /** PhysicsAnimator instance for animating the dismiss target in/out. */
-    private PhysicsAnimator<View> mMagneticTargetAnimator;
-
-    /** Default configuration to use for springing the dismiss target in/out. */
-    private final PhysicsAnimator.SpringConfig mTargetSpringConfig =
-            new PhysicsAnimator.SpringConfig(
-                    SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
-
     // The current movement bounds
     private Rect mMovementBounds = new Rect();
 
@@ -150,12 +107,6 @@
     private int mDeferResizeToNormalBoundsUntilRotation = -1;
     private int mDisplayRotation;
 
-    /**
-     * Runnable that can be posted delayed to show the target. This needs to be saved as a member
-     * variable so we can pass it to removeCallbacks.
-     */
-    private Runnable mShowTargetAction = this::showDismissTargetMaybe;
-
     private Handler mHandler = new Handler();
 
     // Behaviour states
@@ -163,7 +114,6 @@
     private boolean mIsImeShowing;
     private int mImeHeight;
     private int mImeOffset;
-    private int mDismissAreaHeight;
     private boolean mIsShelfShowing;
     private int mShelfHeight;
     private int mMovementBoundsExtraOffsets;
@@ -214,6 +164,7 @@
     public PipTouchHandler(Context context,
             PipMenuActivityController menuController,
             PipBoundsHandler pipBoundsHandler,
+            @NonNull PipBoundsState pipBoundsState,
             PipTaskOrganizer pipTaskOrganizer,
             FloatingContentCoordinator floatingContentCoordinator,
             PipUiEventLogger pipUiEventLogger) {
@@ -221,88 +172,34 @@
         mContext = context;
         mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
         mPipBoundsHandler = pipBoundsHandler;
-        mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+        mPipBoundsState = pipBoundsState;
         mMenuController = menuController;
         mMenuController.addListener(new PipMenuListener());
         mGesture = new DefaultPipTouchGesture();
-        mMotionHelper = new PipMotionHelper(mContext, pipTaskOrganizer, mMenuController,
-                mPipBoundsHandler.getSnapAlgorithm(), floatingContentCoordinator);
+        mMotionHelper = new PipMotionHelper(mContext, pipBoundsState, pipTaskOrganizer,
+                mMenuController, mPipBoundsHandler.getSnapAlgorithm(), floatingContentCoordinator);
         mPipResizeGestureHandler =
                 new PipResizeGestureHandler(context, pipBoundsHandler, mMotionHelper,
                         pipTaskOrganizer, this::getMovementBounds,
                         this::updateMovementBounds, pipUiEventLogger, menuController);
+        mPipDismissTargetHandler = new PipDismissTargetHandler(context, pipUiEventLogger,
+                mMotionHelper, mHandler);
         mTouchState = new PipTouchState(ViewConfiguration.get(context), mHandler,
                 () -> mMenuController.showMenuWithDelay(MENU_STATE_FULL, mMotionHelper.getBounds(),
                         true /* allowMenuTimeout */, willResizeMenu(), shouldShowResizeHandle()),
                 menuController::hideMenu);
 
         Resources res = context.getResources();
-        mEnableDismissDragToEdge = res.getBoolean(R.bool.config_pipEnableDismissDragToEdge);
         mEnableResize = res.getBoolean(R.bool.config_pipEnableResizeForMenu);
         reloadResources();
 
         mFloatingContentCoordinator = floatingContentCoordinator;
-        mConnection = new PipAccessibilityInteractionConnection(mContext, mMotionHelper,
-                pipTaskOrganizer, mPipBoundsHandler.getSnapAlgorithm(),
+        mConnection = new PipAccessibilityInteractionConnection(mContext, pipBoundsState,
+                mMotionHelper, pipTaskOrganizer, mPipBoundsHandler.getSnapAlgorithm(),
                 this::onAccessibilityShowMenu, this::updateMovementBounds, mHandler);
 
         mPipUiEventLogger = pipUiEventLogger;
 
-        mTargetView = new DismissCircleView(context);
-        mTargetViewContainer = new FrameLayout(context);
-        mTargetViewContainer.setBackgroundDrawable(
-                context.getDrawable(R.drawable.floating_dismiss_gradient_transition));
-        mTargetViewContainer.setClipChildren(false);
-        mTargetViewContainer.addView(mTargetView);
-
-        mMagnetizedPip = mMotionHelper.getMagnetizedPip();
-        mMagneticTarget = mMagnetizedPip.addTarget(mTargetView, 0);
-        updateMagneticTargetSize();
-
-        mMagnetizedPip.setAnimateStuckToTarget(
-                (target, velX, velY, flung, after) -> {
-                    if (mEnableDismissDragToEdge) {
-                        mMotionHelper.animateIntoDismissTarget(target, velX, velY, flung, after);
-                    }
-                    return Unit.INSTANCE;
-                });
-        mMagnetizedPip.setMagnetListener(new MagnetizedObject.MagnetListener() {
-            @Override
-            public void onStuckToTarget(@NonNull MagnetizedObject.MagneticTarget target) {
-                // Show the dismiss target, in case the initial touch event occurred within the
-                // magnetic field radius.
-                if (mEnableDismissDragToEdge) {
-                    showDismissTargetMaybe();
-                }
-            }
-
-            @Override
-            public void onUnstuckFromTarget(@NonNull MagnetizedObject.MagneticTarget target,
-                    float velX, float velY, boolean wasFlungOut) {
-                if (wasFlungOut) {
-                    mMotionHelper.flingToSnapTarget(velX, velY, null, null);
-                    hideDismissTarget();
-                } else {
-                    mMotionHelper.setSpringingToTouch(true);
-                }
-            }
-
-            @Override
-            public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
-                mMotionHelper.notifyDismissalPending();
-
-                mHandler.post(() -> {
-                    mMotionHelper.animateDismiss();
-                    hideDismissTarget();
-                });
-
-                mPipUiEventLogger.log(
-                        PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_DRAG_TO_REMOVE);
-            }
-        });
-
-        mMagneticTargetAnimator = PhysicsAnimator.getInstance(mTargetView);
-
         mEnableStash = DeviceConfig.getBoolean(
                 DeviceConfig.NAMESPACE_SYSTEMUI,
                 PIP_STASHING,
@@ -323,27 +220,7 @@
         mExpandedShortestEdgeSize = res.getDimensionPixelSize(
                 R.dimen.pip_expanded_shortest_edge_size);
         mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset);
-        mDismissAreaHeight = res.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height);
-        updateMagneticTargetSize();
-    }
-
-    private void updateMagneticTargetSize() {
-        if (mTargetView == null) {
-            return;
-        }
-
-        final Resources res = mContext.getResources();
-        final int targetSize = res.getDimensionPixelSize(R.dimen.dismiss_circle_size);
-        final FrameLayout.LayoutParams newParams =
-                new FrameLayout.LayoutParams(targetSize, targetSize);
-        newParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
-        newParams.bottomMargin = mContext.getResources().getDimensionPixelSize(
-                R.dimen.floating_dismiss_bottom_margin);
-        mTargetView.setLayoutParams(newParams);
-
-        // Set the magnetic field radius equal to the target size from the center of the target
-        mMagneticTarget.setMagneticFieldRadiusPx(
-                (int) (targetSize * MAGNETIC_FIELD_RADIUS_MULTIPLIER));
+        mPipDismissTargetHandler.updateMagneticTargetSize();
     }
 
     private boolean shouldShowResizeHandle() {
@@ -368,7 +245,7 @@
     }
 
     public void onActivityPinned() {
-        createOrUpdateDismissTarget();
+        mPipDismissTargetHandler.createOrUpdateDismissTarget();
 
         mShowPipMenuOnAnimationEnd = true;
         mPipResizeGestureHandler.onActivityPinned();
@@ -378,7 +255,7 @@
     public void onActivityUnpinned(ComponentName topPipActivity) {
         if (topPipActivity == null) {
             // Clean up state after the last PiP activity is removed
-            cleanUpDismissTarget();
+            mPipDismissTargetHandler.cleanUpDismissTarget();
 
             mFloatingContentCoordinator.onContentRemoved(mMotionHelper);
         }
@@ -409,7 +286,7 @@
         reloadResources();
 
         // Recreate the dismiss target for the new orientation.
-        createOrUpdateDismissTarget();
+        mPipDismissTargetHandler.createOrUpdateDismissTarget();
     }
 
     public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
@@ -553,94 +430,6 @@
         }
     }
 
-    /** Adds the magnetic target view to the WindowManager so it's ready to be animated in. */
-    private void createOrUpdateDismissTarget() {
-        if (!mTargetViewContainer.isAttachedToWindow()) {
-            mHandler.removeCallbacks(mShowTargetAction);
-            mMagneticTargetAnimator.cancel();
-
-            mTargetViewContainer.setVisibility(View.INVISIBLE);
-
-            try {
-                mWindowManager.addView(mTargetViewContainer, getDismissTargetLayoutParams());
-            } catch (IllegalStateException e) {
-                // This shouldn't happen, but if the target is already added, just update its layout
-                // params.
-                mWindowManager.updateViewLayout(
-                        mTargetViewContainer, getDismissTargetLayoutParams());
-            }
-        } else {
-            mWindowManager.updateViewLayout(mTargetViewContainer, getDismissTargetLayoutParams());
-        }
-    }
-
-    /** Returns layout params for the dismiss target, using the latest display metrics. */
-    private WindowManager.LayoutParams getDismissTargetLayoutParams() {
-        final Point windowSize = new Point();
-        mWindowManager.getDefaultDisplay().getRealSize(windowSize);
-
-        final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
-                WindowManager.LayoutParams.MATCH_PARENT,
-                mDismissAreaHeight,
-                0, windowSize.y - mDismissAreaHeight,
-                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
-                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
-                        | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
-                        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
-                PixelFormat.TRANSLUCENT);
-
-        lp.setTitle("pip-dismiss-overlay");
-        lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
-        lp.setFitInsetsTypes(0 /* types */);
-
-        return lp;
-    }
-
-    /** Makes the dismiss target visible and animates it in, if it isn't already visible. */
-    private void showDismissTargetMaybe() {
-        createOrUpdateDismissTarget();
-
-        if (mTargetViewContainer.getVisibility() != View.VISIBLE) {
-
-            mTargetView.setTranslationY(mTargetViewContainer.getHeight());
-            mTargetViewContainer.setVisibility(View.VISIBLE);
-
-            // Cancel in case we were in the middle of animating it out.
-            mMagneticTargetAnimator.cancel();
-            mMagneticTargetAnimator
-                    .spring(DynamicAnimation.TRANSLATION_Y, 0f, mTargetSpringConfig)
-                    .start();
-
-            ((TransitionDrawable) mTargetViewContainer.getBackground()).startTransition(
-                    DISMISS_TRANSITION_DURATION_MS);
-        }
-    }
-
-    /** Animates the magnetic dismiss target out and then sets it to GONE. */
-    private void hideDismissTarget() {
-        mHandler.removeCallbacks(mShowTargetAction);
-        mMagneticTargetAnimator
-                .spring(DynamicAnimation.TRANSLATION_Y,
-                        mTargetViewContainer.getHeight(),
-                        mTargetSpringConfig)
-                .withEndActions(() -> mTargetViewContainer.setVisibility(View.GONE))
-                .start();
-
-        ((TransitionDrawable) mTargetViewContainer.getBackground()).reverseTransition(
-                DISMISS_TRANSITION_DURATION_MS);
-    }
-
-    /**
-     * Removes the dismiss target and cancels any pending callbacks to show it.
-     */
-    private void cleanUpDismissTarget() {
-        mHandler.removeCallbacks(mShowTargetAction);
-
-        if (mTargetViewContainer.isAttachedToWindow()) {
-            mWindowManager.removeViewImmediate(mTargetViewContainer);
-        }
-    }
-
     /**
      * TODO Add appropriate description
      */
@@ -650,7 +439,7 @@
         if (!isRegistered && mTouchState.isUserInteracting()) {
             // If the input consumer is unregistered while the user is interacting, then we may not
             // get the final TOUCH_UP event, so clean up the dismiss target as well
-            cleanUpDismissTarget();
+            mPipDismissTargetHandler.cleanUpDismissTarget();
         }
     }
 
@@ -683,7 +472,7 @@
         }
 
         if ((ev.getAction() == MotionEvent.ACTION_DOWN || mTouchState.isUserInteracting())
-                && mMagnetizedPip.maybeConsumeMotionEvent(ev)) {
+                && mPipDismissTargetHandler.maybeConsumeMotionEvent(ev)) {
             // If the first touch event occurs within the magnetic field, pass the ACTION_DOWN event
             // to the touch state. Touch state needs a DOWN event in order to later process MOVE
             // events it'll receive if the object is dragged out of the magnetic field.
@@ -793,25 +582,6 @@
     }
 
     /**
-     * Updates the appearance of the menu and scrim on top of the PiP while dismissing.
-     */
-    private void updateDismissFraction() {
-        if (mMenuController != null) {
-            Rect bounds = mMotionHelper.getBounds();
-            final float target = mInsetBounds.bottom;
-            float fraction = 0f;
-            if (bounds.bottom > target) {
-                final float distance = bounds.bottom - target;
-                fraction = Math.min(distance / bounds.height(), 1f);
-            }
-            if (Float.compare(fraction, 0f) != 0 || mMenuController.isMenuVisible()) {
-                // Update if the fraction > 0, or if fraction == 0 and the menu was already visible
-                mMenuController.setDismissFraction(fraction);
-            }
-        }
-    }
-
-    /**
      * Sets the controller to update the system of changes from user interaction.
      */
     void setPinnedStackController(IPinnedStackController controller) {
@@ -958,13 +728,7 @@
 
             if (touchState.startedDragging()) {
                 mSavedSnapFraction = -1f;
-
-                if (mEnableDismissDragToEdge) {
-                    if (mTargetViewContainer.getVisibility() != View.VISIBLE) {
-                        mHandler.removeCallbacks(mShowTargetAction);
-                        showDismissTargetMaybe();
-                    }
-                }
+                mPipDismissTargetHandler.showDismissTargetMaybe();
             }
 
             if (touchState.isDragging()) {
@@ -995,9 +759,7 @@
 
         @Override
         public boolean onUp(PipTouchState touchState) {
-            if (mEnableDismissDragToEdge) {
-                hideDismissTarget();
-            }
+            mPipDismissTargetHandler.hideDismissTargetMaybe();
 
             if (!touchState.isUserInteracting()) {
                 return false;
@@ -1023,12 +785,9 @@
                 if (mEnableStash
                         && (animatingBounds.right > mPipBoundsHandler.getDisplayBounds().right
                         || animatingBounds.left < mPipBoundsHandler.getDisplayBounds().left)) {
-                    mMotionHelper.stashToEdge(vel.x, vel.y,
-                            PipTouchHandler.this::updateDismissFraction /* updateAction */,
-                            this::flingEndAction /* endAction */);
+                    mMotionHelper.stashToEdge(vel.x, vel.y, this::flingEndAction /* endAction */);
                 } else {
                     mMotionHelper.flingToSnapTarget(vel.x, vel.y,
-                            PipTouchHandler.this::updateDismissFraction /* updateAction */,
                             this::flingEndAction /* endAction */);
                 }
             } else if (mTouchState.isDoubleTap()) {
@@ -1122,7 +881,6 @@
         pw.println(innerPrefix + "mIsShelfShowing=" + mIsShelfShowing);
         pw.println(innerPrefix + "mShelfHeight=" + mShelfHeight);
         pw.println(innerPrefix + "mSavedSnapFraction=" + mSavedSnapFraction);
-        pw.println(innerPrefix + "mEnableDragToEdgeDismiss=" + mEnableDismissDragToEdge);
         pw.println(innerPrefix + "mMovementBoundsExtraOffsets=" + mMovementBoundsExtraOffsets);
         mPipBoundsHandler.dump(pw, innerPrefix);
         mTouchState.dump(pw, innerPrefix);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipUtils.java
index 6a58ce0..bd2ba32 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipUtils.java
@@ -18,7 +18,6 @@
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
 
 import android.app.ActivityTaskManager;
 import android.app.ActivityTaskManager.RootTaskInfo;
@@ -30,7 +29,6 @@
 import android.util.Pair;
 
 public class PipUtils {
-
     private static final String TAG = "PipUtils";
 
     /**
@@ -58,14 +56,4 @@
         }
         return new Pair<>(null, 0);
     }
-
-    /**
-     * The util to check if device has PIP feature
-     *
-     * @param context application context
-     * @return true if device has PIP feature, false otherwise.
-     */
-    public static boolean hasSystemFeature(Context context) {
-        return context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE);
-    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java
index 3eec20f..4f2d4e5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java
@@ -19,6 +19,10 @@
 import static android.app.ActivityTaskManager.INVALID_STACK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.content.Intent.ACTION_MEDIA_RESOURCE_GRANTED;
+
+import static com.android.wm.shell.pip.tv.PipNotification.ACTION_CLOSE;
+import static com.android.wm.shell.pip.tv.PipNotification.ACTION_MENU;
 
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
@@ -140,17 +144,26 @@
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            if (Intent.ACTION_MEDIA_RESOURCE_GRANTED.equals(action)) {
-                String[] packageNames = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
-                int resourceType = intent.getIntExtra(Intent.EXTRA_MEDIA_RESOURCE_TYPE,
-                        INVALID_RESOURCE_TYPE);
-                if (packageNames != null && packageNames.length > 0
-                        && resourceType == Intent.EXTRA_MEDIA_RESOURCE_TYPE_VIDEO_CODEC) {
-                    handleMediaResourceGranted(packageNames);
-                }
+            if (DEBUG) {
+                Log.d(TAG, "mBroadcastReceiver, action: " + intent.getAction());
             }
-
+            switch (intent.getAction()) {
+                case ACTION_MENU:
+                    showPictureInPictureMenu();
+                    break;
+                case ACTION_CLOSE:
+                    closePip();
+                    break;
+                case ACTION_MEDIA_RESOURCE_GRANTED:
+                    String[] packageNames = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
+                    int resourceType = intent.getIntExtra(Intent.EXTRA_MEDIA_RESOURCE_TYPE,
+                            INVALID_RESOURCE_TYPE);
+                    if (packageNames != null && packageNames.length > 0
+                            && resourceType == Intent.EXTRA_MEDIA_RESOURCE_TYPE_VIDEO_CODEC) {
+                        handleMediaResourceGranted(packageNames);
+                    }
+                    break;
+            }
         }
     };
     private final MediaSessionManager.OnActiveSessionsChangedListener mActiveMediaSessionListener =
@@ -233,8 +246,11 @@
             mPipTaskOrganizer = pipTaskOrganizer;
             mPipTaskOrganizer.registerPipTransitionCallback(this);
             mActivityTaskManager = ActivityTaskManager.getService();
-            IntentFilter intentFilter = new IntentFilter();
-            intentFilter.addAction(Intent.ACTION_MEDIA_RESOURCE_GRANTED);
+
+            final IntentFilter intentFilter = new IntentFilter();
+            intentFilter.addAction(ACTION_CLOSE);
+            intentFilter.addAction(ACTION_MENU);
+            intentFilter.addAction(ACTION_MEDIA_RESOURCE_GRANTED);
             mContext.registerReceiver(mBroadcastReceiver, intentFilter, UserHandle.USER_ALL);
 
             // Initialize the last orientation and apply the current configuration
@@ -249,10 +265,10 @@
             } catch (RemoteException e) {
                 Log.e(TAG, "Failed to register pinned stack listener", e);
             }
-        }
 
-        // TODO(b/169395392) Refactor PipMenuActivity to PipMenuView
-        PipMenuActivity.setPipController(this);
+            // TODO(b/169395392) Refactor PipMenuActivity to PipMenuView
+            PipMenuActivity.setPipController(this);
+        }
     }
 
     private void loadConfigurationsAndApply(Configuration newConfig) {
@@ -315,9 +331,9 @@
         mMediaSessionManager.removeOnActiveSessionsChangedListener(mActiveMediaSessionListener);
         if (removePipStack) {
             try {
-                mActivityTaskManager.removeStack(mPinnedStackId);
+                mActivityTaskManager.removeTask(mPinnedStackId);
             } catch (RemoteException e) {
-                Log.e(TAG, "removeStack failed", e);
+                Log.e(TAG, "removeTask failed", e);
             } finally {
                 mPinnedStackId = INVALID_STACK_ID;
             }
@@ -564,6 +580,7 @@
         } catch (RemoteException e) {
             Log.e(TAG, "getRootTaskInfo failed", e);
         }
+        if (DEBUG) Log.d(TAG, "getPinnedTaskInfo(), taskInfo=" + taskInfo);
         return taskInfo;
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuActivity.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuActivity.java
index 06d2408..e185a96 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuActivity.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuActivity.java
@@ -35,7 +35,7 @@
  */
 public class PipMenuActivity extends Activity implements PipController.Listener {
     private static final String TAG = "PipMenuActivity";
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    private static final boolean DEBUG = PipController.DEBUG;
 
     static final String EXTRA_CUSTOM_ACTIONS = "custom_actions";
 
@@ -51,7 +51,7 @@
         if (DEBUG) Log.d(TAG, "onCreate()");
 
         super.onCreate(bundle);
-        if (sPipController == null || sPipController.isPipShown()) {
+        if (sPipController == null) {
             finish();
         }
         setContentView(R.layout.tv_pip_menu);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java
index 7433085..f5bbd23 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java
@@ -20,10 +20,8 @@
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.RemoteAction;
-import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
@@ -32,9 +30,7 @@
 import android.media.MediaMetadata;
 import android.media.session.MediaController;
 import android.media.session.PlaybackState;
-import android.os.UserHandle;
 import android.text.TextUtils;
-import android.util.Log;
 
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.wm.shell.R;
@@ -49,8 +45,8 @@
     private static final String NOTIFICATION_TAG = PipNotification.class.getSimpleName();
     private static final boolean DEBUG = PipController.DEBUG;
 
-    private static final String ACTION_MENU = "PipNotification.menu";
-    private static final String ACTION_CLOSE = "PipNotification.close";
+    static final String ACTION_MENU = "PipNotification.menu";
+    static final String ACTION_CLOSE = "PipNotification.close";
 
     public static final String NOTIFICATION_CHANNEL_TVPIP = "TPP";
 
@@ -147,23 +143,6 @@
                 }
             };
 
-    private final BroadcastReceiver mEventReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (DEBUG) {
-                Log.d(TAG, "Received " + intent.getAction() + " from the notification UI");
-            }
-            switch (intent.getAction()) {
-                case ACTION_MENU:
-                    mPipController.showPictureInPictureMenu();
-                    break;
-                case ACTION_CLOSE:
-                    mPipController.closePip();
-                    break;
-            }
-        }
-    };
-
     public PipNotification(Context context, PipController pipController) {
         mPackageManager = context.getPackageManager();
 
@@ -182,11 +161,6 @@
         pipController.addListener(mPipListener);
         pipController.addMediaListener(mPipMediaListener);
 
-        IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(ACTION_MENU);
-        intentFilter.addAction(ACTION_CLOSE);
-        context.registerReceiver(mEventReceiver, intentFilter, UserHandle.USER_ALL);
-
         onConfigurationChanged(context);
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
index a0ce9da..f3dadfc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
@@ -26,7 +26,7 @@
 public enum ShellProtoLogGroup implements IProtoLogGroup {
     // NOTE: Since we enable these from the same WM ShellCommand, these names should not conflict
     // with those in the framework ProtoLogGroup
-    WM_SHELL_TASK_ORG(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+    WM_SHELL_TASK_ORG(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
             Consts.TAG_WM_SHELL),
     WM_SHELL_TRANSITIONS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
             Consts.TAG_WM_SHELL),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskOrganizer.java
index 30bc43b..f763d6d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskOrganizer.java
@@ -23,8 +23,10 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.view.Display.DEFAULT_DISPLAY;
 
+import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_SPLIT_SCREEN;
+import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString;
+
 import android.app.ActivityManager.RunningTaskInfo;
-import android.app.WindowConfiguration;
 import android.graphics.Rect;
 import android.os.RemoteException;
 import android.util.Log;
@@ -32,8 +34,12 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
 
+import androidx.annotation.NonNull;
+
 import com.android.wm.shell.ShellTaskOrganizer;
 
+import java.io.PrintWriter;
+
 class SplitScreenTaskOrganizer implements ShellTaskOrganizer.TaskListener {
     private static final String TAG = "SplitScreenTaskOrg";
     private static final boolean DEBUG = SplitScreenController.DEBUG;
@@ -56,17 +62,16 @@
                     ShellTaskOrganizer shellTaskOrganizer) {
         mSplitScreenController = splitScreenController;
         mTaskOrganizer = shellTaskOrganizer;
-        mTaskOrganizer.addListener(this, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
-                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+        mTaskOrganizer.addListenerForType(this, TASK_LISTENER_TYPE_SPLIT_SCREEN);
     }
 
     void init() throws RemoteException {
         synchronized (this) {
             try {
                 mPrimary = mTaskOrganizer.createRootTask(Display.DEFAULT_DISPLAY,
-                        WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+                        WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
                 mSecondary = mTaskOrganizer.createRootTask(Display.DEFAULT_DISPLAY,
-                        WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+                        WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
             } catch (Exception e) {
                 // teardown to prevent callbacks
                 mTaskOrganizer.removeListener(this);
@@ -229,4 +234,16 @@
             mSplitScreenController.ensureNormalSplit();
         }
     }
+
+    @Override
+    public void dump(@NonNull PrintWriter pw, String prefix) {
+        final String innerPrefix = prefix + "  ";
+        final String childPrefix = innerPrefix + "  ";
+        pw.println(prefix + this);
+    }
+
+    @Override
+    public String toString() {
+        return TAG + ":" + taskListenerTypeToString(TASK_LISTENER_TYPE_SPLIT_SCREEN);
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
new file mode 100644
index 0000000..0cedc0a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.wm.shell.flicker.helpers
+
+import android.app.Instrumentation
+import android.support.test.launcherhelper.ILauncherStrategy
+import android.support.test.launcherhelper.LauncherStrategyFactory
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.UiDevice
+import androidx.test.uiautomator.Until
+import com.android.server.wm.flicker.helpers.FIND_TIMEOUT
+import com.android.server.wm.flicker.helpers.waitForIME
+import org.junit.Assert
+
+open class ImeAppHelper(
+    instr: Instrumentation,
+    launcherName: String = "ImeApp",
+    launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
+            .getInstance(instr)
+            .launcherStrategy
+) : FlickerAppHelper(instr, launcherName, launcherStrategy) {
+    open fun openIME(device: UiDevice) {
+        val editText = device.wait(
+                Until.findObject(By.res(getPackage(), "plain_text_input")),
+                FIND_TIMEOUT)
+        Assert.assertNotNull("Text field not found, this usually happens when the device " +
+                "was left in an unknown state (e.g. in split screen)", editText)
+        editText.click()
+        if (!device.waitForIME()) {
+            Assert.fail("IME did not appear")
+        }
+    }
+
+    open fun closeIME(device: UiDevice) {
+        device.pressBack()
+        // Using only the AccessibilityInfo it is not possible to identify if the IME is active
+        device.waitForIdle(1000)
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
index c3c576d..010aa0d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -39,7 +39,7 @@
 
 /**
  * Test Pip launch.
- * To run this test: `atest FlickerTests:PipToAppTest`
+ * To run this test: `atest WMShellFlickerTests:PipToAppTest`
  */
 @RequiresDevice
 @RunWith(Parameterized::class)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
new file mode 100644
index 0000000..43e0225
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.wm.shell.flicker.pip
+
+import android.content.ComponentName
+import android.graphics.Region
+import android.support.test.launcherhelper.LauncherStrategyFactory
+import android.util.Log
+import android.view.Surface
+import android.view.WindowManager
+import androidx.test.filters.RequiresDevice
+import com.android.compatibility.common.util.SystemUtil
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.dsl.runWithFlicker
+import com.android.server.wm.flicker.helpers.closePipWindow
+import com.android.server.wm.flicker.helpers.hasPipWindow
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.wm.shell.flicker.helpers.ImeAppHelper
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+import java.io.IOException
+
+/**
+ * Test Pip launch.
+ * To run this test: `atest WMShellFlickerTests:PipKeyboardTest`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class PipKeyboardTest(
+    rotationName: String,
+    rotation: Int
+) : PipTestBase(rotationName, rotation) {
+    private val windowManager: WindowManager =
+            instrumentation.context.getSystemService(WindowManager::class.java)
+
+    private val keyboardApp = ImeAppHelper(instrumentation, "ImeApp",
+            LauncherStrategyFactory.getInstance(instrumentation).launcherStrategy)
+
+    private val KEYBOARD_ACTIVITY: ComponentName = ComponentName.createRelative(
+            "com.android.wm.shell.flicker.testapp", ".ImeActivity")
+    private val PIP_ACTIVITY_WINDOW_NAME = "PipActivity"
+    private val INPUT_METHOD_WINDOW_NAME = "InputMethod"
+
+    private val testRepetitions = 10
+
+    private val keyboardScenario: FlickerBuilder
+        get() = FlickerBuilder(instrumentation).apply {
+            repeat { testRepetitions }
+            // disable layer tracing
+            withLayerTracing { null }
+            setup {
+                test {
+                    device.wakeUpAndGoToHomeScreen()
+                    device.pressHome()
+                    // launch our target pip app
+                    testApp.open()
+                    this.setRotation(rotation)
+                    testApp.clickEnterPipButton(device)
+                    // open an app with an input field and a keyboard
+                    // UiAutomator doesn't support to launch the multiple Activities in a task.
+                    // So use launchActivity() for the Keyboard Activity.
+                    launchActivity(KEYBOARD_ACTIVITY)
+                }
+            }
+            teardown {
+                test {
+                    keyboardApp.exit()
+
+                    if (device.hasPipWindow()) {
+                        device.closePipWindow()
+                    }
+                    testApp.exit()
+                    this.setRotation(Surface.ROTATION_0)
+                }
+            }
+        }
+
+    /** Ensure the pip window remains visible throughout any keyboard interactions. */
+    @Test
+    fun pipWindow_doesNotLeaveTheScreen_onKeyboardOpenClose() {
+        val testTag = "pipWindow_doesNotLeaveTheScreen_onKeyboardOpenClose"
+        runWithFlicker(keyboardScenario) {
+            withTestName { testTag }
+            transitions {
+                // open the soft keyboard
+                keyboardApp.openIME(device)
+
+                // then close it again
+                keyboardApp.closeIME(device)
+            }
+            assertions {
+                windowManagerTrace {
+                    all("PiP window must remain inside visible bounds") {
+                        coversAtMostRegion(
+                                partialWindowTitle = "PipActivity",
+                                region = Region(windowManager.maximumWindowMetrics.bounds)
+                        )
+                    }
+                }
+            }
+        }
+    }
+
+    /** Ensure the pip window does not obscure the keyboard. */
+    @Test
+    fun pipWindow_doesNotObscure_keyboard() {
+        val testTag = "pipWindow_doesNotObscure_keyboard"
+        runWithFlicker(keyboardScenario) {
+            withTestName { testTag }
+            transitions {
+                // open the soft keyboard
+                keyboardApp.openIME(device)
+            }
+            teardown {
+                eachRun {
+                    // close the keyboard
+                    keyboardApp.closeIME(device)
+                }
+            }
+            assertions {
+                windowManagerTrace {
+                    end {
+                        isAboveWindow(INPUT_METHOD_WINDOW_NAME, PIP_ACTIVITY_WINDOW_NAME)
+                    }
+                }
+            }
+        }
+    }
+
+    private fun launchActivity(
+        activity: ComponentName? = null,
+        action: String? = null,
+        flags: Set<Int> = setOf(),
+        boolExtras: Map<String, Boolean> = mapOf(),
+        intExtras: Map<String, Int> = mapOf(),
+        stringExtras: Map<String, String> = mapOf()
+    ) {
+        require(activity != null || !action.isNullOrBlank()) {
+            "Cannot launch an activity with neither activity name nor action!"
+        }
+        val command = composeCommand(
+                "start", activity, action, flags, boolExtras, intExtras, stringExtras)
+        executeShellCommand(command)
+    }
+
+    private fun composeCommand(
+        command: String,
+        activity: ComponentName?,
+        action: String?,
+        flags: Set<Int>,
+        boolExtras: Map<String, Boolean>,
+        intExtras: Map<String, Int>,
+        stringExtras: Map<String, String>
+    ): String = buildString {
+        append("am ")
+        append(command)
+        activity?.let {
+            append(" -n ")
+            append(it.flattenToShortString())
+        }
+        action?.let {
+            append(" -a ")
+            append(it)
+        }
+        flags.forEach {
+            append(" -f ")
+            append(it)
+        }
+        boolExtras.forEach {
+            append(it.withFlag("ez"))
+        }
+        intExtras.forEach {
+            append(it.withFlag("ei"))
+        }
+        stringExtras.forEach {
+            append(it.withFlag("es"))
+        }
+    }
+
+    private fun Map.Entry<String, *>.withFlag(flag: String): String = " --$flag $key $value"
+
+    private fun executeShellCommand(cmd: String): String {
+        try {
+            return SystemUtil.runShellCommand(instrumentation, cmd)
+        } catch (e: IOException) {
+            Log.e("FlickerTests", "Error running shell command: $cmd")
+            throw e
+        }
+    }
+
+    companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): Collection<Array<Any>> {
+            val supportedRotations = intArrayOf(Surface.ROTATION_0)
+            return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
+        }
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml
index 95dc1d4..7f8321f 100644
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml
@@ -32,6 +32,23 @@
                 <action android:name="android.intent.action.MAIN"/>
                 <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
+            </intent-filter>
+        </activity>
+        <activity android:name=".ImeActivity"
+             android:taskAffinity="com.android.wm.shell.flicker.testapp.ImeActivity"
+             android:label="ImeApp"
+             android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
+            </intent-filter>
         </activity>
     </application>
 </manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_ime.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_ime.xml
new file mode 100644
index 0000000..4708cfd
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_ime.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:focusableInTouchMode="true"
+    android:background="@android:color/holo_green_light">
+    <EditText android:id="@+id/plain_text_input"
+              android:layout_height="wrap_content"
+              android:layout_width="match_parent"
+              android:inputType="text"/>
+</LinearLayout>
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/ImeActivity.java b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/ImeActivity.java
new file mode 100644
index 0000000..8567287
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/ImeActivity.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.testapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+public class ImeActivity extends Activity {
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        WindowManager.LayoutParams p = getWindow().getAttributes();
+        p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
+                .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+        getWindow().setAttributes(p);
+        setContentView(R.layout.activity_ime);
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
index 1bc5cea..07a6bda 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -19,16 +19,28 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 
+import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_MULTI_WINDOW;
+import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_PIP;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
 import android.app.ActivityManager.RunningTaskInfo;
+import android.content.pm.ParceledListSlice;
+import android.os.Binder;
+import android.os.IBinder;
 import android.os.RemoteException;
 import android.view.SurfaceControl;
 import android.window.ITaskOrganizer;
 import android.window.ITaskOrganizerController;
+import android.window.TaskAppearedInfo;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
@@ -79,18 +91,17 @@
         public void onTaskVanished(RunningTaskInfo taskInfo) {
             vanished.add(taskInfo);
         }
-
-        @Override
-        public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
-            // Not currently used
-        }
     }
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mOrganizer = new ShellTaskOrganizer(mTaskOrganizerController, mSyncTransactionQueue,
-                mTransactionPool, mTestExecutor, mTestExecutor);
+        try {
+            doReturn(ParceledListSlice.<TaskAppearedInfo>emptyList())
+                    .when(mTaskOrganizerController).registerTaskOrganizer(any());
+        } catch (RemoteException e) {}
+        mOrganizer = spy(new ShellTaskOrganizer(mTaskOrganizerController, mSyncTransactionQueue,
+                mTransactionPool, mTestExecutor, mTestExecutor));
     }
 
     @Test
@@ -101,10 +112,43 @@
     }
 
     @Test
-    public void testAppearedVanished() {
-        RunningTaskInfo taskInfo = createTaskInfo(WINDOWING_MODE_MULTI_WINDOW);
+    public void testOneListenerPerType() {
+        mOrganizer.addListenerForType(new TrackingTaskListener(), TASK_LISTENER_TYPE_MULTI_WINDOW);
+        try {
+            mOrganizer.addListenerForType(
+                    new TrackingTaskListener(), TASK_LISTENER_TYPE_MULTI_WINDOW);
+            fail("Expected exception due to already registered listener");
+        } catch (Exception e) {
+            // Expected failure
+        }
+    }
+
+    @Test
+    public void testRegisterWithExistingTasks() throws RemoteException {
+        // Setup some tasks
+        RunningTaskInfo task1 = createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW);
+        RunningTaskInfo task2 = createTaskInfo(2, WINDOWING_MODE_MULTI_WINDOW);
+        ArrayList<TaskAppearedInfo> taskInfos = new ArrayList<>();
+        taskInfos.add(new TaskAppearedInfo(task1, new SurfaceControl()));
+        taskInfos.add(new TaskAppearedInfo(task2, new SurfaceControl()));
+        doReturn(new ParceledListSlice(taskInfos))
+                .when(mTaskOrganizerController).registerTaskOrganizer(any());
+
+        // Register and expect the tasks to be stored
+        mOrganizer.registerOrganizer();
+
+        // Check that the tasks are next reported when the listener is added
         TrackingTaskListener listener = new TrackingTaskListener();
-        mOrganizer.addListener(listener, WINDOWING_MODE_MULTI_WINDOW);
+        mOrganizer.addListenerForType(listener, TASK_LISTENER_TYPE_MULTI_WINDOW);
+        assertTrue(listener.appeared.contains(task1));
+        assertTrue(listener.appeared.contains(task2));
+    }
+
+    @Test
+    public void testAppearedVanished() {
+        RunningTaskInfo taskInfo = createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW);
+        TrackingTaskListener listener = new TrackingTaskListener();
+        mOrganizer.addListenerForType(listener, TASK_LISTENER_TYPE_MULTI_WINDOW);
         mOrganizer.onTaskAppeared(taskInfo, null);
         assertTrue(listener.appeared.contains(taskInfo));
 
@@ -114,33 +158,93 @@
 
     @Test
     public void testAddListenerExistingTasks() {
-        RunningTaskInfo taskInfo = createTaskInfo(WINDOWING_MODE_MULTI_WINDOW);
+        RunningTaskInfo taskInfo = createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW);
         mOrganizer.onTaskAppeared(taskInfo, null);
 
         TrackingTaskListener listener = new TrackingTaskListener();
-        mOrganizer.addListener(listener, WINDOWING_MODE_MULTI_WINDOW);
+        mOrganizer.addListenerForType(listener, TASK_LISTENER_TYPE_MULTI_WINDOW);
         assertTrue(listener.appeared.contains(taskInfo));
     }
 
     @Test
     public void testWindowingModeChange() {
-        RunningTaskInfo taskInfo = createTaskInfo(WINDOWING_MODE_MULTI_WINDOW);
+        RunningTaskInfo taskInfo = createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW);
         TrackingTaskListener mwListener = new TrackingTaskListener();
         TrackingTaskListener pipListener = new TrackingTaskListener();
-        mOrganizer.addListener(mwListener, WINDOWING_MODE_MULTI_WINDOW);
-        mOrganizer.addListener(pipListener, WINDOWING_MODE_PINNED);
+        mOrganizer.addListenerForType(mwListener, TASK_LISTENER_TYPE_MULTI_WINDOW);
+        mOrganizer.addListenerForType(pipListener, TASK_LISTENER_TYPE_PIP);
         mOrganizer.onTaskAppeared(taskInfo, null);
         assertTrue(mwListener.appeared.contains(taskInfo));
         assertTrue(pipListener.appeared.isEmpty());
 
-        taskInfo = createTaskInfo(WINDOWING_MODE_PINNED);
+        taskInfo = createTaskInfo(1, WINDOWING_MODE_PINNED);
         mOrganizer.onTaskInfoChanged(taskInfo);
         assertTrue(mwListener.vanished.contains(taskInfo));
         assertTrue(pipListener.appeared.contains(taskInfo));
     }
 
-    private RunningTaskInfo createTaskInfo(int windowingMode) {
+    @Test
+    public void testAddListenerForTaskId_afterTypeListener() {
+        RunningTaskInfo task1 = createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW);
+        TrackingTaskListener mwListener = new TrackingTaskListener();
+        TrackingTaskListener task1Listener = new TrackingTaskListener();
+        mOrganizer.addListenerForType(mwListener, TASK_LISTENER_TYPE_MULTI_WINDOW);
+        mOrganizer.onTaskAppeared(task1, null);
+        assertTrue(mwListener.appeared.contains(task1));
+
+        // Add task 1 specific listener
+        mOrganizer.addListenerForTaskId(task1Listener, 1);
+        assertTrue(mwListener.vanished.contains(task1));
+        assertTrue(task1Listener.appeared.contains(task1));
+    }
+
+    @Test
+    public void testAddListenerForTaskId_beforeTypeListener() {
+        RunningTaskInfo task1 = createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW);
+        TrackingTaskListener mwListener = new TrackingTaskListener();
+        TrackingTaskListener task1Listener = new TrackingTaskListener();
+        mOrganizer.onTaskAppeared(task1, null);
+        mOrganizer.addListenerForTaskId(task1Listener, 1);
+        assertTrue(task1Listener.appeared.contains(task1));
+
+        mOrganizer.addListenerForType(mwListener, TASK_LISTENER_TYPE_MULTI_WINDOW);
+        assertFalse(mwListener.appeared.contains(task1));
+    }
+
+    @Test
+    public void testGetTaskListener() {
+        RunningTaskInfo task1 = createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW);
+
+        TrackingTaskListener mwListener = new TrackingTaskListener();
+        mOrganizer.addListenerForType(mwListener, TASK_LISTENER_TYPE_MULTI_WINDOW);
+
+        TrackingTaskListener cookieListener = new TrackingTaskListener();
+        IBinder cookie = new Binder();
+        task1.addLaunchCookie(cookie);
+        mOrganizer.setPendingLaunchCookieListener(cookie, cookieListener);
+
+        // Priority goes to the cookie listener so we would expect the task appear to show up there
+        // instead of the multi-window type listener.
+        mOrganizer.onTaskAppeared(task1, null);
+        assertTrue(cookieListener.appeared.contains(task1));
+        assertFalse(mwListener.appeared.contains(task1));
+
+        TrackingTaskListener task1Listener = new TrackingTaskListener();
+
+        boolean gotException = false;
+        try {
+            mOrganizer.addListenerForTaskId(task1Listener, 1);
+        } catch (Exception e) {
+            gotException = true;
+        }
+        // It should not be possible to add a task id listener for a task already mapped to a
+        // listener through cookie.
+        assertTrue(gotException);
+    }
+
+    private RunningTaskInfo createTaskInfo(int taskId, int windowingMode) {
         RunningTaskInfo taskInfo = new RunningTaskInfo();
+        taskInfo.taskId = taskId;
         taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode);
         return taskInfo;
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
index 5ff94b6..6d1a3c4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
@@ -25,6 +25,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -38,18 +39,17 @@
 import android.window.DisplayAreaInfo;
 import android.window.IWindowContainerToken;
 import android.window.WindowContainerToken;
+import android.window.WindowContainerTransaction;
 
 import androidx.test.filters.SmallTest;
 
 import com.android.wm.shell.common.DisplayController;
 
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.mockito.Spy;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -65,6 +65,7 @@
     OneHandedAnimationController.OneHandedTransitionAnimator mFakeAnimator;
     WindowContainerToken mToken;
     SurfaceControl mLeash;
+    TestableLooper mTestableLooper;
     @Mock
     IWindowContainerToken mMockRealToken;
     @Mock
@@ -77,12 +78,16 @@
     DisplayController mMockDisplayController;
     @Mock
     SurfaceControl mMockLeash;
-    @Spy
-    Handler mUpdateHandler;
+    @Mock
+    WindowContainerTransaction mMockWindowContainerTransaction;
+
+    Handler mSpyUpdateHandler;
+    Handler.Callback mUpdateCallback = (msg) -> false;
 
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
+        mTestableLooper = TestableLooper.get(this);
         mToken = new WindowContainerToken(mMockRealToken);
         mLeash = new SurfaceControl();
         mDisplay = mContext.getDisplay();
@@ -107,17 +112,14 @@
                 mMockDisplayController,
                 mMockAnimationController,
                 mTutorialHandler);
-        mUpdateHandler = mDisplayAreaOrganizer.getUpdateHandler();
-    }
-
-    @Test
-    public void testGetDisplayAreaUpdateHandler_isNotNull() {
-        assertThat(mUpdateHandler).isNotNull();
+        mSpyUpdateHandler = spy(new Handler(OneHandedThread.get().getLooper(), mUpdateCallback));
+        mDisplayAreaOrganizer.setUpdateHandler(mSpyUpdateHandler);
     }
 
     @Test
     public void testOnDisplayAreaAppeared() {
         mDisplayAreaOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
+        mTestableLooper.processAllMessages();
 
         verify(mMockAnimationController, never()).getAnimator(any(), any(), any());
     }
@@ -125,162 +127,195 @@
     @Test
     public void testOnDisplayAreaVanished() {
         mDisplayAreaOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
+        mTestableLooper.processAllMessages();
         mDisplayAreaOrganizer.onDisplayAreaVanished(mDisplayAreaInfo);
+
+        assertThat(mDisplayAreaOrganizer.mDisplayAreaMap).isEmpty();
     }
 
     @Test
-    public void testOnDisplayAreaInfoChanged_updateDisplayAreaInfo() {
-        final DisplayAreaInfo newDisplayAreaInfo = new DisplayAreaInfo(mToken, DEFAULT_DISPLAY,
-                FEATURE_ONE_HANDED);
-        mDisplayAreaOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
-        mDisplayAreaOrganizer.onDisplayAreaInfoChanged(newDisplayAreaInfo);
-
-        assertThat(mDisplayAreaOrganizer.mDisplayAreaMap.containsKey(mDisplayAreaInfo)).isTrue();
-    }
-
-    @Ignore("b/160848002")
-    @Test
     public void testScheduleOffset() {
         final int xOffSet = 0;
         final int yOffSet = 100;
-
-        TestableLooper.get(this).processAllMessages();
-        mDisplayAreaOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
         mDisplayAreaOrganizer.scheduleOffset(xOffSet, yOffSet);
+        mTestableLooper.processAllMessages();
 
-        assertThat(mUpdateHandler.hasMessages(
-                OneHandedDisplayAreaOrganizer.MSG_OFFSET_ANIMATE)).isEqualTo(true);
+        verify(mSpyUpdateHandler).sendMessage(any());
     }
 
-    @Ignore("b/160848002")
     @Test
-    public void testRotation_portraitToLandscape() {
+    public void testRotation_portrait_0_to_landscape_90() {
         when(mMockLeash.isValid()).thenReturn(false);
         // Rotate 0 -> 90
-        TestableLooper.get(this).processAllMessages();
-        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_90);
+        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_90,
+                mMockWindowContainerTransaction);
+        mTestableLooper.processAllMessages();
 
-        assertThat(mUpdateHandler.hasMessages(
-                OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(true);
-
-        // Rotate 0 -> 270
-        TestableLooper.get(this).processAllMessages();
-        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_270);
-
-        assertThat(mUpdateHandler.hasMessages(
-                OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(true);
-
-        // Rotate 180 -> 90
-        TestableLooper.get(this).processAllMessages();
-        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_90);
-
-        assertThat(mUpdateHandler.hasMessages(
-                OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(true);
-
-        // Rotate 180 -> 270
-        TestableLooper.get(this).processAllMessages();
-        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_270);
-
-        assertThat(mUpdateHandler.hasMessages(
-                OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(true);
+        verify(mSpyUpdateHandler).sendMessage(any());
     }
 
-    @Ignore("b/160848002")
     @Test
-    public void testRotation_landscapeToPortrait() {
+    public void testRotation_portrait_0_to_seascape_270() {
+        when(mMockLeash.isValid()).thenReturn(false);
+        // Rotate 0 -> 270
+        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_270,
+                mMockWindowContainerTransaction);
+        mTestableLooper.processAllMessages();
+
+        verify(mSpyUpdateHandler).sendMessage(any());
+    }
+
+    @Test
+    public void testRotation_portrait_180_to_landscape_90() {
+        when(mMockLeash.isValid()).thenReturn(false);
+        // Rotate 180 -> 90
+        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_90,
+                mMockWindowContainerTransaction);
+        mTestableLooper.processAllMessages();
+
+        verify(mSpyUpdateHandler).sendMessage(any());
+    }
+
+    @Test
+    public void testRotation_portrait_180_to_seascape_270() {
+        when(mMockLeash.isValid()).thenReturn(false);
+        // Rotate 180 -> 270
+        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_270,
+                mMockWindowContainerTransaction);
+        mTestableLooper.processAllMessages();
+
+        verify(mSpyUpdateHandler).sendMessage(any());
+    }
+
+    @Test
+    public void testRotation_landscape_90_to_portrait_0() {
         when(mMockLeash.isValid()).thenReturn(false);
         // Rotate 90 -> 0
-        TestableLooper.get(this).processAllMessages();
-        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_0);
+        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_0,
+                mMockWindowContainerTransaction);
+        mTestableLooper.processAllMessages();
 
-        assertThat(mUpdateHandler.hasMessages(
-                OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(true);
-
-        // Rotate 90 -> 180
-        TestableLooper.get(this).processAllMessages();
-        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_180);
-
-        assertThat(mUpdateHandler.hasMessages(
-                OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(true);
-
-        // Rotate 270 -> 0
-        TestableLooper.get(this).processAllMessages();
-        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_0);
-
-        assertThat(mUpdateHandler.hasMessages(
-                OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(true);
-
-        // Rotate 270 -> 180
-        TestableLooper.get(this).processAllMessages();
-        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_180);
-
-        assertThat(mUpdateHandler.hasMessages(
-                OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(true);
+        verify(mSpyUpdateHandler).sendMessage(any());
     }
 
-    @Ignore("b/160848002")
     @Test
-    public void testRotation_portraitToPortrait() {
+    public void testRotation_landscape_90_to_portrait_180() {
+        when(mMockLeash.isValid()).thenReturn(false);
+        // Rotate 90 -> 180
+        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_180,
+                mMockWindowContainerTransaction);
+        mTestableLooper.processAllMessages();
+
+        verify(mSpyUpdateHandler).sendMessage(any());
+    }
+
+    @Test
+    public void testRotation_Seascape_270_to_portrait_0() {
+        when(mMockLeash.isValid()).thenReturn(false);
+        // Rotate 270 -> 0
+        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_0,
+                mMockWindowContainerTransaction);
+        mTestableLooper.processAllMessages();
+
+        verify(mSpyUpdateHandler).sendMessage(any());
+    }
+
+    @Test
+    public void testRotation_seascape_90_to_portrait_180() {
+        when(mMockLeash.isValid()).thenReturn(false);
+        // Rotate 270 -> 180
+        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_180,
+                mMockWindowContainerTransaction);
+        mTestableLooper.processAllMessages();
+
+        verify(mSpyUpdateHandler).sendMessage(any());
+    }
+
+    @Test
+    public void testRotation_portrait_0_to_portrait_0() {
         when(mMockLeash.isValid()).thenReturn(false);
         // Rotate 0 -> 0
-        TestableLooper.get(this).processAllMessages();
-        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_0);
+        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_0,
+                mMockWindowContainerTransaction);
+        mTestableLooper.processAllMessages();
 
-        assertThat(mUpdateHandler.hasMessages(
-                OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(false);
-
-        // Rotate 0 -> 180
-        TestableLooper.get(this).processAllMessages();
-        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_180);
-
-        assertThat(mUpdateHandler.hasMessages(
-                OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(false);
-
-        // Rotate 180 -> 180
-        TestableLooper.get(this).processAllMessages();
-        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_180);
-
-        assertThat(mUpdateHandler.hasMessages(
-                OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(false);
-
-        // Rotate 180 -> 180
-        TestableLooper.get(this).processAllMessages();
-        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_0);
-
-        assertThat(mUpdateHandler.hasMessages(
-                OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(false);
+        verify(mSpyUpdateHandler, never()).sendMessage(any());
     }
 
-    @Ignore("b/160848002")
     @Test
-    public void testRotation_landscapeToLandscape() {
+    public void testRotation_portrait_0_to_portrait_180() {
+        when(mMockLeash.isValid()).thenReturn(false);
+        // Rotate 0 -> 180
+        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_180,
+                mMockWindowContainerTransaction);
+        mTestableLooper.processAllMessages();
+
+        verify(mSpyUpdateHandler, never()).sendMessage(any());
+    }
+
+    @Test
+    public void testRotation_portrait_180_to_portrait_180() {
+        when(mMockLeash.isValid()).thenReturn(false);
+        // Rotate 180 -> 180
+        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_180,
+                mMockWindowContainerTransaction);
+        mTestableLooper.processAllMessages();
+
+        verify(mSpyUpdateHandler, never()).sendMessage(any());
+    }
+
+    @Test
+    public void testRotation_portrait_180_to_portrait_0() {
+        when(mMockLeash.isValid()).thenReturn(false);
+        // Rotate 180 -> 0
+        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_0,
+                mMockWindowContainerTransaction);
+        mTestableLooper.processAllMessages();
+
+        verify(mSpyUpdateHandler, never()).sendMessage(any());
+    }
+
+    @Test
+    public void testRotation_landscape_90_to_landscape_90() {
         when(mMockLeash.isValid()).thenReturn(false);
         // Rotate 90 -> 90
-        TestableLooper.get(this).processAllMessages();
-        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_90);
+        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_90,
+                mMockWindowContainerTransaction);
+        mTestableLooper.processAllMessages();
 
-        assertThat(mUpdateHandler.hasMessages(
-                OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(false);
+        verify(mSpyUpdateHandler, never()).sendMessage(any());
+    }
 
+    @Test
+    public void testRotation_landscape_90_to_seascape_270() {
+        when(mMockLeash.isValid()).thenReturn(false);
         // Rotate 90 -> 270
-        TestableLooper.get(this).processAllMessages();
-        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_270);
+        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_270,
+                mMockWindowContainerTransaction);
+        mTestableLooper.processAllMessages();
 
-        assertThat(mUpdateHandler.hasMessages(
-                OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(false);
+        verify(mSpyUpdateHandler, never()).sendMessage(any());
+    }
 
+    @Test
+    public void testRotation_seascape_270_to_seascape_270() {
+        when(mMockLeash.isValid()).thenReturn(false);
         // Rotate 270 -> 270
-        TestableLooper.get(this).processAllMessages();
-        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_270);
+        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_270,
+                mMockWindowContainerTransaction);
+        mTestableLooper.processAllMessages();
 
-        assertThat(mUpdateHandler.hasMessages(
-                OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(false);
+        verify(mSpyUpdateHandler, never()).sendMessage(any());
+    }
 
+    @Test
+    public void testRotation_seascape_90_to_landscape_90() {
+        when(mMockLeash.isValid()).thenReturn(false);
         // Rotate 270 -> 90
-        TestableLooper.get(this).processAllMessages();
-        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_90);
+        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_90,
+                mMockWindowContainerTransaction);
+        mTestableLooper.processAllMessages();
 
-        assertThat(mUpdateHandler.hasMessages(
-                OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(false);
+        verify(mSpyUpdateHandler, never()).sendMessage(any());
     }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
index 7d51886..255e749 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.pip;
+package com.android.wm.shell.pip;
 
 import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP;
 import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java
index f514b0b..39117bb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java
@@ -14,13 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.systemui.pip;
+package com.android.wm.shell.pip;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import android.content.ComponentName;
 import android.graphics.Rect;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -58,15 +57,13 @@
 
     private PipBoundsHandler mPipBoundsHandler;
     private DisplayInfo mDefaultDisplayInfo;
-    private ComponentName mTestComponentName1;
-    private ComponentName mTestComponentName2;
+    private PipBoundsState mPipBoundsState;
 
     @Before
     public void setUp() throws Exception {
         initializeMockResources();
-        mPipBoundsHandler = new PipBoundsHandler(mContext);
-        mTestComponentName1 = new ComponentName(mContext, "component1");
-        mTestComponentName2 = new ComponentName(mContext, "component2");
+        mPipBoundsState = new PipBoundsState();
+        mPipBoundsHandler = new PipBoundsHandler(mContext, mPipBoundsState);
 
         mPipBoundsHandler.onDisplayInfoChanged(mDefaultDisplayInfo);
     }
@@ -126,8 +123,9 @@
                 (MAX_ASPECT_RATIO + DEFAULT_ASPECT_RATIO) / 2
         };
         for (float aspectRatio : aspectRatios) {
+            mPipBoundsState.setAspectRatio(aspectRatio);
             final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
-                    mTestComponentName1, aspectRatio, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
+                    EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
             final float actualAspectRatio =
                     destinationBounds.width() / (destinationBounds.height() * 1f);
             assertEquals("Destination bounds matches the given aspect ratio",
@@ -142,8 +140,9 @@
                 MAX_ASPECT_RATIO * 2
         };
         for (float aspectRatio : invalidAspectRatios) {
+            mPipBoundsState.setAspectRatio(aspectRatio);
             final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
-                    mTestComponentName1, aspectRatio, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
+                    EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
             final float actualAspectRatio =
                     destinationBounds.width() / (destinationBounds.height() * 1f);
             assertEquals("Destination bounds fallbacks to default aspect ratio",
@@ -158,8 +157,9 @@
         final Rect currentBounds = new Rect(0, 0, 0, 100);
         currentBounds.right = (int) (currentBounds.height() * aspectRatio) + currentBounds.left;
 
-        final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
-                mTestComponentName1, aspectRatio, currentBounds, EMPTY_MINIMAL_SIZE);
+        mPipBoundsState.setAspectRatio(aspectRatio);
+        final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(currentBounds,
+                EMPTY_MINIMAL_SIZE);
 
         final float actualAspectRatio =
                 destinationBounds.width() / (destinationBounds.height() * 1f);
@@ -182,8 +182,9 @@
         for (int i = 0; i < aspectRatios.length; i++) {
             final float aspectRatio = aspectRatios[i];
             final Size minimalSize = minimalSizes[i];
+            mPipBoundsState.setAspectRatio(aspectRatio);
             final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
-                    mTestComponentName1, aspectRatio, EMPTY_CURRENT_BOUNDS, minimalSize);
+                    EMPTY_CURRENT_BOUNDS, minimalSize);
             assertTrue("Destination bounds is no smaller than minimal requirement",
                     (destinationBounds.width() == minimalSize.getWidth()
                             && destinationBounds.height() >= minimalSize.getHeight())
@@ -203,8 +204,9 @@
         currentBounds.right = (int) (currentBounds.height() * aspectRatio) + currentBounds.left;
         final Size minSize = new Size(currentBounds.width() / 2, currentBounds.height() / 2);
 
+        mPipBoundsState.setAspectRatio(aspectRatio);
         final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
-                mTestComponentName1, aspectRatio, currentBounds, minSize);
+                currentBounds, minSize);
 
         assertTrue("Destination bounds ignores minimal size",
                 destinationBounds.width() > minSize.getWidth()
@@ -212,28 +214,47 @@
     }
 
     @Test
-    public void getDestinationBounds_withDifferentComponentName_ignoreLastPosition() {
-        final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
+    public void getDestinationBounds_reentryStateExists_restoreLastSize() {
+        mPipBoundsState.setAspectRatio(DEFAULT_ASPECT_RATIO);
+        final Rect reentryBounds = mPipBoundsHandler.getDestinationBounds(
+                EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
+        reentryBounds.scale(1.25f);
+        final float reentrySnapFraction = mPipBoundsHandler.getSnapFraction(reentryBounds);
 
-        oldPosition.offset(0, -100);
-        mPipBoundsHandler.onSaveReentryBounds(mTestComponentName1, oldPosition);
+        mPipBoundsState.saveReentryState(reentryBounds, reentrySnapFraction);
+        final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
+                EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
 
-        final Rect newPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName2,
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
+        assertEquals(reentryBounds.width(), destinationBounds.width());
+        assertEquals(reentryBounds.height(), destinationBounds.height());
+    }
 
-        assertNonBoundsInclusionWithMargin("ignore saved bounds", oldPosition, newPosition);
+    @Test
+    public void getDestinationBounds_reentryStateExists_restoreLastPosition() {
+        mPipBoundsState.setAspectRatio(DEFAULT_ASPECT_RATIO);
+        final Rect reentryBounds = mPipBoundsHandler.getDestinationBounds(
+                EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
+        reentryBounds.offset(0, -100);
+        final float reentrySnapFraction = mPipBoundsHandler.getSnapFraction(reentryBounds);
+
+        mPipBoundsState.saveReentryState(reentryBounds, reentrySnapFraction);
+
+        final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
+                EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
+
+        assertBoundsInclusionWithMargin("restoreLastPosition", reentryBounds, destinationBounds);
     }
 
     @Test
     public void setShelfHeight_offsetBounds() {
         final int shelfHeight = 100;
-        final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
+        mPipBoundsState.setAspectRatio(DEFAULT_ASPECT_RATIO);
+        final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(
+                EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
 
         mPipBoundsHandler.setShelfHeight(true, shelfHeight);
-        final Rect newPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
+        final Rect newPosition = mPipBoundsHandler.getDestinationBounds(
+                EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
 
         oldPosition.offset(0, -shelfHeight);
         assertBoundsInclusionWithMargin("offsetBounds by shelf", oldPosition, newPosition);
@@ -242,92 +263,32 @@
     @Test
     public void onImeVisibilityChanged_offsetBounds() {
         final int imeHeight = 100;
-        final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
+        mPipBoundsState.setAspectRatio(DEFAULT_ASPECT_RATIO);
+        final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(
+                EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
 
         mPipBoundsHandler.onImeVisibilityChanged(true, imeHeight);
-        final Rect newPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
+        final Rect newPosition = mPipBoundsHandler.getDestinationBounds(
+                EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
 
         oldPosition.offset(0, -imeHeight);
         assertBoundsInclusionWithMargin("offsetBounds by IME", oldPosition, newPosition);
     }
 
     @Test
-    public void onSaveReentryBounds_restoreLastPosition() {
-        final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
+    public void getDestinationBounds_noReentryState_useDefaultBounds() {
+        mPipBoundsState.setAspectRatio(DEFAULT_ASPECT_RATIO);
+        final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds(
+                EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
 
-        oldPosition.offset(0, -100);
-        mPipBoundsHandler.onSaveReentryBounds(mTestComponentName1, oldPosition);
+        mPipBoundsState.clearReentryState();
 
-        final Rect newPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
-
-        assertBoundsInclusionWithMargin("restoreLastPosition", oldPosition, newPosition);
-    }
-
-    @Test
-    public void onSaveReentryBounds_restoreLastSize() {
-        final Rect oldSize = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
-
-        oldSize.scale(1.25f);
-        mPipBoundsHandler.onSaveReentryBounds(mTestComponentName1, oldSize);
-
-        final Rect newSize = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
-
-        assertEquals(oldSize.width(), newSize.width());
-        assertEquals(oldSize.height(), newSize.height());
-    }
-
-    @Test
-    public void onResetReentryBounds_useDefaultBounds() {
-        final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
-        final Rect newBounds = new Rect(defaultBounds);
-        newBounds.offset(0, -100);
-        mPipBoundsHandler.onSaveReentryBounds(mTestComponentName1, newBounds);
-
-        mPipBoundsHandler.onResetReentryBounds(mTestComponentName1);
-        final Rect actualBounds = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
+        final Rect actualBounds = mPipBoundsHandler.getDestinationBounds(
+                EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
 
         assertBoundsInclusionWithMargin("useDefaultBounds", defaultBounds, actualBounds);
     }
 
-    @Test
-    public void onResetReentryBounds_componentMismatch_restoreLastPosition() {
-        final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
-        final Rect newBounds = new Rect(defaultBounds);
-        newBounds.offset(0, -100);
-        mPipBoundsHandler.onSaveReentryBounds(mTestComponentName1, newBounds);
-
-        mPipBoundsHandler.onResetReentryBounds(mTestComponentName2);
-        final Rect actualBounds = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
-
-        assertBoundsInclusionWithMargin("restoreLastPosition", newBounds, actualBounds);
-    }
-
-    @Test
-    public void onSaveReentryBounds_componentMismatch_restoreLastSize() {
-        final Rect oldSize = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
-
-        oldSize.scale(1.25f);
-        mPipBoundsHandler.onSaveReentryBounds(mTestComponentName1, oldSize);
-
-        mPipBoundsHandler.onResetReentryBounds(mTestComponentName2);
-        final Rect newSize = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
-
-        assertEquals(oldSize.width(), newSize.width());
-        assertEquals(oldSize.height(), newSize.height());
-    }
-
     private void assertBoundsInclusionWithMargin(String from, Rect expected, Rect actual) {
         final Rect expectedWithMargin = new Rect(expected);
         expectedWithMargin.inset(-ROUNDING_ERROR_MARGIN, -ROUNDING_ERROR_MARGIN);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
new file mode 100644
index 0000000..dc9399e
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.wm.shell.pip;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.content.ComponentName;
+import android.graphics.Rect;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.util.Size;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link PipBoundsState}.
+ */
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class PipBoundsStateTest extends PipTestCase {
+
+    private static final Rect DEFAULT_BOUNDS = new Rect(0, 0, 10, 10);
+    private static final float DEFAULT_SNAP_FRACTION = 1.0f;
+
+    private PipBoundsState mPipBoundsState;
+    private ComponentName mTestComponentName1;
+    private ComponentName mTestComponentName2;
+
+    @Before
+    public void setUp() {
+        mPipBoundsState = new PipBoundsState();
+        mTestComponentName1 = new ComponentName(mContext, "component1");
+        mTestComponentName2 = new ComponentName(mContext, "component2");
+    }
+
+    @Test
+    public void testSetBounds() {
+        final Rect bounds = new Rect(0, 0, 100, 100);
+        mPipBoundsState.setBounds(bounds);
+
+        assertEquals(bounds, mPipBoundsState.getBounds());
+    }
+
+    @Test
+    public void testSetReentryState() {
+        final Rect bounds = new Rect(0, 0, 100, 100);
+        final float snapFraction = 0.5f;
+
+        mPipBoundsState.saveReentryState(bounds, snapFraction);
+
+        final PipBoundsState.PipReentryState state = mPipBoundsState.getReentryState();
+        assertEquals(new Size(100, 100), state.getSize());
+        assertEquals(snapFraction, state.getSnapFraction(), 0.01);
+    }
+
+    @Test
+    public void testClearReentryState() {
+        final Rect bounds = new Rect(0, 0, 100, 100);
+        final float snapFraction = 0.5f;
+
+        mPipBoundsState.saveReentryState(bounds, snapFraction);
+        mPipBoundsState.clearReentryState();
+
+        assertNull(mPipBoundsState.getReentryState());
+    }
+
+    @Test
+    public void testSetLastPipComponentName_notChanged_doesNotClearReentryState() {
+        mPipBoundsState.setLastPipComponentName(mTestComponentName1);
+        mPipBoundsState.saveReentryState(DEFAULT_BOUNDS, DEFAULT_SNAP_FRACTION);
+
+        mPipBoundsState.setLastPipComponentName(mTestComponentName1);
+
+        final PipBoundsState.PipReentryState state = mPipBoundsState.getReentryState();
+        assertNotNull(state);
+        assertEquals(new Size(DEFAULT_BOUNDS.width(), DEFAULT_BOUNDS.height()), state.getSize());
+        assertEquals(DEFAULT_SNAP_FRACTION, state.getSnapFraction(), 0.01);
+    }
+
+    @Test
+    public void testSetLastPipComponentName_changed_clearReentryState() {
+        mPipBoundsState.setLastPipComponentName(mTestComponentName1);
+        mPipBoundsState.saveReentryState(DEFAULT_BOUNDS, DEFAULT_SNAP_FRACTION);
+
+        mPipBoundsState.setLastPipComponentName(mTestComponentName2);
+
+        assertNull(mPipBoundsState.getReentryState());
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
new file mode 100644
index 0000000..54543d2
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.wm.shell.pip;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.verify;
+
+import android.os.RemoteException;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.splitscreen.SplitScreen;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Optional;
+
+/**
+ * Unit tests for {@link PipTaskOrganizer}
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class PipTaskOrganizerTest extends PipTestCase {
+    private PipTaskOrganizer mSpiedPipTaskOrganizer;
+
+    @Mock private DisplayController mMockdDisplayController;
+    @Mock private PipBoundsHandler mMockPipBoundsHandler;
+    @Mock private PipSurfaceTransactionHelper mMockPipSurfaceTransactionHelper;
+    @Mock private PipUiEventLogger mMockPipUiEventLogger;
+    @Mock private Optional<SplitScreen> mMockOptionalSplitScreen;
+    @Mock private ShellTaskOrganizer mMockShellTaskOrganizer;
+    @Mock private PipBoundsState mMockPipBoundsState;
+
+    @Before
+    public void setUp() throws RemoteException {
+        MockitoAnnotations.initMocks(this);
+        mSpiedPipTaskOrganizer = new PipTaskOrganizer(mContext, mMockPipBoundsState,
+                mMockPipBoundsHandler, mMockPipSurfaceTransactionHelper, mMockOptionalSplitScreen,
+                mMockdDisplayController, mMockPipUiEventLogger, mMockShellTaskOrganizer);
+    }
+
+    @Test
+    public void instantiatePipTaskOrganizer_addsTaskListener() {
+        verify(mMockShellTaskOrganizer).addListenerForType(any(), anyInt());
+    }
+
+    @Test
+    public void instantiatePipTaskOrganizer_addsDisplayWindowListener() {
+        verify(mMockdDisplayController).addDisplayWindowListener(any());
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index d305c64..a282a48 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -14,32 +14,30 @@
  * limitations under the License.
  */
 
-package com.android.systemui.pip.phone;
+package com.android.wm.shell.pip.phone;
 
 import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
 
+import static org.junit.Assert.assertNull;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.RemoteException;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
-import android.testing.TestableContext;
 import android.testing.TestableLooper;
 
 import com.android.wm.shell.WindowManagerShellWrapper;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.pip.PipBoundsHandler;
+import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipTaskOrganizer;
 import com.android.wm.shell.pip.PipTestCase;
-import com.android.wm.shell.pip.phone.PipAppOpsListener;
-import com.android.wm.shell.pip.phone.PipController;
-import com.android.wm.shell.pip.phone.PipMediaController;
-import com.android.wm.shell.pip.phone.PipTouchHandler;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -54,47 +52,52 @@
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 public class PipControllerTest extends PipTestCase {
-    private com.android.wm.shell.pip.phone.PipController mPipController;
-    private TestableContext mSpiedContext;
+    private PipController mPipController;
 
-    @Mock private DisplayController mMockdDisplayController;
-    @Mock private PackageManager mPackageManager;
-    @Mock private com.android.wm.shell.pip.phone.PipMenuActivityController
-            mMockPipMenuActivityController;
+    @Mock private DisplayController mMockDisplayController;
+    @Mock private PipMenuActivityController mMockPipMenuActivityController;
     @Mock private PipAppOpsListener mMockPipAppOpsListener;
     @Mock private PipBoundsHandler mMockPipBoundsHandler;
     @Mock private PipMediaController mMockPipMediaController;
     @Mock private PipTaskOrganizer mMockPipTaskOrganizer;
     @Mock private PipTouchHandler mMockPipTouchHandler;
     @Mock private WindowManagerShellWrapper mMockWindowManagerShellWrapper;
+    @Mock private PipBoundsState mMockPipBoundsState;
 
     @Before
     public void setUp() throws RemoteException {
         MockitoAnnotations.initMocks(this);
-
-        mSpiedContext = spy(mContext);
-
-        when(mPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)).thenReturn(false);
-        when(mSpiedContext.getPackageManager()).thenReturn(mPackageManager);
-
-        mPipController = new PipController(mSpiedContext, mMockdDisplayController,
-                mMockPipAppOpsListener, mMockPipBoundsHandler, mMockPipMediaController,
-                mMockPipMenuActivityController, mMockPipTaskOrganizer, mMockPipTouchHandler,
-                mMockWindowManagerShellWrapper);
+        mPipController = new PipController(mContext, mMockDisplayController,
+                mMockPipAppOpsListener, mMockPipBoundsHandler, mMockPipBoundsState,
+                mMockPipMediaController, mMockPipMenuActivityController, mMockPipTaskOrganizer,
+                mMockPipTouchHandler, mMockWindowManagerShellWrapper);
     }
 
     @Test
-    public void testNonPipDevice_shouldNotRegisterPipTransitionCallback() {
-        verify(mMockPipTaskOrganizer, never()).registerPipTransitionCallback(any());
+    public void instantiatePipController_registersPipTransitionCallback() {
+        verify(mMockPipTaskOrganizer).registerPipTransitionCallback(any());
     }
 
     @Test
-    public void testNonPipDevice_shouldNotAddDisplayChangingController() {
-        verify(mMockdDisplayController, never()).addDisplayChangingController(any());
+    public void instantiatePipController_addsDisplayChangingController() {
+        verify(mMockDisplayController).addDisplayChangingController(any());
     }
 
     @Test
-    public void testNonPipDevice_shouldNotAddDisplayWindowListener() {
-        verify(mMockdDisplayController, never()).addDisplayWindowListener(any());
+    public void instantiatePipController_addsDisplayWindowListener() {
+        verify(mMockDisplayController).addDisplayWindowListener(any());
+    }
+
+    @Test
+    public void createPip_notSupported_returnsNull() {
+        Context spyContext = spy(mContext);
+        PackageManager mockPackageManager = mock(PackageManager.class);
+        when(mockPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)).thenReturn(false);
+        when(spyContext.getPackageManager()).thenReturn(mockPackageManager);
+
+        assertNull(PipController.create(spyContext, mMockDisplayController,
+                mMockPipAppOpsListener, mMockPipBoundsHandler, mMockPipBoundsState,
+                mMockPipMediaController, mMockPipMenuActivityController, mMockPipTaskOrganizer,
+                mMockPipTouchHandler, mMockWindowManagerShellWrapper));
     }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTaskOrganizerTest.java
deleted file mode 100644
index 663169f..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTaskOrganizerTest.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.pip.phone;
-
-import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.pm.PackageManager;
-import android.os.RemoteException;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableContext;
-import android.testing.TestableLooper;
-
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.pip.PipBoundsHandler;
-import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
-import com.android.wm.shell.pip.PipTaskOrganizer;
-import com.android.wm.shell.pip.PipTestCase;
-import com.android.wm.shell.pip.PipUiEventLogger;
-import com.android.wm.shell.splitscreen.SplitScreen;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.Optional;
-
-/**
- * Unit tests for {@link PipTaskOrganizer}
- */
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class PipTaskOrganizerTest extends PipTestCase {
-    private PipTaskOrganizer mSpiedPipTaskOrganizer;
-    private TestableContext mSpiedContext;
-
-    @Mock private DisplayController mMockdDisplayController;
-    @Mock private PackageManager mPackageManager;
-    @Mock private PipBoundsHandler mMockPipBoundsHandler;
-    @Mock private PipSurfaceTransactionHelper mMockPipSurfaceTransactionHelper;
-    @Mock private PipUiEventLogger mMockPipUiEventLogger;
-    @Mock private Optional<SplitScreen> mMockOptionalSplitScreen;
-    @Mock private ShellTaskOrganizer mMockShellTaskOrganizer;
-
-    @Before
-    public void setUp() throws RemoteException {
-        MockitoAnnotations.initMocks(this);
-
-        mSpiedContext = spy(mContext);
-
-        when(mPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)).thenReturn(false);
-        when(mSpiedContext.getPackageManager()).thenReturn(mPackageManager);
-
-        mSpiedPipTaskOrganizer = spy(new PipTaskOrganizer(mSpiedContext, mMockPipBoundsHandler,
-                mMockPipSurfaceTransactionHelper, mMockOptionalSplitScreen, mMockdDisplayController,
-                mMockPipUiEventLogger, mMockShellTaskOrganizer));
-    }
-
-    @Test
-    public void testNonPipDevice_shellTaskOrganizer_shouldNotAddListener() {
-        verify(mMockShellTaskOrganizer, never()).addListener(any(), anyInt());
-    }
-
-    @Test
-    public void testNonPipDevice_displayController_shouldNotAddDisplayWindowListener() {
-        verify(mMockdDisplayController, never()).addDisplayWindowListener(any());
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
index c96cb20..3f60cc0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.pip.phone;
+package com.android.wm.shell.pip.phone;
 
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
@@ -33,6 +33,7 @@
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.FloatingContentCoordinator;
 import com.android.wm.shell.pip.PipBoundsHandler;
+import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipSnapAlgorithm;
 import com.android.wm.shell.pip.PipTaskOrganizer;
 import com.android.wm.shell.pip.PipTestCase;
@@ -74,6 +75,7 @@
     @Mock
     private PipUiEventLogger mPipUiEventLogger;
 
+    private PipBoundsState mPipBoundsState;
     private PipBoundsHandler mPipBoundsHandler;
     private PipSnapAlgorithm mPipSnapAlgorithm;
     private PipMotionHelper mMotionHelper;
@@ -90,11 +92,12 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mPipBoundsHandler = new PipBoundsHandler(mContext);
+        mPipBoundsState = new PipBoundsState();
+        mPipBoundsHandler = new PipBoundsHandler(mContext, mPipBoundsState);
         mPipSnapAlgorithm = mPipBoundsHandler.getSnapAlgorithm();
         mPipSnapAlgorithm = new PipSnapAlgorithm(mContext);
         mPipTouchHandler = new PipTouchHandler(mContext, mPipMenuActivityController,
-                mPipBoundsHandler, mPipTaskOrganizer, mFloatingContentCoordinator,
+                mPipBoundsHandler, mPipBoundsState, mPipTaskOrganizer, mFloatingContentCoordinator,
                 mPipUiEventLogger);
         mMotionHelper = Mockito.spy(mPipTouchHandler.getMotionHelper());
         mPipResizeGestureHandler = Mockito.spy(mPipTouchHandler.getPipResizeGestureHandler());
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchStateTest.java
index 2702130..40667f7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchStateTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.pip.phone;
+package com.android.wm.shell.pip.phone;
 
 import static android.view.MotionEvent.ACTION_BUTTON_PRESS;
 import static android.view.MotionEvent.ACTION_DOWN;
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 8ab7da5..903ca2a 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -155,11 +155,12 @@
         android: {
             srcs: [
                 "tests/BackupData_test.cpp",
-		"tests/BackupHelpers_test.cpp",
+                "tests/BackupHelpers_test.cpp",
+                "tests/CursorWindow_test.cpp",
                 "tests/ObbFile_test.cpp",
                 "tests/PosixUtils_test.cpp",
             ],
-            shared_libs: common_test_libs + ["libui"],
+            shared_libs: common_test_libs + ["libbinder", "liblog", "libui"],
         },
         host: {
             static_libs: common_test_libs + ["liblog", "libz"],
@@ -185,9 +186,28 @@
         // Actual benchmarks.
         "tests/AssetManager2_bench.cpp",
         "tests/AttributeResolution_bench.cpp",
+        "tests/CursorWindow_bench.cpp",
         "tests/SparseEntry_bench.cpp",
         "tests/Theme_bench.cpp",
     ],
     shared_libs: common_test_libs,
     data: ["tests/data/**/*.apk"],
 }
+
+cc_library {
+    name: "libandroidfw_fuzzer_lib",
+    defaults: ["libandroidfw_defaults"],
+    host_supported: true,
+    srcs: [
+        "CursorWindow.cpp",
+    ],
+    export_include_dirs: ["include"],
+    target: {
+        android: {
+            shared_libs: common_test_libs + ["libbinder", "liblog"],
+        },
+        host: {
+            static_libs: common_test_libs + ["libbinder", "liblog"],
+        },
+    },
+}
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index b9765ea..99dd313 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -39,10 +39,8 @@
 namespace android {
 
 struct FindEntryResult {
-  // A pointer to the resource table entry for this resource.
-  // If the size of the entry is > sizeof(ResTable_entry), it can be cast to
-  // a ResTable_map_entry and processed as a bag/map.
-  ResTable_entry_handle entry;
+  // A pointer to the value of the resource table entry.
+  std::variant<Res_value, const ResTable_map_entry*> entry;
 
   // The configuration for which the resulting entry was defined. This is already swapped to host
   // endianness.
@@ -554,11 +552,9 @@
       if (!overlay_entry) {
         // No id map entry exists for this target resource.
         continue;
-      }
-
-      if (overlay_entry.IsTableEntry()) {
+      } else if (overlay_entry.IsInlineValue()) {
         // The target resource is overlaid by an inline value not represented by a resource.
-        out_entry->entry = overlay_entry.GetTableEntry();
+        out_entry->entry = overlay_entry.GetInlineValue();
         out_entry->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
         cookie = id_map.cookie;
         continue;
@@ -580,7 +576,7 @@
       }
 
       cookie = overlay_cookie;
-      out_entry->entry = std::move(overlay_result.entry);
+      out_entry->entry = overlay_result.entry;
       out_entry->config = overlay_result.config;
       out_entry->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
       if (resource_resolution_logging_enabled_) {
@@ -761,7 +757,19 @@
     return kInvalidCookie;
   }
 
-  out_entry->entry = ResTable_entry_handle::unmanaged(best_entry);
+  const uint16_t entry_size = dtohs(best_entry->size);
+  if (entry_size >= sizeof(ResTable_map_entry) &&
+      (dtohs(best_entry->flags) & ResTable_entry::FLAG_COMPLEX)) {
+    // The entry represents a bag/map.
+    out_entry->entry = reinterpret_cast<const ResTable_map_entry*>(best_entry);
+  } else {
+    // The entry represents a value.
+    Res_value value;
+    value.copyFrom_dtoh(*reinterpret_cast<const Res_value*>(
+        reinterpret_cast<const uint8_t*>(best_entry) + entry_size));
+    out_entry->entry = value;
+  }
+
   out_entry->config = *best_config;
   out_entry->type_flags = type_flags;
   out_entry->package_name = &best_package->GetPackageName();
@@ -905,8 +913,8 @@
     return kInvalidCookie;
   }
 
-  const ResTable_entry* table_entry = *entry.entry;
-  if (dtohs(table_entry->flags) & ResTable_entry::FLAG_COMPLEX) {
+  auto result_map_entry = std::get_if<const ResTable_map_entry*>(&entry.entry);
+  if (result_map_entry != nullptr) {
     if (!may_be_bag) {
       LOG(ERROR) << base::StringPrintf("Resource %08x is a complex map type.", resid);
       return kInvalidCookie;
@@ -920,11 +928,8 @@
     return cookie;
   }
 
-  const Res_value* device_value = reinterpret_cast<const Res_value*>(
-      reinterpret_cast<const uint8_t*>(table_entry) + dtohs(table_entry->size));
-  out_value->copyFrom_dtoh(*device_value);
-
   // Convert the package ID to the runtime assigned package ID.
+  *out_value = std::get<Res_value>(entry.entry);
   entry.dynamic_ref_table->lookupResourceValue(out_value);
 
   *out_selected_config = entry.config;
@@ -1004,19 +1009,15 @@
     return nullptr;
   }
 
-  // Check that the size of the entry header is at least as big as
-  // the desired ResTable_map_entry. Also verify that the entry
-  // was intended to be a map.
-  const ResTable_entry* table_entry = *entry.entry;
-  if (dtohs(table_entry->size) < sizeof(ResTable_map_entry) ||
-      (dtohs(table_entry->flags) & ResTable_entry::FLAG_COMPLEX) == 0) {
+  auto result_map_entry = std::get_if<const ResTable_map_entry*>(&entry.entry);
+  if (result_map_entry == nullptr) {
     // Not a bag, nothing to do.
     return nullptr;
   }
 
-  const ResTable_map_entry* map = reinterpret_cast<const ResTable_map_entry*>(table_entry);
-  const ResTable_map* map_entry =
-      reinterpret_cast<const ResTable_map*>(reinterpret_cast<const uint8_t*>(map) + map->size);
+  auto map = reinterpret_cast<const ResTable_map_entry*>(*result_map_entry);
+  auto map_entry = reinterpret_cast<const ResTable_map*>(
+      reinterpret_cast<const uint8_t*>(map) + map->size);
   const ResTable_map* const map_entry_end = map_entry + dtohl(map->count);
 
   // Keep track of ids that have already been seen to prevent infinite loops caused by circular
diff --git a/libs/androidfw/CursorWindow.cpp b/libs/androidfw/CursorWindow.cpp
index 71c8e1f..1b8db46 100644
--- a/libs/androidfw/CursorWindow.cpp
+++ b/libs/androidfw/CursorWindow.cpp
@@ -14,19 +14,14 @@
  * limitations under the License.
  */
 
-#undef LOG_TAG
 #define LOG_TAG "CursorWindow"
 
 #include <androidfw/CursorWindow.h>
-#include <binder/Parcel.h>
-#include <utils/Log.h>
 
-#include <cutils/ashmem.h>
 #include <sys/mman.h>
 
-#include <assert.h>
-#include <string.h>
-#include <stdlib.h>
+#include "android-base/logging.h"
+#include "cutils/ashmem.h"
 
 namespace android {
 
@@ -36,11 +31,10 @@
  */
 static constexpr const size_t kInlineSize = 16384;
 
-CursorWindow::CursorWindow(const String8& name, int ashmemFd, void* data, size_t size,
-                           size_t inflatedSize, bool readOnly) :
-        mName(name), mAshmemFd(ashmemFd), mData(data), mSize(size),
-        mInflatedSize(inflatedSize), mReadOnly(readOnly) {
-    mHeader = static_cast<Header*>(mData);
+static constexpr const size_t kSlotShift = 4;
+static constexpr const size_t kSlotSizeBytes = 1 << kSlotShift;
+
+CursorWindow::CursorWindow() {
 }
 
 CursorWindow::~CursorWindow() {
@@ -52,234 +46,243 @@
     }
 }
 
-status_t CursorWindow::create(const String8& name, size_t inflatedSize,
-                              CursorWindow** outCursorWindow) {
-    *outCursorWindow = nullptr;
+status_t CursorWindow::create(const String8 &name, size_t inflatedSize, CursorWindow **outWindow) {
+    *outWindow = nullptr;
 
-    size_t size = std::min(kInlineSize, inflatedSize);
-    void* data = calloc(size, 1);
-    if (!data) return NO_MEMORY;
+    CursorWindow* window = new CursorWindow();
+    if (!window) goto fail;
 
-    CursorWindow* window = new CursorWindow(name, -1, data, size,
-                                            inflatedSize, false /*readOnly*/);
-    status_t result = window->clear();
-    if (!result) {
-        LOG_WINDOW("Created new CursorWindow: freeOffset=%d, "
-                "numRows=%d, numColumns=%d, mSize=%zu, mData=%p",
-                window->mHeader->freeOffset,
-                window->mHeader->numRows,
-                window->mHeader->numColumns,
-                window->mSize, window->mData);
-        *outCursorWindow = window;
-        return OK;
-    }
+    window->mName = name;
+    window->mSize = std::min(kInlineSize, inflatedSize);
+    window->mInflatedSize = inflatedSize;
+    window->mData = malloc(window->mSize);
+    if (!window->mData) goto fail;
+    window->mReadOnly = false;
+
+    window->clear();
+    window->updateSlotsData();
+
+    LOG(DEBUG) << "Created: " << window->toString();
+    *outWindow = window;
+    return OK;
+
+fail:
+    LOG(ERROR) << "Failed create";
+fail_silent:
     delete window;
-    return result;
+    return UNKNOWN_ERROR;
 }
 
-status_t CursorWindow::inflate() {
-    // Shortcut when we can't expand any further
-    if (mSize == mInflatedSize) return INVALID_OPERATION;
+status_t CursorWindow::maybeInflate() {
+    int ashmemFd = 0;
+    void* newData = nullptr;
+
+    // Bail early when we can't expand any further
+    if (mReadOnly || mSize == mInflatedSize) {
+        return INVALID_OPERATION;
+    }
 
     String8 ashmemName("CursorWindow: ");
     ashmemName.append(mName);
 
-    status_t result;
-    int ashmemFd = ashmem_create_region(ashmemName.string(), mInflatedSize);
+    ashmemFd = ashmem_create_region(ashmemName.string(), mInflatedSize);
     if (ashmemFd < 0) {
-        result = -errno;
-        ALOGE("CursorWindow: ashmem_create_region() failed: errno=%d.", errno);
-    } else {
-        result = ashmem_set_prot_region(ashmemFd, PROT_READ | PROT_WRITE);
-        if (result < 0) {
-            ALOGE("CursorWindow: ashmem_set_prot_region() failed: errno=%d",errno);
-        } else {
-            void* data = ::mmap(NULL, mInflatedSize, PROT_READ | PROT_WRITE,
-                                MAP_SHARED, ashmemFd, 0);
-            if (data == MAP_FAILED) {
-                result = -errno;
-                ALOGE("CursorWindow: mmap() failed: errno=%d.", errno);
-            } else {
-                result = ashmem_set_prot_region(ashmemFd, PROT_READ);
-                if (result < 0) {
-                    ALOGE("CursorWindow: ashmem_set_prot_region() failed: errno=%d.", errno);
-                } else {
-                    // Move inline contents into new ashmem region
-                    memcpy(data, mData, mSize);
-                    free(mData);
-                    mAshmemFd = ashmemFd;
-                    mData = data;
-                    mHeader = static_cast<Header*>(mData);
-                    mSize = mInflatedSize;
-                    LOG_WINDOW("Inflated CursorWindow: freeOffset=%d, "
-                            "numRows=%d, numColumns=%d, mSize=%zu, mData=%p",
-                            mHeader->freeOffset,
-                            mHeader->numRows,
-                            mHeader->numColumns,
-                            mSize, mData);
-                    return OK;
-                }
-            }
-            ::munmap(data, mInflatedSize);
-        }
-        ::close(ashmemFd);
+        PLOG(ERROR) << "Failed ashmem_create_region";
+        goto fail_silent;
     }
-    return result;
+
+    if (ashmem_set_prot_region(ashmemFd, PROT_READ | PROT_WRITE) < 0) {
+        PLOG(ERROR) << "Failed ashmem_set_prot_region";
+        goto fail_silent;
+    }
+
+    newData = ::mmap(nullptr, mInflatedSize, PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0);
+    if (newData == MAP_FAILED) {
+        PLOG(ERROR) << "Failed mmap";
+        goto fail_silent;
+    }
+
+    if (ashmem_set_prot_region(ashmemFd, PROT_READ) < 0) {
+        PLOG(ERROR) << "Failed ashmem_set_prot_region";
+        goto fail_silent;
+    }
+
+    {
+        // Migrate existing contents into new ashmem region
+        uint32_t slotsSize = mSize - mSlotsOffset;
+        uint32_t newSlotsOffset = mInflatedSize - slotsSize;
+        memcpy(static_cast<uint8_t*>(newData),
+                static_cast<uint8_t*>(mData), mAllocOffset);
+        memcpy(static_cast<uint8_t*>(newData) + newSlotsOffset,
+                static_cast<uint8_t*>(mData) + mSlotsOffset, slotsSize);
+
+        free(mData);
+        mAshmemFd = ashmemFd;
+        mData = newData;
+        mSize = mInflatedSize;
+        mSlotsOffset = newSlotsOffset;
+
+        updateSlotsData();
+    }
+
+    LOG(DEBUG) << "Inflated: " << this->toString();
+    return OK;
+
+fail:
+    LOG(ERROR) << "Failed maybeInflate";
+fail_silent:
+    ::munmap(newData, mInflatedSize);
+    ::close(ashmemFd);
+    return UNKNOWN_ERROR;
 }
 
-status_t CursorWindow::createFromParcel(Parcel* parcel, CursorWindow** outCursorWindow) {
-    *outCursorWindow = nullptr;
+status_t CursorWindow::createFromParcel(Parcel* parcel, CursorWindow** outWindow) {
+    *outWindow = nullptr;
 
-    String8 name;
-    status_t result = parcel->readString8(&name);
-    if (result) return result;
+    CursorWindow* window = new CursorWindow();
+    if (!window) goto fail;
+
+    if (parcel->readString8(&window->mName)) goto fail;
+    if (parcel->readUint32(&window->mNumRows)) goto fail;
+    if (parcel->readUint32(&window->mNumColumns)) goto fail;
+    if (parcel->readUint32(&window->mSize)) goto fail;
+
+    if ((window->mNumRows * window->mNumColumns * kSlotSizeBytes) > window->mSize) {
+        LOG(ERROR) << "Unexpected size " << window->mSize << " for " << window->mNumRows
+                << " rows and " << window->mNumColumns << " columns";
+        goto fail_silent;
+    }
 
     bool isAshmem;
-    result = parcel->readBool(&isAshmem);
-    if (result) return result;
-
+    if (parcel->readBool(&isAshmem)) goto fail;
     if (isAshmem) {
-        return createFromParcelAshmem(parcel, name, outCursorWindow);
-    } else {
-        return createFromParcelInline(parcel, name, outCursorWindow);
-    }
-}
-
-status_t CursorWindow::createFromParcelAshmem(Parcel* parcel, String8& name,
-                                              CursorWindow** outCursorWindow) {
-    status_t result;
-    int actualSize;
-    int ashmemFd = parcel->readFileDescriptor();
-    if (ashmemFd == int(BAD_TYPE)) {
-        result = BAD_TYPE;
-        ALOGE("CursorWindow: readFileDescriptor() failed");
-    } else {
-        ssize_t size = ashmem_get_size_region(ashmemFd);
-        if (size < 0) {
-            result = UNKNOWN_ERROR;
-            ALOGE("CursorWindow: ashmem_get_size_region() failed: errno=%d.", errno);
-        } else {
-            int dupAshmemFd = ::fcntl(ashmemFd, F_DUPFD_CLOEXEC, 0);
-            if (dupAshmemFd < 0) {
-                result = -errno;
-                ALOGE("CursorWindow: fcntl() failed: errno=%d.", errno);
-            } else {
-                // the size of the ashmem descriptor can be modified between ashmem_get_size_region
-                // call and mmap, so we'll check again immediately after memory is mapped
-                void* data = ::mmap(NULL, size, PROT_READ, MAP_SHARED, dupAshmemFd, 0);
-                if (data == MAP_FAILED) {
-                    result = -errno;
-                    ALOGE("CursorWindow: mmap() failed: errno=%d.", errno);
-                } else if ((actualSize = ashmem_get_size_region(dupAshmemFd)) != size) {
-                    ::munmap(data, size);
-                    result = BAD_VALUE;
-                    ALOGE("CursorWindow: ashmem_get_size_region() returned %d, expected %d"
-                            " errno=%d",
-                            actualSize, (int) size, errno);
-                } else {
-                    CursorWindow* window = new CursorWindow(name, dupAshmemFd,
-                            data, size, size, true /*readOnly*/);
-                    LOG_WINDOW("Created CursorWindow from ashmem parcel: freeOffset=%d, "
-                            "numRows=%d, numColumns=%d, mSize=%zu, mData=%p",
-                            window->mHeader->freeOffset,
-                            window->mHeader->numRows,
-                            window->mHeader->numColumns,
-                            window->mSize, window->mData);
-                    *outCursorWindow = window;
-                    return OK;
-                }
-                ::close(dupAshmemFd);
-            }
+        window->mAshmemFd = parcel->readFileDescriptor();
+        if (window->mAshmemFd < 0) {
+            LOG(ERROR) << "Failed readFileDescriptor";
+            goto fail_silent;
         }
+
+        window->mAshmemFd = ::fcntl(window->mAshmemFd, F_DUPFD_CLOEXEC, 0);
+        if (window->mAshmemFd < 0) {
+            PLOG(ERROR) << "Failed F_DUPFD_CLOEXEC";
+            goto fail_silent;
+        }
+
+        window->mData = ::mmap(nullptr, window->mSize, PROT_READ, MAP_SHARED, window->mAshmemFd, 0);
+        if (window->mData == MAP_FAILED) {
+            PLOG(ERROR) << "Failed mmap";
+            goto fail_silent;
+        }
+    } else {
+        window->mAshmemFd = -1;
+
+        if (window->mSize > kInlineSize) {
+            LOG(ERROR) << "Unexpected size " << window->mSize << " for inline window";
+            goto fail_silent;
+        }
+
+        window->mData = malloc(window->mSize);
+        if (!window->mData) goto fail;
+
+        if (parcel->read(window->mData, window->mSize)) goto fail;
     }
-    *outCursorWindow = NULL;
-    return result;
-}
 
-status_t CursorWindow::createFromParcelInline(Parcel* parcel, String8& name,
-                                              CursorWindow** outCursorWindow) {
-    uint32_t sentSize;
-    status_t result = parcel->readUint32(&sentSize);
-    if (result) return result;
-    if (sentSize > kInlineSize) return NO_MEMORY;
+    // We just came from a remote source, so we're read-only
+    // and we can't inflate ourselves
+    window->mInflatedSize = window->mSize;
+    window->mReadOnly = true;
 
-    void* data = calloc(sentSize, 1);
-    if (!data) return NO_MEMORY;
+    window->updateSlotsData();
 
-    result = parcel->read(data, sentSize);
-    if (result) return result;
-
-    CursorWindow* window = new CursorWindow(name, -1, data, sentSize,
-                                            sentSize, true /*readOnly*/);
-    LOG_WINDOW("Created CursorWindow from inline parcel: freeOffset=%d, "
-            "numRows=%d, numColumns=%d, mSize=%zu, mData=%p",
-            window->mHeader->freeOffset,
-            window->mHeader->numRows,
-            window->mHeader->numColumns,
-            window->mSize, window->mData);
-    *outCursorWindow = window;
+    LOG(DEBUG) << "Created from parcel: " << window->toString();
+    *outWindow = window;
     return OK;
+
+fail:
+    LOG(ERROR) << "Failed createFromParcel";
+fail_silent:
+    delete window;
+    return UNKNOWN_ERROR;
 }
 
 status_t CursorWindow::writeToParcel(Parcel* parcel) {
-        LOG_WINDOW("Writing CursorWindow: freeOffset=%d, "
-                "numRows=%d, numColumns=%d, mSize=%zu, mData=%p",
-                mHeader->freeOffset,
-                mHeader->numRows,
-                mHeader->numColumns,
-                mSize, mData);
+    LOG(DEBUG) << "Writing to parcel: " << this->toString();
 
-    status_t result = parcel->writeString8(mName);
-    if (result) return result;
-
+    if (parcel->writeString8(mName)) goto fail;
+    if (parcel->writeUint32(mNumRows)) goto fail;
+    if (parcel->writeUint32(mNumColumns)) goto fail;
     if (mAshmemFd != -1) {
-        result = parcel->writeBool(true);
-        if (result) return result;
-        return writeToParcelAshmem(parcel);
+        if (parcel->writeUint32(mSize)) goto fail;
+        if (parcel->writeBool(true)) goto fail;
+        if (parcel->writeDupFileDescriptor(mAshmemFd)) goto fail;
     } else {
-        result = parcel->writeBool(false);
-        if (result) return result;
-        return writeToParcelInline(parcel);
+        // Since we know we're going to be read-only on the remote side,
+        // we can compact ourselves on the wire, with just enough padding
+        // to ensure our slots stay aligned
+        size_t slotsSize = mSize - mSlotsOffset;
+        size_t compactedSize = mAllocOffset + slotsSize;
+        compactedSize = (compactedSize + 3) & ~3;
+        if (parcel->writeUint32(compactedSize)) goto fail;
+        if (parcel->writeBool(false)) goto fail;
+        void* dest = parcel->writeInplace(compactedSize);
+        if (!dest) goto fail;
+        memcpy(static_cast<uint8_t*>(dest),
+                static_cast<uint8_t*>(mData), mAllocOffset);
+        memcpy(static_cast<uint8_t*>(dest) + compactedSize - slotsSize,
+                static_cast<uint8_t*>(mData) + mSlotsOffset, slotsSize);
     }
-}
+    return OK;
 
-status_t CursorWindow::writeToParcelAshmem(Parcel* parcel) {
-    return parcel->writeDupFileDescriptor(mAshmemFd);
-}
-
-status_t CursorWindow::writeToParcelInline(Parcel* parcel) {
-    status_t result = parcel->writeUint32(mHeader->freeOffset);
-    if (result) return result;
-
-    return parcel->write(mData, mHeader->freeOffset);
+fail:
+    LOG(ERROR) << "Failed writeToParcel";
+fail_silent:
+    return UNKNOWN_ERROR;
 }
 
 status_t CursorWindow::clear() {
     if (mReadOnly) {
         return INVALID_OPERATION;
     }
-
-    mHeader->freeOffset = sizeof(Header) + sizeof(RowSlotChunk);
-    mHeader->firstChunkOffset = sizeof(Header);
-    mHeader->numRows = 0;
-    mHeader->numColumns = 0;
-
-    RowSlotChunk* firstChunk = static_cast<RowSlotChunk*>(offsetToPtr(mHeader->firstChunkOffset));
-    firstChunk->nextChunkOffset = 0;
+    mAllocOffset = 0;
+    mSlotsOffset = mSize;
+    mNumRows = 0;
+    mNumColumns = 0;
     return OK;
 }
 
+void CursorWindow::updateSlotsData() {
+    mSlotsStart = static_cast<uint8_t*>(mData) + mSize - kSlotSizeBytes;
+    mSlotsEnd = static_cast<uint8_t*>(mData) + mSlotsOffset;
+}
+
+void* CursorWindow::offsetToPtr(uint32_t offset, uint32_t bufferSize = 0) {
+    if (offset > mSize) {
+        LOG(ERROR) << "Offset " << offset
+                << " out of bounds, max value " << mSize;
+        return nullptr;
+    }
+    if (offset + bufferSize > mSize) {
+        LOG(ERROR) << "End offset " << (offset + bufferSize)
+                << " out of bounds, max value " << mSize;
+        return nullptr;
+    }
+    return static_cast<uint8_t*>(mData) + offset;
+}
+
+uint32_t CursorWindow::offsetFromPtr(void* ptr) {
+    return static_cast<uint8_t*>(ptr) - static_cast<uint8_t*>(mData);
+}
+
 status_t CursorWindow::setNumColumns(uint32_t numColumns) {
     if (mReadOnly) {
         return INVALID_OPERATION;
     }
-
-    uint32_t cur = mHeader->numColumns;
-    if ((cur > 0 || mHeader->numRows > 0) && cur != numColumns) {
-        ALOGE("Trying to go from %d columns to %d", cur, numColumns);
+    uint32_t cur = mNumColumns;
+    if ((cur > 0 || mNumRows > 0) && cur != numColumns) {
+        LOG(ERROR) << "Trying to go from " << cur << " columns to " << numColumns;
         return INVALID_OPERATION;
     }
-    mHeader->numColumns = numColumns;
+    mNumColumns = numColumns;
     return OK;
 }
 
@@ -287,30 +290,19 @@
     if (mReadOnly) {
         return INVALID_OPERATION;
     }
-
-    // Fill in the row slot
-    RowSlot* rowSlot = allocRowSlot();
-    if (rowSlot == NULL) {
-        return NO_MEMORY;
+    size_t size = mNumColumns * kSlotSizeBytes;
+    int32_t newOffset = mSlotsOffset - size;
+    if (newOffset < (int32_t) mAllocOffset) {
+        maybeInflate();
+        newOffset = mSlotsOffset - size;
+        if (newOffset < (int32_t) mAllocOffset) {
+            return NO_MEMORY;
+        }
     }
-    uint32_t rowSlotOffset = offsetFromPtr(rowSlot);
-
-    // Allocate the slots for the field directory
-    size_t fieldDirSize = mHeader->numColumns * sizeof(FieldSlot);
-    uint32_t fieldDirOffset = alloc(fieldDirSize, true /*aligned*/);
-    if (!fieldDirOffset) {
-        mHeader->numRows--;
-        LOG_WINDOW("The row failed, so back out the new row accounting "
-                "from allocRowSlot %d", mHeader->numRows);
-        return NO_MEMORY;
-    }
-    FieldSlot* fieldDir = static_cast<FieldSlot*>(offsetToPtr(fieldDirOffset));
-    memset(fieldDir, 0, fieldDirSize);
-
-    LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %zu bytes at offset %u\n",
-            mHeader->numRows - 1, rowSlotOffset, fieldDirSize, fieldDirOffset);
-    rowSlot = static_cast<RowSlot*>(offsetToPtr(rowSlotOffset));
-    rowSlot->offset = fieldDirOffset;
+    memset(offsetToPtr(newOffset), 0, size);
+    mSlotsOffset = newOffset;
+    updateSlotsData();
+    mNumRows++;
     return OK;
 }
 
@@ -318,90 +310,48 @@
     if (mReadOnly) {
         return INVALID_OPERATION;
     }
-
-    if (mHeader->numRows > 0) {
-        mHeader->numRows--;
+    size_t size = mNumColumns * kSlotSizeBytes;
+    size_t newOffset = mSlotsOffset + size;
+    if (newOffset > mSize) {
+        return NO_MEMORY;
     }
+    mSlotsOffset = newOffset;
+    updateSlotsData();
+    mNumRows--;
     return OK;
 }
 
-uint32_t CursorWindow::alloc(size_t size, bool aligned) {
-    uint32_t padding;
-    if (aligned) {
-        // 4 byte alignment
-        padding = (~mHeader->freeOffset + 1) & 3;
-    } else {
-        padding = 0;
+status_t CursorWindow::alloc(size_t size, uint32_t* outOffset) {
+    if (mReadOnly) {
+        return INVALID_OPERATION;
     }
-
-    uint32_t offset = mHeader->freeOffset + padding;
-    uint32_t nextFreeOffset = offset + size;
-    if (nextFreeOffset > mSize) {
-        // Try inflating to ashmem before finally giving up
-        inflate();
-        if (nextFreeOffset > mSize) {
-            ALOGW("Window is full: requested allocation %zu bytes, "
-                    "free space %zu bytes, window size %zu bytes",
-                    size, freeSpace(), mSize);
-            return 0;
+    size_t alignedSize = (size + 3) & ~3;
+    size_t newOffset = mAllocOffset + alignedSize;
+    if (newOffset > mSlotsOffset) {
+        maybeInflate();
+        newOffset = mAllocOffset + alignedSize;
+        if (newOffset > mSlotsOffset) {
+            return NO_MEMORY;
         }
     }
-
-    mHeader->freeOffset = nextFreeOffset;
-    return offset;
-}
-
-CursorWindow::RowSlot* CursorWindow::getRowSlot(uint32_t row) {
-    uint32_t chunkPos = row;
-    RowSlotChunk* chunk = static_cast<RowSlotChunk*>(
-            offsetToPtr(mHeader->firstChunkOffset));
-    while (chunkPos >= ROW_SLOT_CHUNK_NUM_ROWS) {
-        chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunk->nextChunkOffset));
-        chunkPos -= ROW_SLOT_CHUNK_NUM_ROWS;
-    }
-    return &chunk->slots[chunkPos];
-}
-
-CursorWindow::RowSlot* CursorWindow::allocRowSlot() {
-    uint32_t chunkPos = mHeader->numRows;
-    RowSlotChunk* chunk = static_cast<RowSlotChunk*>(
-            offsetToPtr(mHeader->firstChunkOffset));
-    while (chunkPos > ROW_SLOT_CHUNK_NUM_ROWS) {
-        chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunk->nextChunkOffset));
-        chunkPos -= ROW_SLOT_CHUNK_NUM_ROWS;
-    }
-    if (chunkPos == ROW_SLOT_CHUNK_NUM_ROWS) {
-        if (!chunk->nextChunkOffset) {
-            uint32_t chunkOffset = offsetFromPtr(chunk);
-            uint32_t newChunk = alloc(sizeof(RowSlotChunk), true /*aligned*/);
-            chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunkOffset));
-            chunk->nextChunkOffset = newChunk;
-            if (!chunk->nextChunkOffset) {
-                return NULL;
-            }
-        }
-        chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunk->nextChunkOffset));
-        chunk->nextChunkOffset = 0;
-        chunkPos = 0;
-    }
-    mHeader->numRows += 1;
-    return &chunk->slots[chunkPos];
+    *outOffset = mAllocOffset;
+    mAllocOffset = newOffset;
+    return OK;
 }
 
 CursorWindow::FieldSlot* CursorWindow::getFieldSlot(uint32_t row, uint32_t column) {
-    if (row >= mHeader->numRows || column >= mHeader->numColumns) {
-        ALOGE("Failed to read row %d, column %d from a CursorWindow which "
-                "has %d rows, %d columns.",
-                row, column, mHeader->numRows, mHeader->numColumns);
-        return NULL;
+    // This is carefully tuned to use as few cycles as
+    // possible, since this is an extremely hot code path;
+    // see CursorWindow_bench.cpp for more details
+    void *result = static_cast<uint8_t*>(mSlotsStart)
+            - (((row * mNumColumns) + column) << kSlotShift);
+    if (result < mSlotsEnd || result > mSlotsStart || column >= mNumColumns) {
+        LOG(ERROR) << "Failed to read row " << row << ", column " << column
+                << " from a window with " << mNumRows << " rows, " << mNumColumns << " columns";
+        return nullptr;
+    } else {
+        return static_cast<FieldSlot*>(result);
     }
-    RowSlot* rowSlot = getRowSlot(row);
-    if (!rowSlot) {
-        ALOGE("Failed to find rowSlot for row %d.", row);
-        return NULL;
-    }
-    FieldSlot* fieldDir = static_cast<FieldSlot*>(offsetToPtr(rowSlot->offset));
-    return &fieldDir[column];
 }
 
 status_t CursorWindow::putBlob(uint32_t row, uint32_t column, const void* value, size_t size) {
@@ -423,16 +373,15 @@
     if (!fieldSlot) {
         return BAD_VALUE;
     }
-    uint32_t fieldSlotOffset = offsetFromPtr(fieldSlot);
 
-    uint32_t offset = alloc(size);
-    if (!offset) {
+    uint32_t offset;
+    if (alloc(size, &offset)) {
         return NO_MEMORY;
     }
 
     memcpy(offsetToPtr(offset), value, size);
 
-    fieldSlot = static_cast<FieldSlot*>(offsetToPtr(fieldSlotOffset));
+    fieldSlot = getFieldSlot(row, column);
     fieldSlot->type = type;
     fieldSlot->data.buffer.offset = offset;
     fieldSlot->data.buffer.size = size;
diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp
index 5f231ff..4e03ce5 100644
--- a/libs/androidfw/Idmap.cpp
+++ b/libs/androidfw/Idmap.cpp
@@ -36,16 +36,12 @@
 
 namespace android {
 
-static bool compare_target_entries(const Idmap_target_entry &e1, const uint32_t target_id) {
-  return dtohl(e1.target_id) < target_id;
-}
-
-static bool compare_overlay_entries(const Idmap_overlay_entry& e1, const uint32_t overlay_id) {
-  return dtohl(e1.overlay_id) < overlay_id;
+uint32_t round_to_4_bytes(uint32_t size) {
+  return size + (4U - (size % 4U)) % 4U;
 }
 
 size_t Idmap_header::Size() const {
-  return sizeof(Idmap_header) + sizeof(uint8_t) * dtohl(debug_info_size);
+  return sizeof(Idmap_header) + sizeof(uint8_t) * round_to_4_bytes(dtohl(debug_info_size));
 }
 
 OverlayStringPool::OverlayStringPool(const LoadedIdmap* loaded_idmap)
@@ -88,7 +84,10 @@
 status_t OverlayDynamicRefTable::lookupResourceId(uint32_t* resId) const {
   const Idmap_overlay_entry* first_entry = entries_;
   const Idmap_overlay_entry* end_entry = entries_ + dtohl(data_header_->overlay_entry_count);
-  auto entry = std::lower_bound(first_entry, end_entry, *resId, compare_overlay_entries);
+  auto entry = std::lower_bound(first_entry, end_entry, *resId,
+                                [](const Idmap_overlay_entry& e1, const uint32_t overlay_id) {
+    return dtohl(e1.overlay_id) < overlay_id;
+  });
 
   if (entry == end_entry || dtohl(entry->overlay_id) != *resId) {
     // A mapping for the target resource id could not be found.
@@ -96,7 +95,7 @@
   }
 
   *resId = (0x00FFFFFFU & dtohl(entry->target_id))
-      | (((uint32_t) target_assigned_package_id_) << 24);
+      | (((uint32_t) target_assigned_package_id_) << 24U);
   return NO_ERROR;
 }
 
@@ -106,62 +105,58 @@
 
 IdmapResMap::IdmapResMap(const Idmap_data_header* data_header,
                          const Idmap_target_entry* entries,
+                         const Idmap_target_entry_inline* inline_entries,
                          uint8_t target_assigned_package_id,
                          const OverlayDynamicRefTable* overlay_ref_table)
     : data_header_(data_header),
       entries_(entries),
+      inline_entries_(inline_entries),
       target_assigned_package_id_(target_assigned_package_id),
-      overlay_ref_table_(overlay_ref_table) { };
+      overlay_ref_table_(overlay_ref_table) { }
 
 IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const {
-  if ((target_res_id >> 24) != target_assigned_package_id_) {
+  if ((target_res_id >> 24U) != target_assigned_package_id_) {
     // The resource id must have the same package id as the target package.
     return {};
   }
 
   // The resource ids encoded within the idmap are build-time resource ids.
   target_res_id = (0x00FFFFFFU & target_res_id)
-      | (((uint32_t) data_header_->target_package_id) << 24);
+      | (((uint32_t) data_header_->target_package_id) << 24U);
 
-  const Idmap_target_entry* first_entry = entries_;
-  const Idmap_target_entry* end_entry = entries_ + dtohl(data_header_->target_entry_count);
-  auto entry = std::lower_bound(first_entry, end_entry, target_res_id, compare_target_entries);
+  // Check if the target resource is mapped to an overlay resource.
+  auto first_entry = entries_;
+  auto end_entry = entries_ + dtohl(data_header_->target_entry_count);
+  auto entry = std::lower_bound(first_entry, end_entry, target_res_id,
+                                [](const Idmap_target_entry &e, const uint32_t target_id) {
+    return dtohl(e.target_id) < target_id;
+  });
 
-  if (entry == end_entry || dtohl(entry->target_id) != target_res_id) {
-    // A mapping for the target resource id could not be found.
-    return {};
-  }
-
-  // A reference should be treated as an alias of the resource. Instead of returning the table
-  // entry, return the alias resource id to look up. The alias resource might not reside within the
-  // overlay package, so the resource id must be fixed with the dynamic reference table of the
-  // overlay before returning.
-  if (entry->type == Res_value::TYPE_REFERENCE
-      || entry->type == Res_value::TYPE_DYNAMIC_REFERENCE) {
-    uint32_t overlay_resource_id = dtohl(entry->value);
-
+  if (entry != end_entry && dtohl(entry->target_id) == target_res_id) {
+    uint32_t overlay_resource_id = dtohl(entry->overlay_id);
     // Lookup the resource without rewriting the overlay resource id back to the target resource id
     // being looked up.
     overlay_ref_table_->lookupResourceIdNoRewrite(&overlay_resource_id);
     return Result(overlay_resource_id);
   }
 
-  // Copy the type and value into the ResTable_entry structure needed by asset manager.
-  uint16_t malloc_size = sizeof(ResTable_entry) + sizeof(Res_value);
-  auto table_entry = reinterpret_cast<ResTable_entry*>(malloc(malloc_size));
-  memset(table_entry, 0, malloc_size);
-  table_entry->size = htods(sizeof(ResTable_entry));
+  // Check if the target resources is mapped to an inline table entry.
+  auto first_inline_entry = inline_entries_;
+  auto end_inline_entry = inline_entries_ + dtohl(data_header_->target_inline_entry_count);
+  auto inline_entry = std::lower_bound(first_inline_entry, end_inline_entry, target_res_id,
+                                       [](const Idmap_target_entry_inline &e,
+                                          const uint32_t target_id) {
+    return dtohl(e.target_id) < target_id;
+  });
 
-  auto table_value = reinterpret_cast<Res_value*>(reinterpret_cast<uint8_t*>(table_entry)
-      + sizeof(ResTable_entry));
-  table_value->dataType = entry->type;
-  table_value->data = entry->value;
-
-  return Result(ResTable_entry_handle::managed(table_entry, [](auto p) { free(p); }));
+  if (inline_entry != end_inline_entry && dtohl(inline_entry->target_id) == target_res_id) {
+    return Result(inline_entry->value);
+  }
+  return {};
 }
 
 static bool is_word_aligned(const void* data) {
-  return (reinterpret_cast<uintptr_t>(data) & 0x03) == 0;
+  return (reinterpret_cast<uintptr_t>(data) & 0x03U) == 0U;
 }
 
 static bool IsValidIdmapHeader(const StringPiece& data) {
@@ -175,7 +170,7 @@
     return false;
   }
 
-  const Idmap_header* header = reinterpret_cast<const Idmap_header*>(data.data());
+  auto header = reinterpret_cast<const Idmap_header*>(data.data());
   if (dtohl(header->magic) != kIdmapMagic) {
     LOG(ERROR) << StringPrintf("Invalid Idmap file: bad magic value (was 0x%08x, expected 0x%08x)",
                                dtohl(header->magic), kIdmapMagic);
@@ -198,11 +193,13 @@
                          const Idmap_header* header,
                          const Idmap_data_header* data_header,
                          const Idmap_target_entry* target_entries,
+                         const Idmap_target_entry_inline* target_inline_entries,
                          const Idmap_overlay_entry* overlay_entries,
                          ResStringPool* string_pool)
      : header_(header),
        data_header_(data_header),
        target_entries_(target_entries),
+       target_inline_entries_(target_inline_entries),
        overlay_entries_(overlay_entries),
        string_pool_(string_pool),
        idmap_path_(std::move(idmap_path)),
@@ -233,7 +230,7 @@
   data_ptr += sizeof(*data_header);
   data_size -= sizeof(*data_header);
 
-  // Make sure there is enough space for the target entries declared in the header.
+  // Make sure there is enough space for the target entries declared in the header
   const auto target_entries = reinterpret_cast<const Idmap_target_entry*>(data_ptr);
   if (data_size / sizeof(Idmap_target_entry) <
       static_cast<size_t>(dtohl(data_header->target_entry_count))) {
@@ -248,6 +245,21 @@
   data_ptr += target_entry_size_bytes;
   data_size -= target_entry_size_bytes;
 
+  // Make sure there is enough space for the target entries declared in the header.
+  const auto target_inline_entries = reinterpret_cast<const Idmap_target_entry_inline*>(data_ptr);
+  if (data_size / sizeof(Idmap_target_entry_inline) <
+      static_cast<size_t>(dtohl(data_header->target_inline_entry_count))) {
+    LOG(ERROR) << StringPrintf("Idmap too small for the number of target inline entries (%d)",
+                               (int)dtohl(data_header->target_inline_entry_count));
+    return {};
+  }
+
+  // Advance the data pointer past the target entries.
+  const size_t target_inline_entry_size_bytes =
+      (dtohl(data_header->target_inline_entry_count) * sizeof(Idmap_target_entry_inline));
+  data_ptr += target_inline_entry_size_bytes;
+  data_size -= target_inline_entry_size_bytes;
+
   // Make sure there is enough space for the overlay entries declared in the header.
   const auto overlay_entries = reinterpret_cast<const Idmap_overlay_entry*>(data_ptr);
   if (data_size / sizeof(Idmap_overlay_entry) <
@@ -257,22 +269,26 @@
     return {};
   }
 
-  // Advance the data pointer past the target entries.
+  // Advance the data pointer past the overlay entries.
   const size_t overlay_entry_size_bytes =
       (dtohl(data_header->overlay_entry_count) * sizeof(Idmap_overlay_entry));
   data_ptr += overlay_entry_size_bytes;
   data_size -= overlay_entry_size_bytes;
 
   // Read the idmap string pool that holds the value of inline string entries.
-  if (data_size < dtohl(data_header->string_pool_length)) {
+  uint32_t string_pool_size = dtohl(*reinterpret_cast<const uint32_t*>(data_ptr));
+  data_ptr += sizeof(uint32_t);
+  data_size -= sizeof(uint32_t);
+
+  if (data_size < string_pool_size) {
     LOG(ERROR) << StringPrintf("Idmap too small for string pool (length %d)",
-                               (int)dtohl(data_header->string_pool_length));
+                               (int)string_pool_size);
     return {};
   }
 
   auto idmap_string_pool = util::make_unique<ResStringPool>();
-  if (dtohl(data_header->string_pool_length) > 0) {
-    status_t err = idmap_string_pool->setTo(data_ptr, dtohl(data_header->string_pool_length));
+  if (string_pool_size > 0) {
+    status_t err = idmap_string_pool->setTo(data_ptr, string_pool_size);
     if (err != NO_ERROR) {
       LOG(ERROR) << "idmap string pool corrupt.";
       return {};
@@ -280,9 +296,10 @@
   }
 
   // Can't use make_unique because LoadedIdmap constructor is private.
-  std::unique_ptr<LoadedIdmap> loaded_idmap = std::unique_ptr<LoadedIdmap>(
+  auto loaded_idmap = std::unique_ptr<LoadedIdmap>(
       new LoadedIdmap(idmap_path.to_string(), getFileModDate(idmap_path.data()), header,
-                      data_header, target_entries, overlay_entries, idmap_string_pool.release()));
+                      data_header, target_entries, target_inline_entries, overlay_entries,
+                      idmap_string_pool.release()));
 
   return std::move(loaded_idmap);
 }
diff --git a/libs/androidfw/fuzz/cursorwindow_fuzzer/Android.bp b/libs/androidfw/fuzz/cursorwindow_fuzzer/Android.bp
new file mode 100644
index 0000000..2dac47b
--- /dev/null
+++ b/libs/androidfw/fuzz/cursorwindow_fuzzer/Android.bp
@@ -0,0 +1,31 @@
+cc_fuzz {
+    name: "cursorwindow_fuzzer",
+    srcs: [
+        "cursorwindow_fuzzer.cpp",
+    ],
+    host_supported: true,
+    corpus: ["corpus/*"],
+    static_libs: ["libgmock"],
+    target: {
+        android: {
+            shared_libs: [
+                "libandroidfw_fuzzer_lib",
+                "libbase",
+                "libbinder",
+                "libcutils",
+                "liblog",
+                "libutils",
+            ],
+        },
+        host: {
+            static_libs: [
+                "libandroidfw_fuzzer_lib",
+                "libbase",
+                "libbinder",
+                "libcutils",
+                "liblog",
+                "libutils",
+            ],
+        },
+    },
+}
diff --git a/libs/androidfw/fuzz/cursorwindow_fuzzer/corpus/typical.bin b/libs/androidfw/fuzz/cursorwindow_fuzzer/corpus/typical.bin
new file mode 100644
index 0000000..c7e22dd
--- /dev/null
+++ b/libs/androidfw/fuzz/cursorwindow_fuzzer/corpus/typical.bin
Binary files differ
diff --git a/libs/androidfw/fuzz/cursorwindow_fuzzer/cursorwindow_fuzzer.cpp b/libs/androidfw/fuzz/cursorwindow_fuzzer/cursorwindow_fuzzer.cpp
new file mode 100644
index 0000000..8dce212
--- /dev/null
+++ b/libs/androidfw/fuzz/cursorwindow_fuzzer/cursorwindow_fuzzer.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <string>
+#include <memory>
+
+#include "android-base/logging.h"
+#include "androidfw/CursorWindow.h"
+#include "binder/Parcel.h"
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+using android::CursorWindow;
+using android::Parcel;
+
+extern "C" int LLVMFuzzerInitialize(int *, char ***) {
+    setenv("ANDROID_LOG_TAGS", "*:s", 1);
+    android::base::InitLogging(nullptr, &android::base::StderrLogger);
+    return 0;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+    Parcel p;
+    p.setData(data, size);
+
+    CursorWindow* w = nullptr;
+    if (!CursorWindow::createFromParcel(&p, &w)) {
+        LOG(WARNING) << "Valid cursor with " << w->getNumRows() << " rows, "
+                << w->getNumColumns() << " cols";
+
+        // Try obtaining heap allocations for most items; we trim the
+        // search space to speed things up
+        auto rows = std::min(w->getNumRows(), static_cast<uint32_t>(128));
+        auto cols = std::min(w->getNumColumns(), static_cast<uint32_t>(128));
+        for (auto row = 0; row < rows; row++) {
+            for (auto col = 0; col < cols; col++) {
+                auto field = w->getFieldSlot(row, col);
+                if (!field) continue;
+                switch (w->getFieldSlotType(field)) {
+                case CursorWindow::FIELD_TYPE_STRING: {
+                    size_t size;
+                    w->getFieldSlotValueString(field, &size);
+                    break;
+                }
+                case CursorWindow::FIELD_TYPE_BLOB: {
+                    size_t size;
+                    w->getFieldSlotValueBlob(field, &size);
+                    break;
+                }
+                }
+            }
+        }
+
+        // Finally, try obtaining the furthest valid field
+        if (rows > 0 && cols > 0) {
+            w->getFieldSlot(w->getNumRows() - 1, w->getNumColumns() - 1);
+        }
+    }
+    delete w;
+
+    return 0;
+}
diff --git a/libs/androidfw/include/androidfw/CursorWindow.h b/libs/androidfw/include/androidfw/CursorWindow.h
index 0bee609..6e55a9a 100644
--- a/libs/androidfw/include/androidfw/CursorWindow.h
+++ b/libs/androidfw/include/androidfw/CursorWindow.h
@@ -20,38 +20,36 @@
 #include <inttypes.h>
 #include <stddef.h>
 #include <stdint.h>
+#include <string>
 
-#include <binder/Parcel.h>
-#include <log/log.h>
-#include <utils/String8.h>
+#include "android-base/stringprintf.h"
+#include "binder/Parcel.h"
+#include "utils/String8.h"
 
-#if LOG_NDEBUG
-
-#define IF_LOG_WINDOW() if (false)
 #define LOG_WINDOW(...)
 
-#else
-
-#define IF_LOG_WINDOW() IF_ALOG(LOG_DEBUG, "CursorWindow")
-#define LOG_WINDOW(...) ALOG(LOG_DEBUG, "CursorWindow", __VA_ARGS__)
-
-#endif
-
 namespace android {
 
 /**
- * This class stores a set of rows from a database in a buffer. The begining of the
- * window has first chunk of RowSlots, which are offsets to the row directory, followed by
- * an offset to the next chunk in a linked-list of additional chunk of RowSlots in case
- * the pre-allocated chunk isn't big enough to refer to all rows. Each row directory has a
- * FieldSlot per column, which has the size, offset, and type of the data for that field.
- * Note that the data types come from sqlite3.h.
+ * This class stores a set of rows from a database in a buffer. Internally
+ * data is structured as a "heap" of string/blob allocations at the bottom
+ * of the memory region, and a "stack" of FieldSlot allocations at the top
+ * of the memory region. Here's an example visual representation:
+ *
+ *   +----------------------------------------------------------------+
+ *   |heap\0of\0strings\0                                 222211110000| ...
+ *   +-------------------+--------------------------------+-------+---+
+ *    ^                  ^                                ^       ^   ^     ^
+ *    |                  |                                |       |   |     |
+ *    |                  +- mAllocOffset    mSlotsOffset -+       |   |     |
+ *    +- mData                                       mSlotsStart -+   |     |
+ *                                                             mSize -+     |
+ *                                                           mInflatedSize -+
  *
  * Strings are stored in UTF-8.
  */
 class CursorWindow {
-    CursorWindow(const String8& name, int ashmemFd, void* data, size_t size,
-                 size_t inflatedSize, bool readOnly);
+    CursorWindow();
 
 public:
     /* Field types. */
@@ -88,9 +86,9 @@
 
     inline String8 name() { return mName; }
     inline size_t size() { return mSize; }
-    inline size_t freeSpace() { return mSize - mHeader->freeOffset; }
-    inline uint32_t getNumRows() { return mHeader->numRows; }
-    inline uint32_t getNumColumns() { return mHeader->numColumns; }
+    inline size_t freeSpace() { return mSlotsOffset - mAllocOffset; }
+    inline uint32_t getNumRows() { return mNumRows; }
+    inline uint32_t getNumColumns() { return mNumColumns; }
 
     status_t clear();
     status_t setNumColumns(uint32_t numColumns);
@@ -138,75 +136,57 @@
         return offsetToPtr(fieldSlot->data.buffer.offset, fieldSlot->data.buffer.size);
     }
 
+    inline std::string toString() const {
+        return android::base::StringPrintf("CursorWindow{name=%s, fd=%d, size=%d, inflatedSize=%d, "
+                "allocOffset=%d, slotsOffset=%d, numRows=%d, numColumns=%d}", mName.c_str(),
+                mAshmemFd, mSize, mInflatedSize, mAllocOffset, mSlotsOffset, mNumRows, mNumColumns);
+    }
+
 private:
-    static const size_t ROW_SLOT_CHUNK_NUM_ROWS = 100;
-
-    struct Header {
-        // Offset of the lowest unused byte in the window.
-        uint32_t freeOffset;
-
-        // Offset of the first row slot chunk.
-        uint32_t firstChunkOffset;
-
-        uint32_t numRows;
-        uint32_t numColumns;
-    };
-
-    struct RowSlot {
-        uint32_t offset;
-    };
-
-    struct RowSlotChunk {
-        RowSlot slots[ROW_SLOT_CHUNK_NUM_ROWS];
-        uint32_t nextChunkOffset;
-    };
-
     String8 mName;
-    int mAshmemFd;
-    void* mData;
-    size_t mSize;
-    size_t mInflatedSize;
-    bool mReadOnly;
-    Header* mHeader;
+    int mAshmemFd = -1;
+    void* mData = nullptr;
+    /**
+     * Pointer to the first FieldSlot, used to optimize the extremely
+     * hot code path of getFieldSlot().
+     */
+    void* mSlotsStart = nullptr;
+    void* mSlotsEnd = nullptr;
+    uint32_t mSize = 0;
+    /**
+     * When a window starts as lightweight inline allocation, this value
+     * holds the "full" size to be created after ashmem inflation.
+     */
+    uint32_t mInflatedSize = 0;
+    /**
+     * Offset to the top of the "heap" of string/blob allocations. By
+     * storing these allocations at the bottom of our memory region we
+     * avoid having to rewrite offsets when inflating.
+     */
+    uint32_t mAllocOffset = 0;
+    /**
+     * Offset to the bottom of the "stack" of FieldSlot allocations.
+     */
+    uint32_t mSlotsOffset = 0;
+    uint32_t mNumRows = 0;
+    uint32_t mNumColumns = 0;
+    bool mReadOnly = false;
 
-    inline void* offsetToPtr(uint32_t offset, uint32_t bufferSize = 0) {
-        if (offset > mSize) {
-            ALOGE("Offset %" PRIu32 " out of bounds, max value %zu", offset, mSize);
-            return NULL;
-        }
-        if (offset + bufferSize > mSize) {
-            ALOGE("End offset %" PRIu32 " out of bounds, max value %zu",
-                    offset + bufferSize, mSize);
-            return NULL;
-        }
-        return static_cast<uint8_t*>(mData) + offset;
-    }
+    void updateSlotsData();
 
-    inline uint32_t offsetFromPtr(void* ptr) {
-        return static_cast<uint8_t*>(ptr) - static_cast<uint8_t*>(mData);
-    }
-
-    static status_t createFromParcelAshmem(Parcel*, String8&, CursorWindow**);
-    static status_t createFromParcelInline(Parcel*, String8&, CursorWindow**);
-
-    status_t writeToParcelAshmem(Parcel*);
-    status_t writeToParcelInline(Parcel*);
+    void* offsetToPtr(uint32_t offset, uint32_t bufferSize);
+    uint32_t offsetFromPtr(void* ptr);
 
     /**
      * By default windows are lightweight inline allocations; this method
      * inflates the window into a larger ashmem region.
      */
-    status_t inflate();
+    status_t maybeInflate();
 
     /**
-     * Allocate a portion of the window. Returns the offset
-     * of the allocation, or 0 if there isn't enough space.
-     * If aligned is true, the allocation gets 4 byte alignment.
+     * Allocate a portion of the window.
      */
-    uint32_t alloc(size_t size, bool aligned = false);
-
-    RowSlot* getRowSlot(uint32_t row);
-    RowSlot* allocRowSlot();
+    status_t alloc(size_t size, uint32_t* outOffset);
 
     status_t putBlobOrString(uint32_t row, uint32_t column,
             const void* value, size_t size, int32_t type);
diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h
index ecc1ce6..ab0f47f 100644
--- a/libs/androidfw/include/androidfw/Idmap.h
+++ b/libs/androidfw/include/androidfw/Idmap.h
@@ -77,40 +77,40 @@
 // A mapping of target resource ids to a values or resource ids that should overlay the target.
 class IdmapResMap {
  public:
-  // Represents the result of a idmap lookup. The result can be one of three possibillities:
+  // Represents the result of a idmap lookup. The result can be one of three possibilities:
   // 1) The result is a resource id which represents the overlay resource that should act as an
   //    alias of the target resource.
   // 2) The result is a table entry which overlays the type and value of the target resource.
   // 3) The result is neither and the target resource is not overlaid.
   class Result {
    public:
-    Result() : data_(nullptr) {};
+    Result() = default;
     explicit Result(uint32_t value) : data_(value) {};
-    explicit Result(ResTable_entry_handle&& value) : data_(value) { };
+    explicit Result(const Res_value& value) : data_(value) { };
 
     // Returns `true` if the resource is overlaid.
-    inline explicit operator bool() const {
-      return !std::get_if<nullptr_t>(&data_);
+    explicit operator bool() const {
+      return std::get_if<std::monostate>(&data_) == nullptr;
     }
 
-    inline bool IsResourceId() const {
-      return std::get_if<uint32_t>(&data_);
+    bool IsResourceId() const {
+      return std::get_if<uint32_t>(&data_) != nullptr;
     }
 
-    inline uint32_t GetResourceId() const {
-      return *std::get_if<uint32_t>(&data_);
+    uint32_t GetResourceId() const {
+      return std::get<uint32_t>(data_);
     }
 
-    inline bool IsTableEntry() const {
-      return std::get_if<ResTable_entry_handle>(&data_);
+    bool IsInlineValue() const {
+      return std::get_if<Res_value>(&data_) != nullptr;
     }
 
-    inline const ResTable_entry_handle& GetTableEntry() const {
-      return *std::get_if<ResTable_entry_handle>(&data_);
+    const Res_value& GetInlineValue() const {
+      return std::get<Res_value>(data_);
     }
 
    private:
-      std::variant<uint32_t, nullptr_t, ResTable_entry_handle> data_;
+      std::variant<std::monostate, uint32_t, Res_value> data_;
   };
 
   // Looks up the value that overlays the target resource id.
@@ -123,11 +123,13 @@
  private:
   explicit IdmapResMap(const Idmap_data_header* data_header,
                        const Idmap_target_entry* entries,
+                       const Idmap_target_entry_inline* inline_entries,
                        uint8_t target_assigned_package_id,
                        const OverlayDynamicRefTable* overlay_ref_table);
 
   const Idmap_data_header* data_header_;
   const Idmap_target_entry* entries_;
+  const Idmap_target_entry_inline* inline_entries_;
   const uint8_t target_assigned_package_id_;
   const OverlayDynamicRefTable* overlay_ref_table_;
 
@@ -163,8 +165,8 @@
   // Returns a mapping from target resource ids to overlay values.
   inline const IdmapResMap GetTargetResourcesMap(
       uint8_t target_assigned_package_id, const OverlayDynamicRefTable* overlay_ref_table) const {
-    return IdmapResMap(data_header_, target_entries_, target_assigned_package_id,
-                       overlay_ref_table);
+    return IdmapResMap(data_header_, target_entries_, target_inline_entries_,
+                       target_assigned_package_id, overlay_ref_table);
   }
 
   // Returns a dynamic reference table for a loaded overlay package.
@@ -184,6 +186,7 @@
   const Idmap_header* header_;
   const Idmap_data_header* data_header_;
   const Idmap_target_entry* target_entries_;
+  const Idmap_target_entry_inline* target_inline_entries_;
   const Idmap_overlay_entry* overlay_entries_;
   const std::unique_ptr<ResStringPool> string_pool_;
 
@@ -200,6 +203,7 @@
                        const Idmap_header* header,
                        const Idmap_data_header* data_header,
                        const Idmap_target_entry* target_entries,
+                       const Idmap_target_entry_inline* target_inline_entries,
                        const Idmap_overlay_entry* overlay_entries,
                        ResStringPool* string_pool);
 
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index e10a7f3..04ba78b 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -41,7 +41,7 @@
 namespace android {
 
 constexpr const static uint32_t kIdmapMagic = 0x504D4449u;
-constexpr const static uint32_t kIdmapCurrentVersion = 0x00000004u;
+constexpr const static uint32_t kIdmapCurrentVersion = 0x00000005u;
 
 /**
  * In C++11, char16_t is defined as *at least* 16 bits. We do a lot of
@@ -1476,7 +1476,7 @@
         // If set, this is a weak resource and may be overriden by strong
         // resources of the same name/type. This is only useful during
         // linking with other resource tables.
-        FLAG_WEAK = 0x0004
+        FLAG_WEAK = 0x0004,
     };
     uint16_t flags;
     
@@ -1586,50 +1586,6 @@
     Res_value value;
 };
 
-
-// A ResTable_entry variant that either holds an unmanaged pointer to a constant ResTable_entry or
-// holds a ResTable_entry which is tied to the lifetime of the handle.
-class ResTable_entry_handle {
- public:
-    ResTable_entry_handle() = default;
-
-    ResTable_entry_handle(const ResTable_entry_handle& handle) {
-      entry_ = handle.entry_;
-    }
-
-    ResTable_entry_handle(ResTable_entry_handle&& handle) noexcept {
-      entry_ = handle.entry_;
-    }
-
-    inline static ResTable_entry_handle managed(ResTable_entry* entry, void (*deleter)(void *)) {
-      return ResTable_entry_handle(std::shared_ptr<const ResTable_entry>(entry, deleter));
-    }
-
-    inline static ResTable_entry_handle unmanaged(const ResTable_entry* entry)  {
-      return ResTable_entry_handle(std::shared_ptr<const ResTable_entry>(entry, [](auto /*p */){}));
-    }
-
-    inline ResTable_entry_handle& operator=(const ResTable_entry_handle& handle) noexcept {
-      entry_ = handle.entry_;
-      return *this;
-    }
-
-    inline ResTable_entry_handle& operator=(ResTable_entry_handle&& handle) noexcept {
-      entry_ = handle.entry_;
-      return *this;
-    }
-
-    inline const ResTable_entry* operator*() & {
-      return entry_.get();
-    }
-
- private:
-    explicit ResTable_entry_handle(std::shared_ptr<const ResTable_entry> entry)
-        : entry_(std::move(entry)) { }
-
-    std::shared_ptr<const ResTable_entry> entry_;
-};
-
 /**
  * A package-id to package name mapping for any shared libraries used
  * in this resource table. The package-id's encoded in this resource
@@ -1740,7 +1696,6 @@
   return first;
 }
 
-#pragma pack(push, 1)
 struct Idmap_header {
   // Always 0x504D4449 ('IDMP')
   uint32_t magic;
@@ -1751,7 +1706,7 @@
   uint32_t overlay_crc32;
 
   uint32_t fulfilled_policies;
-  uint8_t enforce_overlayable;
+  uint32_t enforce_overlayable;
 
   uint8_t target_path[256];
   uint8_t overlay_path[256];
@@ -1765,23 +1720,31 @@
 struct Idmap_data_header {
   uint8_t target_package_id;
   uint8_t overlay_package_id;
+
+  // Padding to ensure 4 byte alignment for target_entry_count
+  uint16_t p0;
+
   uint32_t target_entry_count;
+  uint32_t target_inline_entry_count;
   uint32_t overlay_entry_count;
+
   uint32_t string_pool_index_offset;
-  uint32_t string_pool_length;
 };
 
 struct Idmap_target_entry {
   uint32_t target_id;
-  uint8_t type;
-  uint32_t value;
+  uint32_t overlay_id;
+};
+
+struct Idmap_target_entry_inline {
+  uint32_t target_id;
+  Res_value value;
 };
 
 struct Idmap_overlay_entry {
   uint32_t overlay_id;
   uint32_t target_id;
 };
-#pragma pack(pop)
 
 class AssetManager2;
 
diff --git a/libs/androidfw/tests/CursorWindow_bench.cpp b/libs/androidfw/tests/CursorWindow_bench.cpp
new file mode 100644
index 0000000..f1191c3
--- /dev/null
+++ b/libs/androidfw/tests/CursorWindow_bench.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "benchmark/benchmark.h"
+
+#include "androidfw/CursorWindow.h"
+
+namespace android {
+
+static void BM_CursorWindowWrite(benchmark::State& state, size_t rows, size_t cols) {
+    CursorWindow* w;
+    CursorWindow::create(String8("test"), 1 << 21, &w);
+
+    while (state.KeepRunning()) {
+        w->clear();
+        w->setNumColumns(cols);
+        for (int row = 0; row < rows; row++) {
+            w->allocRow();
+            for (int col = 0; col < cols; col++) {
+                w->putLong(row, col, 0xcafe);
+            }
+        }
+    }
+}
+
+static void BM_CursorWindowWrite4x4(benchmark::State& state) {
+    BM_CursorWindowWrite(state, 4, 4);
+}
+BENCHMARK(BM_CursorWindowWrite4x4);
+
+static void BM_CursorWindowWrite1Kx4(benchmark::State& state) {
+    BM_CursorWindowWrite(state, 1024, 4);
+}
+BENCHMARK(BM_CursorWindowWrite1Kx4);
+
+static void BM_CursorWindowWrite16Kx4(benchmark::State& state) {
+    BM_CursorWindowWrite(state, 16384, 4);
+}
+BENCHMARK(BM_CursorWindowWrite16Kx4);
+
+static void BM_CursorWindowRead(benchmark::State& state, size_t rows, size_t cols) {
+    CursorWindow* w;
+    CursorWindow::create(String8("test"), 1 << 21, &w);
+    w->setNumColumns(cols);
+    for (int row = 0; row < rows; row++) {
+        w->allocRow();
+    }
+
+    while (state.KeepRunning()) {
+        for (int row = 0; row < rows; row++) {
+            for (int col = 0; col < cols; col++) {
+                w->getFieldSlot(row, col);
+            }
+        }
+    }
+}
+
+static void BM_CursorWindowRead4x4(benchmark::State& state) {
+    BM_CursorWindowRead(state, 4, 4);
+}
+BENCHMARK(BM_CursorWindowRead4x4);
+
+static void BM_CursorWindowRead1Kx4(benchmark::State& state) {
+    BM_CursorWindowRead(state, 1024, 4);
+}
+BENCHMARK(BM_CursorWindowRead1Kx4);
+
+static void BM_CursorWindowRead16Kx4(benchmark::State& state) {
+    BM_CursorWindowRead(state, 16384, 4);
+}
+BENCHMARK(BM_CursorWindowRead16Kx4);
+
+}  // namespace android
diff --git a/libs/androidfw/tests/CursorWindow_test.cpp b/libs/androidfw/tests/CursorWindow_test.cpp
new file mode 100644
index 0000000..15be80c
--- /dev/null
+++ b/libs/androidfw/tests/CursorWindow_test.cpp
@@ -0,0 +1,367 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <utility>
+
+#include "androidfw/CursorWindow.h"
+
+#include "TestHelpers.h"
+
+#define CREATE_WINDOW_1K \
+    CursorWindow* w; \
+    CursorWindow::create(String8("test"), 1 << 10, &w);
+
+#define CREATE_WINDOW_1K_3X3 \
+    CursorWindow* w; \
+    CursorWindow::create(String8("test"), 1 << 10, &w); \
+    ASSERT_EQ(w->setNumColumns(3), OK); \
+    ASSERT_EQ(w->allocRow(), OK); \
+    ASSERT_EQ(w->allocRow(), OK); \
+    ASSERT_EQ(w->allocRow(), OK);
+
+#define CREATE_WINDOW_2M \
+    CursorWindow* w; \
+    CursorWindow::create(String8("test"), 1 << 21, &w);
+
+static constexpr const size_t kHalfInlineSize = 8192;
+static constexpr const size_t kGiantSize = 1048576;
+
+namespace android {
+
+TEST(CursorWindowTest, Empty) {
+    CREATE_WINDOW_1K;
+
+    ASSERT_EQ(w->getNumRows(), 0);
+    ASSERT_EQ(w->getNumColumns(), 0);
+    ASSERT_EQ(w->size(), 1 << 10);
+    ASSERT_EQ(w->freeSpace(), 1 << 10);
+}
+
+TEST(CursorWindowTest, SetNumColumns) {
+    CREATE_WINDOW_1K;
+
+    // Once we've locked in columns, we can't adjust
+    ASSERT_EQ(w->getNumColumns(), 0);
+    ASSERT_EQ(w->setNumColumns(4), OK);
+    ASSERT_NE(w->setNumColumns(5), OK);
+    ASSERT_NE(w->setNumColumns(3), OK);
+    ASSERT_EQ(w->getNumColumns(), 4);
+}
+
+TEST(CursorWindowTest, SetNumColumnsAfterRow) {
+    CREATE_WINDOW_1K;
+
+    // Once we've locked in a row, we can't adjust columns
+    ASSERT_EQ(w->getNumColumns(), 0);
+    ASSERT_EQ(w->allocRow(), OK);
+    ASSERT_NE(w->setNumColumns(4), OK);
+    ASSERT_EQ(w->getNumColumns(), 0);
+}
+
+TEST(CursorWindowTest, AllocRow) {
+    CREATE_WINDOW_1K;
+
+    ASSERT_EQ(w->setNumColumns(4), OK);
+
+    // Rolling forward means we have less free space
+    ASSERT_EQ(w->getNumRows(), 0);
+    auto before = w->freeSpace();
+    ASSERT_EQ(w->allocRow(), OK);
+    ASSERT_LT(w->freeSpace(), before);
+    ASSERT_EQ(w->getNumRows(), 1);
+
+    // Verify we can unwind
+    ASSERT_EQ(w->freeLastRow(), OK);
+    ASSERT_EQ(w->freeSpace(), before);
+    ASSERT_EQ(w->getNumRows(), 0);
+
+    // Can't unwind when no rows left
+    ASSERT_NE(w->freeLastRow(), OK);
+}
+
+TEST(CursorWindowTest, AllocRowBounds) {
+    CREATE_WINDOW_1K;
+
+    // 60 columns is 960 bytes, which means only a single row can fit
+    ASSERT_EQ(w->setNumColumns(60), OK);
+    ASSERT_EQ(w->allocRow(), OK);
+    ASSERT_NE(w->allocRow(), OK);
+}
+
+TEST(CursorWindowTest, StoreNull) {
+    CREATE_WINDOW_1K_3X3;
+
+    ASSERT_EQ(w->putNull(1, 1), OK);
+    ASSERT_EQ(w->putNull(0, 0), OK);
+
+    {
+        auto field = w->getFieldSlot(1, 1);
+        ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_NULL);
+    }
+    {
+        auto field = w->getFieldSlot(0, 0);
+        ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_NULL);
+    }
+}
+
+TEST(CursorWindowTest, StoreLong) {
+    CREATE_WINDOW_1K_3X3;
+
+    ASSERT_EQ(w->putLong(1, 1, 0xf00d), OK);
+    ASSERT_EQ(w->putLong(0, 0, 0xcafe), OK);
+
+    {
+        auto field = w->getFieldSlot(1, 1);
+        ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_INTEGER);
+        ASSERT_EQ(w->getFieldSlotValueLong(field), 0xf00d);
+    }
+    {
+        auto field = w->getFieldSlot(0, 0);
+        ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_INTEGER);
+        ASSERT_EQ(w->getFieldSlotValueLong(field), 0xcafe);
+    }
+}
+
+TEST(CursorWindowTest, StoreString) {
+    CREATE_WINDOW_1K_3X3;
+
+    ASSERT_EQ(w->putString(1, 1, "food", 5), OK);
+    ASSERT_EQ(w->putString(0, 0, "cafe", 5), OK);
+
+    size_t size;
+    {
+        auto field = w->getFieldSlot(1, 1);
+        ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_STRING);
+        auto actual = w->getFieldSlotValueString(field, &size);
+        ASSERT_EQ(std::string(actual), "food");
+    }
+    {
+        auto field = w->getFieldSlot(0, 0);
+        ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_STRING);
+        auto actual = w->getFieldSlotValueString(field, &size);
+        ASSERT_EQ(std::string(actual), "cafe");
+    }
+}
+
+TEST(CursorWindowTest, StoreBounds) {
+    CREATE_WINDOW_1K_3X3;
+
+    // Can't work with values beyond bounds
+    ASSERT_NE(w->putLong(0, 3, 0xcafe), OK);
+    ASSERT_NE(w->putLong(3, 0, 0xcafe), OK);
+    ASSERT_NE(w->putLong(3, 3, 0xcafe), OK);
+    ASSERT_EQ(w->getFieldSlot(0, 3), nullptr);
+    ASSERT_EQ(w->getFieldSlot(3, 0), nullptr);
+    ASSERT_EQ(w->getFieldSlot(3, 3), nullptr);
+
+    // Can't work with invalid indexes
+    ASSERT_NE(w->putLong(-1, 0, 0xcafe), OK);
+    ASSERT_NE(w->putLong(0, -1, 0xcafe), OK);
+    ASSERT_NE(w->putLong(-1, -1, 0xcafe), OK);
+    ASSERT_EQ(w->getFieldSlot(-1, 0), nullptr);
+    ASSERT_EQ(w->getFieldSlot(0, -1), nullptr);
+    ASSERT_EQ(w->getFieldSlot(-1, -1), nullptr);
+}
+
+TEST(CursorWindowTest, Inflate) {
+    CREATE_WINDOW_2M;
+
+    auto before = w->size();
+    ASSERT_EQ(w->setNumColumns(4), OK);
+    ASSERT_EQ(w->allocRow(), OK);
+
+    // Scratch buffer that will fit before inflation
+    void* buf = malloc(kHalfInlineSize);
+
+    // Store simple value
+    ASSERT_EQ(w->putLong(0, 0, 0xcafe), OK);
+
+    // Store first object that fits inside
+    memset(buf, 42, kHalfInlineSize);
+    ASSERT_EQ(w->putBlob(0, 1, buf, kHalfInlineSize), OK);
+    ASSERT_EQ(w->size(), before);
+
+    // Store second simple value
+    ASSERT_EQ(w->putLong(0, 2, 0xface), OK);
+
+    // Store second object that requires inflation
+    memset(buf, 84, kHalfInlineSize);
+    ASSERT_EQ(w->putBlob(0, 3, buf, kHalfInlineSize), OK);
+    ASSERT_GT(w->size(), before);
+
+    // Verify data is intact
+    {
+        auto field = w->getFieldSlot(0, 0);
+        ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_INTEGER);
+        ASSERT_EQ(w->getFieldSlotValueLong(field), 0xcafe);
+    }
+    {
+        auto field = w->getFieldSlot(0, 1);
+        ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_BLOB);
+        size_t actualSize;
+        auto actual = w->getFieldSlotValueBlob(field, &actualSize);
+        ASSERT_EQ(actualSize, kHalfInlineSize);
+        memset(buf, 42, kHalfInlineSize);
+        ASSERT_NE(actual, buf);
+        ASSERT_EQ(memcmp(buf, actual, kHalfInlineSize), 0);
+    }
+    {
+        auto field = w->getFieldSlot(0, 2);
+        ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_INTEGER);
+        ASSERT_EQ(w->getFieldSlotValueLong(field), 0xface);
+    }
+    {
+        auto field = w->getFieldSlot(0, 3);
+        ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_BLOB);
+        size_t actualSize;
+        auto actual = w->getFieldSlotValueBlob(field, &actualSize);
+        ASSERT_EQ(actualSize, kHalfInlineSize);
+        memset(buf, 84, kHalfInlineSize);
+        ASSERT_NE(actual, buf);
+        ASSERT_EQ(memcmp(buf, actual, kHalfInlineSize), 0);
+    }
+}
+
+TEST(CursorWindowTest, ParcelEmpty) {
+    CREATE_WINDOW_2M;
+
+    Parcel p;
+    w->writeToParcel(&p);
+    p.setDataPosition(0);
+    w = nullptr;
+
+    ASSERT_EQ(CursorWindow::createFromParcel(&p, &w), OK);
+    ASSERT_EQ(w->getNumRows(), 0);
+    ASSERT_EQ(w->getNumColumns(), 0);
+    ASSERT_EQ(w->size(), 0);
+    ASSERT_EQ(w->freeSpace(), 0);
+
+    // We can't mutate the window after parceling
+    ASSERT_NE(w->setNumColumns(4), OK);
+    ASSERT_NE(w->allocRow(), OK);
+}
+
+TEST(CursorWindowTest, ParcelSmall) {
+    CREATE_WINDOW_2M;
+
+    auto before = w->size();
+    ASSERT_EQ(w->setNumColumns(4), OK);
+    ASSERT_EQ(w->allocRow(), OK);
+
+    // Scratch buffer that will fit before inflation
+    void* buf = malloc(kHalfInlineSize);
+
+    // Store simple value
+    ASSERT_EQ(w->putLong(0, 0, 0xcafe), OK);
+
+    // Store first object that fits inside
+    memset(buf, 42, kHalfInlineSize);
+    ASSERT_EQ(w->putBlob(0, 1, buf, kHalfInlineSize), OK);
+    ASSERT_EQ(w->size(), before);
+
+    // Store second object with zero length
+    ASSERT_EQ(w->putBlob(0, 2, buf, 0), OK);
+    ASSERT_EQ(w->size(), before);
+
+    // Force through a parcel
+    Parcel p;
+    w->writeToParcel(&p);
+    p.setDataPosition(0);
+    w = nullptr;
+
+    ASSERT_EQ(CursorWindow::createFromParcel(&p, &w), OK);
+    ASSERT_EQ(w->getNumRows(), 1);
+    ASSERT_EQ(w->getNumColumns(), 4);
+
+    // Verify data is intact
+    {
+        auto field = w->getFieldSlot(0, 0);
+        ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_INTEGER);
+        ASSERT_EQ(w->getFieldSlotValueLong(field), 0xcafe);
+    }
+    {
+        auto field = w->getFieldSlot(0, 1);
+        ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_BLOB);
+        size_t actualSize;
+        auto actual = w->getFieldSlotValueBlob(field, &actualSize);
+        ASSERT_EQ(actualSize, kHalfInlineSize);
+        memset(buf, 42, kHalfInlineSize);
+        ASSERT_NE(actual, buf);
+        ASSERT_EQ(memcmp(buf, actual, kHalfInlineSize), 0);
+    }
+    {
+        auto field = w->getFieldSlot(0, 2);
+        ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_BLOB);
+        size_t actualSize;
+        auto actual = w->getFieldSlotValueBlob(field, &actualSize);
+        ASSERT_EQ(actualSize, 0);
+        ASSERT_NE(actual, nullptr);
+    }
+}
+
+TEST(CursorWindowTest, ParcelLarge) {
+    CREATE_WINDOW_2M;
+
+    ASSERT_EQ(w->setNumColumns(4), OK);
+    ASSERT_EQ(w->allocRow(), OK);
+
+    // Store simple value
+    ASSERT_EQ(w->putLong(0, 0, 0xcafe), OK);
+
+    // Store object that forces inflation
+    void* buf = malloc(kGiantSize);
+    memset(buf, 42, kGiantSize);
+    ASSERT_EQ(w->putBlob(0, 1, buf, kGiantSize), OK);
+
+    // Store second object with zero length
+    ASSERT_EQ(w->putBlob(0, 2, buf, 0), OK);
+
+    // Force through a parcel
+    Parcel p;
+    w->writeToParcel(&p);
+    p.setDataPosition(0);
+    w = nullptr;
+
+    ASSERT_EQ(CursorWindow::createFromParcel(&p, &w), OK);
+    ASSERT_EQ(w->getNumRows(), 1);
+    ASSERT_EQ(w->getNumColumns(), 4);
+
+    // Verify data is intact
+    {
+        auto field = w->getFieldSlot(0, 0);
+        ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_INTEGER);
+        ASSERT_EQ(w->getFieldSlotValueLong(field), 0xcafe);
+    }
+    {
+        auto field = w->getFieldSlot(0, 1);
+        ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_BLOB);
+        size_t actualSize;
+        auto actual = w->getFieldSlotValueBlob(field, &actualSize);
+        ASSERT_EQ(actualSize, kGiantSize);
+        memset(buf, 42, kGiantSize);
+        ASSERT_EQ(memcmp(buf, actual, kGiantSize), 0);
+    }
+    {
+        auto field = w->getFieldSlot(0, 2);
+        ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_BLOB);
+        size_t actualSize;
+        auto actual = w->getFieldSlotValueBlob(field, &actualSize);
+        ASSERT_EQ(actualSize, 0);
+        ASSERT_NE(actual, nullptr);
+    }
+}
+
+} // android
diff --git a/libs/androidfw/tests/data/overlay/overlay.apk b/libs/androidfw/tests/data/overlay/overlay.apk
index f1ed592..c9bf252 100644
--- a/libs/androidfw/tests/data/overlay/overlay.apk
+++ b/libs/androidfw/tests/data/overlay/overlay.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/overlay/overlay.idmap b/libs/androidfw/tests/data/overlay/overlay.idmap
index 29c5eb6..3ab244e 100644
--- a/libs/androidfw/tests/data/overlay/overlay.idmap
+++ b/libs/androidfw/tests/data/overlay/overlay.idmap
Binary files differ
diff --git a/libs/hwui/FrameInfo.cpp b/libs/hwui/FrameInfo.cpp
index 30ce537..fd18d2f 100644
--- a/libs/hwui/FrameInfo.cpp
+++ b/libs/hwui/FrameInfo.cpp
@@ -31,6 +31,7 @@
         "AnimationStart",
         "PerformTraversalsStart",
         "DrawStart",
+        "FrameDeadline",
         "SyncQueued",
         "SyncStart",
         "IssueDrawCommandsStart",
@@ -45,7 +46,7 @@
                       static_cast<int>(FrameInfoIndex::NumIndexes),
               "size mismatch: FrameInfoNames doesn't match the enum!");
 
-static_assert(static_cast<int>(FrameInfoIndex::NumIndexes) == 18,
+static_assert(static_cast<int>(FrameInfoIndex::NumIndexes) == 19,
               "Must update value in FrameMetrics.java#FRAME_STATS_COUNT (and here)");
 
 void FrameInfo::importUiThreadInfo(int64_t* info) {
diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h
index f5bfedd..bb875e3 100644
--- a/libs/hwui/FrameInfo.h
+++ b/libs/hwui/FrameInfo.h
@@ -27,7 +27,7 @@
 namespace android {
 namespace uirenderer {
 
-#define UI_THREAD_FRAME_INFO_SIZE 10
+#define UI_THREAD_FRAME_INFO_SIZE 11
 
 enum class FrameInfoIndex {
     Flags = 0,
@@ -40,6 +40,7 @@
     AnimationStart,
     PerformTraversalsStart,
     DrawStart,
+    FrameDeadline,
     // End of UI frame info
 
     SyncQueued,
@@ -77,9 +78,11 @@
     explicit UiFrameInfoBuilder(int64_t* buffer) : mBuffer(buffer) {
         memset(mBuffer, 0, UI_THREAD_FRAME_INFO_SIZE * sizeof(int64_t));
         set(FrameInfoIndex::FrameTimelineVsyncId) = INVALID_VSYNC_ID;
+        set(FrameInfoIndex::FrameDeadline) = std::numeric_limits<int64_t>::max();
     }
 
-    UiFrameInfoBuilder& setVsync(nsecs_t vsyncTime, nsecs_t intendedVsync, int64_t vsyncId) {
+    UiFrameInfoBuilder& setVsync(nsecs_t vsyncTime, nsecs_t intendedVsync,
+                                 int64_t vsyncId, int64_t frameDeadline) {
         set(FrameInfoIndex::FrameTimelineVsyncId) = vsyncId;
         set(FrameInfoIndex::Vsync) = vsyncTime;
         set(FrameInfoIndex::IntendedVsync) = intendedVsync;
@@ -89,6 +92,7 @@
         set(FrameInfoIndex::AnimationStart) = vsyncTime;
         set(FrameInfoIndex::PerformTraversalsStart) = vsyncTime;
         set(FrameInfoIndex::DrawStart) = vsyncTime;
+        set(FrameInfoIndex::FrameDeadline) = frameDeadline;
         return *this;
     }
 
@@ -154,7 +158,7 @@
         // GPU start time is approximated to the moment before swapBuffer is invoked.
         // We could add an EGLSyncKHR fence at the beginning of the frame, but that is an overhead.
         int64_t endTime = get(FrameInfoIndex::GpuCompleted);
-        return endTime > 0 ? endTime - get(FrameInfoIndex::SwapBuffers) : -1;
+        return endTime > 0 ? endTime - get(FrameInfoIndex::SwapBuffers) : 0;
     }
 
     inline int64_t& set(FrameInfoIndex index) { return mFrameInfo[static_cast<int>(index)]; }
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index 8df2770b..146bf28 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -135,6 +135,26 @@
     float totalAdvance;
 };
 
+void Canvas::drawGlyphs(const minikin::Font& font, const int* glyphIds, const float* positions,
+                        int glyphCount, const Paint& paint) {
+    // Minikin modify skFont for auto-fakebold/auto-fakeitalic.
+    Paint copied(paint);
+
+    auto glyphFunc = [&](uint16_t* outGlyphIds, float* outPositions) {
+        for (uint32_t i = 0; i < glyphCount; ++i) {
+            outGlyphIds[i] = static_cast<uint16_t>(glyphIds[i]);
+        }
+        memcpy(outPositions, positions, sizeof(float) * 2 * glyphCount);
+    };
+
+    const minikin::MinikinFont* minikinFont = font.typeface().get();
+    SkFont* skfont = &copied.getSkFont();
+    MinikinFontSkia::populateSkFont(skfont, minikinFont, minikin::FontFakery());
+
+    // total advance is used for drawing underline. We do not support underlyine by glyph drawing.
+    drawGlyphs(glyphFunc, glyphCount, copied, 0 /* x */, 0 /* y */, 0 /* total Advance */);
+}
+
 void Canvas::drawText(const uint16_t* text, int textSize, int start, int count, int contextStart,
                       int contextCount, float x, float y, minikin::Bidi bidiFlags,
                       const Paint& origPaint, const Typeface* typeface, minikin::MeasuredText* mt) {
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 817c7ee..772b7a2 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -32,6 +32,7 @@
 class SkVertices;
 
 namespace minikin {
+class Font;
 class Layout;
 class MeasuredText;
 enum class Bidi : uint8_t;
@@ -255,6 +256,9 @@
      */
     virtual void drawVectorDrawable(VectorDrawableRoot* tree) = 0;
 
+    void drawGlyphs(const minikin::Font& font, const int* glyphIds, const float* positions,
+                    int glyphCount, const Paint& paint);
+
     /**
      * Converts utf16 text to glyphs, calculating position and boundary,
      * and delegating the final draw to virtual drawGlyphs method.
diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp
index e76aace..e36e355 100644
--- a/libs/hwui/jni/Shader.cpp
+++ b/libs/hwui/jni/Shader.cpp
@@ -211,14 +211,26 @@
 ///////////////////////////////////////////////////////////////////////////////////////////////
 
 static jlong RuntimeShader_create(JNIEnv* env, jobject, jlong shaderFactory, jlong matrixPtr,
-        jbyteArray inputs, jlong colorSpaceHandle, jboolean isOpaque) {
+        jbyteArray inputs, jlongArray inputShaders, jlong colorSpaceHandle, jboolean isOpaque) {
     SkRuntimeEffect* effect = reinterpret_cast<SkRuntimeEffect*>(shaderFactory);
     AutoJavaByteArray arInputs(env, inputs);
 
+    std::vector<sk_sp<SkShader>> shaderVector;
+    if (inputShaders) {
+        jsize shaderCount = env->GetArrayLength(inputShaders);
+        shaderVector.resize(shaderCount);
+        jlong* arrayPtr = env->GetLongArrayElements(inputShaders, NULL);
+        for (int i = 0; i < shaderCount; i++) {
+            shaderVector[i] = sk_ref_sp(reinterpret_cast<SkShader*>(arrayPtr[i]));
+        }
+        env->ReleaseLongArrayElements(inputShaders, arrayPtr, 0);
+    }
+
     sk_sp<SkData> fData;
     fData = SkData::MakeWithCopy(arInputs.ptr(), arInputs.length());
     const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
-    sk_sp<SkShader> shader = effect->makeShader(fData, nullptr, 0, matrix, isOpaque == JNI_TRUE);
+    sk_sp<SkShader> shader = effect->makeShader(fData, shaderVector.data(), shaderVector.size(),
+                                                matrix, isOpaque == JNI_TRUE);
     ThrowIAE_IfNull(env, shader);
 
     return reinterpret_cast<jlong>(shader.release());
@@ -280,7 +292,7 @@
 
 static const JNINativeMethod gRuntimeShaderMethods[] = {
     { "nativeGetFinalizer",   "()J",    (void*)RuntimeShader_getNativeFinalizer },
-    { "nativeCreate",     "(JJ[BJZ)J",  (void*)RuntimeShader_create     },
+    { "nativeCreate",     "(JJ[B[JJZ)J",  (void*)RuntimeShader_create     },
     { "nativeCreateShaderFactory",     "(Ljava/lang/String;)J",
       (void*)RuntimeShader_createShaderFactory     },
 };
diff --git a/libs/hwui/jni/android_graphics_Canvas.cpp b/libs/hwui/jni/android_graphics_Canvas.cpp
index b6c6cd0..c04340c 100644
--- a/libs/hwui/jni/android_graphics_Canvas.cpp
+++ b/libs/hwui/jni/android_graphics_Canvas.cpp
@@ -30,6 +30,7 @@
 #include <nativehelper/ScopedPrimitiveArray.h>
 #include <nativehelper/ScopedStringChars.h>
 
+#include "FontUtils.h"
 #include "Bitmap.h"
 #include "SkGraphics.h"
 #include "SkRegion.h"
@@ -540,6 +541,21 @@
                                              colorA.ptr() + colorIndex, paint);
 }
 
+static void drawGlyphs(JNIEnv* env, jobject, jlong canvasHandle, jintArray glyphIds,
+                       jfloatArray positions, jint glyphOffset, jint positionOffset,
+                       jint glyphCount, jlong fontHandle, jlong paintHandle) {
+    Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+    FontWrapper* font = reinterpret_cast<FontWrapper*>(fontHandle);
+    AutoJavaIntArray glyphIdArray(env, glyphIds);
+    AutoJavaFloatArray positionArray(env, positions);
+    get_canvas(canvasHandle)->drawGlyphs(
+        *font->font.get(),
+        glyphIdArray.ptr() + glyphOffset,
+        positionArray.ptr() + positionOffset,
+        glyphCount,
+        *paint);
+}
+
 static void drawTextChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray charArray,
                           jint index, jint count, jfloat x, jfloat y, jint bidiFlags,
                           jlong paintHandle) {
@@ -719,6 +735,7 @@
     {"nDrawBitmap","(JJFFJIII)V", (void*) CanvasJNI::drawBitmap},
     {"nDrawBitmap","(JJFFFFFFFFJII)V", (void*) CanvasJNI::drawBitmapRect},
     {"nDrawBitmap", "(J[IIIFFIIZJ)V", (void*)CanvasJNI::drawBitmapArray},
+    {"nDrawGlyphs", "(J[I[FIIIJJ)V", (void*)CanvasJNI::drawGlyphs},
     {"nDrawText","(J[CIIFFIJ)V", (void*) CanvasJNI::drawTextChars},
     {"nDrawText","(JLjava/lang/String;IIFFIJ)V", (void*) CanvasJNI::drawTextString},
     {"nDrawTextRun","(J[CIIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunChars},
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index c89463b..a146b64 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -514,7 +514,8 @@
         proxy.setLightGeometry((Vector3){0, 0, 0}, 0);
         nsecs_t vsync = systemTime(SYSTEM_TIME_MONOTONIC);
         UiFrameInfoBuilder(proxy.frameInfo())
-                .setVsync(vsync, vsync, UiFrameInfoBuilder::INVALID_VSYNC_ID)
+                .setVsync(vsync, vsync, UiFrameInfoBuilder::INVALID_VSYNC_ID,
+                    std::numeric_limits<int64_t>::max())
                 .addFlag(FrameInfoFlags::SurfaceCanvas);
         proxy.syncAndDrawFrame();
     }
diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp
index 6bc318d..4aee6b9 100644
--- a/libs/hwui/jni/fonts/Font.cpp
+++ b/libs/hwui/jni/fonts/Font.cpp
@@ -221,12 +221,33 @@
     return (static_cast<uint64_t>(var.axisTag) << 32) | static_cast<uint64_t>(floatBinary);
 }
 
+// FastNative
+static jstring Font_getFontPath(JNIEnv* env, jobject, jlong fontHandle) {
+    const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle);
+    MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->typeface().get());
+    const std::string& filePath = minikinSkia->getFilePath();
+    if (filePath.empty()) {
+        return nullptr;
+    }
+    return env->NewStringUTF(filePath.c_str());
+}
+
 // Critical Native
 static jlong Font_getNativeFontPtr(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) {
     FontWrapper* font = reinterpret_cast<FontWrapper*>(fontHandle);
     return reinterpret_cast<jlong>(font->font.get());
 }
 
+// Critical Native
+static jboolean Font_isSameBufferAddress(CRITICAL_JNI_PARAMS_COMMA jlong lFontHandle,
+                                         jlong rFontHandle) {
+    FontWrapper* lFont = reinterpret_cast<FontWrapper*>(lFontHandle);
+    FontWrapper* rFont = reinterpret_cast<FontWrapper*>(rFontHandle);
+    const void* lBufferPtr = lFont->font->typeface()->GetFontData();
+    const void* rBufferPtr = rFont->font->typeface()->GetFontData();
+    return lBufferPtr == rBufferPtr;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 struct FontBufferWrapper {
@@ -274,7 +295,9 @@
     { "nGetFontMetrics", "(JJLandroid/graphics/Paint$FontMetrics;)F", (void*) Font_getFontMetrics },
     { "nGetFontInfo", "(J)J", (void*) Font_getFontInfo },
     { "nGetAxisInfo", "(JI)J", (void*) Font_getAxisInfo },
+    { "nGetFontPath", "(J)Ljava/lang/String;", (void*) Font_getFontPath },
     { "nGetNativeFontPtr", "(J)J", (void*) Font_getNativeFontPtr },
+    { "nIsSameBufferAddress", "(JJ)Z", (void*) Font_isSameBufferAddress },
 };
 
 static const JNINativeMethod gFontBufferHelperMethods[] = {
diff --git a/libs/hwui/jni/text/TextShaper.cpp b/libs/hwui/jni/text/TextShaper.cpp
index 9d9e91f..9785aa5 100644
--- a/libs/hwui/jni/text/TextShaper.cpp
+++ b/libs/hwui/jni/text/TextShaper.cpp
@@ -196,7 +196,7 @@
 };
 
 int register_android_graphics_text_TextShaper(JNIEnv* env) {
-    return RegisterMethodsOrDie(env, "android/graphics/text/TextShaper", gMethods,
+    return RegisterMethodsOrDie(env, "android/graphics/text/TextRunShaper", gMethods,
                                 NELEM(gMethods))
         + RegisterMethodsOrDie(env, "android/graphics/text/PositionedGlyphs",
             gResultMethods, NELEM(gResultMethods));
diff --git a/libs/hwui/pipeline/skia/ShaderCache.cpp b/libs/hwui/pipeline/skia/ShaderCache.cpp
index 66aa8c2..3baff7e 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.cpp
+++ b/libs/hwui/pipeline/skia/ShaderCache.cpp
@@ -15,7 +15,7 @@
  */
 
 #include "ShaderCache.h"
-#include <GrContext.h>
+#include <GrDirectContext.h>
 #include <log/log.h>
 #include <openssl/sha.h>
 #include <algorithm>
@@ -206,7 +206,7 @@
     }
 }
 
-void ShaderCache::onVkFrameFlushed(GrContext* context) {
+void ShaderCache::onVkFrameFlushed(GrDirectContext* context) {
     {
         std::lock_guard<std::mutex> lock(mMutex);
 
diff --git a/libs/hwui/pipeline/skia/ShaderCache.h b/libs/hwui/pipeline/skia/ShaderCache.h
index 5b8e668..4dcc9fb 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.h
+++ b/libs/hwui/pipeline/skia/ShaderCache.h
@@ -80,7 +80,7 @@
      * Pipeline cache is saved on disk only if the size of the data has changed or there was
      * a new shader compiled.
      */
-    void onVkFrameFlushed(GrContext* context);
+    void onVkFrameFlushed(GrDirectContext* context);
 
 private:
     // Creation and (the lack of) destruction is handled internally.
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index 212a428..ad6363b 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -29,7 +29,7 @@
 #include <SkSurface.h>
 #include <SkTypes.h>
 
-#include <GrContext.h>
+#include <GrDirectContext.h>
 #include <GrTypes.h>
 #include <vk/GrVkTypes.h>
 
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index b57dee4..85924c5 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -61,7 +61,7 @@
     SkGraphics::SetFontCacheLimit(mMaxCpuFontCacheBytes);
 }
 
-void CacheManager::reset(sk_sp<GrContext> context) {
+void CacheManager::reset(sk_sp<GrDirectContext> context) {
     if (context != mGrContext) {
         destroy();
     }
diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h
index b009cc4..0a6b8dc 100644
--- a/libs/hwui/renderthread/CacheManager.h
+++ b/libs/hwui/renderthread/CacheManager.h
@@ -18,7 +18,7 @@
 #define CACHEMANAGER_H
 
 #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
-#include <GrContext.h>
+#include <GrDirectContext.h>
 #endif
 #include <SkSurface.h>
 #include <utils/String8.h>
@@ -58,13 +58,13 @@
     explicit CacheManager();
 
 #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
-    void reset(sk_sp<GrContext> grContext);
+    void reset(sk_sp<GrDirectContext> grContext);
 #endif
     void destroy();
 
     const size_t mMaxSurfaceArea;
 #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
-    sk_sp<GrContext> mGrContext;
+    sk_sp<GrDirectContext> mGrContext;
 #endif
 
     const size_t mMaxResourceBytes;
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 0435981..eacabfd 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -465,6 +465,7 @@
         mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
         // Notify the callbacks, even if there's nothing to draw so they aren't waiting
         // indefinitely
+        waitOnFences();
         for (auto& func : mFrameCompleteCallbacks) {
             std::invoke(func, mFrameNumber);
         }
@@ -629,10 +630,11 @@
 
     nsecs_t vsync = mRenderThread.timeLord().computeFrameTimeNanos();
     int64_t vsyncId = mRenderThread.timeLord().lastVsyncId();
+    int64_t frameDeadline = mRenderThread.timeLord().lastFrameDeadline();
     int64_t frameInfo[UI_THREAD_FRAME_INFO_SIZE];
     UiFrameInfoBuilder(frameInfo)
         .addFlag(FrameInfoFlags::RTAnimation)
-        .setVsync(vsync, vsync, vsyncId);
+        .setVsync(vsync, vsync, vsyncId, frameDeadline);
 
     TreeInfo info(TreeInfo::MODE_RT_ONLY, *this);
     prepareTree(info, frameInfo, systemTime(SYSTEM_TIME_MONOTONIC), node);
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 1ea595d..c9146b2 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -130,7 +130,8 @@
     int64_t vsync = mFrameInfo[static_cast<int>(FrameInfoIndex::Vsync)];
     int64_t intendedVsync = mFrameInfo[static_cast<int>(FrameInfoIndex::IntendedVsync)];
     int64_t vsyncId = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameTimelineVsyncId)];
-    mRenderThread->timeLord().vsyncReceived(vsync, intendedVsync, vsyncId);
+    int64_t frameDeadline = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameDeadline)];
+    mRenderThread->timeLord().vsyncReceived(vsync, intendedVsync, vsyncId, frameDeadline);
     bool canDraw = mContext->makeCurrent();
     mContext->unpinImages();
 
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
index a04738d..aceb5a5 100644
--- a/libs/hwui/renderthread/IRenderPipeline.h
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -27,7 +27,7 @@
 #include <SkRect.h>
 #include <utils/RefBase.h>
 
-class GrContext;
+class GrDirectContext;
 
 struct ANativeWindow;
 
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 9371656..a101d46 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -52,8 +52,9 @@
 void RenderThread::frameCallback(int64_t frameTimeNanos, void* data) {
     RenderThread* rt = reinterpret_cast<RenderThread*>(data);
     int64_t vsyncId = AChoreographer_getVsyncId(rt->mChoreographer);
+    int64_t frameDeadline = AChoreographer_getFrameDeadline(rt->mChoreographer);
     rt->mVsyncRequested = false;
-    if (rt->timeLord().vsyncReceived(frameTimeNanos, frameTimeNanos, vsyncId) &&
+    if (rt->timeLord().vsyncReceived(frameTimeNanos, frameTimeNanos, vsyncId, frameDeadline) &&
             !rt->mFrameCallbackTaskPending) {
         ATRACE_NAME("queue mFrameCallbackTask");
         rt->mFrameCallbackTaskPending = true;
diff --git a/libs/hwui/renderthread/TimeLord.cpp b/libs/hwui/renderthread/TimeLord.cpp
index 7dc36c4..abb6330 100644
--- a/libs/hwui/renderthread/TimeLord.cpp
+++ b/libs/hwui/renderthread/TimeLord.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 #include "TimeLord.h"
+#include <limits>
 
 namespace android {
 namespace uirenderer {
@@ -22,12 +23,15 @@
 TimeLord::TimeLord() : mFrameIntervalNanos(milliseconds_to_nanoseconds(16)),
                        mFrameTimeNanos(0),
                        mFrameIntendedTimeNanos(0),
-                       mFrameVsyncId(-1) {}
+                       mFrameVsyncId(-1),
+                       mFrameDeadline(std::numeric_limits<int64_t>::max()){}
 
-bool TimeLord::vsyncReceived(nsecs_t vsync, nsecs_t intendedVsync, int64_t vsyncId) {
+bool TimeLord::vsyncReceived(nsecs_t vsync, nsecs_t intendedVsync, int64_t vsyncId,
+                             int64_t frameDeadline) {
     if (intendedVsync > mFrameIntendedTimeNanos) {
         mFrameIntendedTimeNanos = intendedVsync;
         mFrameVsyncId = vsyncId;
+        mFrameDeadline = frameDeadline;
     }
 
     if (vsync > mFrameTimeNanos) {
diff --git a/libs/hwui/renderthread/TimeLord.h b/libs/hwui/renderthread/TimeLord.h
index 23c1e51..fa05c030 100644
--- a/libs/hwui/renderthread/TimeLord.h
+++ b/libs/hwui/renderthread/TimeLord.h
@@ -32,10 +32,12 @@
     nsecs_t frameIntervalNanos() const { return mFrameIntervalNanos; }
 
     // returns true if the vsync is newer, false if it was rejected for staleness
-    bool vsyncReceived(nsecs_t vsync, nsecs_t indendedVsync, int64_t vsyncId);
+    bool vsyncReceived(nsecs_t vsync, nsecs_t indendedVsync, int64_t vsyncId,
+                       int64_t frameDeadline);
     nsecs_t latestVsync() { return mFrameTimeNanos; }
     nsecs_t computeFrameTimeNanos();
     int64_t lastVsyncId() const { return mFrameVsyncId; }
+    int64_t lastFrameDeadline() const { return mFrameDeadline; }
 
 private:
     friend class RenderThread;
@@ -47,6 +49,7 @@
     nsecs_t mFrameTimeNanos;
     nsecs_t mFrameIntendedTimeNanos;
     int64_t mFrameVsyncId;
+    int64_t mFrameDeadline;
 };
 
 } /* namespace renderthread */
diff --git a/libs/hwui/renderthread/VulkanSurface.cpp b/libs/hwui/renderthread/VulkanSurface.cpp
index 1da09b4..acf4931 100644
--- a/libs/hwui/renderthread/VulkanSurface.cpp
+++ b/libs/hwui/renderthread/VulkanSurface.cpp
@@ -16,6 +16,7 @@
 
 #include "VulkanSurface.h"
 
+#include <GrDirectContext.h>
 #include <SkSurface.h>
 #include <algorithm>
 
@@ -117,7 +118,7 @@
 
 VulkanSurface* VulkanSurface::Create(ANativeWindow* window, ColorMode colorMode,
                                      SkColorType colorType, sk_sp<SkColorSpace> colorSpace,
-                                     GrContext* grContext, const VulkanManager& vkManager,
+                                     GrDirectContext* grContext, const VulkanManager& vkManager,
                                      uint32_t extraBuffers) {
     // Connect and set native window to default configurations.
     if (!ConnectAndSetWindowDefaults(window)) {
@@ -310,7 +311,7 @@
 }
 
 VulkanSurface::VulkanSurface(ANativeWindow* window, const WindowInfo& windowInfo,
-                             GrContext* grContext)
+                             GrDirectContext* grContext)
         : mNativeWindow(window), mWindowInfo(windowInfo), mGrContext(grContext) {}
 
 VulkanSurface::~VulkanSurface() {
diff --git a/libs/hwui/renderthread/VulkanSurface.h b/libs/hwui/renderthread/VulkanSurface.h
index 40a44b1..409921b 100644
--- a/libs/hwui/renderthread/VulkanSurface.h
+++ b/libs/hwui/renderthread/VulkanSurface.h
@@ -35,7 +35,7 @@
 class VulkanSurface {
 public:
     static VulkanSurface* Create(ANativeWindow* window, ColorMode colorMode, SkColorType colorType,
-                                 sk_sp<SkColorSpace> colorSpace, GrContext* grContext,
+                                 sk_sp<SkColorSpace> colorSpace, GrDirectContext* grContext,
                                  const VulkanManager& vkManager, uint32_t extraBuffers);
     ~VulkanSurface();
 
@@ -101,7 +101,7 @@
         SkMatrix preTransform;
     };
 
-    VulkanSurface(ANativeWindow* window, const WindowInfo& windowInfo, GrContext* grContext);
+    VulkanSurface(ANativeWindow* window, const WindowInfo& windowInfo, GrDirectContext* grContext);
     static bool InitializeWindowInfoStruct(ANativeWindow* window, ColorMode colorMode,
                                            SkColorType colorType, sk_sp<SkColorSpace> colorSpace,
                                            const VulkanManager& vkManager, uint32_t extraBuffers,
@@ -119,7 +119,7 @@
 
     sp<ANativeWindow> mNativeWindow;
     WindowInfo mWindowInfo;
-    GrContext* mGrContext;
+    GrDirectContext* mGrContext;
 
     uint32_t mPresentCount = 0;
     NativeBufferInfo* mCurrentBufferInfo = nullptr;
diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
index ed89c59..eda5d22 100644
--- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp
+++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
@@ -146,7 +146,7 @@
         testContext.waitForVsync();
         nsecs_t vsync = systemTime(SYSTEM_TIME_MONOTONIC);
         UiFrameInfoBuilder(proxy->frameInfo())
-            .setVsync(vsync, vsync, UiFrameInfoBuilder::INVALID_VSYNC_ID);
+            .setVsync(vsync, vsync, UiFrameInfoBuilder::INVALID_VSYNC_ID, std::numeric_limits<int64_t>::max());
         proxy->syncAndDrawFrame();
     }
 
@@ -167,7 +167,7 @@
         {
             ATRACE_NAME("UI-Draw Frame");
             UiFrameInfoBuilder(proxy->frameInfo())
-                .setVsync(vsync, vsync, UiFrameInfoBuilder::INVALID_VSYNC_ID);
+                .setVsync(vsync, vsync, UiFrameInfoBuilder::INVALID_VSYNC_ID, std::numeric_limits<int64_t>::max());
             scene->doFrame(i);
             proxy->syncAndDrawFrame();
         }
diff --git a/location/java/android/location/Geofence.java b/location/java/android/location/Geofence.java
index 19b017b..10773f8 100644
--- a/location/java/android/location/Geofence.java
+++ b/location/java/android/location/Geofence.java
@@ -58,7 +58,7 @@
     Geofence(double latitude, double longitude, float radius, long expirationRealtimeMs) {
         Preconditions.checkArgumentInRange(latitude, -90.0, 90.0, "latitude");
         Preconditions.checkArgumentInRange(longitude, -180.0, 180.0, "latitude");
-        Preconditions.checkArgument(radius > 0, "invalid radius: " + radius);
+        Preconditions.checkArgument(radius > 0, "invalid radius: %f", radius);
 
         mLatitude = latitude;
         mLongitude = longitude;
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 5ae9dd2..42cf53b 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -106,11 +106,12 @@
     boolean isProviderEnabledForUser(String provider, int userId);
     boolean isLocationEnabledForUser(int userId);
     void setLocationEnabledForUser(boolean enabled, int userId);
+
     void addTestProvider(String name, in ProviderProperties properties, String packageName, String attributionTag);
     void removeTestProvider(String provider, String packageName, String attributionTag);
     void setTestProviderLocation(String provider, in Location location, String packageName, String attributionTag);
     void setTestProviderEnabled(String provider, boolean enabled, String packageName, String attributionTag);
-    List<LocationRequest> getTestProviderCurrentRequests(String provider);
+
     LocationTime getGnssTimeMillis();
 
     void sendExtraCommand(String provider, String command, inout Bundle extras);
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java
index 46bd221..62b4bc1 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -19,7 +19,6 @@
 import static java.util.concurrent.TimeUnit.NANOSECONDS;
 
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Bundle;
@@ -75,7 +74,6 @@
      * gps locations separate from other locations for coarsening. Providers that do not need to
      * support platforms below Android R should not use this constant.
      */
-    @TestApi
     @SystemApi
     @Deprecated
     public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation";
@@ -1074,7 +1072,6 @@
      * @see #isComplete
      * @hide
      */
-    @TestApi
     @SystemApi
     public void makeComplete() {
         if (mProvider == null) mProvider = "?";
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index ff00409..ac775ca 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -89,6 +89,16 @@
 public class LocationManager {
 
     /**
+     * For apps targeting Android S and above, LocationRequest system APIs may not be used with
+     * PendingIntent location requests.
+     *
+     * @hide
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
+    public static final long PREVENT_PENDING_INTENT_SYSTEM_API_USAGE = 169887240L;
+
+    /**
      * For apps targeting Android S and above, location clients may receive historical locations
      * (from before the present time) under some circumstances.
      *
@@ -565,7 +575,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(WRITE_SECURE_SETTINGS)
     public void setLocationEnabledForUser(boolean enabled, @NonNull UserHandle userHandle) {
         try {
@@ -741,7 +750,6 @@
      */
     @Deprecated
     @SystemApi
-    @TestApi
     @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
     public void getCurrentLocation(@NonNull LocationRequest locationRequest,
             @Nullable CancellationSignal cancellationSignal,
@@ -1182,7 +1190,6 @@
      */
     @Deprecated
     @SystemApi
-    @TestApi
     @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
     public void requestLocationUpdates(
             @Nullable LocationRequest locationRequest,
@@ -1213,7 +1220,6 @@
      */
     @Deprecated
     @SystemApi
-    @TestApi
     @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
     public void requestLocationUpdates(
             @Nullable LocationRequest locationRequest,
@@ -1245,7 +1251,6 @@
      */
     @Deprecated
     @SystemApi
-    @TestApi
     @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
     public void requestLocationUpdates(
             @Nullable LocationRequest locationRequest,
@@ -1805,22 +1810,6 @@
     public void clearTestProviderStatus(@NonNull String provider) {}
 
     /**
-     * Get the last list of {@link LocationRequest}s sent to the provider.
-     *
-     * @hide
-     */
-    @TestApi
-    @NonNull
-    public List<LocationRequest> getTestProviderCurrentRequests(String providerName) {
-        Preconditions.checkArgument(providerName != null, "invalid null provider");
-        try {
-            return mService.getTestProviderCurrentRequests(providerName);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * Sets a proximity alert for the location given by the position (latitude, longitude) and the
      * given radius.
      *
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index 3bb4781..1adb2b4 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -21,12 +21,12 @@
 
 import android.Manifest;
 import android.annotation.FloatRange;
+import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledAfter;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -38,6 +38,8 @@
 
 import com.android.internal.util.Preconditions;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Objects;
 
 
@@ -65,16 +67,48 @@
      */
     public static final long PASSIVE_INTERVAL = Long.MAX_VALUE;
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({QUALITY_LOW_POWER, QUALITY_BALANCED_POWER_ACCURACY, QUALITY_HIGH_ACCURACY})
+    public @interface Quality {}
+
+    /**
+     * A quality constant indicating a location provider may choose to satisfy this request by
+     * providing very accurate locations at the expense of potentially increased power usage. Each
+     * location provider may interpret this field differently, but as an example, the network
+     * provider may choose to return only wifi based locations rather than cell based locations in
+     * order to have greater accuracy when this flag is present.
+     */
+    public static final int QUALITY_HIGH_ACCURACY = 100;
+
+    /**
+     * A quality constant indicating a location provider may choose to satisfy this request by
+     * equally balancing power and accuracy constraints. Each location provider may interpret this
+     * field differently, but location providers will generally use their default behavior when this
+     * flag is present.
+     */
+    public static final int QUALITY_BALANCED_POWER_ACCURACY = 102;
+
+    /**
+     * A quality constant indicating a location provider may choose to satisfy this request by
+     * providing less accurate locations in order to save power. Each location provider may
+     * interpret this field differently, but as an example, the network provider may choose to
+     * return cell based locations rather than wifi based locations in order to save power when this
+     * flag is present.
+     */
+    public static final int QUALITY_LOW_POWER = 104;
+
     /**
      * Used with {@link #setQuality} to request the most accurate locations available.
      *
      * <p>This may be up to 1 meter accuracy, although this is implementation dependent.
      *
      * @hide
+     * @deprecated Use {@link #QUALITY_HIGH_ACCURACY} instead.
      */
+    @Deprecated
     @SystemApi
-    @TestApi
-    public static final int ACCURACY_FINE = 100;
+    public static final int ACCURACY_FINE = QUALITY_HIGH_ACCURACY;
 
     /**
      * Used with {@link #setQuality} to request "block" level accuracy.
@@ -84,10 +118,11 @@
      * such as this often consumes less power.
      *
      * @hide
+     * @deprecated Use {@link #QUALITY_BALANCED_POWER_ACCURACY} instead.
      */
+    @Deprecated
     @SystemApi
-    @TestApi
-    public static final int ACCURACY_BLOCK = 102;
+    public static final int ACCURACY_BLOCK = QUALITY_BALANCED_POWER_ACCURACY;
 
     /**
      * Used with {@link #setQuality} to request "city" level accuracy.
@@ -97,10 +132,11 @@
      * such as this often consumes less power.
      *
      * @hide
+     * @deprecated Use {@link #QUALITY_LOW_POWER} instead.
      */
+    @Deprecated
     @SystemApi
-    @TestApi
-    public static final int ACCURACY_CITY = 104;
+    public static final int ACCURACY_CITY = QUALITY_LOW_POWER;
 
     /**
      * Used with {@link #setQuality} to require no direct power impact (passive locations).
@@ -123,9 +159,10 @@
      * possible.
      *
      * @hide
+     * @deprecated Use {@link #QUALITY_LOW_POWER} instead.
      */
+    @Deprecated
     @SystemApi
-    @TestApi
     public static final int POWER_LOW = 201;
 
     /**
@@ -134,9 +171,10 @@
      * <p>This location request will allow high power location work.
      *
      * @hide
+     * @deprecated Use {@link #QUALITY_HIGH_ACCURACY} instead.
      */
+    @Deprecated
     @SystemApi
-    @TestApi
     public static final int POWER_HIGH = 203;
 
     private static final long IMPLICIT_MIN_UPDATE_INTERVAL = -1;
@@ -144,7 +182,7 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "Use {@link "
             + "LocationManager} methods to provide the provider explicitly.")
     @Nullable private String mProvider;
-    private int mQuality;
+    private @Quality int mQuality;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "Use {@link "
             + "LocationRequest} instead.")
     private long mInterval;
@@ -168,7 +206,7 @@
     public static LocationRequest create() {
         // 60 minutes is the default legacy interval
         return new LocationRequest.Builder(60 * 60 * 1000)
-                .setQuality(POWER_LOW)
+                .setQuality(QUALITY_LOW_POWER)
                 .build();
     }
 
@@ -241,7 +279,7 @@
     private LocationRequest(
             @Nullable String provider,
             long intervalMillis,
-            int quality,
+            @Quality int quality,
             long expireAtRealtimeMillis,
             long durationMillis,
             int maxUpdates,
@@ -250,10 +288,7 @@
             boolean hiddenFromAppOps,
             boolean locationSettingsIgnored,
             boolean lowPower,
-            @Nullable WorkSource workSource) {
-        Preconditions.checkArgument(intervalMillis != PASSIVE_INTERVAL || quality == POWER_NONE);
-        Preconditions.checkArgument(minUpdateIntervalMillis <= intervalMillis);
-
+            WorkSource workSource) {
         mProvider = provider;
         mInterval = intervalMillis;
         mQuality = quality;
@@ -265,7 +300,7 @@
         mHideFromAppOps = hiddenFromAppOps;
         mLowPower = lowPower;
         mLocationSettingsIgnored = locationSettingsIgnored;
-        mWorkSource = workSource;
+        mWorkSource = Objects.requireNonNull(workSource);
     }
 
     /**
@@ -297,24 +332,39 @@
     @SystemApi
     @Deprecated
     public @NonNull LocationRequest setQuality(int quality) {
-        mQuality = Builder.checkQuality(quality, true);
+        switch (quality) {
+            case POWER_HIGH:
+                // fall through
+            case ACCURACY_FINE:
+                mQuality = QUALITY_HIGH_ACCURACY;
+                break;
+            case ACCURACY_BLOCK:
+                mQuality = QUALITY_BALANCED_POWER_ACCURACY;
+                break;
+            case POWER_LOW:
+                // fall through
+            case ACCURACY_CITY:
+                mQuality = QUALITY_LOW_POWER;
+                break;
+            case POWER_NONE:
+                mInterval = PASSIVE_INTERVAL;
+                break;
+            default:
+                throw new IllegalArgumentException("invalid quality: " + quality);
+        }
+
         return this;
     }
 
     /**
-     * Returns the quality of the location request.
+     * Returns the quality hint for this location request. The quality hint informs the provider how
+     * it should attempt to manage any accuracy vs power tradeoffs while attempting to satisfy this
+     * location request.
      *
-     * @return the quality of the location request
-     *
-     * @hide
+     * @return the desired quality tradeoffs between accuracy and power
      */
-    @SystemApi
-    public int getQuality() {
-        if (mInterval == PASSIVE_INTERVAL) {
-            return POWER_NONE;
-        } else {
-            return mQuality;
-        }
+    public @Quality int getQuality() {
+        return mQuality;
     }
 
     /**
@@ -570,7 +620,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     public boolean isHiddenFromAppOps() {
         return mHideFromAppOps;
@@ -596,7 +645,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     public boolean isLocationSettingsIgnored() {
         return mLocationSettingsIgnored;
@@ -632,7 +680,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     public boolean isLowPower() {
         return mLowPower;
@@ -645,20 +692,22 @@
     @SystemApi
     @Deprecated
     public void setWorkSource(@Nullable WorkSource workSource) {
+        if (workSource == null) {
+            workSource = new WorkSource();
+        }
         mWorkSource = workSource;
     }
 
     /**
-     * Returns the work source used for power blame for this request. If null, the system is free to
-     * assign power blame as it deems most appropriate.
+     * Returns the work source used for power blame for this request. If empty, the system is free
+     * to assign power blame as it deems most appropriate.
      *
      * @return the work source used for power blame for this request
      *
      * @hide
      */
-    @TestApi
     @SystemApi
-    public @Nullable WorkSource getWorkSource() {
+    public @NonNull WorkSource getWorkSource() {
         return mWorkSource;
     }
 
@@ -746,97 +795,65 @@
         if (mProvider != null) {
             s.append(mProvider).append(" ");
         }
-        if (mQuality != POWER_NONE && mQuality != ACCURACY_BLOCK) {
-            s.append(qualityToString(mQuality)).append(" ");
-        }
         if (mInterval != PASSIVE_INTERVAL) {
             s.append("@");
             TimeUtils.formatDuration(mInterval, s);
+
+            switch (mQuality) {
+                case QUALITY_HIGH_ACCURACY:
+                    s.append(" HIGH_ACCURACY");
+                    break;
+                case QUALITY_BALANCED_POWER_ACCURACY:
+                    s.append(" BALANCED");
+                    break;
+                case QUALITY_LOW_POWER:
+                    s.append(" LOW_POWER");
+                    break;
+            }
         } else {
             s.append("PASSIVE");
         }
         if (mExpireAtRealtimeMillis != Long.MAX_VALUE) {
-            s.append(" expireAt=").append(TimeUtils.formatRealtime(mExpireAtRealtimeMillis));
+            s.append(", expireAt=").append(TimeUtils.formatRealtime(mExpireAtRealtimeMillis));
         }
         if (mDurationMillis != Long.MAX_VALUE) {
-            s.append(" duration=");
+            s.append(", duration=");
             TimeUtils.formatDuration(mDurationMillis, s);
         }
         if (mMaxUpdates != Integer.MAX_VALUE) {
-            s.append(" maxUpdates=").append(mMaxUpdates);
+            s.append(", maxUpdates=").append(mMaxUpdates);
         }
         if (mMinUpdateIntervalMillis != IMPLICIT_MIN_UPDATE_INTERVAL
                 && mMinUpdateIntervalMillis < mInterval) {
-            s.append(" minUpdateInterval=");
+            s.append(", minUpdateInterval=");
             TimeUtils.formatDuration(mMinUpdateIntervalMillis, s);
         }
         if (mMinUpdateDistanceMeters > 0.0) {
-            s.append(" minUpdateDistance=").append(mMinUpdateDistanceMeters);
+            s.append(", minUpdateDistance=").append(mMinUpdateDistanceMeters);
         }
         if (mLowPower) {
-            s.append(" lowPower");
+            s.append(", lowPower");
         }
         if (mHideFromAppOps) {
-            s.append(" hiddenFromAppOps");
+            s.append(", hiddenFromAppOps");
         }
         if (mLocationSettingsIgnored) {
-            s.append(" locationSettingsIgnored");
+            s.append(", locationSettingsIgnored");
         }
-        if (mWorkSource != null) {
-            s.append(" ").append(mWorkSource);
+        if (mWorkSource != null && !mWorkSource.isEmpty()) {
+            s.append(", ").append(mWorkSource);
         }
         s.append(']');
         return s.toString();
     }
 
-    private static String qualityToString(int quality) {
-        switch (quality) {
-            case ACCURACY_FINE:
-                return "ACCURACY_FINE";
-            case ACCURACY_BLOCK:
-                return "ACCURACY_BLOCK";
-            case ACCURACY_CITY:
-                return "ACCURACY_CITY";
-            case POWER_NONE:
-                return "POWER_NONE";
-            case POWER_LOW:
-                return "POWER_LOW";
-            case POWER_HIGH:
-                return "POWER_HIGH";
-            default:
-                return "???";
-        }
-    }
-
     /**
      * A builder class for {@link LocationRequest}.
      */
     public static final class Builder {
 
-        private static int checkQuality(int quality, boolean allowDeprecated) {
-            switch (quality) {
-                case ACCURACY_FINE:
-                    // fall through
-                case ACCURACY_BLOCK:
-                    // fall through
-                case ACCURACY_CITY:
-                    // fall through
-                case POWER_LOW:
-                    // fall through
-                case POWER_HIGH:
-                    return quality;
-                case POWER_NONE:
-                    if (allowDeprecated) {
-                        return quality;
-                    }
-                    // fall through
-                default:
-                    throw new IllegalArgumentException("invalid quality: " + quality);
-            }
-        }
-
         private long mIntervalMillis;
-        private int mQuality;
+        private @Quality int mQuality;
         private long mDurationMillis;
         private int mMaxUpdates;
         private long mMinUpdateIntervalMillis;
@@ -854,7 +871,7 @@
             // gives us a range check
             setIntervalMillis(intervalMillis);
 
-            mQuality = ACCURACY_BLOCK;
+            mQuality = QUALITY_BALANCED_POWER_ACCURACY;
             mDurationMillis = Long.MAX_VALUE;
             mMaxUpdates = Integer.MAX_VALUE;
             mMinUpdateIntervalMillis = IMPLICIT_MIN_UPDATE_INTERVAL;
@@ -882,9 +899,6 @@
 
             // handle edge cases that can only happen with location request that has been modified
             // by deprecated SystemApi methods
-            if (mQuality == POWER_NONE) {
-                mIntervalMillis = PASSIVE_INTERVAL;
-            }
             if (mIntervalMillis == PASSIVE_INTERVAL
                     && mMinUpdateIntervalMillis == IMPLICIT_MIN_UPDATE_INTERVAL) {
                 // this is the legacy default minimum update interval, so if we're forced to
@@ -911,11 +925,17 @@
         }
 
         /**
-         * @hide
+         * Sets the request quality. The quality is a hint to providers on how they should weigh
+         * power vs accuracy tradeoffs. High accuracy locations may cost more power to produce, and
+         * lower accuracy locations may cost less power to produce. Defaults to
+         * {@link #QUALITY_BALANCED_POWER_ACCURACY}.
          */
-        @SystemApi
-        public @NonNull Builder setQuality(int quality) {
-            mQuality = checkQuality(quality, false);
+        public @NonNull Builder setQuality(@Quality int quality) {
+            Preconditions.checkArgument(
+                    quality == QUALITY_LOW_POWER || quality == QUALITY_BALANCED_POWER_ACCURACY
+                            || quality == QUALITY_HIGH_ACCURACY,
+                    "quality must be a defined QUALITY constant, not %d", quality);
+            mQuality = quality;
             return this;
         }
 
@@ -1016,7 +1036,6 @@
          *
          * @hide
          */
-        @TestApi
         @SystemApi
         @RequiresPermission(Manifest.permission.UPDATE_APP_OPS_STATS)
         public @NonNull Builder setHiddenFromAppOps(boolean hiddenFromAppOps) {
@@ -1035,7 +1054,6 @@
          *
          * @hide
          */
-        @TestApi
         @SystemApi
         @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
         public @NonNull Builder setLocationSettingsIgnored(boolean locationSettingsIgnored) {
@@ -1053,7 +1071,6 @@
          *
          * @hide
          */
-        @TestApi
         @SystemApi
         @RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
         public @NonNull Builder setLowPower(boolean lowPower) {
@@ -1062,16 +1079,15 @@
         }
 
         /**
-         * Sets the work source to use for power blame for this location request. Defaults to null,
-         * which implies the system is free to assign power blame as it determines best for this
-         * request (which usually means blaming the owner of the location listener).
+         * Sets the work source to use for power blame for this location request. Defaults to an
+         * empty WorkSource, which implies the system is free to assign power blame as it determines
+         * best for this request (which usually means blaming the owner of the location listener).
          *
          * <p>Permissions enforcement occurs when resulting location request is actually used, not
          * when this method is invoked.
          *
          * @hide
          */
-        @TestApi
         @SystemApi
         @RequiresPermission(Manifest.permission.UPDATE_DEVICE_STATS)
         public @NonNull Builder setWorkSource(@Nullable WorkSource workSource) {
@@ -1099,7 +1115,7 @@
             return new LocationRequest(
                     null,
                     mIntervalMillis,
-                    mIntervalMillis != PASSIVE_INTERVAL ? mQuality : POWER_NONE,
+                    mQuality,
                     Long.MAX_VALUE,
                     mDurationMillis,
                     mMaxUpdates,
@@ -1108,7 +1124,7 @@
                     mHiddenFromAppOps,
                     mLocationSettingsIgnored,
                     mLowPower,
-                    mWorkSource);
+                    new WorkSource(mWorkSource));
         }
     }
 }
diff --git a/location/java/android/location/util/identity/CallerIdentity.java b/location/java/android/location/util/identity/CallerIdentity.java
index c32970f..e023aa1 100644
--- a/location/java/android/location/util/identity/CallerIdentity.java
+++ b/location/java/android/location/util/identity/CallerIdentity.java
@@ -150,6 +150,11 @@
         return mListenerId;
     }
 
+    /** Returns true if this represents a system identity. */
+    public boolean isSystem() {
+        return mUid == Process.SYSTEM_UID;
+    }
+
     /**
      * Adds this identity to the worksource supplied, or if not worksource is supplied, creates a
      * new worksource representing this identity.
diff --git a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
index eb2e23e..4a095c9 100644
--- a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
+++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
@@ -28,7 +28,6 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
-import android.telephony.PhoneNumberUtils;
 import android.telephony.PhoneStateListener;
 import android.telephony.TelephonyManager;
 import android.util.Log;
@@ -161,7 +160,7 @@
                        be set to true when the phone is having emergency call, and then will
                        be set to false by mPhoneStateListener when the emergency call ends.
                 */
-                mIsInEmergencyCall = PhoneNumberUtils.isEmergencyNumber(phoneNumber);
+                mIsInEmergencyCall = mTelephonyManager.isEmergencyNumber(phoneNumber);
                 if (DEBUG) Log.v(TAG, "ACTION_NEW_OUTGOING_CALL - " + getInEmergency());
             } else if (action.equals(LocationManager.MODE_CHANGED_ACTION)) {
                 updateLocationMode();
diff --git a/location/java/com/android/internal/location/ProviderRequest.java b/location/java/com/android/internal/location/ProviderRequest.java
index fee86ce..00ba552 100644
--- a/location/java/com/android/internal/location/ProviderRequest.java
+++ b/location/java/com/android/internal/location/ProviderRequest.java
@@ -16,10 +16,15 @@
 
 package com.android.internal.location;
 
+import static android.location.LocationRequest.QUALITY_BALANCED_POWER_ACCURACY;
+import static android.location.LocationRequest.QUALITY_HIGH_ACCURACY;
+import static android.location.LocationRequest.QUALITY_LOW_POWER;
+
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.location.LocationRequest;
+import android.location.LocationRequest.Quality;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -41,7 +46,7 @@
     public static final long INTERVAL_DISABLED = Long.MAX_VALUE;
 
     public static final ProviderRequest EMPTY_REQUEST = new ProviderRequest(
-            INTERVAL_DISABLED, false, false, Collections.emptyList(), new WorkSource());
+            INTERVAL_DISABLED, QUALITY_BALANCED_POWER_ACCURACY, false, false, new WorkSource());
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "{@link "
             + "ProviderRequest}")
@@ -49,6 +54,7 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "{@link "
             + "ProviderRequest}")
     public final long interval;
+    private final @Quality int mQuality;
     private final boolean mLowPower;
     private final boolean mLocationSettingsIgnored;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "{@link "
@@ -56,15 +62,24 @@
     public final List<LocationRequest> locationRequests;
     private final WorkSource mWorkSource;
 
-    private ProviderRequest(long intervalMillis, boolean lowPower,
-            boolean locationSettingsIgnored, @NonNull List<LocationRequest> locationRequests,
-            @NonNull WorkSource workSource) {
+    private ProviderRequest(long intervalMillis, @Quality int quality, boolean lowPower,
+            boolean locationSettingsIgnored, @NonNull WorkSource workSource) {
         reportLocation = intervalMillis != INTERVAL_DISABLED;
         interval = intervalMillis;
+        mQuality = quality;
         mLowPower = lowPower;
         mLocationSettingsIgnored = locationSettingsIgnored;
-        this.locationRequests = locationRequests;
-        mWorkSource = workSource;
+        if (intervalMillis != INTERVAL_DISABLED) {
+            locationRequests = Collections.singletonList(new LocationRequest.Builder(intervalMillis)
+                    .setQuality(quality)
+                    .setLowPower(lowPower)
+                    .setLocationSettingsIgnored(locationSettingsIgnored)
+                    .setWorkSource(workSource)
+                    .build());
+        } else {
+            locationRequests = Collections.emptyList();
+        }
+        mWorkSource = Objects.requireNonNull(workSource);
     }
 
     /**
@@ -84,6 +99,15 @@
     }
 
     /**
+     * The quality hint for this location request. The quality hint informs the provider how it
+     * should attempt to manage any accuracy vs power tradeoffs while attempting to satisfy this
+     * provider request.
+     */
+    public @Quality int getQuality() {
+        return mQuality;
+    }
+
+    /**
      * Whether any applicable hardware low power modes should be used to satisfy this request.
      */
     public boolean isLowPower() {
@@ -100,13 +124,6 @@
     }
 
     /**
-     * The full list of location requests contributing to this provider request.
-     */
-    public @NonNull List<LocationRequest> getLocationRequests() {
-        return locationRequests;
-    }
-
-    /**
      * The power blame for this provider request.
      */
     public @NonNull WorkSource getWorkSource() {
@@ -117,13 +134,17 @@
             new Parcelable.Creator<ProviderRequest>() {
                 @Override
                 public ProviderRequest createFromParcel(Parcel in) {
-                    return new ProviderRequest(
-                            /* intervalMillis= */ in.readLong(),
-                            /* lowPower= */ in.readBoolean(),
-                            /* locationSettingsIgnored= */ in.readBoolean(),
-                            /* locationRequests= */
-                            in.createTypedArrayList(LocationRequest.CREATOR),
-                            /* workSource= */ in.readTypedObject(WorkSource.CREATOR));
+                    long intervalMillis = in.readLong();
+                    if (intervalMillis == INTERVAL_DISABLED) {
+                        return EMPTY_REQUEST;
+                    } else {
+                        return new ProviderRequest(
+                                intervalMillis,
+                                /* quality= */ in.readInt(),
+                                /* lowPower= */ in.readBoolean(),
+                                /* locationSettingsIgnored= */ in.readBoolean(),
+                                /* workSource= */ in.readTypedObject(WorkSource.CREATOR));
+                    }
                 }
 
                 @Override
@@ -140,10 +161,12 @@
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
         parcel.writeLong(interval);
-        parcel.writeBoolean(mLowPower);
-        parcel.writeBoolean(mLocationSettingsIgnored);
-        parcel.writeTypedList(locationRequests);
-        parcel.writeTypedObject(mWorkSource, flags);
+        if (interval != INTERVAL_DISABLED) {
+            parcel.writeInt(mQuality);
+            parcel.writeBoolean(mLowPower);
+            parcel.writeBoolean(mLocationSettingsIgnored);
+            parcel.writeTypedObject(mWorkSource, flags);
+        }
     }
 
     @Override
@@ -160,16 +183,16 @@
             return that.interval == INTERVAL_DISABLED;
         } else {
             return interval == that.interval
+                    && mQuality == that.mQuality
                     && mLowPower == that.mLowPower
                     && mLocationSettingsIgnored == that.mLocationSettingsIgnored
-                    && locationRequests.equals(that.locationRequests)
                     && mWorkSource.equals(that.mWorkSource);
         }
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(interval, mWorkSource);
+        return Objects.hash(interval, mQuality, mWorkSource);
     }
 
     @Override
@@ -179,6 +202,13 @@
         if (interval != INTERVAL_DISABLED) {
             s.append("@");
             TimeUtils.formatDuration(interval, s);
+            if (mQuality != QUALITY_BALANCED_POWER_ACCURACY) {
+                if (mQuality == QUALITY_HIGH_ACCURACY) {
+                    s.append(", HIGH_ACCURACY");
+                } else if (mQuality == QUALITY_LOW_POWER) {
+                    s.append(", LOW_POWER");
+                }
+            }
             if (mLowPower) {
                 s.append(", lowPower");
             }
@@ -200,9 +230,9 @@
      */
     public static class Builder {
         private long mIntervalMillis = INTERVAL_DISABLED;
+        private int mQuality = QUALITY_BALANCED_POWER_ACCURACY;
         private boolean mLowPower;
         private boolean mLocationSettingsIgnored;
-        private List<LocationRequest> mLocationRequests = Collections.emptyList();
         private WorkSource mWorkSource = new WorkSource();
 
         /**
@@ -216,6 +246,20 @@
         }
 
         /**
+         * Sets the request quality. The quality is a hint to providers on how they should weigh
+         * power vs accuracy tradeoffs. High accuracy locations may cost more power to produce, and
+         * lower accuracy locations may cost less power to produce. Defaults to
+         * {@link LocationRequest#QUALITY_BALANCED_POWER_ACCURACY}.
+         */
+        public @NonNull Builder setQuality(@Quality int quality) {
+            Preconditions.checkArgument(
+                    quality == QUALITY_LOW_POWER || quality == QUALITY_BALANCED_POWER_ACCURACY
+                            || quality == QUALITY_HIGH_ACCURACY);
+            mQuality = quality;
+            return this;
+        }
+
+        /**
          * Sets whether hardware low power mode should be used. False by default.
          */
         public @NonNull Builder setLowPower(boolean lowPower) {
@@ -232,15 +276,6 @@
         }
 
         /**
-         * Sets the {@link LocationRequest}s associated with this request. Empty by default.
-         */
-        public @NonNull Builder setLocationRequests(
-                @NonNull List<LocationRequest> locationRequests) {
-            this.mLocationRequests = Objects.requireNonNull(locationRequests);
-            return this;
-        }
-
-        /**
          * Sets the work source for power blame. Empty by default.
          */
         public @NonNull Builder setWorkSource(@NonNull WorkSource workSource) {
@@ -255,8 +290,8 @@
             if (mIntervalMillis == INTERVAL_DISABLED) {
                 return EMPTY_REQUEST;
             } else {
-                return new ProviderRequest(mIntervalMillis, mLowPower, mLocationSettingsIgnored,
-                        mLocationRequests, mWorkSource);
+                return new ProviderRequest(mIntervalMillis, mQuality, mLowPower,
+                        mLocationSettingsIgnored, mWorkSource);
             }
         }
     }
diff --git a/location/lib/api/current.txt b/location/lib/api/current.txt
index c00ff57..80636c6 100644
--- a/location/lib/api/current.txt
+++ b/location/lib/api/current.txt
@@ -29,18 +29,18 @@
     field public static final String FUSED_PROVIDER = "fused";
   }
 
-  public final class LocationRequestUnbundled {
-    method public long getFastestInterval();
-    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
-    field public static final int POWER_HIGH = 203; // 0xcb
-    field public static final int POWER_LOW = 201; // 0xc9
-    field public static final int POWER_NONE = 200; // 0xc8
+  @Deprecated public final class LocationRequestUnbundled {
+    method @Deprecated public long getFastestInterval();
+    method @Deprecated public long getInterval();
+    method @Deprecated @android.location.LocationRequest.Quality public int getQuality();
+    method @Deprecated public float getSmallestDisplacement();
+    method @Deprecated public boolean isLocationSettingsIgnored();
+    field @Deprecated public static final int ACCURACY_BLOCK = 102; // 0x66
+    field @Deprecated public static final int ACCURACY_CITY = 104; // 0x68
+    field @Deprecated public static final int ACCURACY_FINE = 100; // 0x64
+    field @Deprecated public static final int POWER_HIGH = 203; // 0xcb
+    field @Deprecated public static final int POWER_LOW = 201; // 0xc9
+    field @Deprecated public static final int POWER_NONE = 200; // 0xc8
   }
 
   public final class ProviderPropertiesUnbundled {
@@ -49,7 +49,8 @@
 
   public final class ProviderRequestUnbundled {
     method public long getInterval();
-    method @NonNull public java.util.List<com.android.location.provider.LocationRequestUnbundled> getLocationRequests();
+    method @Deprecated @NonNull public java.util.List<com.android.location.provider.LocationRequestUnbundled> getLocationRequests();
+    method @android.location.LocationRequest.Quality @RequiresApi(android.os.Build.VERSION_CODES.S) public int getQuality();
     method public boolean getReportLocation();
     method @NonNull @RequiresApi(android.os.Build.VERSION_CODES.S) public android.os.WorkSource getWorkSource();
     method @RequiresApi(android.os.Build.VERSION_CODES.Q) public boolean isLocationSettingsIgnored();
@@ -59,3 +60,35 @@
 
 }
 
+package com.android.location.timezone.provider {
+
+  public final class LocationTimeZoneEventUnbundled {
+    method public int getEventType();
+    method @NonNull public java.util.List<java.lang.String> getTimeZoneIds();
+    field public static final int EVENT_TYPE_PERMANENT_FAILURE = 1; // 0x1
+    field public static final int EVENT_TYPE_SUCCESS = 2; // 0x2
+    field public static final int EVENT_TYPE_UNCERTAIN = 3; // 0x3
+  }
+
+  public static final class LocationTimeZoneEventUnbundled.Builder {
+    ctor public LocationTimeZoneEventUnbundled.Builder();
+    method @NonNull public com.android.location.timezone.provider.LocationTimeZoneEventUnbundled build();
+    method @NonNull public com.android.location.timezone.provider.LocationTimeZoneEventUnbundled.Builder setEventType(int);
+    method @NonNull public com.android.location.timezone.provider.LocationTimeZoneEventUnbundled.Builder setTimeZoneIds(@NonNull java.util.List<java.lang.String>);
+  }
+
+  public abstract class LocationTimeZoneProviderBase {
+    ctor public LocationTimeZoneProviderBase(android.content.Context, String);
+    method public final android.os.IBinder getBinder();
+    method protected final android.content.Context getContext();
+    method protected abstract void onSetRequest(@NonNull com.android.location.timezone.provider.LocationTimeZoneProviderRequestUnbundled);
+    method protected final void reportLocationTimeZoneEvent(@NonNull com.android.location.timezone.provider.LocationTimeZoneEventUnbundled);
+  }
+
+  public final class LocationTimeZoneProviderRequestUnbundled {
+    method @IntRange(from=0) public long getInitializationTimeoutMillis();
+    method public boolean getReportLocationTimeZone();
+  }
+
+}
+
diff --git a/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java b/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java
index 92e05ef..0e7c633 100644
--- a/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java
+++ b/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java
@@ -17,6 +17,7 @@
 package com.android.location.provider;
 
 import android.location.LocationRequest;
+import android.location.LocationRequest.Quality;
 
 /**
  * This class is an interface to LocationRequests for unbundled applications.
@@ -24,55 +25,50 @@
  * <p>IMPORTANT: This class is effectively a public API for unbundled
  * applications, and must remain API stable. See README.txt in the root
  * of this package for more information.
+ *
+ * @deprecated Do not use.
  */
+@Deprecated
 public final class LocationRequestUnbundled {
+
     /**
-     * Returned by {@link #getQuality} when requesting the most accurate locations available.
-     *
-     * <p>This may be up to 1 meter accuracy, although this is implementation dependent.
+     * @deprecated Use {@link LocationRequest#QUALITY_HIGH_ACCURACY} instead.
      */
+    @Deprecated
     public static final int ACCURACY_FINE = LocationRequest.ACCURACY_FINE;
 
     /**
-     * Returned by {@link #getQuality} when requesting "block" level accuracy.
-     *
-     * <p>Block level accuracy is considered to be about 100 meter accuracy,
-     * although this is implementation dependent. Using a coarse accuracy
-     * such as this often consumes less power.
+     * @deprecated Use {@link LocationRequest#QUALITY_BALANCED_POWER_ACCURACY} instead.
      */
+    @Deprecated
     public static final int ACCURACY_BLOCK = LocationRequest.ACCURACY_BLOCK;
 
+
     /**
-     * Returned by {@link #getQuality} when requesting "city" level accuracy.
-     *
-     * <p>City level accuracy is considered to be about 10km accuracy,
-     * although this is implementation dependent. Using a coarse accuracy
-     * such as this often consumes less power.
+     * @deprecated Use {@link LocationRequest#QUALITY_LOW_POWER} instead.
      */
+    @Deprecated
     public static final int ACCURACY_CITY = LocationRequest.ACCURACY_CITY;
 
+
     /**
-     * Returned by {@link #getQuality} when requiring no direct power impact (passive locations).
-     *
-     * <p>This location request will not trigger any active location requests,
-     * but will receive locations triggered by other applications. Your application
-     * will not receive any direct power blame for location work.
+     * @deprecated Do not use.
      */
+    @Deprecated
     public static final int POWER_NONE = LocationRequest.POWER_NONE;
 
-    /**
-     * Returned by {@link #getQuality} when requesting low power impact.
-     *
-     * <p>This location request will avoid high power location work where
-     * possible.
-     */
-    public static final int POWER_LOW = LocationRequest.POWER_LOW;
 
     /**
-     * Returned by {@link #getQuality} when allowing high power consumption for location.
-     *
-     * <p>This location request will allow high power location work.
+     * @deprecated Use {@link LocationRequest#QUALITY_LOW_POWER} instead.
      */
+    @Deprecated
+    public static final int POWER_LOW = LocationRequest.POWER_LOW;
+
+
+    /**
+     * @deprecated Use {@link LocationRequest#QUALITY_BALANCED_POWER_ACCURACY} instead.
+     */
+    @Deprecated
     public static final int POWER_HIGH = LocationRequest.POWER_HIGH;
 
     private final LocationRequest delegate;
@@ -102,9 +98,9 @@
     /**
      * Get the quality of the request.
      *
-     * @return an accuracy or power constant
+     * @return a {@link LocationRequest} QUALITY_* constant
      */
-    public int getQuality() {
+    public @Quality int getQuality() {
         return delegate.getQuality();
     }
 
diff --git a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
index 6f5fcc7..f7bac74 100644
--- a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
+++ b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
@@ -25,7 +25,7 @@
 
 import com.android.internal.location.ProviderRequest;
 
-import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -57,6 +57,16 @@
     }
 
     /**
+     * The quality hint for this location request. The quality hint informs the provider how it
+     * should attempt to manage any accuracy vs power tradeoffs while attempting to satisfy this
+     * provider request.
+     */
+    @RequiresApi(Build.VERSION_CODES.S)
+    public @LocationRequest.Quality int getQuality() {
+        return mRequest.getQuality();
+    }
+
+    /**
      * The interval at which a provider should report location. Will return
      * {@link #INTERVAL_DISABLED} for an inactive request.
      */
@@ -84,14 +94,22 @@
 
     /**
      * The full list of location requests contributing to this provider request.
+     *
+     * @deprecated Do not use.
      */
+    @Deprecated
     public @NonNull List<LocationRequestUnbundled> getLocationRequests() {
-        List<LocationRequestUnbundled> result = new ArrayList<>(
-                mRequest.getLocationRequests().size());
-        for (LocationRequest r : mRequest.getLocationRequests()) {
-            result.add(new LocationRequestUnbundled(r));
+        if (!mRequest.isActive()) {
+            return Collections.emptyList();
         }
-        return result;
+
+        return Collections.singletonList(new LocationRequestUnbundled(
+                new LocationRequest.Builder(mRequest.getIntervalMillis())
+                        .setQuality(mRequest.getQuality())
+                        .setLowPower(mRequest.isLowPower())
+                        .setLocationSettingsIgnored(mRequest.isLocationSettingsIgnored())
+                        .setWorkSource(mRequest.getWorkSource())
+                        .build()));
     }
 
     /**
diff --git a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneEventUnbundled.java b/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneEventUnbundled.java
index 3675574..aa0e895 100644
--- a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneEventUnbundled.java
+++ b/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneEventUnbundled.java
@@ -18,7 +18,6 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.location.timezone.LocationTimeZoneEvent;
 import android.os.SystemClock;
@@ -31,8 +30,6 @@
 /**
  * An event from a {@link LocationTimeZoneProviderBase} sent while determining a device's time zone
  * using its location.
- *
- * @hide
  */
 public final class LocationTimeZoneEventUnbundled {
 
@@ -69,7 +66,6 @@
     /**
      * Returns the event type.
      */
-    @Nullable
     public @EventType int getEventType() {
         return mDelegate.getEventType();
     }
@@ -118,8 +114,6 @@
 
     /**
      * A builder of {@link LocationTimeZoneEventUnbundled} instances.
-     *
-     * @hide
      */
     public static final class Builder {
 
diff --git a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderBase.java b/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderBase.java
index 9df7166..68ae722 100644
--- a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderBase.java
+++ b/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderBase.java
@@ -16,6 +16,7 @@
 
 package com.android.location.timezone.provider;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.os.IBinder;
@@ -55,8 +56,6 @@
  *
  * <p>IMPORTANT: This class is effectively a public API for unbundled applications, and must remain
  * API stable.
- *
- * @hide
  */
 public abstract class LocationTimeZoneProviderBase {
 
@@ -85,7 +84,8 @@
     /**
      * Reports a new location time zone event from this provider.
      */
-    protected void reportLocationTimeZoneEvent(LocationTimeZoneEventUnbundled event) {
+    protected final void reportLocationTimeZoneEvent(
+            @NonNull LocationTimeZoneEventUnbundled event) {
         ILocationTimeZoneProviderManager manager = mManager;
         if (manager != null) {
             try {
@@ -102,7 +102,7 @@
      * to start returning location time zones, or to stop returning location time zones, depending
      * on the parameters in the request.
      */
-    protected abstract void onSetRequest(LocationTimeZoneProviderRequestUnbundled request);
+    protected abstract void onSetRequest(@NonNull LocationTimeZoneProviderRequestUnbundled request);
 
     private final class Service extends ILocationTimeZoneProvider.Stub {
 
diff --git a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderRequestUnbundled.java b/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderRequestUnbundled.java
index ab50dc3..10d1038 100644
--- a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderRequestUnbundled.java
+++ b/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderRequestUnbundled.java
@@ -16,6 +16,7 @@
 
 package com.android.location.timezone.provider;
 
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 
 import com.android.internal.location.timezone.LocationTimeZoneProviderRequest;
@@ -27,8 +28,6 @@
  *
  * <p>IMPORTANT: This class is effectively a public API for unbundled code, and must remain API
  * stable.
- *
- * @hide
  */
 public final class LocationTimeZoneProviderRequestUnbundled {
 
@@ -54,6 +53,7 @@
      * true}. Failure to send an event in this time (with some fuzz) may be interpreted as if the
      * provider is uncertain of the time zone, and/or it could lead to the provider being disabled.
      */
+    @IntRange(from = 0)
     public long getInitializationTimeoutMillis() {
         return mRequest.getInitializationTimeoutMillis();
     }
diff --git a/media/Android.bp b/media/Android.bp
index 828707b..8b06bb2 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -1,7 +1,10 @@
 aidl_interface {
     name: "audio_common-aidl",
     unstable: true,
+    host_supported: true,
+    vendor_available: true,
     local_include_dir: "aidl",
+    double_loadable: true,
     srcs: [
         "aidl/android/media/audio/common/AudioChannelMask.aidl",
         "aidl/android/media/audio/common/AudioConfig.aidl",
diff --git a/media/OWNERS b/media/OWNERS
index 36df3a0..e741490 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -1,4 +1,3 @@
-andrewlewis@google.com
 chz@google.com
 elaurent@google.com
 essick@google.com
@@ -15,6 +14,15 @@
 klhyun@google.com
 lajos@google.com
 marcone@google.com
+nchalko@google.com
 philburk@google.com
-sungsoo@google.com
+quxiangfang@google.com
 wonsik@google.com
+
+# LON
+andrewlewis@google.com
+aquilescanta@google.com
+olly@google.com
+
+# SEO
+sungsoo@google.com
diff --git a/media/java/android/media/AudioFocusInfo.java b/media/java/android/media/AudioFocusInfo.java
index 675cf73..3647b6e 100644
--- a/media/java/android/media/AudioFocusInfo.java
+++ b/media/java/android/media/AudioFocusInfo.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -29,7 +28,6 @@
  * @hide
  * A class to encapsulate information about an audio focus owner or request.
  */
-@TestApi
 @SystemApi
 public final class AudioFocusInfo implements Parcelable {
 
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 22b5ca5..8b28cc4 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -3916,7 +3916,6 @@
      * @param requestResult the result to the focus request to be passed to the requester
      * @param ap a valid registered {@link AudioPolicy} configured as a focus policy.
      */
-    @TestApi
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
     public void setFocusRequestResult(@NonNull AudioFocusInfo afi,
@@ -3956,7 +3955,6 @@
      *     if there was an error sending the request.
      * @throws NullPointerException if the {@link AudioFocusInfo} or {@link AudioPolicy} are null.
      */
-    @TestApi
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
     public int dispatchAudioFocusChange(@NonNull AudioFocusInfo afi, int focusChange,
@@ -4051,7 +4049,8 @@
         //     the associated intent will be handled by the component being registered
         mediaButtonIntent.setComponent(eventReceiver);
         PendingIntent pi = PendingIntent.getBroadcast(getContext(),
-                0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
+                0/*requestCode, ignored*/, mediaButtonIntent,
+                PendingIntent.FLAG_IMMUTABLE);
         registerMediaButtonIntent(pi, eventReceiver);
     }
 
@@ -4104,7 +4103,8 @@
         //     the associated intent will be handled by the component being registered
         mediaButtonIntent.setComponent(eventReceiver);
         PendingIntent pi = PendingIntent.getBroadcast(getContext(),
-                0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
+                0/*requestCode, ignored*/, mediaButtonIntent,
+                PendingIntent.FLAG_IMMUTABLE);
         unregisterMediaButtonIntent(pi);
     }
 
@@ -4219,7 +4219,6 @@
      *    {@link android.Manifest.permission#MODIFY_AUDIO_ROUTING} permission,
      *    {@link #SUCCESS} otherwise.
      */
-    @TestApi
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
     public int registerAudioPolicy(@NonNull AudioPolicy policy) {
@@ -4254,7 +4253,6 @@
      * Unregisters an {@link AudioPolicy} asynchronously.
      * @param policy the non-null {@link AudioPolicy} to unregister.
      */
-    @TestApi
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
     public void unregisterAudioPolicyAsync(@NonNull AudioPolicy policy) {
@@ -4281,7 +4279,6 @@
      * associated with mixes of this policy.
      * @param policy the non-null {@link AudioPolicy} to unregister.
      */
-    @TestApi
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
     public void unregisterAudioPolicy(@NonNull AudioPolicy policy) {
@@ -5619,7 +5616,6 @@
      */
 
     /** @hide */
-    @TestApi
     @SystemApi
     public static final int SUCCESS = AudioSystem.SUCCESS;
     /**
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index de2a7b2..fa6a4ff 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -1263,14 +1263,20 @@
 
             // TODO: Check mEncapsulationMode compatibility with MODE_STATIC, etc?
 
-            try {
-                // If the buffer size is not specified in streaming mode,
-                // use a single frame for the buffer size and let the
-                // native code figure out the minimum buffer size.
-                if (mMode == MODE_STREAM && mBufferSizeInBytes == 0) {
-                    mBufferSizeInBytes = mFormat.getChannelCount()
-                            * mFormat.getBytesPerSample(mFormat.getEncoding());
+            // If the buffer size is not specified in streaming mode,
+            // use a single frame for the buffer size and let the
+            // native code figure out the minimum buffer size.
+            if (mMode == MODE_STREAM && mBufferSizeInBytes == 0) {
+                int bytesPerSample = 1;
+                try {
+                    bytesPerSample = mFormat.getBytesPerSample(mFormat.getEncoding());
+                } catch (IllegalArgumentException e) {
+                    // do nothing
                 }
+                mBufferSizeInBytes = mFormat.getChannelCount() * bytesPerSample;
+            }
+
+            try {
                 final AudioTrack track = new AudioTrack(
                         mAttributes, mFormat, mBufferSizeInBytes, mMode, mSessionId, mOffload,
                         mEncapsulationMode, mTunerConfiguration);
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 8845d69..44890be 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -31,6 +31,8 @@
 import android.content.res.AssetManager;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.os.FileUtils;
+import android.os.SystemProperties;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
@@ -70,7 +72,6 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.TimeZone;
-import java.util.UUID;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.zip.CRC32;
@@ -1524,6 +1525,11 @@
         if (fileDescriptor == null) {
             throw new NullPointerException("fileDescriptor cannot be null");
         }
+        boolean optimize = SystemProperties.getBoolean("fuse.sys.transcode_exif_optimize", false);
+        FileDescriptor modernFd = optimize ? FileUtils.convertToModernFd(fileDescriptor) : null;
+        if (modernFd != null) {
+            fileDescriptor = modernFd;
+        }
 
         mAssetInputStream = null;
         mFilename = null;
@@ -2088,28 +2094,18 @@
 
         FileInputStream in = null;
         FileOutputStream out = null;
-        File originalFile = null;
-        if (mFilename != null) {
-            originalFile = new File(mFilename);
-        }
         File tempFile = null;
         try {
-            // Move the original file to temporary file.
+            // Copy the original file to temporary file.
+            tempFile = File.createTempFile("temp", "tmp");
             if (mFilename != null) {
-                String parent = originalFile.getParent();
-                String name = originalFile.getName();
-                String tempPrefix = UUID.randomUUID().toString() + "_";
-                tempFile = new File(parent, tempPrefix + name);
-                if (!originalFile.renameTo(tempFile)) {
-                    throw new IOException("Couldn't rename to " + tempFile.getAbsolutePath());
-                }
+                in = new FileInputStream(mFilename);
             } else if (mSeekableFileDescriptor != null) {
-                tempFile = File.createTempFile("temp", "tmp");
                 Os.lseek(mSeekableFileDescriptor, 0, OsConstants.SEEK_SET);
                 in = new FileInputStream(mSeekableFileDescriptor);
-                out = new FileOutputStream(tempFile);
-                copy(in, out);
             }
+            out = new FileOutputStream(tempFile);
+            copy(in, out);
         } catch (Exception e) {
             throw new IOException("Failed to copy original file to temp file", e);
         } finally {
@@ -2139,12 +2135,23 @@
                 }
             }
         } catch (Exception e) {
+            // Restore original file
+            in = new FileInputStream(tempFile);
             if (mFilename != null) {
-                if (!tempFile.renameTo(originalFile)) {
-                    throw new IOException("Couldn't restore original file: "
-                            + originalFile.getAbsolutePath());
+                out = new FileOutputStream(mFilename);
+            } else if (mSeekableFileDescriptor != null) {
+                try {
+                    Os.lseek(mSeekableFileDescriptor, 0, OsConstants.SEEK_SET);
+                } catch (ErrnoException exception) {
+                    throw new IOException("Failed to save new file. Original file may be "
+                            + "corrupted since error occurred while trying to restore it.",
+                            exception);
                 }
+                out = new FileOutputStream(mSeekableFileDescriptor);
             }
+            copy(in, out);
+            closeQuietly(in);
+            closeQuietly(out);
             throw new IOException("Failed to save new file", e);
         } finally {
             closeQuietly(in);
@@ -2533,11 +2540,20 @@
 
     private void initForFilename(String filename) throws IOException {
         FileInputStream in = null;
+        FileInputStream legacyInputStream = null;
         mAssetInputStream = null;
         mFilename = filename;
         mIsInputStream = false;
         try {
             in = new FileInputStream(filename);
+            boolean optimize = SystemProperties.getBoolean("fuse.sys.transcode_exif_optimize",
+                    false);
+            FileDescriptor modernFd = optimize ? FileUtils.convertToModernFd(in.getFD()) : null;
+            if (modernFd != null) {
+                legacyInputStream = in;
+                in = new FileInputStream(modernFd);
+            }
+
             if (isSeekableFD(in.getFD())) {
                 mSeekableFileDescriptor = in.getFD();
             } else {
@@ -2546,6 +2562,7 @@
             loadAttributes(in);
         } finally {
             closeQuietly(in);
+            closeQuietly(legacyInputStream);
         }
     }
 
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 0747ab13..ae97a71 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -2711,12 +2711,12 @@
             }
         };
 
-        private final Pattern zeroPattern = new Pattern(0, 0);
+        private static final Pattern ZERO_PATTERN = new Pattern(0, 0);
 
         /**
          * The pattern applicable to the protected data in each subsample.
          */
-        private Pattern pattern;
+        private Pattern mPattern = ZERO_PATTERN;
 
         /**
          * Set the subsample count, clear/encrypted sizes, key, IV and mode fields of
@@ -2735,22 +2735,30 @@
             key = newKey;
             iv = newIV;
             mode = newMode;
-            pattern = zeroPattern;
+            mPattern = ZERO_PATTERN;
+        }
+
+        /**
+         * Returns the {@link Pattern encryption pattern}.
+         */
+        public @NonNull Pattern getPattern() {
+            return new Pattern(mPattern.getEncryptBlocks(), mPattern.getSkipBlocks());
         }
 
         /**
          * Set the encryption pattern on a {@link MediaCodec.CryptoInfo} instance.
-         * See {@link MediaCodec.CryptoInfo.Pattern}.
+         * See {@link Pattern}.
          */
         public void setPattern(Pattern newPattern) {
             if (newPattern == null) {
-                newPattern = zeroPattern;
+                newPattern = ZERO_PATTERN;
             }
-            pattern = newPattern;
+            setPattern(newPattern.getEncryptBlocks(), newPattern.getSkipBlocks());
         }
 
+        // Accessed from android_media_MediaExtractor.cpp.
         private void setPattern(int blocksToEncrypt, int blocksToSkip) {
-            pattern = new Pattern(blocksToEncrypt, blocksToSkip);
+            mPattern = new Pattern(blocksToEncrypt, blocksToSkip);
         }
 
         @Override
@@ -2772,9 +2780,9 @@
             builder.append(", encrypted ");
             builder.append(Arrays.toString(numBytesOfEncryptedData));
             builder.append(", pattern (encrypt: ");
-            builder.append(pattern.mEncryptBlocks);
+            builder.append(mPattern.mEncryptBlocks);
             builder.append(", skip: ");
-            builder.append(pattern.mSkipBlocks);
+            builder.append(mPattern.mSkipBlocks);
             builder.append(")");
             return builder.toString();
         }
diff --git a/media/java/android/media/MediaMetadata.java b/media/java/android/media/MediaMetadata.java
index dbf4ad0..78eeca1 100644
--- a/media/java/android/media/MediaMetadata.java
+++ b/media/java/android/media/MediaMetadata.java
@@ -427,7 +427,7 @@
 
     private MediaMetadata(Parcel in) {
         mBundle = in.readBundle();
-        mBitmapDimensionLimit = Math.max(in.readInt(), 0);
+        mBitmapDimensionLimit = Math.max(in.readInt(), 1);
     }
 
     /**
@@ -518,17 +518,18 @@
 
     /**
      * Gets the width/height limit (in pixels) for the bitmaps when this metadata was created.
-     * This method returns a positive value or zero.
+     * This method always returns a positive value.
      * <p>
-     * If it returns a positive value, then all the bitmaps in this metadata has width/height
+     * If it returns {@link Integer#MAX_VALUE}, then no scaling down was applied to the bitmaps
+     * when this metadata was created.
+     * <p>
+     * If it returns another positive value, then all the bitmaps in this metadata has width/height
      * not greater than this limit. Bitmaps may have been scaled down according to the limit.
      * <p>
-     * If it returns zero, then no scaling down was applied to the bitmaps when this metadata
-     * was created.
      *
      * @see Builder#setBitmapDimensionLimit(int)
      */
-    public @IntRange(from = 0) int getBitmapDimensionLimit() {
+    public @IntRange(from = 1) int getBitmapDimensionLimit() {
         return mBitmapDimensionLimit;
     }
 
@@ -738,7 +739,7 @@
      */
     public static final class Builder {
         private final Bundle mBundle;
-        private int mBitmapDimensionLimit;
+        private int mBitmapDimensionLimit = Integer.MAX_VALUE;
 
         /**
          * Create an empty Builder. Any field that should be included in the
@@ -925,14 +926,21 @@
          * Bitmaps will be replaced with scaled down copies if their width (or height) is
          * larger than {@code bitmapDimensionLimit}.
          * <p>
-         * In order to unset the limit, pass zero as {@code bitmapDimensionLimit}.
+         * In order to unset the limit, pass {@link Integer#MAX_VALUE} as
+         * {@code bitmapDimensionLimit}.
          *
          * @param bitmapDimensionLimit The maximum width/height (in pixels) for bitmaps
-         *                             contained in the metadata. Pass {@code 0} to unset the limit.
+         *                             contained in the metadata. Non-positive values are ignored.
+         *                             Pass {@link Integer#MAX_VALUE} to unset the limit.
          */
         @NonNull
-        public Builder setBitmapDimensionLimit(int bitmapDimensionLimit) {
-            mBitmapDimensionLimit = Math.max(bitmapDimensionLimit, 0);
+        public Builder setBitmapDimensionLimit(@IntRange(from = 1) int bitmapDimensionLimit) {
+            if (bitmapDimensionLimit > 0) {
+                mBitmapDimensionLimit = bitmapDimensionLimit;
+            } else {
+                Log.w(TAG, "setBitmapDimensionLimit(): Ignoring non-positive bitmapDimensionLimit: "
+                        + bitmapDimensionLimit);
+            }
             return this;
         }
 
@@ -942,7 +950,7 @@
          * @return The new MediaMetadata instance
          */
         public MediaMetadata build() {
-            if (mBitmapDimensionLimit > 0) {
+            if (mBitmapDimensionLimit != Integer.MAX_VALUE) {
                 for (String key : mBundle.keySet()) {
                     Object value = mBundle.get(key);
                     if (value instanceof Bitmap) {
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index cf03b06..ca8b9b9 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -16,10 +16,13 @@
 
 package android.media;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -27,7 +30,10 @@
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.os.Build;
+import android.os.Bundle;
+import android.os.FileUtils;
 import android.os.IBinder;
+import android.os.SystemProperties;
 import android.text.TextUtils;
 
 import java.io.FileDescriptor;
@@ -45,7 +51,6 @@
  * frame and meta data from an input media file.
  */
 public class MediaMetadataRetriever implements AutoCloseable {
-
     // borrowed from ExoPlayer
     private static final String[] STANDARD_GENRES = new String[] {
             // These are the official ID3v1 genres.
@@ -293,7 +298,19 @@
      * non-negative.
      * @throws IllegalArgumentException if the arguments are invalid
      */
-    public native void setDataSource(FileDescriptor fd, long offset, long length)
+    public void setDataSource(FileDescriptor fd, long offset, long length)
+            throws IllegalArgumentException  {
+        boolean optimize = SystemProperties.getBoolean("fuse.sys.transcode_retriever_optimize",
+                false);
+        FileDescriptor modernFd = optimize ? FileUtils.convertToModernFd(fd) : null;
+        if (modernFd == null) {
+            _setDataSource(fd, offset, length);
+        } else {
+            _setDataSource(modernFd, offset, length);
+        }
+    }
+
+    private native void _setDataSource(FileDescriptor fd, long offset, long length)
             throws IllegalArgumentException;
 
     /**
@@ -337,7 +354,12 @@
         try {
             ContentResolver resolver = context.getContentResolver();
             try {
-                fd = resolver.openAssetFileDescriptor(uri, "r");
+                boolean optimize =
+                        SystemProperties.getBoolean("fuse.sys.transcode_retriever_optimize", false);
+                Bundle opts = new Bundle();
+                opts.putBoolean("android.provider.extra.ACCEPT_ORIGINAL_MEDIA_FORMAT", true);
+                fd = optimize ? resolver.openTypedAssetFileDescriptor(uri, "*/*", opts)
+                        : resolver.openAssetFileDescriptor(uri, "r");
             } catch(FileNotFoundException e) {
                 throw new IllegalArgumentException("could not access " + uri);
             }
@@ -1331,7 +1353,6 @@
      * @see MediaFormat#COLOR_RANGE_FULL
      */
     public static final int METADATA_KEY_COLOR_RANGE    = 37;
-    // Add more here...
 
     /**
      * This key retrieves the sample rate in Hz, if available.
@@ -1344,4 +1365,13 @@
      * This is a signed 32-bit integer formatted as a string in base 10.
      */
     public static final int METADATA_KEY_BITS_PER_SAMPLE = 39;
+
+    /**
+     * This key retrieves the video codec mimetype if available.
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static final int METADATA_KEY_VIDEO_CODEC_MIME_TYPE = 40;
+
+    // Add more here...
 }
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 851c1ec..47d276a 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -31,6 +31,8 @@
 import android.media.SubtitleController.Anchor;
 import android.media.SubtitleTrack.RenderingWidget;
 import android.net.Uri;
+import android.os.Bundle;
+import android.os.FileUtils;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
@@ -1104,7 +1106,13 @@
     }
 
     private boolean attemptDataSource(ContentResolver resolver, Uri uri) {
-        try (AssetFileDescriptor afd = resolver.openAssetFileDescriptor(uri, "r")) {
+        boolean optimize = SystemProperties.getBoolean("fuse.sys.transcode_player_optimize",
+                false);
+        Bundle opts = new Bundle();
+        opts.putBoolean("android.provider.extra.ACCEPT_ORIGINAL_MEDIA_FORMAT", true);
+        try (AssetFileDescriptor afd = optimize
+                ? resolver.openTypedAssetFileDescriptor(uri, "*/*", opts)
+                : resolver.openAssetFileDescriptor(uri, "r")) {
             setDataSource(afd);
             return true;
         } catch (NullPointerException | SecurityException | IOException ex) {
@@ -1245,7 +1253,13 @@
      */
     public void setDataSource(FileDescriptor fd, long offset, long length)
             throws IOException, IllegalArgumentException, IllegalStateException {
-        _setDataSource(fd, offset, length);
+        boolean optimize = SystemProperties.getBoolean("fuse.sys.transcode_player_optimize", false);
+        FileDescriptor modernFd = optimize ? FileUtils.convertToModernFd(fd) : null;
+        if (modernFd == null) {
+            _setDataSource(fd, offset, length);
+        } else {
+            _setDataSource(modernFd, offset, length);
+        }
     }
 
     private native void _setDataSource(FileDescriptor fd, long offset, long length)
@@ -2899,8 +2913,13 @@
 
         AssetFileDescriptor fd = null;
         try {
+            boolean optimize = SystemProperties.getBoolean("fuse.sys.transcode_player_optimize",
+                    false);
             ContentResolver resolver = context.getContentResolver();
-            fd = resolver.openAssetFileDescriptor(uri, "r");
+            Bundle opts = new Bundle();
+            opts.putBoolean("android.provider.extra.ACCEPT_ORIGINAL_MEDIA_FORMAT", true);
+            fd = optimize ? resolver.openTypedAssetFileDescriptor(uri, "*/*", opts)
+                    : resolver.openAssetFileDescriptor(uri, "r");
             if (fd == null) {
                 return;
             }
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 2b3f420c..4d87fb3 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -45,6 +45,9 @@
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.Display;
+import android.view.DisplayAddress;
+
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -1737,7 +1740,9 @@
          */
         private static final int DEFAULT_PLAYBACK_VOLUME = DEFAULT_PLAYBACK_MAX_VOLUME;
 
-        RouteInfo(RouteCategory category) {
+        /** @hide */
+        @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+        public RouteInfo(RouteCategory category) {
             mCategory = category;
             mDeviceType = DEVICE_TYPE_UNKNOWN;
         }
@@ -2078,7 +2083,9 @@
             return mPresentationDisplay;
         }
 
-        boolean updatePresentationDisplay() {
+        /** @hide */
+        @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+        public boolean updatePresentationDisplay() {
             Display display = choosePresentationDisplay();
             if (mPresentationDisplay != display) {
                 mPresentationDisplay = display;
@@ -2088,41 +2095,81 @@
         }
 
         private Display choosePresentationDisplay() {
-            if ((mSupportedTypes & ROUTE_TYPE_LIVE_VIDEO) != 0) {
-                Display[] displays = sStatic.getAllPresentationDisplays();
+            if ((getSupportedTypes() & ROUTE_TYPE_LIVE_VIDEO) == 0) {
+                return null;
+            }
+            final Display[] displays = getAllPresentationDisplays();
+            if (displays == null || displays.length == 0) {
+                return null;
+            }
 
-                // Ensure that the specified display is valid for presentations.
-                // This check will normally disallow the default display unless it was
-                // configured as a presentation display for some reason.
-                if (mPresentationDisplayId >= 0) {
-                    for (Display display : displays) {
-                        if (display.getDisplayId() == mPresentationDisplayId) {
-                            return display;
-                        }
+            // Ensure that the specified display is valid for presentations.
+            // This check will normally disallow the default display unless it was
+            // configured as a presentation display for some reason.
+            if (mPresentationDisplayId >= 0) {
+                for (Display display : displays) {
+                    if (display.getDisplayId() == mPresentationDisplayId) {
+                        return display;
                     }
-                    return null;
                 }
+                return null;
+            }
 
-                // Find the indicated Wifi display by its address.
-                if (mDeviceAddress != null) {
-                    for (Display display : displays) {
-                        if (display.getType() == Display.TYPE_WIFI
-                                && mDeviceAddress.equals(display.getAddress())) {
-                            return display;
-                        }
+            // Find the indicated Wifi display by its address.
+            if (getDeviceAddress() != null) {
+                for (Display display : displays) {
+                    if (display.getType() == Display.TYPE_WIFI
+                            && displayAddressEquals(display)) {
+                        return display;
                     }
-                    return null;
-                }
-
-                // For the default route, choose the first presentation display from the list.
-                if (this == sStatic.mDefaultAudioVideo && displays.length > 0) {
-                    return displays[0];
                 }
             }
+
+            // Returns the first hard-wired display.
+            for (Display display : displays) {
+                if (display.getType() == Display.TYPE_EXTERNAL) {
+                    return display;
+                }
+            }
+
+            // Returns the first non-default built-in display.
+            for (Display display : displays) {
+                if (display.getType() == Display.TYPE_INTERNAL) {
+                    return display;
+                }
+            }
+
+            // For the default route, choose the first presentation display from the list.
+            if (this == getDefaultAudioVideo()) {
+                return displays[0];
+            }
             return null;
         }
 
         /** @hide */
+        @VisibleForTesting
+        public Display[] getAllPresentationDisplays() {
+            return sStatic.getAllPresentationDisplays();
+        }
+
+        /** @hide */
+        @VisibleForTesting
+        public RouteInfo getDefaultAudioVideo() {
+            return sStatic.mDefaultAudioVideo;
+        }
+
+        private boolean displayAddressEquals(Display display) {
+            final DisplayAddress displayAddress = display.getAddress();
+            // mDeviceAddress recorded mac address. If displayAddress is not a kind of Network,
+            // return false early.
+            if (!(displayAddress instanceof DisplayAddress.Network)) {
+                return false;
+            }
+            final DisplayAddress.Network networkAddress = (DisplayAddress.Network) displayAddress;
+            return getDeviceAddress().equals(networkAddress.toString());
+        }
+
+        /** @hide */
         @UnsupportedAppUsage
         public String getDeviceAddress() {
             return mDeviceAddress;
diff --git a/media/java/android/media/MediaTranscodeManager.java b/media/java/android/media/MediaTranscodeManager.java
index 8cbe52f..0464036 100644
--- a/media/java/android/media/MediaTranscodeManager.java
+++ b/media/java/android/media/MediaTranscodeManager.java
@@ -22,7 +22,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
@@ -101,7 +100,6 @@
  TODO(hkuang): Clarify whether supports framerate conversion.
  @hide
  */
-@TestApi
 @SystemApi
 public final class MediaTranscodeManager {
     private static final String TAG = "MediaTranscodeManager";
@@ -443,7 +441,7 @@
                 }
 
                 @Override
-                public void onAwaitNumberOfJobsChanged(int jobId, int oldAwaitNumber,
+                public void onAwaitNumberOfSessionsChanged(int jobId, int oldAwaitNumber,
                         int newAwaitNumber) throws RemoteException {
                     //TODO(hkuang): Implement this.
                 }
@@ -1081,7 +1079,7 @@
         private final MediaTranscodeManager mManager;
         private Executor mListenerExecutor;
         private OnTranscodingFinishedListener mListener;
-        private int mJobId = -1;
+        private int mSessionId = -1;
         // Lock for internal state.
         private final Object mLock = new Object();
         @GuardedBy("mLock")
@@ -1104,7 +1102,7 @@
         private TranscodingJob(
                 @NonNull MediaTranscodeManager manager,
                 @NonNull TranscodingRequest request,
-                @NonNull TranscodingJobParcel parcel,
+                @NonNull TranscodingSessionParcel parcel,
                 @NonNull @CallbackExecutor Executor executor,
                 @NonNull OnTranscodingFinishedListener listener) {
             Objects.requireNonNull(manager, "manager must not be null");
@@ -1112,7 +1110,7 @@
             Objects.requireNonNull(executor, "listenerExecutor must not be null");
             Objects.requireNonNull(listener, "listener must not be null");
             mManager = manager;
-            mJobId = parcel.jobId;
+            mSessionId = parcel.sessionId;
             mListenerExecutor = executor;
             mListener = listener;
             mRequest = request;
@@ -1196,16 +1194,16 @@
                 synchronized (mManager.mPendingTranscodingJobs) {
                     try {
                         // Submits the request to MediaTranscoding service.
-                        TranscodingJobParcel jobParcel = new TranscodingJobParcel();
-                        if (!client.submitRequest(mRequest.writeToParcel(), jobParcel)) {
+                        TranscodingSessionParcel sessionParcel = new TranscodingSessionParcel();
+                        if (!client.submitRequest(mRequest.writeToParcel(), sessionParcel)) {
                             mHasRetried = true;
                             throw new UnsupportedOperationException("Failed to enqueue request");
                         }
 
                         // Replace the old job id wit the new one.
-                        mJobId = jobParcel.jobId;
+                        mSessionId = sessionParcel.sessionId;
                         // Adds the new job back into pending jobs.
-                        mManager.mPendingTranscodingJobs.put(mJobId, this);
+                        mManager.mPendingTranscodingJobs.put(mSessionId, this);
                     } catch (RemoteException re) {
                         throw new MediaTranscodingException.ServiceNotAvailableException(
                                 "Failed to resubmit request to Transcoding service");
@@ -1229,7 +1227,7 @@
                         ITranscodingClient client = mManager.getTranscodingClient();
                         // The client may be gone.
                         if (client != null) {
-                            client.cancelJob(mJobId);
+                            client.cancelSession(mSessionId);
                         }
                     } catch (RemoteException re) {
                         //TODO(hkuang): Find out what to do if failing to cancel the job.
@@ -1272,7 +1270,7 @@
          * @return job id.
          */
         public int getJobId() {
-            return mJobId;
+            return mSessionId;
         }
 
         /**
@@ -1326,7 +1324,7 @@
                     break;
             }
             return String.format(" Job: {id: %d, status: %s, result: %s, progress: %d}",
-                    mJobId, status, result, mProgress);
+                    mSessionId, status, result, mProgress);
         }
 
         private void updateProgress(int newProgress) {
@@ -1383,7 +1381,7 @@
 
         // Submits the request to MediaTranscoding service.
         try {
-            TranscodingJobParcel jobParcel = new TranscodingJobParcel();
+            TranscodingSessionParcel sessionParcel = new TranscodingSessionParcel();
             // Synchronizes the access to mPendingTranscodingJobs to make sure the job Id is
             // inserted in the mPendingTranscodingJobs in the callback handler.
             synchronized (mPendingTranscodingJobs) {
@@ -1399,15 +1397,15 @@
                         }
                     }
 
-                    if (!mTranscodingClient.submitRequest(requestParcel, jobParcel)) {
+                    if (!mTranscodingClient.submitRequest(requestParcel, sessionParcel)) {
                         throw new UnsupportedOperationException("Failed to enqueue request");
                     }
                 }
 
-                // Wraps the TranscodingJobParcel into a TranscodingJob and returns it to client for
+                // Wraps the TranscodingSessionParcel into a TranscodingJob and returns it to client for
                 // tracking.
                 TranscodingJob job = new TranscodingJob(this, transcodingRequest,
-                        jobParcel,
+                        sessionParcel,
                         listenerExecutor,
                         listener);
 
diff --git a/media/java/android/media/audiopolicy/AudioMix.java b/media/java/android/media/audiopolicy/AudioMix.java
index 61113bc..4e451c6 100644
--- a/media/java/android/media/audiopolicy/AudioMix.java
+++ b/media/java/android/media/audiopolicy/AudioMix.java
@@ -19,7 +19,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.media.AudioDeviceInfo;
 import android.media.AudioFormat;
@@ -32,7 +31,6 @@
 /**
  * @hide
  */
-@TestApi
 @SystemApi
 public class AudioMix {
 
diff --git a/media/java/android/media/audiopolicy/AudioMixingRule.java b/media/java/android/media/audiopolicy/AudioMixingRule.java
index 68c9593..f6f982a 100644
--- a/media/java/android/media/audiopolicy/AudioMixingRule.java
+++ b/media/java/android/media/audiopolicy/AudioMixingRule.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.media.AudioAttributes;
 import android.os.Parcel;
@@ -42,7 +41,6 @@
  *         .build();
  * </pre>
  */
-@TestApi
 @SystemApi
 public class AudioMixingRule {
 
diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java
index 8a17465..3e8d76a 100644
--- a/media/java/android/media/audiopolicy/AudioPolicy.java
+++ b/media/java/android/media/audiopolicy/AudioPolicy.java
@@ -58,7 +58,6 @@
  * @hide
  * AudioPolicy provides access to the management of audio routing and audio focus.
  */
-@TestApi
 @SystemApi
 public class AudioPolicy {
 
@@ -418,7 +417,6 @@
      * @param devices list of devices to which the audio stream of the application may be routed.
      * @return true if the change was successful, false otherwise.
      */
-    @TestApi
     @SystemApi
     public boolean setUidDeviceAffinity(int uid, @NonNull List<AudioDeviceInfo> devices) {
         if (devices == null) {
@@ -460,7 +458,6 @@
      * @param uid UID of the application affected.
      * @return true if the change was successful, false otherwise.
      */
-    @TestApi
     @SystemApi
     public boolean removeUidDeviceAffinity(int uid) {
         synchronized (mLock) {
@@ -486,7 +483,6 @@
      * {@link UserHandle#getIdentifier}. Not to be confused with application uid.
      * @return true if the change was successful, false otherwise.
      */
-    @TestApi
     @SystemApi
     public boolean removeUserIdDeviceAffinity(@UserIdInt int userId) {
         synchronized (mLock) {
@@ -519,7 +515,6 @@
      * @param devices list of devices to which the audio stream of the application may be routed.
      * @return true if the change was successful, false otherwise.
      */
-    @TestApi
     @SystemApi
     public boolean setUserIdDeviceAffinity(@UserIdInt int userId,
             @NonNull List<AudioDeviceInfo> devices) {
diff --git a/media/java/android/media/midi/MidiDeviceServer.java b/media/java/android/media/midi/MidiDeviceServer.java
index 51d5520..d5916b9 100644
--- a/media/java/android/media/midi/MidiDeviceServer.java
+++ b/media/java/android/media/midi/MidiDeviceServer.java
@@ -384,14 +384,14 @@
 
     private void updateDeviceStatus() {
         // clear calling identity, since we may be in a Binder call from one of our clients
-        long identityToken = Binder.clearCallingIdentity();
-
-        MidiDeviceStatus status = new MidiDeviceStatus(mDeviceInfo, mInputPortOpen,
-                mOutputPortOpenCount);
-        if (mCallback != null) {
-            mCallback.onDeviceStatusChanged(this, status);
-        }
+        final long identityToken = Binder.clearCallingIdentity();
         try {
+            MidiDeviceStatus status = new MidiDeviceStatus(mDeviceInfo, mInputPortOpen,
+                    mOutputPortOpenCount);
+            if (mCallback != null) {
+                mCallback.onDeviceStatusChanged(this, status);
+            }
+
             mMidiManager.setDeviceStatus(mServer, status);
         } catch (RemoteException e) {
             Log.e(TAG, "RemoteException in updateDeviceStatus");
diff --git a/media/java/android/media/musicrecognition/IMusicRecognitionManager.aidl b/media/java/android/media/musicrecognition/IMusicRecognitionManager.aidl
new file mode 100644
index 0000000..9e71afa
--- /dev/null
+++ b/media/java/android/media/musicrecognition/IMusicRecognitionManager.aidl
@@ -0,0 +1,14 @@
+package android.media.musicrecognition;
+
+import android.media.musicrecognition.RecognitionRequest;
+import android.os.IBinder;
+
+/**
+ * Used by {@link MusicRecognitionManager} to tell system server to begin open an audio stream to
+ * the designated lookup service.
+ *
+ * @hide
+ */
+interface IMusicRecognitionManager {
+    void beginRecognition(in RecognitionRequest recognitionRequest, in IBinder callback);
+}
\ No newline at end of file
diff --git a/media/java/android/media/musicrecognition/IMusicRecognitionManagerCallback.aidl b/media/java/android/media/musicrecognition/IMusicRecognitionManagerCallback.aidl
new file mode 100644
index 0000000..e70504f
--- /dev/null
+++ b/media/java/android/media/musicrecognition/IMusicRecognitionManagerCallback.aidl
@@ -0,0 +1,15 @@
+package android.media.musicrecognition;
+
+import android.os.Bundle;
+import android.media.MediaMetadata;
+
+/**
+ * Callback used by system server to notify invoker of {@link MusicRecognitionManager} of the result
+ *
+ * @hide
+ */
+oneway interface IMusicRecognitionManagerCallback {
+    void onRecognitionSucceeded(in MediaMetadata result, in Bundle extras);
+    void onRecognitionFailed(int failureCode);
+    void onAudioStreamClosed();
+}
\ No newline at end of file
diff --git a/media/java/android/media/musicrecognition/IMusicRecognitionService.aidl b/media/java/android/media/musicrecognition/IMusicRecognitionService.aidl
new file mode 100644
index 0000000..26543ed
--- /dev/null
+++ b/media/java/android/media/musicrecognition/IMusicRecognitionService.aidl
@@ -0,0 +1,18 @@
+package android.media.musicrecognition;
+
+import android.media.AudioFormat;
+import android.os.ParcelFileDescriptor;
+import android.os.IBinder;
+import android.media.musicrecognition.IMusicRecognitionServiceCallback;
+
+/**
+ * Interface from the system to a {@link MusicRecognitionService}.
+ *
+ * @hide
+ */
+oneway interface IMusicRecognitionService {
+  void onAudioStreamStarted(
+      in ParcelFileDescriptor fd,
+      in AudioFormat audioFormat,
+      in IMusicRecognitionServiceCallback callback);
+}
\ No newline at end of file
diff --git a/media/java/android/media/musicrecognition/IMusicRecognitionServiceCallback.aidl b/media/java/android/media/musicrecognition/IMusicRecognitionServiceCallback.aidl
new file mode 100644
index 0000000..15215c4
--- /dev/null
+++ b/media/java/android/media/musicrecognition/IMusicRecognitionServiceCallback.aidl
@@ -0,0 +1,15 @@
+package android.media.musicrecognition;
+
+import android.os.Bundle;
+import android.media.MediaMetadata;
+
+/**
+ * Interface from a {@MusicRecognitionService} the system.
+ *
+ * @hide
+ */
+oneway interface IMusicRecognitionServiceCallback {
+  void onRecognitionSucceeded(in MediaMetadata result, in Bundle extras);
+
+  void onRecognitionFailed(int failureCode);
+}
\ No newline at end of file
diff --git a/media/java/android/media/musicrecognition/MusicRecognitionManager.java b/media/java/android/media/musicrecognition/MusicRecognitionManager.java
new file mode 100644
index 0000000..6bbcfd3
--- /dev/null
+++ b/media/java/android/media/musicrecognition/MusicRecognitionManager.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.musicrecognition;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.media.MediaMetadata;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+
+/**
+ * System service that manages music recognition.
+ *
+ * @hide
+ */
+@SystemApi
+@SystemService(Context.MUSIC_RECOGNITION_SERVICE)
+public class MusicRecognitionManager {
+
+    /**
+     * Error code provided by RecognitionCallback#onRecognitionFailed()
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"RECOGNITION_FAILED_"},
+            value = {RECOGNITION_FAILED_UNKNOWN,
+                    RECOGNITION_FAILED_NOT_FOUND,
+                    RECOGNITION_FAILED_NO_CONNECTIVITY,
+                    RECOGNITION_FAILED_SERVICE_UNAVAILABLE,
+                    RECOGNITION_FAILED_SERVICE_KILLED,
+                    RECOGNITION_FAILED_TIMEOUT,
+                    RECOGNITION_FAILED_AUDIO_UNAVAILABLE})
+    public @interface RecognitionFailureCode {
+    }
+
+    /** Catchall error code. */
+    public static final int RECOGNITION_FAILED_UNKNOWN = -1;
+    /** Recognition was performed but no result could be identified. */
+    public static final int RECOGNITION_FAILED_NOT_FOUND = 1;
+    /** Recognition failed because the server couldn't be reached. */
+    public static final int RECOGNITION_FAILED_NO_CONNECTIVITY = 2;
+    /**
+     * Recognition was not possible because the application which provides it is not available (for
+     * example, disabled).
+     */
+    public static final int RECOGNITION_FAILED_SERVICE_UNAVAILABLE = 3;
+    /** Recognition failed because the recognizer was killed. */
+    public static final int RECOGNITION_FAILED_SERVICE_KILLED = 5;
+    /** Recognition attempt timed out. */
+    public static final int RECOGNITION_FAILED_TIMEOUT = 6;
+    /** Recognition failed due to an issue with obtaining an audio stream. */
+    public static final int RECOGNITION_FAILED_AUDIO_UNAVAILABLE = 7;
+
+    /** Callback interface for the caller of this api. */
+    public interface RecognitionCallback {
+        /**
+         * Should be invoked by receiving app with the result of the search.
+         *
+         * @param recognitionRequest original request that started the recognition
+         * @param result result of the search
+         * @param extras extra data to be supplied back to the caller. Note that all
+         *               executable parameters and file descriptors would be removed from the
+         *               supplied bundle
+         */
+        void onRecognitionSucceeded(@NonNull RecognitionRequest recognitionRequest,
+                @NonNull MediaMetadata result, @Nullable Bundle extras);
+
+        /**
+         * Invoked when the search is not successful (possibly but not necessarily due to error).
+         *
+         * @param recognitionRequest original request that started the recognition
+         * @param failureCode failure code describing reason for failure
+         */
+        void onRecognitionFailed(@NonNull RecognitionRequest recognitionRequest,
+                @RecognitionFailureCode int failureCode);
+
+        /**
+         * Invoked by the system once the audio stream is closed either due to error, reaching the
+         * limit, or the remote service closing the stream.  Always called per
+         * #beingStreamingSearch() invocation.
+         */
+        void onAudioStreamClosed();
+    }
+
+    private final IMusicRecognitionManager mService;
+
+    /** @hide */
+    public MusicRecognitionManager(IMusicRecognitionManager service) {
+        mService = service;
+    }
+
+    /**
+     * Constructs an {@link android.media.AudioRecord} from the given parameters and streams the
+     * audio bytes to the designated cloud lookup service.  After the lookup is done, the given
+     * callback will be invoked by the system with the result or lack thereof.
+     *
+     * @param recognitionRequest audio parameters for the stream to search
+     * @param callbackExecutor where the callback is invoked
+     * @param callback invoked when the result is available
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_MUSIC_RECOGNITION)
+    public void beginStreamingSearch(
+            @NonNull RecognitionRequest recognitionRequest,
+            @NonNull @CallbackExecutor Executor callbackExecutor,
+            @NonNull RecognitionCallback callback) {
+        try {
+            mService.beginRecognition(
+                    requireNonNull(recognitionRequest),
+                    new MusicRecognitionCallbackWrapper(
+                            requireNonNull(recognitionRequest),
+                            requireNonNull(callback),
+                            requireNonNull(callbackExecutor)));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    private final class MusicRecognitionCallbackWrapper extends
+            IMusicRecognitionManagerCallback.Stub {
+
+        @NonNull
+        private final RecognitionRequest mRecognitionRequest;
+        @NonNull
+        private final RecognitionCallback mCallback;
+        @NonNull
+        private final Executor mCallbackExecutor;
+
+        MusicRecognitionCallbackWrapper(
+                RecognitionRequest recognitionRequest,
+                RecognitionCallback callback,
+                Executor callbackExecutor) {
+            mRecognitionRequest = recognitionRequest;
+            mCallback = callback;
+            mCallbackExecutor = callbackExecutor;
+        }
+
+        @Override
+        public void onRecognitionSucceeded(MediaMetadata result, Bundle extras) {
+            mCallbackExecutor.execute(
+                    () -> mCallback.onRecognitionSucceeded(mRecognitionRequest, result, extras));
+        }
+
+        @Override
+        public void onRecognitionFailed(@RecognitionFailureCode int failureCode) {
+            mCallbackExecutor.execute(
+                    () -> mCallback.onRecognitionFailed(mRecognitionRequest, failureCode));
+        }
+
+        @Override
+        public void onAudioStreamClosed() {
+            mCallbackExecutor.execute(mCallback::onAudioStreamClosed);
+        }
+    }
+}
diff --git a/media/java/android/media/musicrecognition/MusicRecognitionService.java b/media/java/android/media/musicrecognition/MusicRecognitionService.java
new file mode 100644
index 0000000..e2071b8
--- /dev/null
+++ b/media/java/android/media/musicrecognition/MusicRecognitionService.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.musicrecognition;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.media.AudioFormat;
+import android.media.MediaMetadata;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * Implemented by an app that wants to offer music search lookups. The system will start the
+ * service and stream up to 16 seconds of audio over the given file descriptor.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class MusicRecognitionService extends Service {
+
+    private static final String TAG = MusicRecognitionService.class.getSimpleName();
+
+    /** Callback for the result of the remote search. */
+    public interface Callback {
+        /**
+         * Call this method to pass back a successful search result.
+         *
+         * @param result successful result of the search
+         * @param extras extra data to be supplied back to the caller. Note that all executable
+         *               parameters and file descriptors would be removed from the supplied bundle
+         */
+        void onRecognitionSucceeded(@NonNull MediaMetadata result, @Nullable Bundle extras);
+
+        /**
+         * Call this method if the search does not find a result on an error occurred.
+         */
+        void onRecognitionFailed(@MusicRecognitionManager.RecognitionFailureCode int failureCode);
+    }
+
+    /**
+     * Action used to start this service.
+     *
+     * @hide
+     */
+    public static final String ACTION_MUSIC_SEARCH_LOOKUP =
+            "android.service.musicrecognition.MUSIC_RECOGNITION";
+
+    private Handler mHandler;
+    private final IMusicRecognitionService mServiceInterface =
+            new IMusicRecognitionService.Stub() {
+                @Override
+                public void onAudioStreamStarted(ParcelFileDescriptor fd,
+                        AudioFormat audioFormat,
+                        IMusicRecognitionServiceCallback callback) {
+                    mHandler.sendMessage(
+                            obtainMessage(MusicRecognitionService.this::onRecognize, fd,
+                                    audioFormat,
+                                    new Callback() {
+                                        @Override
+                                        public void onRecognitionSucceeded(
+                                                @NonNull MediaMetadata result,
+                                                @Nullable Bundle extras) {
+                                            try {
+                                                callback.onRecognitionSucceeded(result, extras);
+                                            } catch (RemoteException e) {
+                                                e.rethrowFromSystemServer();
+                                            }
+                                        }
+
+                                        @Override
+                                        public void onRecognitionFailed(int failureCode) {
+                                            try {
+                                                callback.onRecognitionFailed(failureCode);
+                                            } catch (RemoteException e) {
+                                                e.rethrowFromSystemServer();
+                                            }
+                                        }
+                                    }));
+                }
+            };
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mHandler = new Handler(Looper.getMainLooper(), null, true);
+    }
+
+    /**
+     * Read audio from this stream. You must invoke the callback whether the music is recognized or
+     * not.
+     *
+     * @param stream containing music to be recognized. Close when you are finished.
+     * @param audioFormat describes sample rate, channels and endianness of the stream
+     * @param callback to invoke after lookup is finished. Must always be called.
+     */
+    public abstract void onRecognize(@NonNull ParcelFileDescriptor stream,
+            @NonNull AudioFormat audioFormat,
+            @NonNull Callback callback);
+
+    /**
+     * @hide
+     */
+    @Nullable
+    @Override
+    public IBinder onBind(@NonNull Intent intent) {
+        if (ACTION_MUSIC_SEARCH_LOOKUP.equals(intent.getAction())) {
+            return mServiceInterface.asBinder();
+        }
+        Log.w(TAG,
+                "Tried to bind to wrong intent (should be " + ACTION_MUSIC_SEARCH_LOOKUP + ": "
+                        + intent);
+        return null;
+    }
+}
diff --git a/media/java/android/media/musicrecognition/RecognitionRequest.aidl b/media/java/android/media/musicrecognition/RecognitionRequest.aidl
new file mode 100644
index 0000000..757b5701
--- /dev/null
+++ b/media/java/android/media/musicrecognition/RecognitionRequest.aidl
@@ -0,0 +1,18 @@
+/* Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT 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.musicrecognition;
+
+parcelable RecognitionRequest;
\ No newline at end of file
diff --git a/media/java/android/media/musicrecognition/RecognitionRequest.java b/media/java/android/media/musicrecognition/RecognitionRequest.java
new file mode 100644
index 0000000..e4f4848
--- /dev/null
+++ b/media/java/android/media/musicrecognition/RecognitionRequest.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.musicrecognition;
+
+import static android.media.AudioAttributes.CONTENT_TYPE_MUSIC;
+import static android.media.AudioFormat.ENCODING_PCM_16BIT;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.media.AudioAttributes;
+import android.media.AudioFormat;
+import android.media.AudioRecord;
+import android.media.MediaRecorder;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Encapsulates parameters for making music recognition queries via {@link MusicRecognitionManager}.
+ *
+ * @hide
+ */
+@SystemApi
+public final class RecognitionRequest implements Parcelable {
+    @NonNull private final AudioAttributes mAudioAttributes;
+    @NonNull private final AudioFormat mAudioFormat;
+    private final int mCaptureSession;
+    private final int mMaxAudioLengthSeconds;
+    private final int mIgnoreBeginningFrames;
+
+    private RecognitionRequest(Builder b) {
+        mAudioAttributes = requireNonNull(b.mAudioAttributes);
+        mAudioFormat = requireNonNull(b.mAudioFormat);
+        mCaptureSession = b.mCaptureSession;
+        mMaxAudioLengthSeconds = b.mMaxAudioLengthSeconds;
+        mIgnoreBeginningFrames = b.mIgnoreBeginningFrames;
+    }
+
+    @NonNull
+    public AudioAttributes getAudioAttributes() {
+        return mAudioAttributes;
+    }
+
+    @NonNull
+    public AudioFormat getAudioFormat() {
+        return mAudioFormat;
+    }
+
+    public int getCaptureSession() {
+        return mCaptureSession;
+    }
+
+    @SuppressWarnings("MethodNameUnits")
+    public int getMaxAudioLengthSeconds() {
+        return mMaxAudioLengthSeconds;
+    }
+
+    public int getIgnoreBeginningFrames() {
+        return mIgnoreBeginningFrames;
+    }
+
+    /**
+     * Builder for constructing StreamSearchRequest objects.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final class Builder {
+        private AudioFormat mAudioFormat = new AudioFormat.Builder()
+                .setSampleRate(16000)
+                .setEncoding(ENCODING_PCM_16BIT)
+                .build();
+        private AudioAttributes mAudioAttributes = new AudioAttributes.Builder()
+                .setContentType(CONTENT_TYPE_MUSIC)
+                .build();
+        private int mCaptureSession = MediaRecorder.AudioSource.MIC;
+        private int mMaxAudioLengthSeconds = 24; // Max enforced in system server.
+        private int mIgnoreBeginningFrames = 0;
+
+        /** Attributes passed to the constructed {@link AudioRecord}. */
+        @NonNull
+        public Builder setAudioAttributes(@NonNull AudioAttributes audioAttributes) {
+            mAudioAttributes = audioAttributes;
+            return this;
+        }
+
+        /** AudioFormat passed to the constructed {@link AudioRecord}. */
+        @NonNull
+        public Builder setAudioFormat(@NonNull AudioFormat audioFormat) {
+            mAudioFormat = audioFormat;
+            return this;
+        }
+
+        /** Constant from {@link android.media.MediaRecorder.AudioSource}. */
+        @NonNull
+        public Builder setCaptureSession(int captureSession) {
+            mCaptureSession = captureSession;
+            return this;
+        }
+
+        /** Maximum number of seconds to stream from the audio source. */
+        @NonNull
+        public Builder setMaxAudioLengthSeconds(int maxAudioLengthSeconds) {
+            mMaxAudioLengthSeconds = maxAudioLengthSeconds;
+            return this;
+        }
+
+        /** Number of samples to drop from the start of the stream. */
+        @NonNull
+        public Builder setIgnoreBeginningFrames(int ignoreBeginningFrames) {
+            mIgnoreBeginningFrames = ignoreBeginningFrames;
+            return this;
+        }
+
+        /** Returns the constructed request. */
+        @NonNull
+        public RecognitionRequest build() {
+            return new RecognitionRequest(this);
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeParcelable(mAudioFormat, flags);
+        dest.writeParcelable(mAudioAttributes, flags);
+        dest.writeInt(mCaptureSession);
+        dest.writeInt(mMaxAudioLengthSeconds);
+        dest.writeInt(mIgnoreBeginningFrames);
+    }
+
+    private RecognitionRequest(Parcel in) {
+        mAudioFormat = in.readParcelable(AudioFormat.class.getClassLoader());
+        mAudioAttributes = in.readParcelable(AudioAttributes.class.getClassLoader());
+        mCaptureSession = in.readInt();
+        mMaxAudioLengthSeconds = in.readInt();
+        mIgnoreBeginningFrames = in.readInt();
+    }
+
+    @NonNull public static final Creator<RecognitionRequest> CREATOR =
+            new Creator<RecognitionRequest>() {
+
+        @Override
+        public RecognitionRequest createFromParcel(Parcel p) {
+            return new RecognitionRequest(p);
+        }
+
+        @Override
+        public RecognitionRequest[] newArray(int size) {
+            return new RecognitionRequest[size];
+        }
+    };
+}
diff --git a/media/java/android/media/permission/ClearCallingIdentityContext.java b/media/java/android/media/permission/ClearCallingIdentityContext.java
index 364a2e8..2d58b24 100644
--- a/media/java/android/media/permission/ClearCallingIdentityContext.java
+++ b/media/java/android/media/permission/ClearCallingIdentityContext.java
@@ -47,11 +47,13 @@
         return new ClearCallingIdentityContext();
     }
 
+    @SuppressWarnings("AndroidFrameworkBinderIdentity")
     private ClearCallingIdentityContext() {
         mRestoreKey = Binder.clearCallingIdentity();
     }
 
     @Override
+    @SuppressWarnings("AndroidFrameworkBinderIdentity")
     public void close() {
         Binder.restoreCallingIdentity(mRestoreKey);
     }
diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
index 5fd8f73..1557ea6 100644
--- a/media/java/android/media/session/ISessionManager.aidl
+++ b/media/java/android/media/session/ISessionManager.aidl
@@ -44,11 +44,11 @@
     void dispatchMediaKeyEvent(String packageName, boolean asSystemService, in KeyEvent keyEvent,
             boolean needWakeLock);
     boolean dispatchMediaKeyEventToSessionAsSystemService(String packageName,
-            in MediaSession.Token sessionToken, in KeyEvent keyEvent);
+            in KeyEvent keyEvent, in MediaSession.Token sessionToken);
     void dispatchVolumeKeyEvent(String packageName, String opPackageName, boolean asSystemService,
             in KeyEvent keyEvent, int stream, boolean musicOnly);
     void dispatchVolumeKeyEventToSessionAsSystemService(String packageName, String opPackageName,
-            in MediaSession.Token sessionToken, in KeyEvent keyEvent);
+            in KeyEvent keyEvent, in MediaSession.Token sessionToken);
     void dispatchAdjustVolume(String packageName, String opPackageName, int suggestedStream,
             int delta, int flags);
     void addSessionsListener(in IActiveSessionsListener listener, in ComponentName compName,
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index e17e069..f582d2a 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -609,25 +609,6 @@
     }
 
     /**
-     * Return true if this is considered an active playback state.
-     *
-     * @hide
-     */
-    public static boolean isActiveState(int state) {
-        switch (state) {
-            case PlaybackState.STATE_FAST_FORWARDING:
-            case PlaybackState.STATE_REWINDING:
-            case PlaybackState.STATE_SKIPPING_TO_PREVIOUS:
-            case PlaybackState.STATE_SKIPPING_TO_NEXT:
-            case PlaybackState.STATE_BUFFERING:
-            case PlaybackState.STATE_CONNECTING:
-            case PlaybackState.STATE_PLAYING:
-                return true;
-        }
-        return false;
-    }
-
-    /**
      * Returns whether the given bundle includes non-framework Parcelables.
      */
     static boolean hasCustomParcelable(@Nullable Bundle bundle) {
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 3acb951..b7b9e4b 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -493,43 +493,46 @@
     }
 
     /**
-     * Send a media key event. The receiver will be selected automatically.
+     * Sends a media key event. The receiver will be selected automatically.
      *
-     * @param keyEvent The KeyEvent to send.
+     * @param keyEvent the key event to send
      * @hide
      */
     public void dispatchMediaKeyEvent(@NonNull KeyEvent keyEvent) {
-        dispatchMediaKeyEvent(keyEvent, false);
+        dispatchMediaKeyEventInternal(keyEvent, /*asSystemService=*/false, /*needWakeLock=*/false);
     }
 
     /**
-     * Send a media key event. The receiver will be selected automatically.
+     * Sends a media key event. The receiver will be selected automatically.
      *
-     * @param keyEvent The KeyEvent to send.
-     * @param needWakeLock True if a wake lock should be held while sending the key.
+     * @param keyEvent the key event to send
+     * @param needWakeLock true if a wake lock should be held while sending the key
      * @hide
      */
     public void dispatchMediaKeyEvent(@NonNull KeyEvent keyEvent, boolean needWakeLock) {
-        dispatchMediaKeyEventInternal(false, keyEvent, needWakeLock);
+        dispatchMediaKeyEventInternal(keyEvent, /*asSystemService=*/false, needWakeLock);
     }
 
     /**
-     * Send a media key event as system component. The receiver will be selected automatically.
+     * Sends a media key event as system service. The receiver will be selected automatically.
      * <p>
      * Should be only called by the {@link com.android.internal.policy.PhoneWindow} or
      * {@link android.view.FallbackEventHandler} when the foreground activity didn't consume the key
      * from the hardware devices.
      *
-     * @param keyEvent The KeyEvent to send.
+     * @param keyEvent the key event to send
      * @hide
      */
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public void dispatchMediaKeyEventAsSystemService(@NonNull KeyEvent keyEvent) {
-        dispatchMediaKeyEventInternal(true, keyEvent, false);
+        dispatchMediaKeyEventInternal(keyEvent, /*asSystemService=*/true, /*needWakeLock=*/false);
     }
 
-    private void dispatchMediaKeyEventInternal(boolean asSystemService, @NonNull KeyEvent keyEvent,
+    private void dispatchMediaKeyEventInternal(KeyEvent keyEvent, boolean asSystemService,
             boolean needWakeLock) {
+        if (keyEvent == null) {
+            throw new NullPointerException("keyEvent shouldn't be null");
+        }
         try {
             mService.dispatchMediaKeyEvent(mContext.getPackageName(), asSystemService, keyEvent,
                     needWakeLock);
@@ -539,31 +542,31 @@
     }
 
     /**
-     * Dispatches the media button event as system service to the session.
+     * Sends a media key event as system service to the given session.
      * <p>
      * Should be only called by the {@link com.android.internal.policy.PhoneWindow} when the
      * foreground activity didn't consume the key from the hardware devices.
      *
-     * @param sessionToken session token
-     * @param keyEvent media key event
+     * @param keyEvent the key event to send
+     * @param sessionToken the session token to which the key event should be dispatched
      * @return {@code true} if the event was sent to the session, {@code false} otherwise
      * @hide
      */
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
-    public boolean dispatchMediaKeyEventAsSystemService(@NonNull MediaSession.Token sessionToken,
-            @NonNull KeyEvent keyEvent) {
+    public boolean dispatchMediaKeyEventToSessionAsSystemService(@NonNull KeyEvent keyEvent,
+            @NonNull MediaSession.Token sessionToken) {
         if (sessionToken == null) {
-            throw new IllegalArgumentException("sessionToken shouldn't be null");
+            throw new NullPointerException("sessionToken shouldn't be null");
         }
         if (keyEvent == null) {
-            throw new IllegalArgumentException("keyEvent shouldn't be null");
+            throw new NullPointerException("keyEvent shouldn't be null");
         }
         if (!KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) {
             return false;
         }
         try {
-            return mService.dispatchMediaKeyEventToSessionAsSystemService(mContext.getPackageName(),
-                    sessionToken, keyEvent);
+            return mService.dispatchMediaKeyEventToSessionAsSystemService(
+                    mContext.getPackageName(), keyEvent, sessionToken);
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to send key event.", e);
         }
@@ -571,13 +574,16 @@
     }
 
     /**
-     * Send a volume key event. The receiver will be selected automatically.
+     * Sends a volume key event. The receiver will be selected automatically.
      *
-     * @param keyEvent The volume KeyEvent to send.
+     * @param keyEvent the volume key event to send
+     * @param streamType type of stream
+     * @param musicOnly true if key event should only be sent to music stream
      * @hide
      */
-    public void dispatchVolumeKeyEvent(@NonNull KeyEvent keyEvent, int stream, boolean musicOnly) {
-        dispatchVolumeKeyEventInternal(false, keyEvent, stream, musicOnly);
+    public void dispatchVolumeKeyEvent(@NonNull KeyEvent keyEvent, int streamType,
+            boolean musicOnly) {
+        dispatchVolumeKeyEventInternal(keyEvent, streamType, musicOnly, /*asSystemService=*/false);
     }
 
     /**
@@ -592,17 +598,21 @@
      * Valid stream types include {@link AudioManager.PublicStreamTypes} and
      * {@link AudioManager#USE_DEFAULT_STREAM_TYPE}.
      *
-     * @param keyEvent volume key event
+     * @param keyEvent the volume key event to send
      * @param streamType type of stream
      * @hide
      */
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public void dispatchVolumeKeyEventAsSystemService(@NonNull KeyEvent keyEvent, int streamType) {
-        dispatchVolumeKeyEventInternal(true, keyEvent, streamType, false);
+        dispatchVolumeKeyEventInternal(keyEvent, streamType, /*musicOnly=*/false,
+                /*asSystemService=*/true);
     }
 
-    private void dispatchVolumeKeyEventInternal(boolean asSystemService, @NonNull KeyEvent keyEvent,
-            int stream, boolean musicOnly) {
+    private void dispatchVolumeKeyEventInternal(@NonNull KeyEvent keyEvent, int stream,
+            boolean musicOnly, boolean asSystemService) {
+        if (keyEvent == null) {
+            throw new NullPointerException("keyEvent shouldn't be null");
+        }
         try {
             mService.dispatchVolumeKeyEvent(mContext.getPackageName(), mContext.getOpPackageName(),
                     asSystemService, keyEvent, stream, musicOnly);
@@ -617,22 +627,22 @@
      * Should be only called by the {@link com.android.internal.policy.PhoneWindow} when the
      * foreground activity didn't consume the key from the hardware devices.
      *
-     * @param sessionToken sessionToken
-     * @param keyEvent volume key event
+     * @param keyEvent the volume key event to send
+     * @param sessionToken the session token to which the key event should be dispatched
      * @hide
      */
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
-    public void dispatchVolumeKeyEventAsSystemService(@NonNull MediaSession.Token sessionToken,
-            @NonNull KeyEvent keyEvent) {
+    public void dispatchVolumeKeyEventToSessionAsSystemService(@NonNull KeyEvent keyEvent,
+            @NonNull MediaSession.Token sessionToken) {
         if (sessionToken == null) {
-            throw new IllegalArgumentException("sessionToken shouldn't be null");
+            throw new NullPointerException("sessionToken shouldn't be null");
         }
         if (keyEvent == null) {
-            throw new IllegalArgumentException("keyEvent shouldn't be null");
+            throw new NullPointerException("keyEvent shouldn't be null");
         }
         try {
             mService.dispatchVolumeKeyEventToSessionAsSystemService(mContext.getPackageName(),
-                    mContext.getOpPackageName(), sessionToken, keyEvent);
+                    mContext.getOpPackageName(), keyEvent, sessionToken);
         } catch (RemoteException e) {
             Log.wtf(TAG, "Error calling dispatchVolumeKeyEventAsSystemService", e);
         }
diff --git a/media/java/android/media/session/PlaybackState.java b/media/java/android/media/session/PlaybackState.java
index 8dd6127..b1a88ed 100644
--- a/media/java/android/media/session/PlaybackState.java
+++ b/media/java/android/media/session/PlaybackState.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.LongDef;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -480,6 +481,25 @@
         return mExtras;
     }
 
+    /**
+     * Returns whether this is considered as an active playback state.
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public boolean isActiveState() {
+        switch (mState) {
+            case PlaybackState.STATE_FAST_FORWARDING:
+            case PlaybackState.STATE_REWINDING:
+            case PlaybackState.STATE_SKIPPING_TO_PREVIOUS:
+            case PlaybackState.STATE_SKIPPING_TO_NEXT:
+            case PlaybackState.STATE_BUFFERING:
+            case PlaybackState.STATE_CONNECTING:
+            case PlaybackState.STATE_PLAYING:
+                return true;
+        }
+        return false;
+    }
+
     public static final @android.annotation.NonNull Parcelable.Creator<PlaybackState> CREATOR =
             new Parcelable.Creator<PlaybackState>() {
         @Override
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index 4380c13..ed99fad 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -25,7 +25,7 @@
 import android.media.tv.ITvInputHardware;
 import android.media.tv.ITvInputHardwareCallback;
 import android.media.tv.ITvInputManagerCallback;
-import android.media.tv.TvChannelInfo;
+import android.media.tv.TunedInfo;
 import android.media.tv.TvContentRatingSystemInfo;
 import android.media.tv.TvInputHardwareInfo;
 import android.media.tv.TvInputInfo;
@@ -89,7 +89,7 @@
     void timeShiftSetPlaybackParams(in IBinder sessionToken, in PlaybackParams params, int userId);
     void timeShiftEnablePositionTracking(in IBinder sessionToken, boolean enable, int userId);
 
-    List<TvChannelInfo> getCurrentTvChannelInfos(int userId);
+    List<TunedInfo> getCurrentTunedInfos(int userId);
 
     // For the recording session
     void startRecording(in IBinder sessionToken, in Uri programUri, in Bundle params, int userId);
diff --git a/media/java/android/media/tv/ITvInputManagerCallback.aidl b/media/java/android/media/tv/ITvInputManagerCallback.aidl
index 9f80bf5..3128ba7 100644
--- a/media/java/android/media/tv/ITvInputManagerCallback.aidl
+++ b/media/java/android/media/tv/ITvInputManagerCallback.aidl
@@ -16,7 +16,7 @@
 
 package android.media.tv;
 
-import android.media.tv.TvChannelInfo;
+import android.media.tv.TunedInfo;
 import android.media.tv.TvInputInfo;
 
 /**
@@ -29,5 +29,5 @@
     void onInputUpdated(in String inputId);
     void onInputStateChanged(in String inputId, int state);
     void onTvInputInfoUpdated(in TvInputInfo TvInputInfo);
-    void onCurrentTvChannelInfosUpdated(in List<TvChannelInfo> currentTvChannelInfos);
+    void onCurrentTunedInfosUpdated(in List<TunedInfo> currentTunedInfos);
 }
diff --git a/media/java/android/media/tv/TvChannelInfo.aidl b/media/java/android/media/tv/TunedInfo.aidl
similarity index 95%
rename from media/java/android/media/tv/TvChannelInfo.aidl
rename to media/java/android/media/tv/TunedInfo.aidl
index 71cd0a7..b7c1d52 100644
--- a/media/java/android/media/tv/TvChannelInfo.aidl
+++ b/media/java/android/media/tv/TunedInfo.aidl
@@ -16,4 +16,4 @@
 
 package android.media.tv;
 
-parcelable TvChannelInfo;
+parcelable TunedInfo;
diff --git a/media/java/android/media/tv/TvChannelInfo.java b/media/java/android/media/tv/TunedInfo.java
similarity index 87%
rename from media/java/android/media/tv/TvChannelInfo.java
rename to media/java/android/media/tv/TunedInfo.java
index 11cb1f7..6199c89 100644
--- a/media/java/android/media/tv/TvChannelInfo.java
+++ b/media/java/android/media/tv/TunedInfo.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -31,11 +32,13 @@
 
 
 /**
- * This class is used to specify information of a TV channel.
+ * Contains information about a {@link TvInputService.Session} that is currently tuned to a channel
+ * or pass-through input.
  * @hide
  */
-public final class TvChannelInfo implements Parcelable {
-    static final String TAG = "TvChannelInfo";
+@SystemApi
+public final class TunedInfo implements Parcelable {
+    static final String TAG = "TunedInfo";
 
     /**
      * App tag for {@link #getAppTag()}: the corresponding application of the channel is the same as
@@ -67,21 +70,21 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface AppType {}
 
-    public static final @NonNull Parcelable.Creator<TvChannelInfo> CREATOR =
-            new Parcelable.Creator<TvChannelInfo>() {
+    public static final @NonNull Parcelable.Creator<TunedInfo> CREATOR =
+            new Parcelable.Creator<TunedInfo>() {
                 @Override
-                public TvChannelInfo createFromParcel(Parcel source) {
+                public TunedInfo createFromParcel(Parcel source) {
                     try {
-                        return new TvChannelInfo(source);
+                        return new TunedInfo(source);
                     } catch (Exception e) {
-                        Log.e(TAG, "Exception creating TvChannelInfo from parcel", e);
+                        Log.e(TAG, "Exception creating TunedInfo from parcel", e);
                         return null;
                     }
                 }
 
                 @Override
-                public TvChannelInfo[] newArray(int size) {
-                    return new TvChannelInfo[size];
+                public TunedInfo[] newArray(int size) {
+                    return new TunedInfo[size];
                 }
             };
 
@@ -94,7 +97,7 @@
     private final int mAppTag;
 
     /** @hide */
-    public TvChannelInfo(
+    public TunedInfo(
             String inputId, @Nullable Uri channelUri, boolean isRecordingSession,
             boolean isForeground, @AppType int appType, int appTag) {
         mInputId = inputId;
@@ -106,7 +109,7 @@
     }
 
 
-    private TvChannelInfo(Parcel source) {
+    private TunedInfo(Parcel source) {
         mInputId = source.readString();
         String uriString = source.readString();
         mChannelUri = uriString == null ? null : Uri.parse(uriString);
@@ -194,11 +197,11 @@
 
     @Override
     public boolean equals(Object o) {
-        if (!(o instanceof TvChannelInfo)) {
+        if (!(o instanceof TunedInfo)) {
             return false;
         }
 
-        TvChannelInfo other = (TvChannelInfo) o;
+        TunedInfo other = (TunedInfo) o;
 
         return TextUtils.equals(mInputId, other.getInputId())
                 && Objects.equals(mChannelUri, other.mChannelUri)
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index c80f3c6..e9959be 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -901,12 +901,13 @@
         }
 
         /**
-         * This is called when the information about current TV channels has been updated.
+         * This is called when the information about current tuned information has been updated.
          *
-         * @param tvChannelInfos a list of {@link TvChannelInfo} objects of new current channels.
+         * @param tunedInfos a list of {@link TunedInfo} objects of new tuned information.
          * @hide
          */
-        public void onCurrentTvChannelInfosUpdated(@NonNull List<TvChannelInfo> tvChannelInfos) {
+        @SystemApi
+        public void onCurrentTunedInfosUpdated(@NonNull List<TunedInfo> tunedInfos) {
         }
     }
 
@@ -968,12 +969,11 @@
             });
         }
 
-        public void postCurrentTvChannelInfosUpdated(
-                final List<TvChannelInfo> currentTvChannelInfos) {
+        public void postCurrentTunedInfosUpdated(final List<TunedInfo> currentTunedInfos) {
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    mCallback.onCurrentTvChannelInfosUpdated(currentTvChannelInfos);
+                    mCallback.onCurrentTunedInfosUpdated(currentTunedInfos);
                 }
             });
         }
@@ -1283,10 +1283,10 @@
             }
 
             @Override
-            public void onCurrentTvChannelInfosUpdated(List<TvChannelInfo> currentTvChannelInfos) {
+            public void onCurrentTunedInfosUpdated(List<TunedInfo> currentTunedInfos) {
                 synchronized (mLock) {
                     for (TvInputCallbackRecord record : mCallbackRecords) {
-                        record.postCurrentTvChannelInfosUpdated(currentTvChannelInfos);
+                        record.postCurrentTunedInfosUpdated(currentTunedInfos);
                     }
                 }
             }
@@ -1981,18 +1981,19 @@
     }
 
     /**
-     * Returns the list of TV channel information for {@link TvInputService.Session} that are
+     * Returns the list of session information for {@link TvInputService.Session} that are
      * currently in use.
      * <p> Permission com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS is required to get
-     * the channel URIs. If the permission is not granted, {@link TvChannelInfo#getChannelUri()}
-     * returns {@code null}.
+     * the channel URIs. If the permission is not granted,
+     * {@link TunedInfo#getChannelUri()} returns {@code null}.
      * @hide
      */
+    @SystemApi
     @RequiresPermission("com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS")
     @NonNull
-    public List<TvChannelInfo> getCurrentTvChannelInfos() {
+    public List<TunedInfo> getCurrentTunedInfos() {
         try {
-            return mService.getCurrentTvChannelInfos(mUserId);
+            return mService.getCurrentTunedInfos(mUserId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index e148d0e..1881e38 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -98,15 +98,49 @@
      * Invalid timestamp.
      *
      * <p>Returned by {@link android.media.tv.tuner.filter.TimeFilter#getSourceTime()},
-     * {@link android.media.tv.tuner.filter.TimeFilter#getTimeStamp()}, or
-     * {@link Tuner#getAvSyncTime(int)} when the requested timestamp is not available.
+     * {@link android.media.tv.tuner.filter.TimeFilter#getTimeStamp()},
+     * {@link Tuner#getAvSyncTime(int)} or {@link TsRecordEvent#getPts()} and
+     * {@link MmtpRecordEvent#getPts()} when the requested timestamp is not available.
      *
      * @see android.media.tv.tuner.filter.TimeFilter#getSourceTime()
      * @see android.media.tv.tuner.filter.TimeFilter#getTimeStamp()
      * @see Tuner#getAvSyncTime(int)
+     * @see android.media.tv.tuner.filter.TsRecordEvent#getPts()
+     * @see android.media.tv.tuner.filter.MmtpRecordEvent#getPts()
      */
-    public static final long INVALID_TIMESTAMP = -1L;
-
+    public static final long INVALID_TIMESTAMP =
+            android.hardware.tv.tuner.V1_1.Constants.Constant64Bit.INVALID_PRESENTATION_TIME_STAMP;
+    /**
+     * Invalid mpu sequence number in MmtpRecordEvent.
+     *
+     * <p>Returned by {@link MmtpRecordEvent#getMpuSequenceNumber()} when the requested sequence
+     * number is not available.
+     *
+     * @see android.media.tv.tuner.filter.MmtpRecordEvent#getMpuSequenceNumber()
+     */
+    public static final int INVALID_MMTP_RECORD_EVENT_MPT_SEQUENCE_NUM =
+            android.hardware.tv.tuner.V1_1.Constants.Constant
+                    .INVALID_MMTP_RECORD_EVENT_MPT_SEQUENCE_NUM;
+    /**
+     * Invalid local transport stream id.
+     *
+     * <p>Returned by {@link #linkFrontendToCiCam(int)} when the requested failed
+     * or the hal implementation does not support the operation.
+     *
+     * @see #linkFrontendToCiCam(int)
+     */
+    public static final int INVALID_LTS_ID =
+            android.hardware.tv.tuner.V1_1.Constants.Constant.INVALID_LTS_ID;
+    /**
+     * Invalid 64-bit filter ID.
+     */
+    public static final long INVALID_FILTER_ID_64BIT =
+            android.hardware.tv.tuner.V1_1.Constants.Constant64Bit.INVALID_FILTER_ID_64BIT;
+    /**
+     * Invalid frequency that is used as the default frontend frequency setting.
+     */
+    public static final int INVALID_FRONTEND_SETTING_FREQUENCY =
+            android.hardware.tv.tuner.V1_1.Constants.Constant.INVALID_FRONTEND_SETTING_FREQUENCY;
 
     /** @hide */
     @IntDef(prefix = "SCAN_TYPE_", value = {SCAN_TYPE_UNDEFINED, SCAN_TYPE_AUTO, SCAN_TYPE_BLIND})
@@ -204,6 +238,7 @@
     private final Context mContext;
     private final TunerResourceManager mTunerResourceManager;
     private final int mClientId;
+    private static int sTunerVersion = TunerVersionChecker.TUNER_VERSION_UNKNOWN;
 
     private Frontend mFrontend;
     private EventHandler mHandler;
@@ -255,6 +290,14 @@
     public Tuner(@NonNull Context context, @Nullable String tvInputSessionId,
             @TvInputService.PriorityHintUseCaseType int useCase) {
         nativeSetup();
+        sTunerVersion = nativeGetTunerVersion();
+        if (sTunerVersion == TunerVersionChecker.TUNER_VERSION_UNKNOWN) {
+            Log.e(TAG, "Unknown Tuner version!");
+        } else {
+            Log.d(TAG, "Current Tuner version is "
+                    + TunerVersionChecker.getMajorVersion(sTunerVersion) + "."
+                    + TunerVersionChecker.getMinorVersion(sTunerVersion) + ".");
+        }
         mContext = context;
         mTunerResourceManager = (TunerResourceManager)
                 context.getSystemService(Context.TV_TUNER_RESOURCE_MGR_SERVICE);
@@ -295,6 +338,11 @@
     }
 
     /** @hide */
+    public static int getTunerVersion() {
+        return sTunerVersion;
+    }
+
+    /** @hide */
     public List<Integer> getFrontendIds() {
         return nativeGetFrontendIds();
     }
@@ -419,6 +467,11 @@
     /**
      * Native method to get all frontend IDs.
      */
+    private native int nativeGetTunerVersion();
+
+    /**
+     * Native method to get all frontend IDs.
+     */
     private native List<Integer> nativeGetFrontendIds();
 
     /**
@@ -437,7 +490,9 @@
     private native Integer nativeGetAvSyncHwId(Filter filter);
     private native Long nativeGetAvSyncTime(int avSyncId);
     private native int nativeConnectCiCam(int ciCamId);
+    private native int nativeLinkCiCam(int ciCamId);
     private native int nativeDisconnectCiCam();
+    private native int nativeUnlinkCiCam(int ciCamId);
     private native FrontendInfo nativeGetFrontendInfo(int id);
     private native Filter nativeOpenFilter(int type, int subType, long bufferSize);
     private native TimeFilter nativeOpenTimeFilter();
@@ -568,6 +623,10 @@
      * OnTuneEventListener#SIGNAL_NO_SIGNAL} events sent to the {@link OnTuneEventListener}
      * specified in {@link #setOnTuneEventListener(Executor, OnTuneEventListener)}.
      *
+     * <p>Tuning with {@link android.media.tv.tuner.frontend.DtmbFrontendSettings} is only
+     * supported in Tuner 1.1 or higher version. Unsupported version will cause no-op. Use {@link
+     * TunerVersionChecker.getTunerVersion()} to get the version information.
+     *
      * @param settings Signal delivery information the frontend uses to
      *                 search and lock the signal.
      * @return result status of tune operation.
@@ -578,6 +637,12 @@
     public int tune(@NonNull FrontendSettings settings) {
         Log.d(TAG, "Tune to " + settings.getFrequency());
         mFrontendType = settings.getType();
+        if (mFrontendType == FrontendSettings.TYPE_DTMB) {
+            if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
+                    TunerVersionChecker.TUNER_VERSION_1_1, "Tuner with DTMB Frontend")) {
+                return RESULT_UNAVAILABLE;
+            }
+        }
         if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) {
             mFrontendInfo = null;
             Log.d(TAG, "Write Stats Log for tuning.");
@@ -607,6 +672,10 @@
      *
      * <p>Details for channels found are returned via {@link ScanCallback}.
      *
+     * <p>Scanning with {@link android.media.tv.tuner.frontend.DtmbFrontendSettings} is only
+     * supported in Tuner 1.1 or higher version. Unsupported version will cause no-op. Use {@link
+     * TunerVersionChecker.getTunerVersion()} to get the version information.
+     *
      * @param settings A {@link FrontendSettings} to configure the frontend.
      * @param scanType The scan type.
      * @throws SecurityException     if the caller does not have appropriate permissions.
@@ -622,6 +691,12 @@
                             + "started.");
         }
         mFrontendType = settings.getType();
+        if (mFrontendType == FrontendSettings.TYPE_DTMB) {
+            if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
+                    TunerVersionChecker.TUNER_VERSION_1_1, "Scan with DTMB Frontend")) {
+                return RESULT_UNAVAILABLE;
+            }
+        }
         if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) {
             mScanCallback = scanCallback;
             mScanCallbackExecutor = executor;
@@ -700,7 +775,8 @@
      *
      * <p>This retrieve the statuses of the frontend for given status types.
      *
-     * @param statusTypes an array of status types which the caller requests.
+     * @param statusTypes an array of status types which the caller requests. Any types that are not
+     *        in {@link FrontendInfo.getStatusCapabilities()} would be ignored.
      * @return statuses which response the caller's requests. {@code null} if the operation failed.
      */
     @Nullable
@@ -760,6 +836,33 @@
     }
 
     /**
+     * Link Conditional Access Modules (CAM) Frontend to support Common Interface (CI) by-pass mode.
+     *
+     * <p>It is used by the client to link CI-CAM to a Frontend. CI by-pass mode requires that
+     * the CICAM also receives the TS concurrently from the frontend when the Demux is receiving
+     * the TS directly from the frontend.
+     *
+     * <p>Use {@link #unlinkFrontendToCicam(int)} to disconnect.
+     *
+     * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause
+     * no-op and return {@link INVALID_LTS_ID}. Use {@link TunerVersionChecker.getTunerVersion()} to
+     * check the version.
+     *
+     * @param ciCamId specify CI-CAM Id to link.
+     * @return Local transport stream id when connection is successfully established. Failed
+     *         operation returns {@link INVALID_LTS_ID}.
+     */
+    public int linkFrontendToCiCam(int ciCamId) {
+        if (TunerVersionChecker.checkHigherOrEqualVersionTo(TunerVersionChecker.TUNER_VERSION_1_1,
+                "linkFrontendToCiCam")) {
+            if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) {
+                return nativeLinkCiCam(ciCamId);
+            }
+        }
+        return INVALID_LTS_ID;
+    }
+
+    /**
      * Disconnects Conditional Access Modules (CAM)
      *
      * <p>The demux will use the output from the frontend as the input after this call.
@@ -775,6 +878,28 @@
     }
 
     /**
+     * Unlink Conditional Access Modules (CAM) Frontend.
+     *
+     * <p>It is used by the client to unlink CI-CAM to a Frontend.
+     *
+     * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause
+     * no-op. Use {@link TunerVersionChecker.getTunerVersion()} to check the version.
+     *
+     * @param ciCamId specify CI-CAM Id to unlink.
+     * @return result status of the operation.
+     */
+    @Result
+    public int unlinkFrontendToCiCam(int ciCamId) {
+        if (TunerVersionChecker.checkHigherOrEqualVersionTo(TunerVersionChecker.TUNER_VERSION_1_1,
+                "unlinkFrontendToCiCam")) {
+            if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) {
+                return nativeUnlinkCiCam(ciCamId);
+            }
+        }
+        return RESULT_UNAVAILABLE;
+    }
+
+    /**
      * Gets the frontend information.
      *
      * @return The frontend information. {@code null} if the operation failed.
@@ -923,6 +1048,20 @@
         }
     }
 
+    private void onModulationReported(int modulation) {
+        if (mScanCallbackExecutor != null && mScanCallback != null) {
+            mScanCallbackExecutor.execute(
+                    () -> mScanCallback.onModulationReported(modulation));
+        }
+    }
+
+    private void onPriorityReported(boolean isHighPriority) {
+        if (mScanCallbackExecutor != null && mScanCallback != null) {
+            mScanCallbackExecutor.execute(
+                    () -> mScanCallback.onPriorityReported(isHighPriority));
+        }
+    }
+
     /**
      * Opens a filter object based on the given types and buffer size.
      *
diff --git a/media/java/android/media/tv/tuner/TunerVersionChecker.java b/media/java/android/media/tv/tuner/TunerVersionChecker.java
new file mode 100644
index 0000000..b40ba1e
--- /dev/null
+++ b/media/java/android/media/tv/tuner/TunerVersionChecker.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.tv.tuner;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Utility class to check the currently running Tuner Hal implementation version.
+ *
+ * APIs that are not supported by the HAL implementation version would be no-op.
+ *
+ * @hide
+ */
+@SystemApi
+public final class TunerVersionChecker {
+    private static final String TAG = "TunerVersionChecker";
+
+    private TunerVersionChecker() {}
+
+    /** @hide */
+    @IntDef(prefix = "TUNER_VERSION_", value = {TUNER_VERSION_UNKNOWN, TUNER_VERSION_1_0,
+                                                TUNER_VERSION_1_1})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TunerVersion {}
+    /**
+     * Unknown Tuner version.
+     */
+    public static final int TUNER_VERSION_UNKNOWN = 0;
+    /**
+     * Tuner version 1.0.
+     */
+    public static final int TUNER_VERSION_1_0 = (1 << 16);
+    /**
+     * Tuner version 1.1.
+     */
+    public static final int TUNER_VERSION_1_1 = ((1 << 16) | 1);
+
+    /**
+     * Get the current running Tuner version.
+     *
+     * @return Tuner version.
+     */
+    @TunerVersion
+    public static int getTunerVersion() {
+        return Tuner.getTunerVersion();
+    }
+
+    /**
+     * Check if the current running Tuner version supports the given version.
+     *
+     * <p>Note that we treat different major versions as unsupported among each other. If any
+     * feature could be supported across major versions, please use
+     * {@link #isHigherOrEqualVersionTo(int)} to check.
+     *
+     * @param version the version to support.
+     *
+     * @return true if the current version is under the same major version as the given version
+     * and has higher or the same minor version as the given version.
+     * @hide
+     */
+    @TestApi
+    public static boolean supportTunerVersion(@TunerVersion int version) {
+        int currentVersion = Tuner.getTunerVersion();
+        return isHigherOrEqualVersionTo(version)
+                && (getMajorVersion(version) == getMajorVersion(currentVersion));
+    }
+
+    /**
+     * Check if the current running Tuner version is higher than or equal to a given version.
+     *
+     * @param version the version to compare.
+     *
+     * @return true if the current version is higher or equal to the support version.
+     * @hide
+     */
+    @TestApi
+    public static boolean isHigherOrEqualVersionTo(@TunerVersion int version) {
+        int currentVersion = Tuner.getTunerVersion();
+        return currentVersion >= version;
+    }
+
+    /**
+     * Get the major version from a version number.
+     *
+     * @param version the version to be checked.
+     *
+     * @return the major version number.
+     * @hide
+     */
+    @TestApi
+    public static int getMajorVersion(@TunerVersion int version) {
+        return ((version & 0xFFFF0000) >>> 16);
+    }
+
+    /**
+     * Get the major version from a version number.
+     *
+     * @param version the version to be checked.
+     *
+     * @return the minor version number.
+     * @hide
+     */
+    @TestApi
+    public static int getMinorVersion(@TunerVersion int version) {
+        return (version & 0xFFFF);
+    }
+
+    /** @hide */
+    public static boolean checkHigherOrEqualVersionTo(
+            @TunerVersion int version, String methodName) {
+        if (!TunerVersionChecker.isHigherOrEqualVersionTo(version)) {
+            Log.e(TAG, "Current Tuner version "
+                    + TunerVersionChecker.getMajorVersion(Tuner.getTunerVersion()) + "."
+                    + TunerVersionChecker.getMinorVersion(Tuner.getTunerVersion())
+                    + " does not support " + methodName + ".");
+            return false;
+        }
+        return true;
+    }
+
+    /** @hide */
+    public static boolean checkSupportVersion(@TunerVersion int version, String methodName) {
+        if (!TunerVersionChecker.supportTunerVersion(version)) {
+            Log.e(TAG, "Current Tuner version "
+                    + TunerVersionChecker.getMajorVersion(Tuner.getTunerVersion()) + "."
+                    + TunerVersionChecker.getMinorVersion(Tuner.getTunerVersion())
+                    + " does not support " + methodName + ".");
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
index bb00bb3..597278b 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
@@ -123,13 +123,14 @@
     /**
      * Attaches a filter to DVR interface for playback.
      *
-     * <p>This method will be deprecated. Now it's a no-op.
-     * <p>Filters opened by {@link Tuner#openFilter} are used for DVR playback.
+     * @deprecated attaching filters is not valid in Dvr Playback use case. This API is a no-op.
+     *             Filters opened by {@link Tuner#openFilter} are used for DVR playback.
      *
      * @param filter the filter to be attached.
      * @return result status of the operation.
      */
     @Result
+    @Deprecated
     public int attachFilter(@NonNull Filter filter) {
         // no-op
         return Tuner.RESULT_UNAVAILABLE;
@@ -138,13 +139,14 @@
     /**
      * Detaches a filter from DVR interface.
      *
-     * <p>This method will be deprecated. Now it's a no-op.
-     * <p>Filters opened by {@link Tuner#openFilter} are used for DVR playback.
+     * @deprecated detaching filters is not valid in Dvr Playback use case. This API is a no-op.
+     *             Filters opened by {@link Tuner#openFilter} are used for DVR playback.
      *
      * @param filter the filter to be detached.
      * @return result status of the operation.
      */
     @Result
+    @Deprecated
     public int detachFilter(@NonNull Filter filter) {
         // no-op
         return Tuner.RESULT_UNAVAILABLE;
diff --git a/media/java/android/media/tv/tuner/filter/AvSettings.java b/media/java/android/media/tv/tuner/filter/AvSettings.java
index e9b3660..5d9d531 100644
--- a/media/java/android/media/tv/tuner/filter/AvSettings.java
+++ b/media/java/android/media/tv/tuner/filter/AvSettings.java
@@ -16,9 +16,15 @@
 
 package android.media.tv.tuner.filter;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
+import android.hardware.tv.tuner.V1_1.Constants;
 import android.media.tv.tuner.TunerUtils;
+import android.media.tv.tuner.TunerVersionChecker;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 /**
  * Filter Settings for a Video and Audio.
@@ -27,15 +33,160 @@
  */
 @SystemApi
 public class AvSettings extends Settings {
-    private final boolean mIsPassthrough;
+    /** @hide */
+    @IntDef(prefix = "VIDEO_STREAM_TYPE_",
+            value = {VIDEO_STREAM_TYPE_UNDEFINED, VIDEO_STREAM_TYPE_RESERVED,
+                    VIDEO_STREAM_TYPE_MPEG1, VIDEO_STREAM_TYPE_MPEG2,
+                    VIDEO_STREAM_TYPE_MPEG4P2, VIDEO_STREAM_TYPE_AVC, VIDEO_STREAM_TYPE_HEVC,
+                    VIDEO_STREAM_TYPE_VC1, VIDEO_STREAM_TYPE_VP8, VIDEO_STREAM_TYPE_VP9,
+                    VIDEO_STREAM_TYPE_AV1, VIDEO_STREAM_TYPE_AVS, VIDEO_STREAM_TYPE_AVS2})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface VideoStreamType {}
 
-    private AvSettings(int mainType, boolean isAudio, boolean isPassthrough) {
+    /*
+     * Undefined Video stream type
+     */
+    public static final int VIDEO_STREAM_TYPE_UNDEFINED = Constants.VideoStreamType.UNDEFINED;
+    /*
+     * ITU-T | ISO/IEC Reserved
+     */
+    public static final int VIDEO_STREAM_TYPE_RESERVED = Constants.VideoStreamType.RESERVED;
+    /*
+     * ISO/IEC 11172
+     */
+    public static final int VIDEO_STREAM_TYPE_MPEG1 = Constants.VideoStreamType.MPEG1;
+    /*
+     * ITU-T Rec.H.262 and ISO/IEC 13818-2
+     */
+    public static final int VIDEO_STREAM_TYPE_MPEG2 = Constants.VideoStreamType.MPEG2;
+    /*
+     * ISO/IEC 14496-2 (MPEG-4 H.263 based video)
+     */
+    public static final int VIDEO_STREAM_TYPE_MPEG4P2 = Constants.VideoStreamType.MPEG4P2;
+    /*
+     * ITU-T Rec.H.264 and ISO/IEC 14496-10
+     */
+    public static final int VIDEO_STREAM_TYPE_AVC = Constants.VideoStreamType.AVC;
+    /*
+     * ITU-T Rec. H.265 and ISO/IEC 23008-2
+     */
+    public static final int VIDEO_STREAM_TYPE_HEVC = Constants.VideoStreamType.HEVC;
+    /*
+     * Microsoft VC.1
+     */
+    public static final int VIDEO_STREAM_TYPE_VC1 = Constants.VideoStreamType.VC1;
+    /*
+     * Google VP8
+     */
+    public static final int VIDEO_STREAM_TYPE_VP8 = Constants.VideoStreamType.VP8;
+    /*
+     * Google VP9
+     */
+    public static final int VIDEO_STREAM_TYPE_VP9 = Constants.VideoStreamType.VP9;
+    /*
+     * AOMedia Video 1
+     */
+    public static final int VIDEO_STREAM_TYPE_AV1 = Constants.VideoStreamType.AV1;
+    /*
+     * Chinese Standard
+     */
+    public static final int VIDEO_STREAM_TYPE_AVS = Constants.VideoStreamType.AVS;
+    /*
+     * New Chinese Standard
+     */
+    public static final int VIDEO_STREAM_TYPE_AVS2 = Constants.VideoStreamType.AVS2;
+
+    /** @hide */
+    @IntDef(prefix = "AUDIO_STREAM_TYPE_",
+            value = {AUDIO_STREAM_TYPE_UNDEFINED, AUDIO_STREAM_TYPE_PCM, AUDIO_STREAM_TYPE_MP3,
+                    AUDIO_STREAM_TYPE_MPEG1, AUDIO_STREAM_TYPE_MPEG2, AUDIO_STREAM_TYPE_MPEGH,
+                    AUDIO_STREAM_TYPE_AAC, AUDIO_STREAM_TYPE_AC3, AUDIO_STREAM_TYPE_EAC3,
+                    AUDIO_STREAM_TYPE_AC4, AUDIO_STREAM_TYPE_DTS, AUDIO_STREAM_TYPE_DTS_HD,
+                    AUDIO_STREAM_TYPE_WMA, AUDIO_STREAM_TYPE_OPUS, AUDIO_STREAM_TYPE_VORBIS,
+                    AUDIO_STREAM_TYPE_DRA})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AudioStreamType {}
+
+    /*
+     * Undefined Audio stream type
+     */
+    public static final int AUDIO_STREAM_TYPE_UNDEFINED = Constants.AudioStreamType.UNDEFINED;
+    /*
+     * Uncompressed Audio
+     */
+    public static final int AUDIO_STREAM_TYPE_PCM = Constants.AudioStreamType.PCM;
+    /*
+     * MPEG Audio Layer III versions
+     */
+    public static final int AUDIO_STREAM_TYPE_MP3 = Constants.AudioStreamType.MP3;
+    /*
+     * ISO/IEC 11172 Audio
+     */
+    public static final int AUDIO_STREAM_TYPE_MPEG1 = Constants.AudioStreamType.MPEG1;
+    /*
+     * ISO/IEC 13818-3
+     */
+    public static final int AUDIO_STREAM_TYPE_MPEG2 = Constants.AudioStreamType.MPEG2;
+    /*
+     * ISO/IEC 23008-3 (MPEG-H Part 3)
+     */
+    public static final int AUDIO_STREAM_TYPE_MPEGH = Constants.AudioStreamType.MPEGH;
+    /*
+     * ISO/IEC 14496-3
+     */
+    public static final int AUDIO_STREAM_TYPE_AAC = Constants.AudioStreamType.AAC;
+    /*
+     * Dolby Digital
+     */
+    public static final int AUDIO_STREAM_TYPE_AC3 = Constants.AudioStreamType.AC3;
+    /*
+     * Dolby Digital Plus
+     */
+    public static final int AUDIO_STREAM_TYPE_EAC3 = Constants.AudioStreamType.EAC3;
+    /*
+     * Dolby AC-4
+     */
+    public static final int AUDIO_STREAM_TYPE_AC4 = Constants.AudioStreamType.AC4;
+    /*
+     * Basic DTS
+     */
+    public static final int AUDIO_STREAM_TYPE_DTS = Constants.AudioStreamType.DTS;
+    /*
+     * High Resolution DTS
+     */
+    public static final int AUDIO_STREAM_TYPE_DTS_HD = Constants.AudioStreamType.DTS_HD;
+    /*
+     * Windows Media Audio
+     */
+    public static final int AUDIO_STREAM_TYPE_WMA = Constants.AudioStreamType.WMA;
+    /*
+     * Opus Interactive Audio Codec
+     */
+    public static final int AUDIO_STREAM_TYPE_OPUS = Constants.AudioStreamType.OPUS;
+    /*
+     * VORBIS Interactive Audio Codec
+     */
+    public static final int AUDIO_STREAM_TYPE_VORBIS = Constants.AudioStreamType.VORBIS;
+    /*
+     * SJ/T 11368-2006
+     */
+    public static final int AUDIO_STREAM_TYPE_DRA = Constants.AudioStreamType.DRA;
+
+
+    private final boolean mIsPassthrough;
+    private int mAudioStreamType = AUDIO_STREAM_TYPE_UNDEFINED;
+    private int mVideoStreamType = VIDEO_STREAM_TYPE_UNDEFINED;
+
+    private AvSettings(int mainType, boolean isAudio, boolean isPassthrough,
+            int audioStreamType, int videoStreamType) {
         super(TunerUtils.getFilterSubtype(
                 mainType,
                 isAudio
                         ? Filter.SUBTYPE_AUDIO
                         : Filter.SUBTYPE_VIDEO));
         mIsPassthrough = isPassthrough;
+        mAudioStreamType = audioStreamType;
+        mVideoStreamType = videoStreamType;
     }
 
     /**
@@ -46,6 +197,20 @@
     }
 
     /**
+     * Get the Audio Stream Type.
+     */
+    public int getAudioStreamType() {
+        return mAudioStreamType;
+    }
+
+    /**
+     * Get the Video Stream Type.
+     */
+    public int getVideoStreamType() {
+        return mVideoStreamType;
+    }
+
+    /**
      * Creates a builder for {@link AvSettings}.
      *
      * @param mainType the filter main type.
@@ -63,6 +228,8 @@
         private final int mMainType;
         private final boolean mIsAudio;
         private boolean mIsPassthrough;
+        private int mAudioStreamType = AUDIO_STREAM_TYPE_UNDEFINED;
+        private int mVideoStreamType = VIDEO_STREAM_TYPE_UNDEFINED;
 
         private Builder(int mainType, boolean isAudio) {
             mMainType = mainType;
@@ -79,11 +246,48 @@
         }
 
         /**
+         * Sets the Audio Stream Type.
+         *
+         * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause
+         * no-op. Use {@link TunerVersionChecker.getTunerVersion()} to check the version.
+         *
+         * @param audioStreamType the {@link AudioStreamType} to set.
+         */
+        @NonNull
+        public Builder setAudioStreamType(@AudioStreamType int audioStreamType) {
+            if (TunerVersionChecker.checkHigherOrEqualVersionTo(
+                    TunerVersionChecker.TUNER_VERSION_1_1, "setAudioStreamType") && mIsAudio) {
+                mAudioStreamType = audioStreamType;
+                mVideoStreamType = VIDEO_STREAM_TYPE_UNDEFINED;
+            }
+            return this;
+        }
+
+        /**
+         * Sets the Video Stream Type.
+         *
+         * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause
+         * no-op. Use {@link TunerVersionChecker.getTunerVersion()} to check the version.
+         *
+         * @param videoStreamType the {@link VideoStreamType} to set.
+         */
+        @NonNull
+        public Builder setVideoStreamType(@VideoStreamType int videoStreamType) {
+            if (TunerVersionChecker.checkHigherOrEqualVersionTo(
+                    TunerVersionChecker.TUNER_VERSION_1_1, "setVideoStreamType") && !mIsAudio) {
+                mVideoStreamType = videoStreamType;
+                mAudioStreamType = AUDIO_STREAM_TYPE_UNDEFINED;
+            }
+            return this;
+        }
+
+        /**
          * Builds a {@link AvSettings} object.
          */
         @NonNull
         public AvSettings build() {
-            return new AvSettings(mMainType, mIsAudio, mIsPassthrough);
+            return new AvSettings(mMainType, mIsAudio, mIsPassthrough,
+                    mAudioStreamType, mVideoStreamType);
         }
     }
 }
diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java
index f0015b7..2f2d8f7 100644
--- a/media/java/android/media/tv/tuner/filter/Filter.java
+++ b/media/java/android/media/tv/tuner/filter/Filter.java
@@ -185,7 +185,7 @@
     private long mNativeContext;
     private FilterCallback mCallback;
     private Executor mExecutor;
-    private final int mId;
+    private final long mId;
     private int mMainType;
     private int mSubtype;
     private Filter mSource;
@@ -196,6 +196,7 @@
     private native int nativeConfigureFilter(
             int type, int subType, FilterConfiguration settings);
     private native int nativeGetId();
+    private native long nativeGetId64Bit();
     private native int nativeSetDataSource(Filter source);
     private native int nativeStartFilter();
     private native int nativeStopFilter();
@@ -204,7 +205,7 @@
     private native int nativeClose();
 
     // Called by JNI
-    private Filter(int id) {
+    private Filter(long id) {
         mId = id;
     }
 
@@ -269,6 +270,16 @@
     }
 
     /**
+     * Gets the 64-bit filter Id.
+     */
+    public long getId64Bit() {
+        synchronized (mLock) {
+            TunerUtils.checkResourceState(TAG, mIsClosed);
+            return nativeGetId64Bit();
+        }
+    }
+
+    /**
      * Sets the filter's data source.
      *
      * A filter uses demux as data source by default. If the data was packetized
diff --git a/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java
index f54b686..62d55f5 100644
--- a/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java
@@ -16,10 +16,12 @@
 
 package android.media.tv.tuner.filter;
 
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.Size;
 import android.annotation.SystemApi;
+import android.media.tv.tuner.TunerVersionChecker;
 
 /**
  * Filter configuration for a IP filter.
@@ -28,20 +30,28 @@
  */
 @SystemApi
 public final class IpFilterConfiguration extends FilterConfiguration {
+    /**
+     * Undefined filter type.
+     */
+    public static final int INVALID_IP_FILTER_CONTEXT_ID =
+            android.hardware.tv.tuner.V1_1.Constants.Constant.INVALID_IP_FILTER_CONTEXT_ID;
+
     private final byte[] mSrcIpAddress;
     private final byte[] mDstIpAddress;
     private final int mSrcPort;
     private final int mDstPort;
     private final boolean mPassthrough;
+    private final int mIpFilterContextId;
 
     private IpFilterConfiguration(Settings settings, byte[] srcAddr, byte[] dstAddr, int srcPort,
-            int dstPort, boolean passthrough) {
+            int dstPort, boolean passthrough, int ipCid) {
         super(settings);
         mSrcIpAddress = srcAddr;
         mDstIpAddress = dstAddr;
         mSrcPort = srcPort;
         mDstPort = dstPort;
         mPassthrough = passthrough;
+        mIpFilterContextId = ipCid;
     }
 
     @Override
@@ -86,6 +96,16 @@
     public boolean isPassthrough() {
         return mPassthrough;
     }
+    /**
+     * Gets the ip filter context id. Default value is {@link #INVALID_IP_FILTER_CONTEXT_ID}.
+     *
+     * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would return
+     * default value. Use {@link TunerVersionChecker.getTunerVersion()} to check the version.
+     */
+    @IntRange(from = 0, to = 0xefff)
+    public int getIpFilterContextId() {
+        return mIpFilterContextId;
+    }
 
     /**
      * Creates a builder for {@link IpFilterConfiguration}.
@@ -105,6 +125,7 @@
         private int mDstPort = 0;
         private boolean mPassthrough = false;
         private Settings mSettings;
+        private int mIpCid = INVALID_IP_FILTER_CONTEXT_ID;
 
         private Builder() {
         }
@@ -170,6 +191,21 @@
         }
 
         /**
+         * Sets the ip filter context id. Default value is {@link #INVALID_IP_FILTER_CONTEXT_ID}.
+         *
+         * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause
+         * no-op. Use {@link TunerVersionChecker.getTunerVersion()} to check the version.
+         */
+        @NonNull
+        public Builder setIpFilterContextId(int ipContextId) {
+            if (TunerVersionChecker.checkHigherOrEqualVersionTo(
+                        TunerVersionChecker.TUNER_VERSION_1_1, "setIpFilterContextId")) {
+                mIpCid = ipContextId;
+            }
+            return this;
+        }
+
+        /**
          * Builds a {@link IpFilterConfiguration} object.
          */
         @NonNull
@@ -180,8 +216,8 @@
                     "The lengths of src and dst IP address must be 4 or 16 and must be the same."
                             + "srcLength=" + ipAddrLength + ", dstLength=" + mDstIpAddress.length);
             }
-            return new IpFilterConfiguration(
-                    mSettings, mSrcIpAddress, mDstIpAddress, mSrcPort, mDstPort, mPassthrough);
+            return new IpFilterConfiguration(mSettings, mSrcIpAddress, mDstIpAddress, mSrcPort,
+                    mDstPort, mPassthrough, mIpCid);
         }
     }
 }
diff --git a/media/java/android/media/tv/tuner/filter/MediaEvent.java b/media/java/android/media/tv/tuner/filter/MediaEvent.java
index 57a04fd..91be5c3 100644
--- a/media/java/android/media/tv/tuner/filter/MediaEvent.java
+++ b/media/java/android/media/tv/tuner/filter/MediaEvent.java
@@ -187,7 +187,6 @@
 
     /**
      * Releases the MediaEvent object.
-     * @hide
      */
     public void release() {
         synchronized (mLock) {
diff --git a/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java b/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java
index 466fa3e..7060bd72 100644
--- a/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java
+++ b/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java
@@ -29,11 +29,15 @@
 public class MmtpRecordEvent extends FilterEvent {
     private final int mScHevcIndexMask;
     private final long mDataLength;
+    private final int mMpuSequenceNumber;
+    private final long mPts;
 
     // This constructor is used by JNI code only
-    private MmtpRecordEvent(int scHevcIndexMask, long dataLength) {
+    private MmtpRecordEvent(int scHevcIndexMask, long dataLength, int mpuSequenceNumber, long pts) {
         mScHevcIndexMask = scHevcIndexMask;
         mDataLength = dataLength;
+        mMpuSequenceNumber = mpuSequenceNumber;
+        mPts = pts;
     }
 
     /**
@@ -51,4 +55,20 @@
     public long getDataLength() {
         return mDataLength;
     }
+
+    /**
+     * Get the MPU sequence number of the filtered data.
+     */
+    public int getMpuSequenceNumber() {
+        return mMpuSequenceNumber;
+    }
+
+    /**
+     * Get the Presentation Time Stamp(PTS) for the audio or video frame. It is based on 90KHz
+     * and has the same format as the PTS in ISO/IEC 13818-1. It is used only for the SC and
+     * the SC_HEVC.
+     */
+    public long getPts() {
+        return mPts;
+    }
 }
diff --git a/media/java/android/media/tv/tuner/filter/TsRecordEvent.java b/media/java/android/media/tv/tuner/filter/TsRecordEvent.java
index 7a14bb8..258e2f2 100644
--- a/media/java/android/media/tv/tuner/filter/TsRecordEvent.java
+++ b/media/java/android/media/tv/tuner/filter/TsRecordEvent.java
@@ -32,13 +32,15 @@
     private final int mTsIndexMask;
     private final int mScIndexMask;
     private final long mDataLength;
+    private final long mPts;
 
     // This constructor is used by JNI code only
-    private TsRecordEvent(int pid, int tsIndexMask, int scIndexMask, long dataLength) {
+    private TsRecordEvent(int pid, int tsIndexMask, int scIndexMask, long dataLength, long pts) {
         mPid = pid;
         mTsIndexMask = tsIndexMask;
         mScIndexMask = scIndexMask;
         mDataLength = dataLength;
+        mPts = pts;
     }
 
     /**
@@ -72,4 +74,13 @@
     public long getDataLength() {
         return mDataLength;
     }
+
+    /**
+     * Gets the Presentation Time Stamp(PTS) for the audio or video frame. It is based on 90KHz
+     * and has the same format as the PTS in ISO/IEC 13818-1. It is used only for the SC and
+     * the SC_HEVC.
+     */
+    public long getPts() {
+        return mPts;
+    }
 }
diff --git a/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java
index 1d36da3..c6a5bb0 100644
--- a/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java
@@ -21,6 +21,7 @@
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.hardware.tv.tuner.V1_0.Constants;
+import android.media.tv.tuner.TunerVersionChecker;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -164,9 +165,32 @@
      */
     public static final int SIF_L_PRIME = Constants.FrontendAnalogSifStandard.L_PRIME;
 
+    /** @hide */
+    @IntDef(prefix = "AFT_FLAG_",
+            value = {AFT_FLAG_UNDEFINED, AFT_FLAG_TRUE, AFT_FLAG_FALSE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AftFlag {}
+
+    /**
+     * Aft flag is not defined.
+     */
+    public static final int AFT_FLAG_UNDEFINED =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendAnalogAftFlag.UNDEFINED;
+    /**
+     * Aft flag is set true.
+     */
+    public static final int AFT_FLAG_TRUE =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendAnalogAftFlag.AFT_TRUE;
+    /**
+     * Aft flag is not set.
+     */
+    public static final int AFT_FLAG_FALSE =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendAnalogAftFlag.AFT_FALSE;
+
 
     private final int mSignalType;
     private final int mSifStandard;
+    private final int mAftFlag;
 
     @Override
     public int getType() {
@@ -191,6 +215,14 @@
     }
 
     /**
+     * Gets AFT flag.
+     */
+    @AftFlag
+    public int getAftFlag() {
+        return mAftFlag;
+    }
+
+    /**
      * Creates a builder for {@link AnalogFrontendSettings}.
      */
     @NonNull
@@ -198,10 +230,11 @@
         return new Builder();
     }
 
-    private AnalogFrontendSettings(int frequency, int signalType, int sifStandard) {
+    private AnalogFrontendSettings(int frequency, int signalType, int sifStandard, int aftFlag) {
         super(frequency);
         mSignalType = signalType;
         mSifStandard = sifStandard;
+        mAftFlag = aftFlag;
     }
 
     /**
@@ -211,6 +244,7 @@
         private int mFrequency = 0;
         private int mSignalType = SIGNAL_TYPE_UNDEFINED;
         private int mSifStandard = SIF_UNDEFINED;
+        private int mAftFlag = AFT_FLAG_UNDEFINED;
 
         private Builder() {}
 
@@ -227,6 +261,24 @@
         }
 
         /**
+         * Set Aft flag.
+         *
+         * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause
+         * no-op. Use {@link TunerVersionChecker.getTunerVersion()} to check the version.
+         *
+         * @param aftFlag the value to set the aft flag. The default value is
+         * {@link #AFT_FLAG_UNDEFINED}.
+         */
+        @NonNull
+        public Builder setAftFlag(@AftFlag int aftFlag) {
+            if (TunerVersionChecker.checkHigherOrEqualVersionTo(
+                    TunerVersionChecker.TUNER_VERSION_1_1, "setAftFlag")) {
+                mAftFlag = aftFlag;
+            }
+            return this;
+        }
+
+        /**
          * Sets analog signal type.
          *
          * <p>Default value is {@link #SIGNAL_TYPE_UNDEFINED}.
@@ -253,7 +305,7 @@
          */
         @NonNull
         public AnalogFrontendSettings build() {
-            return new AnalogFrontendSettings(mFrequency, mSignalType, mSifStandard);
+            return new AnalogFrontendSettings(mFrequency, mSignalType, mSifStandard, mAftFlag);
         }
     }
 }
diff --git a/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java
index f9eabc5..ed1ce2d 100644
--- a/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java
@@ -377,8 +377,8 @@
          */
         @NonNull
         public Atsc3FrontendSettings build() {
-            return new Atsc3FrontendSettings(
-                mFrequency, mBandwidth, mDemodOutputFormat, mPlpSettings);
+            return new Atsc3FrontendSettings(mFrequency, mBandwidth, mDemodOutputFormat,
+                    mPlpSettings);
         }
     }
 
diff --git a/media/java/android/media/tv/tuner/frontend/DtmbFrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/DtmbFrontendCapabilities.java
new file mode 100644
index 0000000..9fc3a23
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/DtmbFrontendCapabilities.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.tv.tuner.frontend;
+
+import android.annotation.SystemApi;
+
+/**
+ * DTMB Capabilities.
+ *
+ * <p>DTMB Frontend is only supported in Tuner HAL 1.1 or higher.
+ * @hide
+ */
+@SystemApi
+public class DtmbFrontendCapabilities extends FrontendCapabilities {
+    private final int mModulationCap;
+    private final int mTransmissionModeCap;
+    private final int mGuardIntervalCap;
+    private final int mTimeInterleaveModeCap;
+    private final int mCodeRateCap;
+    private final int mBandwidthCap;
+
+    private DtmbFrontendCapabilities(int modulationCap, int transmissionModeCap,
+            int guardIntervalCap, int timeInterleaveModeCap, int codeRateCap, int bandwidthCap) {
+        mModulationCap = modulationCap;
+        mTransmissionModeCap = transmissionModeCap;
+        mGuardIntervalCap = guardIntervalCap;
+        mTimeInterleaveModeCap = timeInterleaveModeCap;
+        mCodeRateCap = codeRateCap;
+        mBandwidthCap = bandwidthCap;
+    }
+
+    /**
+     * Gets modulation capability.
+     *
+     * @return full modulation capabilies. If the caps bitwise AND with any value from
+     * bit masks {@link DtmbFrontendSettings.Modulation} is true, then that modulation is supported.
+     */
+    @DtmbFrontendSettings.Modulation
+    public int getModulationCapability() {
+        return mModulationCap;
+    }
+
+    /**
+     * Gets Transmission Mode capability.
+     *
+     * @return full Transmission Mode capabilies. If the caps bitwise AND with any value from
+     * bit masks {@link DtmbFrontendSettings.TransmissionMode} is true, then that transmission mode
+     * is supported.
+     */
+    @DtmbFrontendSettings.TransmissionMode
+    public int getTransmissionModeCapability() {
+        return mTransmissionModeCap;
+    }
+
+    /**
+     * Gets Guard Interval capability.
+     *
+     * @return full Guard Interval capabilies. If the caps bitwise AND with any value from
+     * bit masks {@link DtmbFrontendSettings.GuardInterval} is true, then that Guard Interval is
+     * supported.
+     */
+    @DtmbFrontendSettings.GuardInterval
+    public int getGuardIntervalCapability() {
+        return mGuardIntervalCap;
+    }
+
+    /**
+     * Gets Time Interleave Mode capability.
+     *
+     * @return full Time Interleave Mode capabilies. If the caps bitwise AND with any value from
+     * bit masks {@link DtmbFrontendSettings.TimeInterleaveMode} is true, then that Time Interleave
+     * Mode is supported.
+     */
+    @DtmbFrontendSettings.TimeInterleaveMode
+    public int getTimeInterleaveModeCapability() {
+        return mTimeInterleaveModeCap;
+    }
+
+    /**
+     * Gets Code Rate capability.
+     *
+     * @return full Code Rate capabilies. If the caps bitwise AND with any value from
+     * bit masks {@link DtmbFrontendSettings.CodeRate} is true, then that Code Rate is supported.
+     */
+    @DtmbFrontendSettings.CodeRate
+    public int getCodeRateCapability() {
+        return mCodeRateCap;
+    }
+
+    /**
+     * Gets Bandwidth capability.
+     *
+     * @return full Bandwidth capabilies. If the caps bitwise AND with any value from
+     * bit masks {@link DtmbFrontendSettings.Bandwidth} is true, then that Bandwidth is supported.
+     */
+    @DtmbFrontendSettings.Bandwidth
+    public int getBandwidthCapability() {
+        return mBandwidthCap;
+    }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java
new file mode 100644
index 0000000..2c3fe6a
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java
@@ -0,0 +1,441 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.tv.tuner.frontend;
+
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Frontend settings for DTMB.
+ *
+ * <p>DTMB Frontend is only supported in Tuner HAL 1.1 or higher. Use {@link
+ * android.media.tv.tuner.TunerVersionChecker.getTunerVersion()} to get the version information.
+ *
+ * @hide
+ */
+@SystemApi
+public class DtmbFrontendSettings extends FrontendSettings {
+
+    /** @hide */
+    @IntDef(flag = true,
+            prefix = "BANDWIDTH_",
+            value = {BANDWIDTH_UNDEFINED, BANDWIDTH_AUTO, BANDWIDTH_6MHZ, BANDWIDTH_8MHZ})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Bandwidth {}
+
+    /**
+     * Bandwidth not defined.
+     */
+    public static final int BANDWIDTH_UNDEFINED =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbBandwidth.UNDEFINED;
+    /**
+     * Hardware is able to detect and set bandwidth automatically
+     */
+    public static final int BANDWIDTH_AUTO =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbBandwidth.AUTO;
+    /**
+     * 6 MHz bandwidth.
+     */
+    public static final int BANDWIDTH_6MHZ =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbBandwidth.BANDWIDTH_6MHZ;
+    /**
+     * 8 MHz bandwidth.
+     */
+    public static final int BANDWIDTH_8MHZ =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbBandwidth.BANDWIDTH_8MHZ;
+
+
+    /** @hide */
+    @IntDef(flag = true,
+            prefix = "TIME_INTERLEAVE_MODE_",
+            value = {TIME_INTERLEAVE_MODE_UNDEFINED, TIME_INTERLEAVE_MODE_AUTO,
+                    TIME_INTERLEAVE_MODE_TIMER_INT_240, TIME_INTERLEAVE_MODE_TIMER_INT_720})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TimeInterleaveMode {}
+
+    /**
+     * Time Interleave Mode undefined.
+     */
+    public static final int TIME_INTERLEAVE_MODE_UNDEFINED =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbTimeInterleaveMode.UNDEFINED;
+    /**
+     * Hardware is able to detect and set time interleave mode automatically
+     */
+    public static final int TIME_INTERLEAVE_MODE_AUTO =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbTimeInterleaveMode.AUTO;
+    /**
+     * Time Interleave Mode timer int 240.
+     */
+    public static final int TIME_INTERLEAVE_MODE_TIMER_INT_240 =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbTimeInterleaveMode.TIMER_INT_240;
+    /**
+     * Time Interleave Mode timer int 720.
+     */
+    public static final int TIME_INTERLEAVE_MODE_TIMER_INT_720 =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbTimeInterleaveMode.TIMER_INT_720;
+
+
+    /** @hide */
+    @IntDef(flag = true,
+            prefix = "GUARD_INTERVAL_",
+            value = {GUARD_INTERVAL_UNDEFINED, GUARD_INTERVAL_AUTO,
+            GUARD_INTERVAL_PN_420_VARIOUS, GUARD_INTERVAL_PN_595_CONST,
+            GUARD_INTERVAL_PN_945_VARIOUS, GUARD_INTERVAL_PN_420_CONST,
+            GUARD_INTERVAL_PN_945_CONST, GUARD_INTERVAL_PN_RESERVED})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface GuardInterval {}
+
+    /**
+     * Guard Interval undefined.
+     */
+    public static final int GUARD_INTERVAL_UNDEFINED =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbGuardInterval.UNDEFINED;
+    /**
+     * Hardware is able to detect and set Guard Interval automatically.
+     */
+    public static final int GUARD_INTERVAL_AUTO =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbGuardInterval.AUTO;
+    /**
+     * PN_420_VARIOUS Guard Interval.
+     */
+    public static final int GUARD_INTERVAL_PN_420_VARIOUS =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbGuardInterval.PN_420_VARIOUS;
+    /**
+     * PN_595_CONST Guard Interval.
+     */
+    public static final int GUARD_INTERVAL_PN_595_CONST =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbGuardInterval.PN_595_CONST;
+    /**
+     * PN_945_VARIOUS Guard Interval.
+     */
+    public static final int GUARD_INTERVAL_PN_945_VARIOUS =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbGuardInterval.PN_945_VARIOUS;
+    /**
+     * PN_420_CONST Guard Interval.
+     */
+    public static final int GUARD_INTERVAL_PN_420_CONST =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbGuardInterval.PN_420_CONST;
+    /**
+     * PN_945_CONST Guard Interval.
+     */
+    public static final int GUARD_INTERVAL_PN_945_CONST =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbGuardInterval.PN_945_CONST;
+    /**
+     * PN_RESERVED Guard Interval.
+     */
+    public static final int GUARD_INTERVAL_PN_RESERVED =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbGuardInterval.PN_RESERVED;
+
+
+    /** @hide */
+    @IntDef(flag = true,
+            prefix = "MODULATION_",
+            value = {MODULATION_CONSTELLATION_UNDEFINED, MODULATION_CONSTELLATION_AUTO,
+                    MODULATION_CONSTELLATION_4QAM, MODULATION_CONSTELLATION_4QAM_NR,
+                    MODULATION_CONSTELLATION_16QAM, MODULATION_CONSTELLATION_32QAM,
+                    MODULATION_CONSTELLATION_64QAM})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Modulation {}
+
+    /**
+     * Constellation not defined.
+     */
+    public static final int MODULATION_CONSTELLATION_UNDEFINED =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbModulation.UNDEFINED;
+    /**
+     * Hardware is able to detect and set Constellation automatically.
+     */
+    public static final int MODULATION_CONSTELLATION_AUTO =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbModulation.AUTO;
+    /**
+     * 4QAM Constellation.
+     */
+    public static final int MODULATION_CONSTELLATION_4QAM =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbModulation.CONSTELLATION_4QAM;
+    /**
+     * 4QAM_NR Constellation.
+     */
+    public static final int MODULATION_CONSTELLATION_4QAM_NR =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbModulation.CONSTELLATION_4QAM_NR;
+    /**
+     * 16QAM Constellation.
+     */
+    public static final int MODULATION_CONSTELLATION_16QAM =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbModulation.CONSTELLATION_16QAM;
+    /**
+     * 32QAM Constellation.
+     */
+    public static final int MODULATION_CONSTELLATION_32QAM =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbModulation.CONSTELLATION_32QAM;
+    /**
+     * 64QAM Constellation.
+     */
+    public static final int MODULATION_CONSTELLATION_64QAM =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbModulation.CONSTELLATION_64QAM;
+
+    /** @hide */
+    @IntDef(flag = true,
+            prefix = "CODERATE_",
+            value = {CODERATE_UNDEFINED, CODERATE_AUTO, CODERATE_2_5, CODERATE_3_5, CODERATE_4_5})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CodeRate {}
+
+    /**
+     * Code rate undefined.
+     */
+    public static final int CODERATE_UNDEFINED =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbCodeRate.UNDEFINED;
+    /**
+     * Hardware is able to detect and set code rate automatically.
+     */
+    public static final int CODERATE_AUTO =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbCodeRate.AUTO;
+    /**
+     * 2/5 code rate.
+     */
+    public static final int CODERATE_2_5 =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbCodeRate.CODERATE_2_5;
+    /**
+     * 3/5 code rate.
+     */
+    public static final int CODERATE_3_5 =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbCodeRate.CODERATE_3_5;
+    /**
+     * 4/5 code rate.
+     */
+    public static final int CODERATE_4_5 =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbCodeRate.CODERATE_4_5;
+
+    /** @hide */
+    @IntDef(flag = true,
+            prefix = "TRANSMISSION_MODE_",
+            value = {TRANSMISSION_MODE_UNDEFINED, TRANSMISSION_MODE_AUTO,
+                    TRANSMISSION_MODE_C1, TRANSMISSION_MODE_C3780})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TransmissionMode {}
+
+    /**
+     * Transmission Mode undefined.
+     */
+    public static final int TRANSMISSION_MODE_UNDEFINED =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbTransmissionMode.UNDEFINED;
+    /**
+     * Hardware is able to detect and set Transmission Mode automatically
+     */
+    public static final int TRANSMISSION_MODE_AUTO =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbTransmissionMode.AUTO;
+    /**
+     * C1 Transmission Mode.
+     */
+    public static final int TRANSMISSION_MODE_C1 =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbTransmissionMode.C1;
+    /**
+     * C3780 Transmission Mode.
+     */
+    public static final int TRANSMISSION_MODE_C3780 =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbTransmissionMode.C3780;
+
+
+    private final int mModulation;
+    private final int mCodeRate;
+    private final int mTransmissionMode;
+    private final int mBandwidth;
+    private final int mGuardInterval;
+    private final int mTimeInterleaveMode;
+
+    private DtmbFrontendSettings(int frequency, int modulation, int codeRate, int transmissionMode,
+            int guardInterval, int timeInterleaveMode, int bandwidth) {
+        super(frequency);
+        mModulation = modulation;
+        mCodeRate = codeRate;
+        mTransmissionMode = transmissionMode;
+        mGuardInterval = guardInterval;
+        mTimeInterleaveMode = timeInterleaveMode;
+        mBandwidth = bandwidth;
+    }
+
+    /**
+     * Gets Modulation.
+     */
+    @Modulation
+    public int getModulation() {
+        return mModulation;
+    }
+
+    /**
+     * Gets Code Rate.
+     */
+    @Modulation
+    public int getCodeRate() {
+        return mCodeRate;
+    }
+
+    /**
+     * Gets Transmission Mode.
+     */
+    @Modulation
+    public int getTransmissionMode() {
+        return mTransmissionMode;
+    }
+
+    /**
+     * Gets Bandwidth.
+     */
+    @Modulation
+    public int getBandwidth() {
+        return mBandwidth;
+    }
+
+    /**
+     * Gets Time Interleave Mode.
+     */
+    @Modulation
+    public int getTimeInterleaveMode() {
+        return mTimeInterleaveMode;
+    }
+
+
+    /**
+     * Gets Guard Interval.
+     */
+    @Modulation
+    public int getGuardInterval() {
+        return mGuardInterval;
+    }
+
+    /**
+     * Creates a builder for {@link AtscFrontendSettings}.
+     */
+    @NonNull
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Builder for {@link AtscFrontendSettings}.
+     */
+    public static final class Builder {
+        private int mFrequency = 0;
+        private int mModulation = MODULATION_CONSTELLATION_UNDEFINED;
+        private int mCodeRate = CODERATE_UNDEFINED;
+        private int mTransmissionMode = TRANSMISSION_MODE_UNDEFINED;
+        private int mBandwidth = BANDWIDTH_UNDEFINED;
+        private int mTimeInterleaveMode = TIME_INTERLEAVE_MODE_UNDEFINED;
+        private int mGuardInterval = GUARD_INTERVAL_UNDEFINED;
+
+        private Builder() {
+        }
+
+        /**
+         * Sets frequency in Hz.
+         *
+         * <p>Default value is 0.
+         */
+        @NonNull
+        @IntRange(from = 1)
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public Builder setFrequency(int frequency) {
+            mFrequency = frequency;
+            return this;
+        }
+
+        /**
+         * Sets Modulation.
+         *
+         * <p>Default value is {@link #MODULATION_CONSTELLATION_UNDEFINED}.
+         */
+        @NonNull
+        public Builder setModulation(@Modulation int modulation) {
+            mModulation = modulation;
+            return this;
+        }
+
+        /**
+         * Sets Code Rate.
+         *
+         * <p>Default value is {@link #CODERATE_UNDEFINED}.
+         */
+        @NonNull
+        public Builder setCodeRate(@CodeRate int codeRate) {
+            mCodeRate = codeRate;
+            return this;
+        }
+
+        /**
+         * Sets Bandwidth.
+         *
+         * <p>Default value is {@link #BANDWIDTH_UNDEFINED}.
+         */
+        @NonNull
+        public Builder setBandwidth(@Bandwidth int bandwidth) {
+            mBandwidth = bandwidth;
+            return this;
+        }
+
+        /**
+         * Sets Time Interleave Mode.
+         *
+         * <p>Default value is {@link #TIME_INTERLEAVE_MODE_UNDEFINED}.
+         */
+        @NonNull
+        public Builder setTimeInterleaveMode(@TimeInterleaveMode int timeInterleaveMode) {
+            mTimeInterleaveMode = timeInterleaveMode;
+            return this;
+        }
+
+        /**
+         * Sets Guard Interval.
+         *
+         * <p>Default value is {@link #GUARD_INTERVAL_UNDEFINED}.
+         */
+        @NonNull
+        public Builder setGuardInterval(@GuardInterval int guardInterval) {
+            mGuardInterval = guardInterval;
+            return this;
+        }
+        /**
+         * Sets Transmission Mode.
+         *
+         * <p>Default value is {@link #TRANSMISSION_MODE_UNDEFINED}.
+         */
+        @NonNull
+        public Builder setTransmissionMode(@TransmissionMode int transmissionMode) {
+            mTransmissionMode = transmissionMode;
+            return this;
+        }
+
+        /**
+         * Builds a {@link DtmbFrontendSettings} object.
+         */
+        @NonNull
+        public DtmbFrontendSettings build() {
+            return new DtmbFrontendSettings(mFrequency, mModulation, mCodeRate,
+                    mTransmissionMode, mGuardInterval, mTimeInterleaveMode, mBandwidth);
+        }
+    }
+
+    @Override
+    public int getType() {
+        return FrontendSettings.TYPE_DTMB;
+    }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
index 271e91e..e6968bb 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
@@ -21,6 +21,8 @@
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.hardware.tv.tuner.V1_0.Constants;
+import android.media.tv.tuner.TunerVersionChecker;
+import android.media.tv.tuner.frontend.FrontendSettings.FrontendSpectralInversion;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -117,7 +119,11 @@
     public static final int ANNEX_C = Constants.FrontendDvbcAnnex.C;
 
 
-    /** @hide */
+    /**
+     * @deprecated Use the {@link FrontendSettings#FrontendSpectralInversion} instead.
+     * @hide
+     */
+    @Deprecated
     @IntDef(prefix = "SPECTRAL_INVERSION_",
             value = {SPECTRAL_INVERSION_UNDEFINED, SPECTRAL_INVERSION_NORMAL,
                     SPECTRAL_INVERSION_INVERTED})
@@ -126,20 +132,97 @@
 
     /**
      * Spectral Inversion Type undefined.
+     *
+     * @deprecated Use the {@link FrontendSettings#FRONTEND_SPECTRAL_INVERSION_UNDEFINED} instead.
      */
+    @Deprecated
     public static final int SPECTRAL_INVERSION_UNDEFINED =
             Constants.FrontendDvbcSpectralInversion.UNDEFINED;
     /**
      * Normal Spectral Inversion.
+     *
+     * @deprecated Use the {@link FrontendSettings#FRONTEND_SPECTRAL_INVERSION_NORMAL} instead.
      */
+    @Deprecated
     public static final int SPECTRAL_INVERSION_NORMAL =
             Constants.FrontendDvbcSpectralInversion.NORMAL;
     /**
      * Inverted Spectral Inversion.
+     *
+     * @deprecated Use the {@link FrontendSettings#FRONTEND_SPECTRAL_INVERSION_INVERTED} instead.
      */
+    @Deprecated
     public static final int SPECTRAL_INVERSION_INVERTED =
             Constants.FrontendDvbcSpectralInversion.INVERTED;
 
+    /** @hide */
+    @IntDef(flag = true,
+            prefix = "TIME_INTERLEAVE_MODE_",
+            value = {TIME_INTERLEAVE_MODE_UNDEFINED, TIME_INTERLEAVE_MODE_AUTO,
+                    TIME_INTERLEAVE_MODE_128_1_0, TIME_INTERLEAVE_MODE_128_1_1,
+                    TIME_INTERLEAVE_MODE_64_2, TIME_INTERLEAVE_MODE_32_4,
+                    TIME_INTERLEAVE_MODE_16_8, TIME_INTERLEAVE_MODE_8_16,
+                    TIME_INTERLEAVE_MODE_128_2, TIME_INTERLEAVE_MODE_128_3,
+                    TIME_INTERLEAVE_MODE_128_4})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TimeInterleaveMode {}
+
+    /**
+     * Time interleave mode undefined.
+     */
+    public static final int TIME_INTERLEAVE_MODE_UNDEFINED =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendCableTimeInterleaveMode.UNDEFINED;
+    /**
+     * Hardware is able to detect and set Time Interleave Mode automatically.
+     */
+    public static final int TIME_INTERLEAVE_MODE_AUTO =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendCableTimeInterleaveMode.AUTO;
+    /**
+     * 128/1/0 Time Interleave Mode.
+     */
+    public static final int TIME_INTERLEAVE_MODE_128_1_0 = android.hardware.tv.tuner.V1_1.Constants
+            .FrontendCableTimeInterleaveMode.INTERLEAVING_128_1_0;
+    /**
+     * 128/1/1 Time Interleave Mode.
+     */
+    public static final int TIME_INTERLEAVE_MODE_128_1_1 = android.hardware.tv.tuner.V1_1.Constants
+            .FrontendCableTimeInterleaveMode.INTERLEAVING_128_1_1;
+    /**
+     * 64/2 Time Interleave Mode.
+     */
+    public static final int TIME_INTERLEAVE_MODE_64_2 = android.hardware.tv.tuner.V1_1.Constants
+            .FrontendCableTimeInterleaveMode.INTERLEAVING_64_2;
+    /**
+     * 32/4 Time Interleave Mode.
+     */
+    public static final int TIME_INTERLEAVE_MODE_32_4 = android.hardware.tv.tuner.V1_1.Constants
+            .FrontendCableTimeInterleaveMode.INTERLEAVING_32_4;
+    /**
+     * 16/8 Time Interleave Mode.
+     */
+    public static final int TIME_INTERLEAVE_MODE_16_8 = android.hardware.tv.tuner.V1_1.Constants
+            .FrontendCableTimeInterleaveMode.INTERLEAVING_16_8;
+    /**
+     * 8/16 Time Interleave Mode.
+     */
+    public static final int TIME_INTERLEAVE_MODE_8_16 = android.hardware.tv.tuner.V1_1.Constants
+            .FrontendCableTimeInterleaveMode.INTERLEAVING_8_16;
+    /**
+     * 128/2 Time Interleave Mode.
+     */
+    public static final int TIME_INTERLEAVE_MODE_128_2 = android.hardware.tv.tuner.V1_1.Constants
+            .FrontendCableTimeInterleaveMode.INTERLEAVING_128_2;
+    /**
+     * 128/3 Time Interleave Mode.
+     */
+    public static final int TIME_INTERLEAVE_MODE_128_3 = android.hardware.tv.tuner.V1_1.Constants
+            .FrontendCableTimeInterleaveMode.INTERLEAVING_128_3;
+    /**
+     * 128/4 Time Interleave Mode.
+     */
+    public static final int TIME_INTERLEAVE_MODE_128_4 = android.hardware.tv.tuner.V1_1.Constants
+            .FrontendCableTimeInterleaveMode.INTERLEAVING_128_4;
+
 
     private final int mModulation;
     private final long mInnerFec;
@@ -147,9 +230,11 @@
     private final int mOuterFec;
     private final int mAnnex;
     private final int mSpectralInversion;
+    // Dvbc time interleave mode is only supported in Tuner 1.1 or higher.
+    private final int mInterleaveMode;
 
     private DvbcFrontendSettings(int frequency, int modulation, long innerFec, int symbolRate,
-            int outerFec, int annex, int spectralInversion) {
+            int outerFec, int annex, int spectralInversion, int interleaveMode) {
         super(frequency);
         mModulation = modulation;
         mInnerFec = innerFec;
@@ -157,6 +242,7 @@
         mOuterFec = outerFec;
         mAnnex = annex;
         mSpectralInversion = spectralInversion;
+        mInterleaveMode = interleaveMode;
     }
 
     /**
@@ -196,10 +282,17 @@
     /**
      * Gets Spectral Inversion.
      */
-    @SpectralInversion
+    @FrontendSpectralInversion
     public int getSpectralInversion() {
         return mSpectralInversion;
     }
+    /**
+     * Gets Time Interleave Mode.
+     */
+    @TimeInterleaveMode
+    public int getTimeInterleaveMode() {
+        return mInterleaveMode;
+    }
 
     /**
      * Creates a builder for {@link DvbcFrontendSettings}.
@@ -219,7 +312,8 @@
         private int mSymbolRate = 0;
         private int mOuterFec = OUTER_FEC_UNDEFINED;
         private int mAnnex = ANNEX_UNDEFINED;
-        private int mSpectralInversion = SPECTRAL_INVERSION_UNDEFINED;
+        private int mSpectralInversion = FrontendSettings.FRONTEND_SPECTRAL_INVERSION_UNDEFINED;
+        private int mInterleaveMode = TIME_INTERLEAVE_MODE_UNDEFINED;
 
         private Builder() {
         }
@@ -289,13 +383,30 @@
         /**
          * Sets Spectral Inversion.
          *
-         * <p>Default value is {@link #SPECTRAL_INVERSION_UNDEFINED}.
+         * <p>Default value is {@link FrontendSettings#FRONTEND_SPECTRAL_INVERSION_UNDEFINED}.
          */
         @NonNull
-        public Builder setSpectralInversion(@SpectralInversion int spectralInversion) {
+        public Builder setSpectralInversion(@FrontendSpectralInversion int spectralInversion) {
             mSpectralInversion = spectralInversion;
             return this;
         }
+        /**
+         * Set the time interleave mode.
+         *
+         * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause
+         * no-op. Use {@link TunerVersionChecker.getTunerVersion()} to check the version.
+         *
+         * @param interleaveMode the value to set as the time interleave mode. Default value is
+         * {@link #TIME_INTERLEAVE_MODE_UNDEFINED}.
+         */
+        @NonNull
+        public Builder setTimeInterleaveMode(@TimeInterleaveMode int interleaveMode) {
+            if (TunerVersionChecker.checkHigherOrEqualVersionTo(
+                        TunerVersionChecker.TUNER_VERSION_1_1, "setTimeInterleaveMode")) {
+                mInterleaveMode = interleaveMode;
+            }
+            return this;
+        }
 
         /**
          * Builds a {@link DvbcFrontendSettings} object.
@@ -303,7 +414,7 @@
         @NonNull
         public DvbcFrontendSettings build() {
             return new DvbcFrontendSettings(mFrequency, mModulation, mInnerFec, mSymbolRate,
-                mOuterFec, mAnnex, mSpectralInversion);
+                mOuterFec, mAnnex, mSpectralInversion, mInterleaveMode);
         }
     }
 
diff --git a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
index 60b070f..fadc004 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
@@ -23,6 +23,8 @@
 import android.annotation.SystemApi;
 import android.hardware.tv.tuner.V1_0.Constants;
 import android.media.tv.tuner.Tuner;
+import android.media.tv.tuner.TunerVersionChecker;
+
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -36,6 +38,44 @@
 public class DvbsFrontendSettings extends FrontendSettings {
     /** @hide */
     @IntDef(flag = true,
+            prefix = "SCAN_TYPE_",
+            value = {SCAN_TYPE_UNDEFINED, SCAN_TYPE_DIRECT, SCAN_TYPE_DISEQC,
+                    SCAN_TYPE_UNICABLE, SCAN_TYPE_JESS})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ScanType {}
+
+    /**
+     * Dvbs scan type undefined.
+     */
+    public static final int SCAN_TYPE_UNDEFINED =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDvbsScanType.UNDEFINED;
+
+    /**
+     * Dvbs scan type DIRECT.
+     */
+    public static final int SCAN_TYPE_DIRECT =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDvbsScanType.DIRECT;
+
+    /**
+     * Dvbs scan type DISEQC.
+     */
+    public static final int SCAN_TYPE_DISEQC =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDvbsScanType.DISEQC;
+
+    /**
+     * Dvbs scan type UNICABLE.
+     */
+    public static final int SCAN_TYPE_UNICABLE =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDvbsScanType.UNICABLE;
+
+    /**
+     * Dvbs scan type JESS.
+     */
+    public static final int SCAN_TYPE_JESS =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDvbsScanType.JESS;
+
+    /** @hide */
+    @IntDef(flag = true,
             prefix = "MODULATION_",
             value = {MODULATION_UNDEFINED, MODULATION_AUTO, MODULATION_MOD_QPSK,
                     MODULATION_MOD_8PSK, MODULATION_MOD_16QAM, MODULATION_MOD_16PSK,
@@ -218,9 +258,14 @@
     private final int mInputStreamId;
     private final int mStandard;
     private final int mVcmMode;
+    // Dvbs scan type is only supported in Tuner 1.1 or higher.
+    private final int mScanType;
+    // isDiseqcRxMessage is only supported in Tuner 1.1 or higher.
+    private final boolean mIsDiseqcRxMessage;
 
     private DvbsFrontendSettings(int frequency, int modulation, DvbsCodeRate codeRate,
-            int symbolRate, int rolloff, int pilot, int inputStreamId, int standard, int vcm) {
+            int symbolRate, int rolloff, int pilot, int inputStreamId, int standard, int vcm,
+            int scanType, boolean isDiseqcRxMessage) {
         super(frequency);
         mModulation = modulation;
         mCodeRate = codeRate;
@@ -230,6 +275,8 @@
         mInputStreamId = inputStreamId;
         mStandard = standard;
         mVcmMode = vcm;
+        mScanType = scanType;
+        mIsDiseqcRxMessage = isDiseqcRxMessage;
     }
 
     /**
@@ -286,6 +333,22 @@
     public int getVcmMode() {
         return mVcmMode;
     }
+    /**
+     * Get scan type.
+     */
+    @ScanType
+    public int getScanType() {
+        return mScanType;
+    }
+    /**
+     * To receive Diseqc Message or not. Default value is false.
+     *
+     * The setter {@link Builder#setDiseqcRxMessage(boolean)} is only supported with Tuner HAL 1.1
+     * or higher.
+     */
+    public boolean isDiseqcRxMessage() {
+        return mIsDiseqcRxMessage;
+    }
 
     /**
      * Creates a builder for {@link DvbsFrontendSettings}.
@@ -308,6 +371,8 @@
         private int mInputStreamId = Tuner.INVALID_STREAM_ID;
         private int mStandard = STANDARD_AUTO;
         private int mVcmMode = VCM_MODE_UNDEFINED;
+        private int mScanType = SCAN_TYPE_UNDEFINED;
+        private boolean mIsDiseqcRxMessage = false;
 
         private Builder() {
         }
@@ -325,6 +390,39 @@
         }
 
         /**
+         * Set the scan type.
+         *
+         * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause
+         * no-op. Use {@link TunerVersionChecker.getTunerVersion()} to check the version.
+         *
+         * @param scanType the value to set as the scan type. Default value is
+         * {@link android.media.tv.tuner.frontend.DvbsFrontendSettings#DVBS_SCAN_TYPE_UNDEFINED}.
+         */
+        @NonNull
+        public Builder setScanType(@ScanType int scanType) {
+            if (TunerVersionChecker.checkHigherOrEqualVersionTo(
+                        TunerVersionChecker.TUNER_VERSION_1_1, "setScanType")) {
+                mScanType = scanType;
+            }
+            return this;
+        }
+
+        /**
+         * Set true to receive Diseqc Message.
+         *
+         * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause
+         * no-op. Use {@link TunerVersionChecker.getTunerVersion()} to check the version.
+         */
+        @NonNull
+        public Builder setDiseqcRxMessage(boolean isDiseqcRxMessage) {
+            if (TunerVersionChecker.checkHigherOrEqualVersionTo(
+                        TunerVersionChecker.TUNER_VERSION_1_1, "setDiseqcRxMessage")) {
+                mIsDiseqcRxMessage = isDiseqcRxMessage;
+            }
+            return this;
+        }
+
+        /**
          * Sets Modulation.
          *
          * <p>Default value is {@link #MODULATION_UNDEFINED}.
@@ -411,7 +509,8 @@
         @NonNull
         public DvbsFrontendSettings build() {
             return new DvbsFrontendSettings(mFrequency, mModulation, mCodeRate, mSymbolRate,
-                    mRolloff, mPilot, mInputStreamId, mStandard, mVcmMode);
+                    mRolloff, mPilot, mInputStreamId, mStandard, mVcmMode, mScanType,
+                    mIsDiseqcRxMessage);
         }
     }
 
diff --git a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
index 5c057de..07d1797 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
@@ -21,6 +21,7 @@
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.hardware.tv.tuner.V1_0.Constants;
+import android.media.tv.tuner.TunerVersionChecker;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -75,8 +76,21 @@
      * 32K Transmission Mode.
      */
     public static final int TRANSMISSION_MODE_32K = Constants.FrontendDvbtTransmissionMode.MODE_32K;
-
-
+    /**
+     * 8K Transmission Extended Mode.
+     */
+    public static final int TRANSMISSION_MODE_EXTENDED_8K =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDvbtTransmissionMode.MODE_8K_E;
+    /**
+     * 16K Transmission Extended Mode.
+     */
+    public static final int TRANSMISSION_MODE_EXTENDED_16K =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDvbtTransmissionMode.MODE_16K_E;
+    /**
+     * 32K Transmission Extended Mode.
+     */
+    public static final int TRANSMISSION_MODE_EXTENDED_32K =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDvbtTransmissionMode.MODE_32K_E;
 
     /** @hide */
     @IntDef(flag = true,
@@ -124,8 +138,9 @@
     @IntDef(flag = true,
             prefix = "CONSTELLATION_",
             value = {CONSTELLATION_UNDEFINED, CONSTELLATION_AUTO, CONSTELLATION_QPSK,
-                    CONSTELLATION_16QAM, CONSTELLATION_64QAM,
-                    CONSTELLATION_256QAM})
+                    CONSTELLATION_16QAM, CONSTELLATION_64QAM, CONSTELLATION_256QAM,
+                    CONSTELLATION_QPSK_R, CONSTELLATION_16QAM_R, CONSTELLATION_64QAM_R,
+                    CONSTELLATION_256QAM_R})
     @Retention(RetentionPolicy.SOURCE)
     public @interface Constellation {}
 
@@ -157,7 +172,30 @@
      */
     public static final int CONSTELLATION_256QAM =
             Constants.FrontendDvbtConstellation.CONSTELLATION_256QAM;
-
+    /**
+     * QPSK Rotated Constellation.
+     */
+    public static final int CONSTELLATION_QPSK_R =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDvbtConstellation
+                    .CONSTELLATION_QPSK_R;
+    /**
+     * 16QAM Rotated Constellation.
+     */
+    public static final int CONSTELLATION_16QAM_R =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDvbtConstellation
+                    .CONSTELLATION_16QAM_R;
+    /**
+     * 64QAM Rotated Constellation.
+     */
+    public static final int CONSTELLATION_64QAM_R =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDvbtConstellation
+                    .CONSTELLATION_64QAM_R;
+    /**
+     * 256QAM Rotated Constellation.
+     */
+    public static final int CONSTELLATION_256QAM_R =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDvbtConstellation
+                    .CONSTELLATION_256QAM_R;
 
     /** @hide */
     @IntDef(flag = true,
@@ -366,8 +404,7 @@
      */
     public static final int PLP_MODE_MANUAL = Constants.FrontendDvbtPlpMode.MANUAL;
 
-
-    private final int mTransmissionMode;
+    private int mTransmissionMode;
     private final int mBandwidth;
     private final int mConstellation;
     private final int mHierarchy;
@@ -489,6 +526,19 @@
         return mPlpGroupId;
     }
 
+    private static boolean isExtendedTransmissionMode(@TransmissionMode int transmissionMode) {
+        return transmissionMode == TRANSMISSION_MODE_EXTENDED_8K
+                || transmissionMode == TRANSMISSION_MODE_EXTENDED_16K
+                || transmissionMode == TRANSMISSION_MODE_EXTENDED_32K;
+    }
+
+    private static boolean isExtendedConstellation(@Constellation int constellation) {
+        return constellation == CONSTELLATION_QPSK_R
+                || constellation == CONSTELLATION_16QAM_R
+                || constellation == CONSTELLATION_64QAM_R
+                || constellation == CONSTELLATION_256QAM_R;
+    }
+
     /**
      * Creates a builder for {@link DvbtFrontendSettings}.
      */
@@ -534,13 +584,23 @@
         /**
          * Sets Transmission Mode.
          *
+         * <p>{@link #TRANSMISSION_MODE_EXTENDED_8K}, {@link #TRANSMISSION_MODE_EXTENDED_16K} and
+         * {@link #TRANSMISSION_MODE_EXTENDED_32K} are only supported by Tuner HAL 1.1 or higher.
+         * Unsupported version would cause no-op. Use {@link TunerVersionChecker.getTunerVersion()}
+         * to check the version.
+         *
          * <p>Default value is {@link #TRANSMISSION_MODE_UNDEFINED}.
          */
         @NonNull
         public Builder setTransmissionMode(@TransmissionMode int transmissionMode) {
-            mTransmissionMode = transmissionMode;
+            if (!isExtendedTransmissionMode(transmissionMode)
+                    || TunerVersionChecker.checkHigherOrEqualVersionTo(
+                            TunerVersionChecker.TUNER_VERSION_1_1, "set TransmissionMode Ext")) {
+                mTransmissionMode = transmissionMode;
+            }
             return this;
         }
+
         /**
          * Sets Bandwidth.
          *
@@ -554,11 +614,20 @@
         /**
          * Sets Constellation.
          *
+         * <p>{@link #CONSTELLATION_QPSK_R}, {@link #CONSTELLATION_16QAM_R},
+         * {@link #CONSTELLATION_64QAM_R} and {@link #CONSTELLATION_256QAM_Rare} are only supported
+         * by Tuner HAL 1.1 or higher. Unsupported version would cause no-op. Use
+         * {@link TunerVersionChecker.getTunerVersion()} to check the version.
+         *
          * <p>Default value is {@link #CONSTELLATION_UNDEFINED}.
          */
         @NonNull
         public Builder setConstellation(@Constellation int constellation) {
-            mConstellation = constellation;
+            if (!isExtendedConstellation(constellation)
+                    || TunerVersionChecker.checkHigherOrEqualVersionTo(
+                            TunerVersionChecker.TUNER_VERSION_1_1, "set Constellation Ext")) {
+                mConstellation = constellation;
+            }
             return this;
         }
         /**
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
index 2f2fa97..2147622 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
@@ -17,9 +17,12 @@
 package android.media.tv.tuner.frontend;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.LongDef;
 import android.annotation.SystemApi;
 import android.hardware.tv.tuner.V1_0.Constants;
+import android.media.tv.tuner.Tuner;
+import android.media.tv.tuner.TunerVersionChecker;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -34,7 +37,7 @@
     /** @hide */
     @IntDef(prefix = "TYPE_",
             value = {TYPE_UNDEFINED, TYPE_ANALOG, TYPE_ATSC, TYPE_ATSC3, TYPE_DVBC, TYPE_DVBS,
-                    TYPE_DVBT, TYPE_ISDBS, TYPE_ISDBS3, TYPE_ISDBT})
+                    TYPE_DVBT, TYPE_ISDBS, TYPE_ISDBS3, TYPE_ISDBT, TYPE_DTMB})
     @Retention(RetentionPolicy.SOURCE)
     public @interface Type {}
 
@@ -78,7 +81,10 @@
      * Integrated Services Digital Broadcasting-Terrestrial (ISDB-T) frontend type.
      */
     public static final int TYPE_ISDBT = Constants.FrontendType.ISDBT;
-
+    /**
+     * Digital Terrestrial Multimedia Broadcast standard (DTMB) frontend type.
+     */
+    public static final int TYPE_DTMB = android.hardware.tv.tuner.V1_1.Constants.FrontendType.DTMB;
 
 
     /** @hide */
@@ -241,9 +247,36 @@
      */
     public static final long FEC_77_90 = Constants.FrontendInnerFec.FEC_77_90;
 
+    /** @hide */
+    @IntDef(prefix = "FRONTEND_SPECTRAL_INVERSION_",
+            value = {FRONTEND_SPECTRAL_INVERSION_UNDEFINED, FRONTEND_SPECTRAL_INVERSION_NORMAL,
+                    FRONTEND_SPECTRAL_INVERSION_INVERTED})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FrontendSpectralInversion {}
+
+    /**
+     * Spectral Inversion Type undefined.
+     */
+    public static final int FRONTEND_SPECTRAL_INVERSION_UNDEFINED =
+            Constants.FrontendDvbcSpectralInversion.UNDEFINED;
+    /**
+     * Normal Spectral Inversion.
+     */
+    public static final int FRONTEND_SPECTRAL_INVERSION_NORMAL =
+            Constants.FrontendDvbcSpectralInversion.NORMAL;
+    /**
+     * Inverted Spectral Inversion.
+     */
+    public static final int FRONTEND_SPECTRAL_INVERSION_INVERTED =
+            Constants.FrontendDvbcSpectralInversion.INVERTED;
+
 
 
     private final int mFrequency;
+    // End frequency is only supported in Tuner 1.1 or higher.
+    private int mEndFrequency = Tuner.INVALID_FRONTEND_SETTING_FREQUENCY;
+    // General spectral inversion is only supported in Tuner 1.1 or higher.
+    private int mSpectralInversion = FRONTEND_SPECTRAL_INVERSION_UNDEFINED;
 
     FrontendSettings(int frequency) {
         mFrequency = frequency;
@@ -263,4 +296,57 @@
     public int getFrequency() {
         return mFrequency;
     }
+
+    /**
+     * Get the end frequency.
+     *
+     * @return the end frequency in Hz.
+     */
+    public int getEndFrequency() {
+        return mEndFrequency;
+    }
+
+    /**
+     * Get the spectral inversion.
+     *
+     * @return the value of the spectral inversion.
+     */
+    @FrontendSpectralInversion
+    public int getFrontendSpectralInversion() {
+        return mSpectralInversion;
+    }
+
+    /**
+     * Set Spectral Inversion.
+     *
+     * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause
+     * no-op. Use {@link TunerVersionChecker.getTunerVersion()} to check the version.
+     *
+     * @param inversion the value to set as the spectral inversion. Default value is {@link
+     * #FRONTEND_SPECTRAL_INVERSION_UNDEFINED}.
+     */
+    public void setSpectralInversion(@FrontendSpectralInversion int inversion) {
+        if (TunerVersionChecker.checkHigherOrEqualVersionTo(
+                TunerVersionChecker.TUNER_VERSION_1_1, "setSpectralInversion")) {
+            mSpectralInversion = inversion;
+        }
+    }
+
+    /**
+     * Set End Frequency. This API is only supported with Tuner HAL 1.1 or higher. Otherwise it
+     * would be no-op.
+     *
+     * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause
+     * no-op. Use {@link TunerVersionChecker.getTunerVersion()} to check the version.
+     *
+     * @param endFrequency the end frequency used during blind scan. The default value is
+     * {@link android.media.tv.tuner.Tuner#INVALID_FRONTEND_SETTING_FREQUENCY}.
+     */
+    @IntRange(from = 1)
+    public void setEndFrequency(int endFrequency) {
+        if (TunerVersionChecker.checkHigherOrEqualVersionTo(
+                TunerVersionChecker.TUNER_VERSION_1_1, "setEndFrequency")) {
+            mEndFrequency = endFrequency;
+        }
+    }
 }
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
index c265bb9..dd9347c 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
@@ -21,6 +21,7 @@
 import android.annotation.SystemApi;
 import android.hardware.tv.tuner.V1_0.Constants;
 import android.media.tv.tuner.Lnb;
+import android.media.tv.tuner.TunerVersionChecker;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -43,7 +44,13 @@
             FRONTEND_STATUS_TYPE_EWBS, FRONTEND_STATUS_TYPE_AGC, FRONTEND_STATUS_TYPE_LNA,
             FRONTEND_STATUS_TYPE_LAYER_ERROR, FRONTEND_STATUS_TYPE_MER,
             FRONTEND_STATUS_TYPE_FREQ_OFFSET, FRONTEND_STATUS_TYPE_HIERARCHY,
-            FRONTEND_STATUS_TYPE_RF_LOCK, FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO})
+            FRONTEND_STATUS_TYPE_RF_LOCK, FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO,
+            FRONTEND_STATUS_TYPE_BERS, FRONTEND_STATUS_TYPE_CODERATES,
+            FRONTEND_STATUS_TYPE_BANDWIDTH, FRONTEND_STATUS_TYPE_GUARD_INTERVAL,
+            FRONTEND_STATUS_TYPE_TRANSMISSION_MODE, FRONTEND_STATUS_TYPE_UEC,
+            FRONTEND_STATUS_TYPE_T2_SYSTEM_ID, FRONTEND_STATUS_TYPE_INTERLEAVINGS,
+            FRONTEND_STATUS_TYPE_ISDBT_SEGMENTS, FRONTEND_STATUS_TYPE_TS_DATA_RATES,
+            FRONTEND_STATUS_TYPE_MODULATIONS_EXT})
     @Retention(RetentionPolicy.SOURCE)
     public @interface FrontendStatusType {}
 
@@ -145,10 +152,83 @@
      */
     public static final int FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO =
             Constants.FrontendStatusType.ATSC3_PLP_INFO;
-
+    /**
+     * BERS Type. Only supported in Tuner HAL 1.1 or higher.
+     */
+    public static final int FRONTEND_STATUS_TYPE_BERS =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.BERS;
+    /**
+     * Coderate Type. Only supported in Tuner HAL 1.1 or higher.
+     */
+    public static final int FRONTEND_STATUS_TYPE_CODERATES =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.CODERATES;
+    /**
+     * Bandwidth Type. Only supported in Tuner HAL 1.1 or higher.
+     */
+    public static final int FRONTEND_STATUS_TYPE_BANDWIDTH =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.BANDWIDTH;
+    /**
+     * Guard Interval Type. Only supported in Tuner HAL 1.1 or higher.
+     */
+    public static final int FRONTEND_STATUS_TYPE_GUARD_INTERVAL =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.GUARD_INTERVAL;
+    /**
+     * Transmission Mode Type. Only supported in Tuner HAL 1.1 or higher.
+     */
+    public static final int FRONTEND_STATUS_TYPE_TRANSMISSION_MODE =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.TRANSMISSION_MODE;
+    /**
+     * UEC Type. Only supported in Tuner HAL 1.1 or higher.
+     */
+    public static final int FRONTEND_STATUS_TYPE_UEC =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.UEC;
+    /**
+     * T2 System Id Type. Only supported in Tuner HAL 1.1 or higher.
+     */
+    public static final int FRONTEND_STATUS_TYPE_T2_SYSTEM_ID =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.T2_SYSTEM_ID;
+    /**
+     * Interleavings Type. Only supported in Tuner HAL 1.1 or higher.
+     */
+    public static final int FRONTEND_STATUS_TYPE_INTERLEAVINGS =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.INTERLEAVINGS;
+    /**
+     * ISDBT Segments Type. Only supported in Tuner HAL 1.1 or higher.
+     */
+    public static final int FRONTEND_STATUS_TYPE_ISDBT_SEGMENTS =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.ISDBT_SEGMENTS;
+    /**
+     * TS Data Rates Type. Only supported in Tuner HAL 1.1 or higher.
+     */
+    public static final int FRONTEND_STATUS_TYPE_TS_DATA_RATES =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.TS_DATA_RATES;
+    /**
+     * Extended Modulations Type. Only supported in Tuner HAL 1.1 or higher.
+     */
+    public static final int FRONTEND_STATUS_TYPE_MODULATIONS_EXT =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.MODULATIONS;
 
     /** @hide */
     @IntDef(value = {
+            AtscFrontendSettings.MODULATION_UNDEFINED,
+            AtscFrontendSettings.MODULATION_AUTO,
+            AtscFrontendSettings.MODULATION_MOD_8VSB,
+            AtscFrontendSettings.MODULATION_MOD_16VSB,
+            Atsc3FrontendSettings.MODULATION_UNDEFINED,
+            Atsc3FrontendSettings.MODULATION_AUTO,
+            Atsc3FrontendSettings.MODULATION_MOD_QPSK,
+            Atsc3FrontendSettings.MODULATION_MOD_16QAM,
+            Atsc3FrontendSettings.MODULATION_MOD_64QAM,
+            Atsc3FrontendSettings.MODULATION_MOD_256QAM,
+            Atsc3FrontendSettings.MODULATION_MOD_1024QAM,
+            Atsc3FrontendSettings.MODULATION_MOD_4096QAM,
+            DtmbFrontendSettings.MODULATION_CONSTELLATION_UNDEFINED,
+            DtmbFrontendSettings.MODULATION_CONSTELLATION_AUTO,
+            DtmbFrontendSettings.MODULATION_CONSTELLATION_4QAM,
+            DtmbFrontendSettings.MODULATION_CONSTELLATION_4QAM_NR,
+            DtmbFrontendSettings.MODULATION_CONSTELLATION_16QAM,
+            DtmbFrontendSettings.MODULATION_CONSTELLATION_32QAM,
+            DtmbFrontendSettings.MODULATION_CONSTELLATION_64QAM,
             DvbcFrontendSettings.MODULATION_UNDEFINED,
             DvbcFrontendSettings.MODULATION_AUTO,
             DvbcFrontendSettings.MODULATION_MOD_16QAM,
@@ -171,6 +251,16 @@
             DvbsFrontendSettings.MODULATION_MOD_128APSK,
             DvbsFrontendSettings.MODULATION_MOD_256APSK,
             DvbsFrontendSettings.MODULATION_MOD_RESERVED,
+            DvbtFrontendSettings.CONSTELLATION_UNDEFINED,
+            DvbtFrontendSettings.CONSTELLATION_AUTO,
+            DvbtFrontendSettings.CONSTELLATION_QPSK,
+            DvbtFrontendSettings.CONSTELLATION_16QAM,
+            DvbtFrontendSettings.CONSTELLATION_64QAM,
+            DvbtFrontendSettings.CONSTELLATION_256QAM,
+            DvbtFrontendSettings.CONSTELLATION_QPSK_R,
+            DvbtFrontendSettings.CONSTELLATION_16QAM_R,
+            DvbtFrontendSettings.CONSTELLATION_64QAM_R,
+            DvbtFrontendSettings.CONSTELLATION_256QAM_R,
             IsdbsFrontendSettings.MODULATION_UNDEFINED,
             IsdbsFrontendSettings.MODULATION_AUTO,
             IsdbsFrontendSettings.MODULATION_MOD_BPSK,
@@ -192,6 +282,101 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface FrontendModulation {}
 
+    /** @hide */
+    @IntDef(value = {
+            Atsc3FrontendSettings.TIME_INTERLEAVE_MODE_UNDEFINED,
+            Atsc3FrontendSettings.TIME_INTERLEAVE_MODE_AUTO,
+            Atsc3FrontendSettings.TIME_INTERLEAVE_MODE_CTI,
+            Atsc3FrontendSettings.TIME_INTERLEAVE_MODE_HTI,
+            DtmbFrontendSettings.TIME_INTERLEAVE_MODE_UNDEFINED,
+            DtmbFrontendSettings.TIME_INTERLEAVE_MODE_AUTO,
+            DtmbFrontendSettings.TIME_INTERLEAVE_MODE_TIMER_INT_240,
+            DtmbFrontendSettings.TIME_INTERLEAVE_MODE_TIMER_INT_720,
+            DvbcFrontendSettings.TIME_INTERLEAVE_MODE_UNDEFINED,
+            DvbcFrontendSettings.TIME_INTERLEAVE_MODE_AUTO,
+            DvbcFrontendSettings.TIME_INTERLEAVE_MODE_128_1_0,
+            DvbcFrontendSettings.TIME_INTERLEAVE_MODE_128_1_1,
+            DvbcFrontendSettings.TIME_INTERLEAVE_MODE_64_2,
+            DvbcFrontendSettings.TIME_INTERLEAVE_MODE_32_4,
+            DvbcFrontendSettings.TIME_INTERLEAVE_MODE_16_8,
+            DvbcFrontendSettings.TIME_INTERLEAVE_MODE_8_16,
+            DvbcFrontendSettings.TIME_INTERLEAVE_MODE_128_2,
+            DvbcFrontendSettings.TIME_INTERLEAVE_MODE_128_3,
+            DvbcFrontendSettings.TIME_INTERLEAVE_MODE_128_4})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FrontendInterleaveMode {}
+
+    /** @hide */
+    @IntDef(value = {
+            Atsc3FrontendSettings.BANDWIDTH_UNDEFINED,
+            Atsc3FrontendSettings.BANDWIDTH_AUTO,
+            Atsc3FrontendSettings.BANDWIDTH_BANDWIDTH_6MHZ,
+            Atsc3FrontendSettings.BANDWIDTH_BANDWIDTH_7MHZ,
+            Atsc3FrontendSettings.BANDWIDTH_BANDWIDTH_8MHZ,
+            DtmbFrontendSettings.BANDWIDTH_UNDEFINED,
+            DtmbFrontendSettings.BANDWIDTH_AUTO,
+            DtmbFrontendSettings.BANDWIDTH_6MHZ,
+            DtmbFrontendSettings.BANDWIDTH_8MHZ,
+            DvbtFrontendSettings.BANDWIDTH_UNDEFINED,
+            DvbtFrontendSettings.BANDWIDTH_AUTO,
+            DvbtFrontendSettings.BANDWIDTH_8MHZ,
+            DvbtFrontendSettings.BANDWIDTH_7MHZ,
+            DvbtFrontendSettings.BANDWIDTH_6MHZ,
+            DvbtFrontendSettings.BANDWIDTH_5MHZ,
+            DvbtFrontendSettings.BANDWIDTH_1_7MHZ,
+            DvbtFrontendSettings.BANDWIDTH_10MHZ,
+            IsdbtFrontendSettings.BANDWIDTH_UNDEFINED,
+            IsdbtFrontendSettings.BANDWIDTH_AUTO,
+            IsdbtFrontendSettings.BANDWIDTH_8MHZ,
+            IsdbtFrontendSettings.BANDWIDTH_7MHZ,
+            IsdbtFrontendSettings.BANDWIDTH_6MHZ})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FrontendBandwidth {}
+
+    /** @hide */
+    @IntDef(value = {
+            DtmbFrontendSettings.TRANSMISSION_MODE_UNDEFINED,
+            DtmbFrontendSettings.TRANSMISSION_MODE_AUTO,
+            DtmbFrontendSettings.TRANSMISSION_MODE_C1,
+            DtmbFrontendSettings.TRANSMISSION_MODE_C3780,
+            DvbtFrontendSettings.TRANSMISSION_MODE_UNDEFINED,
+            DvbtFrontendSettings.TRANSMISSION_MODE_AUTO,
+            DvbtFrontendSettings.TRANSMISSION_MODE_2K,
+            DvbtFrontendSettings.TRANSMISSION_MODE_8K,
+            DvbtFrontendSettings.TRANSMISSION_MODE_4K,
+            DvbtFrontendSettings.TRANSMISSION_MODE_1K,
+            DvbtFrontendSettings.TRANSMISSION_MODE_16K,
+            DvbtFrontendSettings.TRANSMISSION_MODE_32K,
+            IsdbtFrontendSettings.MODE_UNDEFINED,
+            IsdbtFrontendSettings.MODE_AUTO,
+            IsdbtFrontendSettings.MODE_1,
+            IsdbtFrontendSettings.MODE_2,
+            IsdbtFrontendSettings.MODE_3})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FrontendTransmissionMode {}
+
+    /** @hide */
+    @IntDef(value = {
+            DtmbFrontendSettings.GUARD_INTERVAL_UNDEFINED,
+            DtmbFrontendSettings.GUARD_INTERVAL_AUTO,
+            DtmbFrontendSettings.GUARD_INTERVAL_PN_420_VARIOUS,
+            DtmbFrontendSettings.GUARD_INTERVAL_PN_595_CONST,
+            DtmbFrontendSettings.GUARD_INTERVAL_PN_945_VARIOUS,
+            DtmbFrontendSettings.GUARD_INTERVAL_PN_420_CONST,
+            DtmbFrontendSettings.GUARD_INTERVAL_PN_945_CONST,
+            DtmbFrontendSettings.GUARD_INTERVAL_PN_RESERVED,
+            DvbtFrontendSettings.GUARD_INTERVAL_UNDEFINED,
+            DvbtFrontendSettings.GUARD_INTERVAL_AUTO,
+            DvbtFrontendSettings.GUARD_INTERVAL_1_32,
+            DvbtFrontendSettings.GUARD_INTERVAL_1_16,
+            DvbtFrontendSettings.GUARD_INTERVAL_1_8,
+            DvbtFrontendSettings.GUARD_INTERVAL_1_4,
+            DvbtFrontendSettings.GUARD_INTERVAL_1_128,
+            DvbtFrontendSettings.GUARD_INTERVAL_19_128,
+            DvbtFrontendSettings.GUARD_INTERVAL_19_256,
+            DvbtFrontendSettings.GUARD_INTERVAL_19_128})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FrontendGuardInterval {}
 
     private Boolean mIsDemodLocked;
     private Integer mSnr;
@@ -215,6 +400,18 @@
     private Integer mHierarchy;
     private Boolean mIsRfLocked;
     private Atsc3PlpTuningInfo[] mPlpInfo;
+    private int[] mBers;
+    private int[] mCodeRates;
+    private Integer mBandwidth;
+    private Integer mGuardInterval;
+    private Integer mTransmissionMode;
+    private Integer mUec;
+    private Integer mSystemId;
+    private int[] mInterleaving;
+    private int[] mTsDataRate;
+    private int[] mIsdbtSegment;
+    private int[] mModulationsExt;
+
 
     // Constructed and fields set by JNI code.
     private FrontendStatus() {
@@ -323,7 +520,7 @@
     /**
      * Gets Spectral Inversion for DVBC.
      */
-    @DvbcFrontendSettings.SpectralInversion
+    @FrontendSettings.FrontendSpectralInversion
     public int getSpectralInversion() {
         if (mInversion == null) {
             throw new IllegalStateException();
@@ -437,6 +634,182 @@
     }
 
     /**
+     * Gets an array of BERS status.
+     *
+     * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
+     * {@link TunerVersionChecker.getTunerVersion()} to check the version.
+     */
+    @NonNull
+    public int[] getBers() {
+        TunerVersionChecker.checkHigherOrEqualVersionTo(
+                TunerVersionChecker.TUNER_VERSION_1_1, "getBers status");
+        if (mBers == null) {
+            throw new IllegalStateException();
+        }
+        return mBers;
+    }
+
+    /**
+     * Gets an array of code rates status.
+     *
+     * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
+     * {@link TunerVersionChecker.getTunerVersion()} to check the version.
+     */
+    @NonNull
+    public int[] getCodeRates() {
+        TunerVersionChecker.checkHigherOrEqualVersionTo(
+                TunerVersionChecker.TUNER_VERSION_1_1, "getCodeRates status");
+        if (mCodeRates == null) {
+            throw new IllegalStateException();
+        }
+        return mCodeRates;
+    }
+
+    /**
+     * Gets bandwidth status.
+     *
+     * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
+     * {@link TunerVersionChecker.getTunerVersion()} to check the version.
+     */
+    @FrontendBandwidth
+    public int getBandwidth() {
+        TunerVersionChecker.checkHigherOrEqualVersionTo(
+                TunerVersionChecker.TUNER_VERSION_1_1, "getBandwidth status");
+        if (mBandwidth == null) {
+            throw new IllegalStateException();
+        }
+        return mBandwidth;
+    }
+
+    /**
+     * Gets guard interval status.
+     *
+     * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
+     * {@link TunerVersionChecker.getTunerVersion()} to check the version.
+     */
+    @FrontendGuardInterval
+    public int getGuardInterval() {
+        TunerVersionChecker.checkHigherOrEqualVersionTo(
+                TunerVersionChecker.TUNER_VERSION_1_1, "getGuardInterval status");
+        if (mGuardInterval == null) {
+            throw new IllegalStateException();
+        }
+        return mGuardInterval;
+    }
+
+    /**
+     * Gets tansmission mode status.
+     *
+     * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
+     * {@link TunerVersionChecker.getTunerVersion()} to check the version.
+     */
+    @FrontendTransmissionMode
+    public int getTransmissionMode() {
+        TunerVersionChecker.checkHigherOrEqualVersionTo(
+                TunerVersionChecker.TUNER_VERSION_1_1, "getTransmissionMode status");
+        if (mTransmissionMode == null) {
+            throw new IllegalStateException();
+        }
+        return mTransmissionMode;
+    }
+
+    /**
+     * Gets UEC status.
+     *
+     * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
+     * {@link TunerVersionChecker.getTunerVersion()} to check the version.
+     */
+    public int getUec() {
+        TunerVersionChecker.checkHigherOrEqualVersionTo(
+                TunerVersionChecker.TUNER_VERSION_1_1, "getUec status");
+        if (mUec == null) {
+            throw new IllegalStateException();
+        }
+        return mUec;
+    }
+
+    /**
+     * Gets system id status.
+     *
+     * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
+     * {@link TunerVersionChecker.getTunerVersion()} to check the version.
+     */
+    public int getSystemId() {
+        TunerVersionChecker.checkHigherOrEqualVersionTo(
+                TunerVersionChecker.TUNER_VERSION_1_1, "getSystemId status");
+        if (mSystemId == null) {
+            throw new IllegalStateException();
+        }
+        return mSystemId;
+    }
+
+    /**
+     * Gets an array of interleaving status. Array value should be withink {@link
+     * FrontendInterleaveMode}.
+     *
+     * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
+     * {@link TunerVersionChecker.getTunerVersion()} to check the version.
+     */
+    @NonNull
+    public int[] getInterleaving() {
+        TunerVersionChecker.checkHigherOrEqualVersionTo(
+                TunerVersionChecker.TUNER_VERSION_1_1, "getInterleaving status");
+        if (mInterleaving == null) {
+            throw new IllegalStateException();
+        }
+        return mInterleaving;
+    }
+
+    /**
+     * Gets an array of isdbt segment status.
+     *
+     * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
+     * {@link TunerVersionChecker.getTunerVersion()} to check the version.
+     */
+    @NonNull
+    public int[] getIsdbtSegment() {
+        TunerVersionChecker.checkHigherOrEqualVersionTo(
+                TunerVersionChecker.TUNER_VERSION_1_1, "getIsdbtSegment status");
+        if (mIsdbtSegment == null) {
+            throw new IllegalStateException();
+        }
+        return mIsdbtSegment;
+    }
+
+    /**
+     * Gets an array of TS data rate status.
+     *
+     * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
+     * {@link TunerVersionChecker.getTunerVersion()} to check the version.
+     */
+    @NonNull
+    public int[] getTsDataRate() {
+        TunerVersionChecker.checkHigherOrEqualVersionTo(
+                TunerVersionChecker.TUNER_VERSION_1_1, "getTsDataRate status");
+        if (mTsDataRate == null) {
+            throw new IllegalStateException();
+        }
+        return mTsDataRate;
+    }
+
+    /**
+     * Gets an array of the extended modulations status. Array value should be withink {@link
+     * FrontendModulation}.
+     *
+     * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
+     * {@link TunerVersionChecker.getTunerVersion()} to check the version.
+     */
+    @NonNull
+    public int[] getModulationsExt() {
+        TunerVersionChecker.checkHigherOrEqualVersionTo(
+                TunerVersionChecker.TUNER_VERSION_1_1, "getModulationsExt status");
+        if (mModulationsExt == null) {
+            throw new IllegalStateException();
+        }
+        return mModulationsExt;
+    }
+
+    /**
      * Status for each tuning Physical Layer Pipes.
      */
     public static class Atsc3PlpTuningInfo {
diff --git a/media/java/android/media/tv/tuner/frontend/ScanCallback.java b/media/java/android/media/tv/tuner/frontend/ScanCallback.java
index b0491fd..9bf7a5d 100644
--- a/media/java/android/media/tv/tuner/frontend/ScanCallback.java
+++ b/media/java/android/media/tv/tuner/frontend/ScanCallback.java
@@ -70,4 +70,9 @@
     /** Frontend signal type. */
     void onSignalTypeReported(@AnalogFrontendSettings.SignalType int signalType);
 
+    /** Frontend modulation reported. */
+    default void onModulationReported(@FrontendStatus.FrontendModulation int modulation) {}
+
+    /** Frontend scan message priority reported. */
+    default void onPriorityReported(boolean isHighPriority) {}
 }
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 4e27c8e..724965d 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -142,6 +142,7 @@
     shared_libs: [
         "android.hardware.graphics.bufferqueue@2.0",
         "android.hardware.tv.tuner@1.0",
+        "android.hardware.tv.tuner@1.1",
         "libandroid_runtime",
         "libcutils",
         "libfmq",
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 0b0e162..71c86cc 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -2662,7 +2662,7 @@
     gFields.cryptoInfoModeID = env->GetFieldID(clazz.get(), "mode", "I");
     CHECK(gFields.cryptoInfoModeID != NULL);
 
-    gFields.cryptoInfoPatternID = env->GetFieldID(clazz.get(), "pattern",
+    gFields.cryptoInfoPatternID = env->GetFieldID(clazz.get(), "mPattern",
         "Landroid/media/MediaCodec$CryptoInfo$Pattern;");
     CHECK(gFields.cryptoInfoPatternID != NULL);
 
diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp
index 6fbd29c..126897a 100644
--- a/media/jni/android_media_MediaMetadataRetriever.cpp
+++ b/media/jni/android_media_MediaMetadataRetriever.cpp
@@ -704,7 +704,7 @@
             (void *)android_media_MediaMetadataRetriever_setDataSourceAndHeaders
         },
 
-        {"setDataSource",   "(Ljava/io/FileDescriptor;JJ)V",
+        {"_setDataSource",   "(Ljava/io/FileDescriptor;JJ)V",
                 (void *)android_media_MediaMetadataRetriever_setDataSourceFD},
         {"_setDataSource",   "(Landroid/media/MediaDataSource;)V",
                 (void *)android_media_MediaMetadataRetriever_setDataSourceCallback},
diff --git a/media/jni/android_media_Utils.cpp b/media/jni/android_media_Utils.cpp
index 2ef7b9e..b6c47fca 100644
--- a/media/jni/android_media_Utils.cpp
+++ b/media/jni/android_media_Utils.cpp
@@ -140,6 +140,27 @@
     fmt = applyFormatOverrides(fmt, containerFormat);
     switch (fmt) {
         case HAL_PIXEL_FORMAT_YCbCr_420_888:
+            // Width and height should be multiple of 2. Wrong dataSize would be returned otherwise.
+            if (buffer->width % 2 != 0) {
+                ALOGE("YCbCr_420_888: width (%d) should be a multiple of 2", buffer->width);
+                return BAD_VALUE;
+            }
+
+            if (buffer->height % 2 != 0) {
+                ALOGE("YCbCr_420_888: height (%d) should be a multiple of 2", buffer->height);
+                return BAD_VALUE;
+            }
+
+            if (buffer->width <= 0) {
+                ALOGE("YCbCr_420_888: width (%d) should be a > 0", buffer->width);
+                return BAD_VALUE;
+            }
+
+            if (buffer->height <= 0) {
+                ALOGE("YCbCr_420_888: height (%d) should be a > 0", buffer->height);
+                return BAD_VALUE;
+            }
+
             pData =
                 (idx == 0) ?
                     buffer->data :
@@ -160,6 +181,27 @@
             break;
         // NV21
         case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+            // Width and height should be multiple of 2. Wrong dataSize would be returned otherwise.
+            if (buffer->width % 2 != 0) {
+                ALOGE("YCrCb_420_SP: width (%d) should be a multiple of 2", buffer->width);
+                return BAD_VALUE;
+            }
+
+            if (buffer->height % 2 != 0) {
+                ALOGE("YCrCb_420_SP: height (%d) should be a multiple of 2", buffer->height);
+                return BAD_VALUE;
+            }
+
+            if (buffer->width <= 0) {
+                ALOGE("YCrCb_420_SP: width (%d) should be a > 0", buffer->width);
+                return BAD_VALUE;
+            }
+
+            if (buffer->height <= 0) {
+                ALOGE("YCrCb_420_SP: height (%d) should be a > 0", buffer->height);
+                return BAD_VALUE;
+            }
+
             cr = buffer->data + (buffer->stride * buffer->height);
             cb = cr + 1;
             // only map until last pixel
@@ -178,6 +220,27 @@
             rStride = buffer->width;
             break;
         case HAL_PIXEL_FORMAT_YV12:
+            // Width and height should be multiple of 2. Wrong dataSize would be returned otherwise.
+            if (buffer->width % 2 != 0) {
+                ALOGE("YV12: width (%d) should be a multiple of 2", buffer->width);
+                return BAD_VALUE;
+            }
+
+            if (buffer->height % 2 != 0) {
+                ALOGE("YV12: height (%d) should be a multiple of 2", buffer->height);
+                return BAD_VALUE;
+            }
+
+            if (buffer->width <= 0) {
+                ALOGE("YV12: width (%d) should be a > 0", buffer->width);
+                return BAD_VALUE;
+            }
+
+            if (buffer->height <= 0) {
+                ALOGE("YV12: height (%d) should be a > 0", buffer->height);
+                return BAD_VALUE;
+            }
+
             // Y and C stride need to be 16 pixel aligned.
             LOG_ALWAYS_FATAL_IF(buffer->stride % 16,
                                 "Stride is not 16 pixel aligned %d", buffer->stride);
@@ -344,6 +407,11 @@
     int flexFormat = format;
     if (isPossiblyYUV(format)) {
         res = buffer->lockAsyncYCbCr(inUsage, rect, &ycbcr, fenceFd);
+
+        if (res != OK) {
+            ALOGW("lockAsyncYCbCr failed with error %d", res);
+        }
+
         pData = ycbcr.y;
         flexFormat = HAL_PIXEL_FORMAT_YCbCr_420_888;
     }
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 5daf8b0..1be0d44 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -22,7 +22,6 @@
 #include "android_runtime/AndroidRuntime.h"
 
 #include <android-base/logging.h>
-#include <android/hardware/tv/tuner/1.0/ITuner.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <nativehelper/JNIHelp.h>
 #include <nativehelper/ScopedLocalRef.h>
@@ -34,7 +33,6 @@
 using ::android::hardware::hidl_bitfield;
 using ::android::hardware::hidl_vec;
 using ::android::hardware::tv::tuner::V1_0::AudioExtraMetaData;
-using ::android::hardware::tv::tuner::V1_0::Constant;
 using ::android::hardware::tv::tuner::V1_0::DataFormat;
 using ::android::hardware::tv::tuner::V1_0::DemuxAlpFilterSettings;
 using ::android::hardware::tv::tuner::V1_0::DemuxAlpFilterType;
@@ -129,12 +127,39 @@
 using ::android::hardware::tv::tuner::V1_0::FrontendStatusAtsc3PlpInfo;
 using ::android::hardware::tv::tuner::V1_0::FrontendStatusType;
 using ::android::hardware::tv::tuner::V1_0::FrontendType;
-using ::android::hardware::tv::tuner::V1_0::ITuner;
 using ::android::hardware::tv::tuner::V1_0::LnbPosition;
 using ::android::hardware::tv::tuner::V1_0::LnbTone;
 using ::android::hardware::tv::tuner::V1_0::LnbVoltage;
 using ::android::hardware::tv::tuner::V1_0::PlaybackSettings;
 using ::android::hardware::tv::tuner::V1_0::RecordSettings;
+using ::android::hardware::tv::tuner::V1_1::AudioStreamType;
+using ::android::hardware::tv::tuner::V1_1::AvStreamType;
+using ::android::hardware::tv::tuner::V1_1::Constant;
+using ::android::hardware::tv::tuner::V1_1::Constant64Bit;
+using ::android::hardware::tv::tuner::V1_1::FrontendAnalogAftFlag;
+using ::android::hardware::tv::tuner::V1_1::FrontendAnalogSettingsExt1_1;
+using ::android::hardware::tv::tuner::V1_1::FrontendBandwidth;
+using ::android::hardware::tv::tuner::V1_1::FrontendCableTimeInterleaveMode;
+using ::android::hardware::tv::tuner::V1_1::FrontendDvbsScanType;
+using ::android::hardware::tv::tuner::V1_1::FrontendDvbcSettingsExt1_1;
+using ::android::hardware::tv::tuner::V1_1::FrontendDvbsSettingsExt1_1;
+using ::android::hardware::tv::tuner::V1_1::FrontendDvbtSettingsExt1_1;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbBandwidth;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbCapabilities;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbCodeRate;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbGuardInterval;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbModulation;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbSettings;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbTimeInterleaveMode;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbTransmissionMode;
+using ::android::hardware::tv::tuner::V1_1::FrontendGuardInterval;
+using ::android::hardware::tv::tuner::V1_1::FrontendInterleaveMode;
+using ::android::hardware::tv::tuner::V1_1::FrontendModulation;
+using ::android::hardware::tv::tuner::V1_1::FrontendSpectralInversion;
+using ::android::hardware::tv::tuner::V1_1::FrontendStatusExt1_1;
+using ::android::hardware::tv::tuner::V1_1::FrontendStatusTypeExt1_1;
+using ::android::hardware::tv::tuner::V1_1::FrontendTransmissionMode;
+using ::android::hardware::tv::tuner::V1_1::VideoStreamType;
 
 struct fields_t {
     jfieldID tunerContext;
@@ -310,9 +335,9 @@
 
 /////////////// MediaEvent ///////////////////////
 
-MediaEvent::MediaEvent(sp<IFilter> iFilter, hidl_handle avHandle,
-        uint64_t dataId, uint64_t dataLength, jobject obj) : mIFilter(iFilter),
-        mDataId(dataId), mDataLength(dataLength), mBuffer(nullptr),
+MediaEvent::MediaEvent(sp<Filter> filter, hidl_handle avHandle,
+        uint64_t dataId, uint64_t dataSize, jobject obj) : mFilter(filter),
+        mDataId(dataId), mDataSize(dataSize), mBuffer(nullptr),
         mDataIdRefCnt(0), mAvHandleRefCnt(0), mIonHandle(nullptr) {
     JNIEnv *env = AndroidRuntime::getJNIEnv();
     mMediaEventObj = env->NewWeakGlobalRef(obj);
@@ -336,7 +361,8 @@
 
 void MediaEvent::finalize() {
     if (mAvHandleRefCnt == 0) {
-        mIFilter->releaseAvHandle(hidl_handle(mAvHandle), mDataIdRefCnt == 0 ? mDataId : 0);
+        mFilter->mFilterSp->releaseAvHandle(
+                hidl_handle(mAvHandle), mDataIdRefCnt == 0 ? mDataId : 0);
         native_handle_close(mAvHandle);
     }
 }
@@ -349,7 +375,47 @@
     if (mLinearBlockObj != NULL) {
         return mLinearBlockObj;
     }
-    mIonHandle = new C2HandleIon(dup(mAvHandle->data[0]), mDataLength);
+
+    int fd;
+    int numInts = 0;
+    int memIndex;
+    int dataSize;
+    if (mAvHandle->numFds == 0) {
+        if (mFilter->mAvSharedHandle == NULL) {
+            ALOGE("Shared AV memory handle is not initialized.");
+            return NULL;
+        }
+        if (mFilter->mAvSharedHandle->numFds == 0) {
+            ALOGE("Shared AV memory handle is empty.");
+            return NULL;
+        }
+        fd = mFilter->mAvSharedHandle->data[0];
+        dataSize = mFilter->mAvSharedMemSize;
+        numInts = mFilter->mAvSharedHandle->numInts;
+        if (numInts > 0) {
+            // If the first int in the shared native handle has value, use it as the index
+            memIndex = mFilter->mAvSharedHandle->data[mFilter->mAvSharedHandle->numFds];
+        }
+    } else {
+        fd = mAvHandle->data[0];
+        dataSize = mDataSize;
+        numInts = mAvHandle->numInts;
+        if (numInts > 0) {
+            // Otherwise if the first int in the av native handle returned from the filter
+            // event has value, use it as the index
+            memIndex = mAvHandle->data[mAvHandle->numFds];
+        } else {
+            if (mFilter->mAvSharedHandle != NULL) {
+                numInts = mFilter->mAvSharedHandle->numInts;
+                if (numInts > 0) {
+                    // If the first int in the shared native handle has value, use it as the index
+                    memIndex = mFilter->mAvSharedHandle->data[mFilter->mAvSharedHandle->numFds];
+                }
+            }
+        }
+    }
+
+    mIonHandle = new C2HandleIon(dup(fd), dataSize);
     std::shared_ptr<C2LinearBlock> block = _C2BlockFactory::CreateLinearBlock(mIonHandle);
     if (block != nullptr) {
         // CreateLinearBlock delete mIonHandle after it create block successfully.
@@ -358,13 +424,11 @@
         JNIEnv *env = AndroidRuntime::getJNIEnv();
         std::unique_ptr<JMediaCodecLinearBlock> context{new JMediaCodecLinearBlock};
         context->mBlock = block;
-        std::shared_ptr<C2Buffer> pC2Buffer = context->toC2Buffer(0, mDataLength);
+        std::shared_ptr<C2Buffer> pC2Buffer = context->toC2Buffer(0, dataSize);
         context->mBuffer = pC2Buffer;
         mC2Buffer = pC2Buffer;
-        if (mAvHandle->numInts > 0) {
-            // use first int in the native_handle as the index
-            int index = mAvHandle->data[mAvHandle->numFds];
-            std::shared_ptr<C2Param> c2param = std::make_shared<C2DataIdInfo>(index, mDataId);
+        if (numInts > 0) {
+            std::shared_ptr<C2Param> c2param = std::make_shared<C2DataIdInfo>(memIndex, mDataId);
             std::shared_ptr<C2Info> info(std::static_pointer_cast<C2Info>(c2param));
             pC2Buffer->setInfo(info);
         }
@@ -471,7 +535,7 @@
 
         if (mediaEvent.avMemory.getNativeHandle() != NULL || mediaEvent.avDataId != 0) {
             sp<MediaEvent> mediaEventSp =
-                           new MediaEvent(mIFilter, mediaEvent.avMemory,
+                           new MediaEvent(mFilter, mediaEvent.avMemory,
                                mediaEvent.avDataId, dataLength + offset, obj);
             mediaEventSp->mAvHandleRefCnt++;
             env->SetLongField(obj, eventContext, (jlong) mediaEventSp.get());
@@ -505,10 +569,11 @@
 }
 
 jobjectArray FilterCallback::getTsRecordEvent(
-        jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events) {
+        jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events,
+                const std::vector<DemuxFilterEventExt::Event>& eventsExt) {
     JNIEnv *env = AndroidRuntime::getJNIEnv();
     jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/TsRecordEvent");
-    jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IIIJ)V");
+    jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IIIJJ)V");
 
     for (int i = 0; i < events.size(); i++) {
         auto event = events[i];
@@ -537,28 +602,39 @@
 
         jlong byteNumber = static_cast<jlong>(tsRecordEvent.byteNumber);
 
+        jlong pts = (eventsExt.size() > i) ? static_cast<jlong>(eventsExt[i].tsRecord().pts)
+                : static_cast<jlong>(Constant64Bit::INVALID_PRESENTATION_TIME_STAMP);
+
         jobject obj =
-                env->NewObject(eventClazz, eventInit, jpid, ts, sc, byteNumber);
+                env->NewObject(eventClazz, eventInit, jpid, ts, sc, byteNumber, pts);
         env->SetObjectArrayElement(arr, i, obj);
     }
     return arr;
 }
 
 jobjectArray FilterCallback::getMmtpRecordEvent(
-        jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events) {
+        jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events,
+                const std::vector<DemuxFilterEventExt::Event>& eventsExt) {
     JNIEnv *env = AndroidRuntime::getJNIEnv();
     jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/MmtpRecordEvent");
-    jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IJ)V");
+    jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IJIJ)V");
 
     for (int i = 0; i < events.size(); i++) {
         auto event = events[i];
+
         DemuxFilterMmtpRecordEvent mmtpRecordEvent = event.mmtpRecord();
 
         jint scHevcIndexMask = static_cast<jint>(mmtpRecordEvent.scHevcIndexMask);
         jlong byteNumber = static_cast<jlong>(mmtpRecordEvent.byteNumber);
+        jint mpuSequenceNumber = (eventsExt.size() > i)
+                ? static_cast<jint>(eventsExt[i].mmtpRecord().mpuSequenceNumber)
+                : static_cast<jint>(Constant::INVALID_MMTP_RECORD_EVENT_MPT_SEQUENCE_NUM);
+        jlong pts = (eventsExt.size() > i) ? static_cast<jlong>(eventsExt[i].mmtpRecord().pts)
+                : static_cast<jlong>(Constant64Bit::INVALID_PRESENTATION_TIME_STAMP);
 
         jobject obj =
-                env->NewObject(eventClazz, eventInit, scHevcIndexMask, byteNumber);
+                env->NewObject(eventClazz, eventInit, scHevcIndexMask, byteNumber,
+                        mpuSequenceNumber, pts);
         env->SetObjectArrayElement(arr, i, obj);
     }
     return arr;
@@ -627,12 +703,14 @@
     return arr;
 }
 
-Return<void> FilterCallback::onFilterEvent(const DemuxFilterEvent& filterEvent) {
-    ALOGD("FilterCallback::onFilterEvent");
+Return<void> FilterCallback::onFilterEvent_1_1(const DemuxFilterEvent& filterEvent,
+        const DemuxFilterEventExt& filterEventExt) {
+    ALOGD("FilterCallback::onFilterEvent_1_1");
 
     JNIEnv *env = AndroidRuntime::getJNIEnv();
 
     std::vector<DemuxFilterEvent::Event> events = filterEvent.events;
+    std::vector<DemuxFilterEventExt::Event> eventsExt = filterEventExt.events;
     jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/FilterEvent");
     jobjectArray array = env->NewObjectArray(events.size(), eventClazz, NULL);
 
@@ -652,11 +730,11 @@
                 break;
             }
             case DemuxFilterEvent::Event::hidl_discriminator::tsRecord: {
-                array = getTsRecordEvent(array, events);
+                array = getTsRecordEvent(array, events, eventsExt);
                 break;
             }
             case DemuxFilterEvent::Event::hidl_discriminator::mmtpRecord: {
-                array = getMmtpRecordEvent(array, events);
+                array = getMmtpRecordEvent(array, events, eventsExt);
                 break;
             }
             case DemuxFilterEvent::Event::hidl_discriminator::download: {
@@ -677,18 +755,26 @@
         }
     }
     env->CallVoidMethod(
-            mFilter,
+            mFilter->mFilterObj,
             gFields.onFilterEventID,
             array);
     return Void();
 }
 
+Return<void> FilterCallback::onFilterEvent(const DemuxFilterEvent& filterEvent) {
+    ALOGD("FilterCallback::onFilterEvent");
+    std::vector<DemuxFilterEventExt::Event> emptyEventsExt;
+    DemuxFilterEventExt emptyFilterEventExt {
+            .events = emptyEventsExt,
+    };
+    return onFilterEvent_1_1(filterEvent, emptyFilterEventExt);
+}
 
 Return<void> FilterCallback::onFilterStatus(const DemuxFilterStatus status) {
     ALOGD("FilterCallback::onFilterStatus");
     JNIEnv *env = AndroidRuntime::getJNIEnv();
     env->CallVoidMethod(
-            mFilter,
+            mFilter->mFilterObj,
             gFields.onFilterStatusID,
             (jint)status);
     return Void();
@@ -696,17 +782,11 @@
 
 void FilterCallback::setFilter(const sp<Filter> filter) {
     ALOGD("FilterCallback::setFilter");
-    mFilter = filter->mFilterObj;
-    mIFilter = filter->mFilterSp;
+    // JNI Object
+    mFilter = filter;
 }
 
-FilterCallback::~FilterCallback() {
-    JNIEnv *env = AndroidRuntime::getJNIEnv();
-    if (mFilter != NULL) {
-        env->DeleteWeakGlobalRef(mFilter);
-        mFilter = NULL;
-    }
-}
+FilterCallback::~FilterCallback() {}
 
 /////////////// Filter ///////////////////////
 
@@ -916,9 +996,82 @@
     return Void();
 }
 
+Return<void> FrontendCallback::onScanMessageExt1_1(FrontendScanMessageTypeExt1_1 type,
+        const FrontendScanMessageExt1_1& message) {
+    ALOGD("FrontendCallback::onScanMessageExt1_1, type=%d", type);
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    jclass clazz = env->FindClass("android/media/tv/tuner/Tuner");
+    switch(type) {
+        case FrontendScanMessageTypeExt1_1::MODULATION: {
+            jint modulation = -1;
+            switch (message.modulation().getDiscriminator()) {
+                case FrontendModulation::hidl_discriminator::dvbc: {
+                    modulation = (jint) message.modulation().dvbc();
+                    break;
+                }
+                case FrontendModulation::hidl_discriminator::dvbt: {
+                    modulation = (jint) message.modulation().dvbt();
+                    break;
+                }
+                case FrontendModulation::hidl_discriminator::dvbs: {
+                    modulation = (jint) message.modulation().dvbs();
+                    break;
+                }
+                case FrontendModulation::hidl_discriminator::isdbs: {
+                    modulation = (jint) message.modulation().isdbs();
+                    break;
+                }
+                case FrontendModulation::hidl_discriminator::isdbs3: {
+                    modulation = (jint) message.modulation().isdbs3();
+                    break;
+                }
+                case FrontendModulation::hidl_discriminator::isdbt: {
+                    modulation = (jint) message.modulation().isdbt();
+                    break;
+                }
+                case FrontendModulation::hidl_discriminator::atsc: {
+                    modulation = (jint) message.modulation().atsc();
+                    break;
+                }
+                case FrontendModulation::hidl_discriminator::atsc3: {
+                    modulation = (jint) message.modulation().atsc3();
+                    break;
+                }
+                case FrontendModulation::hidl_discriminator::dtmb: {
+                    modulation = (jint) message.modulation().dtmb();
+                    break;
+                }
+                default: {
+                    break;
+                }
+            }
+            if (modulation > 0) {
+                env->CallVoidMethod(
+                        mObject,
+                        env->GetMethodID(clazz, "onModulationReported", "(I)V"),
+                        modulation);
+            }
+            break;
+        }
+        case FrontendScanMessageTypeExt1_1::HIGH_PRIORITY: {
+            bool isHighPriority = message.isHighPriority();
+            env->CallVoidMethod(
+                    mObject,
+                    env->GetMethodID(clazz, "onPriorityReported", "([B)V"),
+                    isHighPriority);
+            break;
+        }
+        default:
+            break;
+    }
+    return Void();
+}
+
 /////////////// Tuner ///////////////////////
 
 sp<ITuner> JTuner::mTuner;
+sp<::android::hardware::tv::tuner::V1_1::ITuner> JTuner::mTuner_1_1;
+int JTuner::mTunerVersion = 0;
 
 JTuner::JTuner(JNIEnv *env, jobject thiz)
     : mClass(NULL) {
@@ -950,13 +1103,28 @@
 
 sp<ITuner> JTuner::getTunerService() {
     if (mTuner == nullptr) {
-        mTuner = ITuner::getService();
+        mTunerVersion = 0;
+        mTuner_1_1 = ::android::hardware::tv::tuner::V1_1::ITuner::getService();
 
-        if (mTuner == nullptr) {
-            ALOGW("Failed to get tuner service.");
-        }
-    }
-    return mTuner;
+        if (mTuner_1_1 == nullptr) {
+            ALOGW("Failed to get tuner 1.1 service.");
+            mTuner = ITuner::getService();
+            if (mTuner == nullptr) {
+                ALOGW("Failed to get tuner 1.0 service.");
+            } else {
+                mTunerVersion = 1 << 16;
+            }
+        } else {
+            mTuner = static_cast<sp<ITuner>>(mTuner_1_1);
+            mTunerVersion = ((1 << 16) | 1);
+         }
+     }
+     return mTuner;
+}
+
+jint JTuner::getTunerVersion() {
+    ALOGD("JTuner::getTunerVersion()");
+    return (jint) mTunerVersion;
 }
 
 jobject JTuner::getFrontendIds() {
@@ -996,6 +1164,7 @@
         return NULL;
     }
     mFe = fe;
+    mFe_1_1 = ::android::hardware::tv::tuner::V1_1::IFrontend::castFrom(mFe);
     mFeId = id;
     if (mDemux != NULL) {
         mDemux->setFrontendDataSource(mFeId);
@@ -1128,6 +1297,33 @@
             guardIntervalCap);
 }
 
+jobject JTuner::getDtmbFrontendCaps(JNIEnv *env, int id) {
+    jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DtmbFrontendCapabilities");
+    jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(IIIIII)V");
+
+    if (mTuner_1_1 == NULL) {
+        ALOGD("1.1 Tuner is not found. Dtmb Frontend Caps are not supported.");
+        return NULL;
+    }
+
+    Result result;
+    FrontendDtmbCapabilities dtmbCaps;
+    mTuner_1_1->getFrontendDtmbCapabilities(id,
+            [&](Result r, const FrontendDtmbCapabilities& caps) {
+        dtmbCaps = caps;
+        result = r;
+    });
+    jint modulationCap = dtmbCaps.modulationCap;
+    jint transmissionModeCap = dtmbCaps.transmissionModeCap;
+    jint guardIntervalCap = dtmbCaps.guardIntervalCap;
+    jint interleaveModeCap = dtmbCaps.interleaveModeCap;
+    jint codeRateCap = dtmbCaps.codeRateCap;
+    jint bandwidthCap = dtmbCaps.bandwidthCap;
+
+    return env->NewObject(clazz, capsInit, modulationCap, transmissionModeCap, guardIntervalCap,
+            interleaveModeCap, codeRateCap, bandwidthCap);
+}
+
 jobject JTuner::getFrontendInfo(int id) {
     FrontendInfo feInfo;
     Result res;
@@ -1158,6 +1354,15 @@
     FrontendInfo::FrontendCapabilities caps = feInfo.frontendCaps;
 
     jobject jcaps = NULL;
+
+    if (feInfo.type == static_cast<FrontendType>(
+            ::android::hardware::tv::tuner::V1_1::FrontendType::DTMB)) {
+        if (mTuner_1_1 == NULL) {
+            return NULL;
+        }
+        jcaps = getDtmbFrontendCaps(env, id);
+    }
+
     switch(feInfo.type) {
         case FrontendType::ANALOG:
             if (FrontendInfo::FrontendCapabilities::hidl_discriminator::analogCaps
@@ -1305,12 +1510,21 @@
     return lnbObj;
 }
 
-int JTuner::tune(const FrontendSettings& settings) {
+int JTuner::tune(const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1) {
     if (mFe == NULL) {
         ALOGE("frontend is not initialized");
         return (int)Result::INVALID_STATE;
     }
-    Result result = mFe->tune(settings);
+    Result result;
+    sp<::android::hardware::tv::tuner::V1_1::IFrontend> fe_1_1 =
+            ::android::hardware::tv::tuner::V1_1::IFrontend::castFrom(mFe);
+    if (fe_1_1 == NULL) {
+        ALOGD("1.1 frontend is not found. Using 1.0 instead.");
+        result = mFe->tune(settings);
+        return (int)result;
+    }
+
+    result = fe_1_1->tune_1_1(settings, settingsExt1_1);
     return (int)result;
 }
 
@@ -1323,12 +1537,22 @@
     return (int)result;
 }
 
-int JTuner::scan(const FrontendSettings& settings, FrontendScanType scanType) {
+int JTuner::scan(const FrontendSettings& settings, FrontendScanType scanType,
+        const FrontendSettingsExt1_1& settingsExt1_1) {
     if (mFe == NULL) {
         ALOGE("frontend is not initialized");
         return (int)Result::INVALID_STATE;
     }
-    Result result = mFe->scan(settings, scanType);
+    Result result;
+    sp<::android::hardware::tv::tuner::V1_1::IFrontend> fe_1_1 =
+            ::android::hardware::tv::tuner::V1_1::IFrontend::castFrom(mFe);
+    if (fe_1_1 == NULL) {
+        ALOGD("1.1 frontend is not found. Using 1.0 instead.");
+        result = mFe->scan(settings, scanType);
+        return (int)result;
+    }
+
+    result = fe_1_1->scan_1_1(settings, scanType, settingsExt1_1);
     return (int)result;
 }
 
@@ -1455,6 +1679,27 @@
     return (int) r;
 }
 
+int JTuner::linkCiCam(int id) {
+    if (mFe_1_1 == NULL) {
+        ALOGE("frontend 1.1 is not initialized");
+        return (int)Constant::INVALID_LTS_ID;
+    }
+
+    Result res;
+    uint32_t ltsId;
+    mFe_1_1->linkCiCam(static_cast<uint32_t>(id),
+            [&](Result r, uint32_t id) {
+                res = r;
+                ltsId = id;
+            });
+
+    if (res != Result::SUCCESS) {
+        return (int)Constant::INVALID_LTS_ID;
+    }
+
+    return (int) ltsId;
+}
+
 int JTuner::disconnectCiCam() {
     if (mDemux == NULL) {
         Result r = openDemux();
@@ -1466,6 +1711,18 @@
     return (int) r;
 }
 
+
+int JTuner::unlinkCiCam(int id) {
+    if (mFe_1_1 == NULL) {
+        ALOGE("frontend 1.1 is not initialized");
+        return (int)Result::INVALID_STATE;
+    }
+
+    Result r = mFe_1_1->unlinkCiCam(static_cast<uint32_t>(id));
+
+    return (int) r;
+}
+
 jobject JTuner::openDescrambler() {
     ALOGD("JTuner::openDescrambler");
     if (mTuner == nullptr || mDemux == nullptr) {
@@ -1504,6 +1761,7 @@
     }
 
     sp<IFilter> iFilterSp;
+    sp<::android::hardware::tv::tuner::V1_1::IFilter> iFilterSp_1_1;
     sp<FilterCallback> callback = new FilterCallback();
     Result res;
     mDemux->openFilter(type, bufferSize, callback,
@@ -1515,24 +1773,54 @@
         ALOGD("Failed to open filter, type = %d", type.mainType);
         return NULL;
     }
-    int fId;
+    uint64_t fId;
     iFilterSp->getId([&](Result, uint32_t filterId) {
         fId = filterId;
     });
+    iFilterSp_1_1 = ::android::hardware::tv::tuner::V1_1::IFilter::castFrom(iFilterSp);
+    if (iFilterSp_1_1 != NULL) {
+        iFilterSp_1_1->getId64Bit([&](Result, uint64_t filterId64Bit) {
+            fId = filterId64Bit;
+        });
+    }
 
     JNIEnv *env = AndroidRuntime::getJNIEnv();
     jobject filterObj =
             env->NewObject(
                     env->FindClass("android/media/tv/tuner/filter/Filter"),
                     gFields.filterInitID,
-                    (jint) fId);
+                    (jlong) fId);
 
     sp<Filter> filterSp = new Filter(iFilterSp, filterObj);
     filterSp->incStrong(filterObj);
     env->SetLongField(filterObj, gFields.filterContext, (jlong)filterSp.get());
-
+    filterSp->mIsMediaFilter = false;
+    filterSp->mAvSharedHandle = NULL;
     callback->setFilter(filterSp);
 
+    if (type.mainType == DemuxFilterMainType::MMTP) {
+        if (type.subType.mmtpFilterType() == DemuxMmtpFilterType::AUDIO ||
+                type.subType.mmtpFilterType() == DemuxMmtpFilterType::VIDEO) {
+            filterSp->mIsMediaFilter = true;
+        }
+    }
+
+    if (type.mainType == DemuxFilterMainType::TS) {
+        if (type.subType.tsFilterType() == DemuxTsFilterType::AUDIO ||
+                type.subType.tsFilterType() == DemuxTsFilterType::VIDEO) {
+            filterSp->mIsMediaFilter = true;
+        }
+    }
+
+    if (iFilterSp_1_1 != NULL && filterSp->mIsMediaFilter) {
+        iFilterSp_1_1->getAvSharedHandle([&](Result r, hidl_handle avMemory, uint64_t avMemSize) {
+            if (r == Result::SUCCESS) {
+                filterSp->mAvSharedHandle = native_handle_clone(avMemory.getNativeHandle());
+                filterSp->mAvSharedMemSize = avMemSize;
+            }
+        });
+    }
+
     return filterObj;
 }
 
@@ -1655,18 +1943,49 @@
     }
     JNIEnv *env = AndroidRuntime::getJNIEnv();
     jsize size = env->GetArrayLength(types);
-    std::vector<FrontendStatusType> v(size);
-    env->GetIntArrayRegion(types, 0, size, reinterpret_cast<jint*>(&v[0]));
+    jint intTypes[size];
+    env->GetIntArrayRegion(types, 0, size, intTypes);
+    std::vector<FrontendStatusType> v;
+    std::vector<FrontendStatusTypeExt1_1> v_1_1;
+    for (int i = 0; i < size; i++) {
+        if (isV1_1ExtendedStatusType(intTypes[i])) {
+            v_1_1.push_back(static_cast<FrontendStatusTypeExt1_1>(intTypes[i]));
+        } else {
+            v.push_back(static_cast<FrontendStatusType>(intTypes[i]));
+        }
+    }
 
     Result res;
     hidl_vec<FrontendStatus> status;
-    mFe->getStatus(v,
-            [&](Result r, const hidl_vec<FrontendStatus>& s) {
-                res = r;
-                status = s;
-            });
-    if (res != Result::SUCCESS) {
-        return NULL;
+    hidl_vec<FrontendStatusExt1_1> status_1_1;
+
+    if (v.size() > 0) {
+        mFe->getStatus(v,
+                [&](Result r, const hidl_vec<FrontendStatus>& s) {
+                    res = r;
+                    status = s;
+                });
+        if (res != Result::SUCCESS) {
+            return NULL;
+        }
+    }
+
+    if (v_1_1.size() > 0) {
+        sp<::android::hardware::tv::tuner::V1_1::IFrontend> iFeSp_1_1;
+        iFeSp_1_1 = ::android::hardware::tv::tuner::V1_1::IFrontend::castFrom(mFe);
+
+        if (iFeSp_1_1 != NULL) {
+            iFeSp_1_1->getStatusExt1_1(v_1_1,
+                [&](Result r, const hidl_vec<FrontendStatusExt1_1>& s) {
+                    res = r;
+                    status_1_1 = s;
+                });
+            if (res != Result::SUCCESS) {
+                return NULL;
+            }
+        } else {
+            ALOGW("getStatusExt1_1 is not supported with the current HAL implementation.");
+        }
     }
 
     jclass clazz = env->FindClass("android/media/tv/tuner/frontend/FrontendStatus");
@@ -1894,14 +2213,283 @@
         }
     }
 
+    for (auto s : status_1_1) {
+        switch(s.getDiscriminator()) {
+            case FrontendStatusExt1_1::hidl_discriminator::modulations: {
+                jfieldID field = env->GetFieldID(clazz, "mModulationsExt", "[I");
+                std::vector<FrontendModulation> v = s.modulations();
+
+                jintArray valObj = env->NewIntArray(v.size());
+                bool valid = false;
+                jint m[1];
+                for (int i = 0; i < v.size(); i++) {
+                    auto modulation = v[i];
+                    switch(modulation.getDiscriminator()) {
+                        case FrontendModulation::hidl_discriminator::dvbc: {
+                            m[0] = static_cast<jint>(modulation.dvbc());
+                            env->SetIntArrayRegion(valObj, i, 1, m);
+                            valid = true;
+                            break;
+                        }
+                        case FrontendModulation::hidl_discriminator::dvbs: {
+                            m[0] = static_cast<jint>(modulation.dvbs());
+                            env->SetIntArrayRegion(valObj, i, 1, m);
+                            valid = true;
+                           break;
+                        }
+                        case FrontendModulation::hidl_discriminator::dvbt: {
+                            m[0] = static_cast<jint>(modulation.dvbt());
+                            env->SetIntArrayRegion(valObj, i, 1, m);
+                            valid = true;
+                            break;
+                        }
+                        case FrontendModulation::hidl_discriminator::isdbs: {
+                            m[0] = static_cast<jint>(modulation.isdbs());
+                            env->SetIntArrayRegion(valObj, i, 1, m);
+                            valid = true;
+                            break;
+                        }
+                        case FrontendModulation::hidl_discriminator::isdbs3: {
+                            m[0] = static_cast<jint>(modulation.isdbs3());
+                            env->SetIntArrayRegion(valObj, i, 1, m);
+                            valid = true;
+                            break;
+                        }
+                        case FrontendModulation::hidl_discriminator::isdbt: {
+                            m[0] = static_cast<jint>(modulation.isdbt());
+                            env->SetIntArrayRegion(valObj, i, 1, m);
+                            valid = true;
+                            break;
+                        }
+                        case FrontendModulation::hidl_discriminator::atsc: {
+                            m[0] = static_cast<jint>(modulation.atsc());
+                            env->SetIntArrayRegion(valObj, i, 1, m);
+                            valid = true;
+                            break;
+                        }
+                        case FrontendModulation::hidl_discriminator::atsc3: {
+                            m[0] = static_cast<jint>(modulation.atsc3());
+                            env->SetIntArrayRegion(valObj, i, 1, m);
+                            valid = true;
+                            break;
+                        }
+                        case FrontendModulation::hidl_discriminator::dtmb: {
+                            m[0] = static_cast<jint>(modulation.dtmb());
+                            env->SetIntArrayRegion(valObj, i, 1, m);
+                            valid = true;
+                            break;
+                        }
+                        default:
+                            break;
+                    }
+                }
+                if (valid) {
+                    env->SetObjectField(statusObj, field, valObj);
+                }
+                break;
+            }
+            case FrontendStatusExt1_1::hidl_discriminator::bers: {
+                jfieldID field = env->GetFieldID(clazz, "mBers", "[I");
+                std::vector<uint32_t> v = s.bers();
+
+                jintArray valObj = env->NewIntArray(v.size());
+                env->SetIntArrayRegion(valObj, 0, v.size(), reinterpret_cast<jint*>(&v[0]));
+
+                env->SetObjectField(statusObj, field, valObj);
+                break;
+            }
+            case FrontendStatusExt1_1::hidl_discriminator::codeRates: {
+                jfieldID field = env->GetFieldID(clazz, "mCodeRates", "[I");
+                std::vector<::android::hardware::tv::tuner::V1_1::FrontendInnerFec> v
+                        = s.codeRates();
+
+                jintArray valObj = env->NewIntArray(v.size());
+                env->SetIntArrayRegion(valObj, 0, v.size(), reinterpret_cast<jint*>(&v[0]));
+
+                env->SetObjectField(statusObj, field, valObj);
+                break;
+            }
+            case FrontendStatusExt1_1::hidl_discriminator::bandwidth: {
+                jfieldID field = env->GetFieldID(clazz, "mBandwidth", "Ljava/lang/Integer;");
+                auto bandwidth = s.bandwidth();
+                jint intBandwidth;
+                bool valid = true;
+                switch(bandwidth.getDiscriminator()) {
+                    case FrontendBandwidth::hidl_discriminator::atsc3: {
+                        intBandwidth = static_cast<jint>(bandwidth.atsc3());
+                        break;
+                    }
+                    case FrontendBandwidth::hidl_discriminator::dvbt: {
+                        intBandwidth = static_cast<jint>(bandwidth.dvbt());
+                        break;
+                    }
+                    case FrontendBandwidth::hidl_discriminator::isdbt: {
+                        intBandwidth = static_cast<jint>(bandwidth.isdbt());
+                        break;
+                    }
+                    case FrontendBandwidth::hidl_discriminator::dtmb: {
+                        intBandwidth = static_cast<jint>(bandwidth.dtmb());
+                        break;
+                    }
+                    default:
+                        valid = false;
+                        break;
+                }
+                if (valid) {
+                    jobject newIntegerObj = env->NewObject(intClazz, initInt, intBandwidth);
+                    env->SetObjectField(statusObj, field, newIntegerObj);
+                }
+                break;
+            }
+            case FrontendStatusExt1_1::hidl_discriminator::interval: {
+                jfieldID field = env->GetFieldID(clazz, "mGuardInterval", "Ljava/lang/Integer;");
+                auto interval = s.interval();
+                jint intInterval;
+                bool valid = true;
+                switch(interval.getDiscriminator()) {
+                    case FrontendGuardInterval::hidl_discriminator::dvbt: {
+                        intInterval = static_cast<jint>(interval.dvbt());
+                        break;
+                    }
+                    case FrontendGuardInterval::hidl_discriminator::isdbt: {
+                        intInterval = static_cast<jint>(interval.isdbt());
+                        break;
+                    }
+                    case FrontendGuardInterval::hidl_discriminator::dtmb: {
+                        intInterval = static_cast<jint>(interval.dtmb());
+                        break;
+                    }
+                    default:
+                        valid = false;
+                        break;
+                }
+                if (valid) {
+                    jobject newIntegerObj = env->NewObject(intClazz, initInt, intInterval);
+                    env->SetObjectField(statusObj, field, newIntegerObj);
+                }
+                break;
+            }
+            case FrontendStatusExt1_1::hidl_discriminator::transmissionMode: {
+                jfieldID field = env->GetFieldID(clazz, "mTransmissionMode", "Ljava/lang/Integer;");
+                auto transmissionMode = s.transmissionMode();
+                jint intTransmissionMode;
+                bool valid = true;
+                switch(transmissionMode.getDiscriminator()) {
+                    case FrontendTransmissionMode::hidl_discriminator::dvbt: {
+                        intTransmissionMode = static_cast<jint>(transmissionMode.dvbt());
+                        break;
+                    }
+                    case FrontendTransmissionMode::hidl_discriminator::isdbt: {
+                        intTransmissionMode = static_cast<jint>(transmissionMode.isdbt());
+                        break;
+                    }
+                    case FrontendTransmissionMode::hidl_discriminator::dtmb: {
+                        intTransmissionMode = static_cast<jint>(transmissionMode.dtmb());
+                        break;
+                    }
+                    default:
+                        valid = false;
+                        break;
+                }
+                if (valid) {
+                    jobject newIntegerObj = env->NewObject(intClazz, initInt, intTransmissionMode);
+                    env->SetObjectField(statusObj, field, newIntegerObj);
+                }
+                break;
+            }
+            case FrontendStatusExt1_1::hidl_discriminator::uec: {
+                jfieldID field = env->GetFieldID(clazz, "mUec", "Ljava/lang/Integer;");
+                jobject newIntegerObj = env->NewObject(
+                        intClazz, initInt, static_cast<jint>(s.uec()));
+                env->SetObjectField(statusObj, field, newIntegerObj);
+                break;
+            }
+            case FrontendStatusExt1_1::hidl_discriminator::systemId: {
+                jfieldID field = env->GetFieldID(clazz, "mSystemId", "Ljava/lang/Integer;");
+                jobject newIntegerObj = env->NewObject(
+                        intClazz, initInt, static_cast<jint>(s.systemId()));
+                env->SetObjectField(statusObj, field, newIntegerObj);
+                break;
+            }
+            case FrontendStatusExt1_1::hidl_discriminator::interleaving: {
+                jfieldID field = env->GetFieldID(clazz, "mInterleaving", "[I");
+
+                std::vector<FrontendInterleaveMode> v = s.interleaving();
+                jintArray valObj = env->NewIntArray(v.size());
+                bool valid = false;
+                jint in[1];
+                for (int i = 0; i < v.size(); i++) {
+                    auto interleaving = v[i];
+                    switch(interleaving.getDiscriminator()) {
+                        case FrontendInterleaveMode::hidl_discriminator::atsc3: {
+                            in[0] = static_cast<jint>(interleaving.atsc3());
+                            env->SetIntArrayRegion(valObj, i, 1, in);
+                            valid = true;
+                            break;
+                        }
+                        case FrontendInterleaveMode::hidl_discriminator::dvbc: {
+                            in[0] = static_cast<jint>(interleaving.dvbc());
+                            env->SetIntArrayRegion(valObj, i, 1, in);
+                            valid = true;
+                           break;
+                        }
+                        case FrontendInterleaveMode::hidl_discriminator::dtmb: {
+                            in[0] = static_cast<jint>(interleaving.dtmb());
+                            env->SetIntArrayRegion(valObj, i, 1, in);
+                            valid = true;
+                           break;
+                        }
+                        default:
+                            break;
+                    }
+                }
+                if (valid) {
+                    env->SetObjectField(statusObj, field, valObj);
+                }
+                break;
+            }
+            case FrontendStatusExt1_1::hidl_discriminator::isdbtSegment: {
+                jfieldID field = env->GetFieldID(clazz, "mIsdbtSegment", "[I");
+                std::vector<uint8_t> v = s.isdbtSegment();
+
+                jintArray valObj = env->NewIntArray(v.size());
+                env->SetIntArrayRegion(valObj, 0, v.size(), reinterpret_cast<jint*>(&v[0]));
+
+                env->SetObjectField(statusObj, field, valObj);
+                break;
+            }
+            case FrontendStatusExt1_1::hidl_discriminator::tsDataRate: {
+                jfieldID field = env->GetFieldID(clazz, "mTsDataRate", "[I");
+                std::vector<uint32_t> v = s.tsDataRate();
+
+                jintArray valObj = env->NewIntArray(v.size());
+                env->SetIntArrayRegion(valObj, 0, v.size(), reinterpret_cast<jint*>(&v[0]));
+
+                env->SetObjectField(statusObj, field, valObj);
+                break;
+            }
+            default: {
+                break;
+            }
+        }
+    }
     return statusObj;
 }
 
+bool JTuner::isV1_1ExtendedStatusType(int type) {
+    return (type > static_cast<int>(FrontendStatusType::ATSC3_PLP_INFO)
+                && type <= static_cast<int>(FrontendStatusTypeExt1_1::TS_DATA_RATES));
+}
+
 jint JTuner::closeFrontend() {
     Result r = Result::SUCCESS;
     if (mFe != NULL) {
         r = mFe->close();
     }
+    if (r == Result::SUCCESS) {
+        mFe = NULL;
+        mFe_1_1 = NULL;
+    }
     return (jint) r;
 }
 
@@ -1910,6 +2498,9 @@
     if (mDemux != NULL) {
         r = mDemux->close();
     }
+    if (r == Result::SUCCESS) {
+        mDemux = NULL;
+    }
     return (jint) r;
 }
 
@@ -1962,6 +2553,22 @@
     return freq;
 }
 
+static uint32_t getFrontendSettingsEndFreq(JNIEnv *env, const jobject& settings) {
+    jclass clazz = env->FindClass("android/media/tv/tuner/frontend/FrontendSettings");
+    jfieldID endFreqField = env->GetFieldID(clazz, "mEndFrequency", "I");
+    uint32_t endFreq = static_cast<uint32_t>(env->GetIntField(settings, endFreqField));
+    return endFreq;
+}
+
+static FrontendSpectralInversion getFrontendSettingsSpectralInversion(
+        JNIEnv *env, const jobject& settings) {
+    jclass clazz = env->FindClass("android/media/tv/tuner/frontend/FrontendSettings");
+    jfieldID inversionField = env->GetFieldID(clazz, "mSpectralInversion", "I");
+    FrontendSpectralInversion inversion =
+            static_cast<FrontendSpectralInversion>(env->GetIntField(settings, inversionField));
+    return inversion;
+}
+
 static FrontendSettings getAnalogFrontendSettings(JNIEnv *env, const jobject& settings) {
     FrontendSettings frontendSettings;
     uint32_t freq = getFrontendSettingsFreq(env, settings);
@@ -1981,6 +2588,18 @@
     return frontendSettings;
 }
 
+static void getAnalogFrontendSettingsExt1_1(JNIEnv *env, const jobject& settings,
+        FrontendSettingsExt1_1& settingsExt1_1) {
+    jclass clazz = env->FindClass("android/media/tv/tuner/frontend/AnalogFrontendSettings");
+    FrontendAnalogAftFlag aftFlag =
+            static_cast<FrontendAnalogAftFlag>(
+                    env->GetIntField(settings, env->GetFieldID(clazz, "mAftFlag", "I")));
+    FrontendAnalogSettingsExt1_1 analogExt1_1 {
+        .aftFlag = aftFlag,
+    };
+    settingsExt1_1.settingExt.analog(analogExt1_1);
+}
+
 static hidl_vec<FrontendAtsc3PlpSettings> getAtsc3PlpSettings(
         JNIEnv *env, const jobject& settings) {
     jclass clazz = env->FindClass("android/media/tv/tuner/frontend/Atsc3FrontendSettings");
@@ -2100,6 +2719,19 @@
     return frontendSettings;
 }
 
+static void getDvbcFrontendSettingsExt1_1(JNIEnv *env, const jobject& settings,
+        FrontendSettingsExt1_1& settingsExt1_1) {
+    jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbcFrontendSettings");
+    FrontendCableTimeInterleaveMode interleaveMode =
+            static_cast<FrontendCableTimeInterleaveMode>(
+                    env->GetIntField(settings, env->GetFieldID(clazz, "mInterleaveMode", "I")));
+
+    FrontendDvbcSettingsExt1_1 dvbcExt1_1 {
+        .interleaveMode = interleaveMode,
+    };
+    settingsExt1_1.settingExt.dvbc(dvbcExt1_1);
+}
+
 static FrontendDvbsCodeRate getDvbsCodeRate(JNIEnv *env, const jobject& settings) {
     jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbsFrontendSettings");
     jobject jcodeRate =
@@ -2141,7 +2773,6 @@
     uint32_t freq = getFrontendSettingsFreq(env, settings);
     jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbsFrontendSettings");
 
-
     FrontendDvbsModulation modulation =
             static_cast<FrontendDvbsModulation>(
                     env->GetIntField(settings, env->GetFieldID(clazz, "mModulation", "I")));
@@ -2180,6 +2811,22 @@
     return frontendSettings;
 }
 
+static void getDvbsFrontendSettingsExt1_1(JNIEnv *env, const jobject& settings,
+        FrontendSettingsExt1_1& settingsExt1_1) {
+    jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbsFrontendSettings");
+    FrontendDvbsScanType scanType =
+            static_cast<FrontendDvbsScanType>(
+                    env->GetIntField(settings, env->GetFieldID(clazz, "mScanType", "I")));
+    bool isDiseqcRxMessage = static_cast<bool>(env->GetBooleanField(
+            settings, env->GetFieldID(clazz, "mIsDiseqcRxMessage", "B")));
+
+    FrontendDvbsSettingsExt1_1 dvbsExt1_1 {
+        .scanType = scanType,
+        .isDiseqcRxMessage = isDiseqcRxMessage,
+    };
+    settingsExt1_1.settingExt.dvbs(dvbsExt1_1);
+}
+
 static FrontendSettings getDvbtFrontendSettings(JNIEnv *env, const jobject& settings) {
     FrontendSettings frontendSettings;
     uint32_t freq = getFrontendSettingsFreq(env, settings);
@@ -2246,6 +2893,25 @@
     return frontendSettings;
 }
 
+static void getDvbtFrontendSettingsExt1_1(JNIEnv *env, const jobject& settings,
+        FrontendSettingsExt1_1& settingsExt1_1) {
+    jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbtFrontendSettings");
+
+    FrontendDvbtSettingsExt1_1 dvbtExt1_1;
+    int transmissionMode =
+            env->GetIntField(settings, env->GetFieldID(clazz, "mTransmissionMode", "I"));
+    dvbtExt1_1.transmissionMode = static_cast<
+            ::android::hardware::tv::tuner::V1_1::FrontendDvbtTransmissionMode>(
+                    transmissionMode);
+
+    int constellation =
+            env->GetIntField(settings, env->GetFieldID(clazz, "mConstellation", "I"));
+    dvbtExt1_1.constellation = static_cast<
+            ::android::hardware::tv::tuner::V1_1::FrontendDvbtConstellation>(constellation);
+
+    settingsExt1_1.settingExt.dvbt(dvbtExt1_1);
+}
+
 static FrontendSettings getIsdbsFrontendSettings(JNIEnv *env, const jobject& settings) {
     FrontendSettings frontendSettings;
     uint32_t freq = getFrontendSettingsFreq(env, settings);
@@ -2354,6 +3020,41 @@
     return frontendSettings;
 }
 
+static void getDtmbFrontendSettings(JNIEnv *env, const jobject& settings,
+        FrontendSettingsExt1_1& settingsExt1_1) {
+    uint32_t freq = getFrontendSettingsFreq(env, settings);
+    jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DtmbFrontendSettings");
+    FrontendDtmbModulation modulation =
+            static_cast<FrontendDtmbModulation>(
+                    env->GetIntField(settings, env->GetFieldID(clazz, "mModulation", "I")));
+    FrontendDtmbBandwidth bandwidth =
+            static_cast<FrontendDtmbBandwidth>(
+                    env->GetIntField(settings, env->GetFieldID(clazz, "mBandwidth", "I")));
+    FrontendDtmbTransmissionMode transmissionMode =
+            static_cast<FrontendDtmbTransmissionMode>(
+                    env->GetIntField(settings, env->GetFieldID(clazz, "mTransmissionMode", "I")));
+    FrontendDtmbCodeRate codeRate =
+            static_cast<FrontendDtmbCodeRate>(
+                    env->GetIntField(settings, env->GetFieldID(clazz, "mCodeRate", "I")));
+    FrontendDtmbGuardInterval guardInterval =
+            static_cast<FrontendDtmbGuardInterval>(
+                    env->GetIntField(settings, env->GetFieldID(clazz, "mGuardInterval", "I")));
+    FrontendDtmbTimeInterleaveMode interleaveMode =
+            static_cast<FrontendDtmbTimeInterleaveMode>(
+                    env->GetIntField(settings, env->GetFieldID(clazz, "mTimeInterleaveMode", "I")));
+
+    FrontendDtmbSettings frontendDtmbSettings {
+            .frequency = freq,
+            .modulation = modulation,
+            .bandwidth = bandwidth,
+            .transmissionMode = transmissionMode,
+            .codeRate = codeRate,
+            .guardInterval = guardInterval,
+            .interleaveMode = interleaveMode,
+    };
+    settingsExt1_1.settingExt.dtmb(frontendDtmbSettings);
+}
+
 static FrontendSettings getFrontendSettings(JNIEnv *env, int type, jobject settings) {
     ALOGD("getFrontendSettings %d", type);
 
@@ -2386,6 +3087,59 @@
     }
 }
 
+static FrontendSettingsExt1_1 getFrontendSettingsExt1_1(JNIEnv *env, int type, jobject settings) {
+    ALOGD("getFrontendSettingsExt1_1 %d", type);
+
+    FrontendSettingsExt1_1 settingsExt1_1 {
+        .endFrequency = static_cast<uint32_t>(Constant::INVALID_FRONTEND_SETTING_FREQUENCY),
+        .inversion = FrontendSpectralInversion::UNDEFINED,
+    };
+    settingsExt1_1.settingExt.noinit();
+
+    if (type == static_cast<int>(::android::hardware::tv::tuner::V1_1::FrontendType::DTMB)) {
+        getDtmbFrontendSettings(env, settings, settingsExt1_1);
+    } else {
+        FrontendType feType = static_cast<FrontendType>(type);
+        switch(feType) {
+            case FrontendType::DVBS:
+                getDvbsFrontendSettingsExt1_1(env, settings, settingsExt1_1);
+                break;
+            case FrontendType::DVBT:
+                getDvbtFrontendSettingsExt1_1(env, settings, settingsExt1_1);
+                break;
+            case FrontendType::ANALOG:
+                getAnalogFrontendSettingsExt1_1(env, settings, settingsExt1_1);
+                break;
+            case FrontendType::ATSC3:
+                break;
+            case FrontendType::ATSC:
+                break;
+            case FrontendType::DVBC:
+                getDvbcFrontendSettingsExt1_1(env, settings, settingsExt1_1);
+                break;
+            case FrontendType::ISDBS:
+                break;
+            case FrontendType::ISDBS3:
+                break;
+            case FrontendType::ISDBT:
+                break;
+            default:
+                // should never happen because a type is associated with a subclass of
+                // FrontendSettings and not set by users
+                jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+                    "Unsupported frontend type %d", type);
+                return FrontendSettingsExt1_1();
+        }
+    }
+
+    uint32_t endFreq = getFrontendSettingsEndFreq(env, settings);
+    FrontendSpectralInversion inversion = getFrontendSettingsSpectralInversion(env, settings);
+    settingsExt1_1.endFrequency = endFreq;
+    settingsExt1_1.inversion = inversion;
+
+    return settingsExt1_1;
+}
+
 static sp<Filter> getFilter(JNIEnv *env, jobject filter) {
     return (Filter *)env->GetLongField(filter, gFields.filterContext);
 }
@@ -2460,7 +3214,7 @@
     jclass filterClazz = env->FindClass("android/media/tv/tuner/filter/Filter");
     gFields.filterContext = env->GetFieldID(filterClazz, "mNativeContext", "J");
     gFields.filterInitID =
-            env->GetMethodID(filterClazz, "<init>", "(I)V");
+            env->GetMethodID(filterClazz, "<init>", "(J)V");
     gFields.onFilterStatusID =
             env->GetMethodID(filterClazz, "onFilterStatus", "(I)V");
     gFields.onFilterEventID =
@@ -2501,6 +3255,11 @@
     setTuner(env,thiz, tuner);
 }
 
+static jint android_media_tv_Tuner_native_get_tuner_version(JNIEnv *env, jobject thiz) {
+    sp<JTuner> tuner = getTuner(env, thiz);
+    return tuner->getTunerVersion();
+}
+
 static jobject android_media_tv_Tuner_get_frontend_ids(JNIEnv *env, jobject thiz) {
     sp<JTuner> tuner = getTuner(env, thiz);
     return tuner->getFrontendIds();
@@ -2522,7 +3281,9 @@
 
 static int android_media_tv_Tuner_tune(JNIEnv *env, jobject thiz, jint type, jobject settings) {
     sp<JTuner> tuner = getTuner(env, thiz);
-    return tuner->tune(getFrontendSettings(env, type, settings));
+    FrontendSettings setting = getFrontendSettings(env, type, settings);
+    FrontendSettingsExt1_1 settingExt = getFrontendSettingsExt1_1(env, type, settings);
+    return tuner->tune(setting, settingExt);
 }
 
 static int android_media_tv_Tuner_stop_tune(JNIEnv *env, jobject thiz) {
@@ -2533,8 +3294,9 @@
 static int android_media_tv_Tuner_scan(
         JNIEnv *env, jobject thiz, jint settingsType, jobject settings, jint scanType) {
     sp<JTuner> tuner = getTuner(env, thiz);
-    return tuner->scan(getFrontendSettings(
-            env, settingsType, settings), static_cast<FrontendScanType>(scanType));
+    FrontendSettings setting = getFrontendSettings(env, settingsType, settings);
+    FrontendSettingsExt1_1 settingExt = getFrontendSettingsExt1_1(env, settingsType, settings);
+    return tuner->scan(setting, static_cast<FrontendScanType>(scanType), settingExt);
 }
 
 static int android_media_tv_Tuner_stop_scan(JNIEnv *env, jobject thiz) {
@@ -2579,11 +3341,21 @@
     return tuner->connectCiCam(id);
 }
 
+static int android_media_tv_Tuner_link_cicam(JNIEnv *env, jobject thiz, jint id) {
+    sp<JTuner> tuner = getTuner(env, thiz);
+    return tuner->linkCiCam(id);
+}
+
 static int android_media_tv_Tuner_disconnect_cicam(JNIEnv *env, jobject thiz) {
     sp<JTuner> tuner = getTuner(env, thiz);
     return tuner->disconnectCiCam();
 }
 
+static int android_media_tv_Tuner_unlink_cicam(JNIEnv *env, jobject thiz, jint id) {
+    sp<JTuner> tuner = getTuner(env, thiz);
+    return tuner->unlinkCiCam(id);
+}
+
 static jobject android_media_tv_Tuner_get_frontend_info(JNIEnv *env, jobject thiz, jint id) {
     sp<JTuner> tuner = getTuner(env, thiz);
     return tuner->getFrontendInfo(id);
@@ -2719,6 +3491,31 @@
     return filterAvSettings;
 }
 
+static bool getAvStreamType(JNIEnv *env, jobject filterConfigObj, AvStreamType& type) {
+    jobject settingsObj =
+            env->GetObjectField(
+                    filterConfigObj,
+                    env->GetFieldID(
+                            env->FindClass("android/media/tv/tuner/filter/FilterConfiguration"),
+                            "mSettings",
+                            "Landroid/media/tv/tuner/filter/Settings;"));
+    jclass clazz = env->FindClass("android/media/tv/tuner/filter/AvSettings");
+    AvStreamType streamType;
+    AudioStreamType audioStreamType = static_cast<AudioStreamType>(
+            env->GetIntField(settingsObj, env->GetFieldID(clazz, "mAudioStreamType", "I")));
+    if (audioStreamType != AudioStreamType::UNDEFINED) {
+        type.audio(audioStreamType);
+        return true;
+    }
+    VideoStreamType videoStreamType = static_cast<VideoStreamType>(
+            env->GetIntField(settingsObj, env->GetFieldID(clazz, "mVideoStreamType", "I")));
+    if (videoStreamType != VideoStreamType::UNDEFINED) {
+        type.video(videoStreamType);
+        return true;
+    }
+    return false;
+}
+
 static DemuxFilterPesDataSettings getFilterPesDataSettings(JNIEnv *env, const jobject& settings) {
     jclass clazz = env->FindClass("android/media/tv/tuner/filter/PesSettings");
     uint16_t streamId = static_cast<uint16_t>(
@@ -2991,6 +3788,29 @@
     return filterSettings;
 }
 
+static Result configureIpFilterContextId(
+        JNIEnv *env, sp<IFilter> iFilterSp, jobject ipFilterConfigObj) {
+    jclass clazz = env->FindClass(
+            "android/media/tv/tuner/filter/IpFilterConfiguration");
+    uint32_t cid = env->GetIntField(ipFilterConfigObj, env->GetFieldID(
+            clazz, "mIpFilterContextId", "I"));
+    Result res = Result::SUCCESS;
+    if (cid != static_cast<uint32_t>(Constant::INVALID_IP_FILTER_CONTEXT_ID)) {
+        sp<::android::hardware::tv::tuner::V1_1::IFilter> iFilterSp_1_1;
+        iFilterSp_1_1 = ::android::hardware::tv::tuner::V1_1::IFilter::castFrom(iFilterSp);
+
+        if (iFilterSp_1_1 != NULL) {
+            res = iFilterSp_1_1->configureIpCid(cid);
+            if (res != Result::SUCCESS) {
+                return res;
+            }
+        } else {
+            ALOGW("configureIpCid is not supported with the current HAL implementation.");
+        }
+    }
+    return res;
+}
+
 static jint copyData(JNIEnv *env, std::unique_ptr<MQ>& mq, EventFlag* flag, jbyteArray buffer,
         jlong offset, jlong size) {
     ALOGD("copyData, size=%ld, offset=%ld", (long) size, (long) offset);
@@ -3018,6 +3838,16 @@
     return size;
 }
 
+static bool isAvFilterSettings(DemuxFilterSettings filterSettings) {
+    return (filterSettings.getDiscriminator() == DemuxFilterSettings::hidl_discriminator::ts
+            && filterSettings.ts().filterSettings.getDiscriminator()
+                    == DemuxTsFilterSettings::FilterSettings::hidl_discriminator::av)
+            ||
+            (filterSettings.getDiscriminator() == DemuxFilterSettings::hidl_discriminator::mmtp
+            && filterSettings.mmtp().filterSettings.getDiscriminator()
+                    == DemuxMmtpFilterSettings::FilterSettings::hidl_discriminator::av);
+}
+
 static jint android_media_tv_Tuner_configure_filter(
         JNIEnv *env, jobject filter, int type, int subtype, jobject settings) {
     ALOGD("configure filter type=%d, subtype=%d", type, subtype);
@@ -3034,6 +3864,27 @@
         return (jint) res;
     }
 
+    if (static_cast<DemuxFilterMainType>(type) == DemuxFilterMainType::IP) {
+        res = configureIpFilterContextId(env, iFilterSp, settings);
+        if (res != Result::SUCCESS) {
+            return (jint) res;
+        }
+    }
+
+    AvStreamType streamType;
+    if (isAvFilterSettings(filterSettings) && getAvStreamType(env, settings, streamType)) {
+        sp<::android::hardware::tv::tuner::V1_1::IFilter> iFilterSp_1_1;
+        iFilterSp_1_1 = ::android::hardware::tv::tuner::V1_1::IFilter::castFrom(iFilterSp);
+        if (iFilterSp_1_1 != NULL) {
+            res = iFilterSp_1_1->configureAvStreamType(streamType);
+        } else {
+            ALOGW("configureAvStreamType is not supported with the current HAL implementation.");
+        }
+        if (res != Result::SUCCESS) {
+            return (jint) res;
+        }
+    }
+
     MQDescriptorSync<uint8_t> filterMQDesc;
     Result getQueueDescResult = Result::UNKNOWN_ERROR;
     if (filterSp->mFilterMQ == NULL) {
@@ -3071,6 +3922,36 @@
     return (jint) id;
 }
 
+static jlong android_media_tv_Tuner_get_filter_64bit_id(JNIEnv* env, jobject filter) {
+    sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter();
+    if (iFilterSp == NULL) {
+        ALOGD("Failed to get filter ID: filter not found");
+        return static_cast<jlong>(
+                ::android::hardware::tv::tuner::V1_1::Constant64Bit::INVALID_FILTER_ID_64BIT);
+    }
+
+    sp<::android::hardware::tv::tuner::V1_1::IFilter> iFilterSp_1_1;
+    iFilterSp_1_1 = ::android::hardware::tv::tuner::V1_1::IFilter::castFrom(iFilterSp);
+    Result res;
+    uint64_t id;
+
+    if (iFilterSp_1_1 != NULL) {
+        iFilterSp_1_1->getId64Bit(
+            [&](Result r, uint64_t filterId64Bit) {
+                res = r;
+                id = filterId64Bit;
+        });
+    } else {
+        ALOGW("getId64Bit is not supported with the current HAL implementation.");
+        return static_cast<jlong>(
+                ::android::hardware::tv::tuner::V1_1::Constant64Bit::INVALID_FILTER_ID_64BIT);
+    }
+
+    return (res == Result::SUCCESS) ?
+            static_cast<jlong>(id) : static_cast<jlong>(
+                    ::android::hardware::tv::tuner::V1_1::Constant64Bit::INVALID_FILTER_ID_64BIT);
+}
+
 static jint android_media_tv_Tuner_set_filter_data_source(
         JNIEnv* env, jobject filter, jobject srcFilter) {
     sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter();
@@ -3684,6 +4565,7 @@
 static const JNINativeMethod gTunerMethods[] = {
     { "nativeInit", "()V", (void *)android_media_tv_Tuner_native_init },
     { "nativeSetup", "()V", (void *)android_media_tv_Tuner_native_setup },
+    { "nativeGetTunerVersion", "()I", (void *)android_media_tv_Tuner_native_get_tuner_version },
     { "nativeGetFrontendIds", "()Ljava/util/List;",
             (void *)android_media_tv_Tuner_get_frontend_ids },
     { "nativeOpenFrontendByHandle", "(I)Landroid/media/tv/tuner/Tuner$Frontend;",
@@ -3705,6 +4587,10 @@
     { "nativeGetAvSyncTime", "(I)Ljava/lang/Long;",
             (void *)android_media_tv_Tuner_get_av_sync_time },
     { "nativeConnectCiCam", "(I)I", (void *)android_media_tv_Tuner_connect_cicam },
+    { "nativeLinkCiCam", "(I)I",
+            (void *)android_media_tv_Tuner_link_cicam },
+    { "nativeUnlinkCiCam", "(I)I",
+            (void *)android_media_tv_Tuner_unlink_cicam },
     { "nativeDisconnectCiCam", "()I", (void *)android_media_tv_Tuner_disconnect_cicam },
     { "nativeGetFrontendInfo", "(I)Landroid/media/tv/tuner/frontend/FrontendInfo;",
             (void *)android_media_tv_Tuner_get_frontend_info },
@@ -3735,6 +4621,8 @@
     { "nativeConfigureFilter", "(IILandroid/media/tv/tuner/filter/FilterConfiguration;)I",
             (void *)android_media_tv_Tuner_configure_filter },
     { "nativeGetId", "()I", (void *)android_media_tv_Tuner_get_filter_id },
+    { "nativeGetId64Bit", "()J",
+            (void *)android_media_tv_Tuner_get_filter_64bit_id },
     { "nativeSetDataSource", "(Landroid/media/tv/tuner/filter/Filter;)I",
             (void *)android_media_tv_Tuner_set_filter_data_source },
     { "nativeStartFilter", "()I", (void *)android_media_tv_Tuner_start_filter },
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index c4deeaf..71d2983 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -17,7 +17,13 @@
 #ifndef _ANDROID_MEDIA_TV_TUNER_H_
 #define _ANDROID_MEDIA_TV_TUNER_H_
 
-#include <android/hardware/tv/tuner/1.0/ITuner.h>
+#include <android/hardware/tv/tuner/1.1/IFilter.h>
+#include <android/hardware/tv/tuner/1.1/IFilterCallback.h>
+#include <android/hardware/tv/tuner/1.1/IFrontend.h>
+#include <android/hardware/tv/tuner/1.1/IFrontendCallback.h>
+#include <android/hardware/tv/tuner/1.1/ITuner.h>
+#include <android/hardware/tv/tuner/1.1/types.h>
+
 #include <C2BlockInternal.h>
 #include <C2HandleIonInternal.h>
 #include <C2ParamDef.h>
@@ -38,6 +44,7 @@
 using ::android::hardware::hidl_vec;
 using ::android::hardware::kSynchronizedReadWrite;
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterEvent;
+using ::android::hardware::tv::tuner::V1_1::DemuxFilterEventExt;
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterStatus;
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterType;
 using ::android::hardware::tv::tuner::V1_0::DemuxPid;
@@ -49,14 +56,14 @@
 using ::android::hardware::tv::tuner::V1_0::FrontendScanMessageType;
 using ::android::hardware::tv::tuner::V1_0::FrontendScanType;
 using ::android::hardware::tv::tuner::V1_0::FrontendSettings;
+using ::android::hardware::tv::tuner::V1_1::FrontendSettingsExt1_1;
 using ::android::hardware::tv::tuner::V1_0::IDemux;
 using ::android::hardware::tv::tuner::V1_0::IDescrambler;
 using ::android::hardware::tv::tuner::V1_0::IDvr;
 using ::android::hardware::tv::tuner::V1_0::IDvrCallback;
 using ::android::hardware::tv::tuner::V1_0::IFilter;
-using ::android::hardware::tv::tuner::V1_0::IFilterCallback;
+using ::android::hardware::tv::tuner::V1_1::IFilterCallback;
 using ::android::hardware::tv::tuner::V1_0::IFrontend;
-using ::android::hardware::tv::tuner::V1_0::IFrontendCallback;
 using ::android::hardware::tv::tuner::V1_0::ILnb;
 using ::android::hardware::tv::tuner::V1_0::ILnbCallback;
 using ::android::hardware::tv::tuner::V1_0::ITimeFilter;
@@ -66,6 +73,9 @@
 using ::android::hardware::tv::tuner::V1_0::PlaybackStatus;
 using ::android::hardware::tv::tuner::V1_0::RecordStatus;
 using ::android::hardware::tv::tuner::V1_0::Result;
+using ::android::hardware::tv::tuner::V1_1::FrontendScanMessageExt1_1;
+using ::android::hardware::tv::tuner::V1_1::FrontendScanMessageTypeExt1_1;
+using ::android::hardware::tv::tuner::V1_1::IFrontendCallback;
 
 using MQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
 
@@ -112,18 +122,32 @@
     int mFd;
 };
 
+struct Filter : public RefBase {
+    Filter(sp<IFilter> sp, jobject obj);
+    ~Filter();
+    int close();
+    sp<IFilter> getIFilter();
+    sp<IFilter> mFilterSp;
+    std::unique_ptr<MQ> mFilterMQ;
+    EventFlag* mFilterMQEventFlag;
+    jweak mFilterObj;
+    native_handle_t* mAvSharedHandle;
+    uint64_t mAvSharedMemSize;
+    bool mIsMediaFilter;
+};
+
 struct MediaEvent : public RefBase {
-    MediaEvent(sp<IFilter> iFilter, hidl_handle avHandle, uint64_t dataId,
-        uint64_t dataLength, jobject obj);
+    MediaEvent(sp<Filter> filter, hidl_handle avHandle, uint64_t dataId,
+        uint64_t dataSize, jobject obj);
     ~MediaEvent();
     jobject getLinearBlock();
     uint64_t getAudioHandle();
     void finalize();
 
-    sp<IFilter> mIFilter;
+    sp<Filter> mFilter;
     native_handle_t* mAvHandle;
     uint64_t mDataId;
-    uint64_t mDataLength;
+    uint64_t mDataSize;
     uint8_t* mBuffer;
     android::Mutex mLock;
     int mDataIdRefCnt;
@@ -134,26 +158,16 @@
     std::weak_ptr<C2Buffer> mC2Buffer;
 };
 
-struct Filter : public RefBase {
-    Filter(sp<IFilter> sp, jobject obj);
-    ~Filter();
-    int close();
-    sp<IFilter> getIFilter();
-    sp<IFilter> mFilterSp;
-    std::unique_ptr<MQ> mFilterMQ;
-    EventFlag* mFilterMQEventFlag;
-    jweak mFilterObj;
-};
-
 struct FilterCallback : public IFilterCallback {
     ~FilterCallback();
+    virtual Return<void> onFilterEvent_1_1(const DemuxFilterEvent& filterEvent,
+            const DemuxFilterEventExt& filterEventExt);
     virtual Return<void> onFilterEvent(const DemuxFilterEvent& filterEvent);
     virtual Return<void> onFilterStatus(const DemuxFilterStatus status);
 
     void setFilter(const sp<Filter> filter);
 private:
-    jweak mFilter;
-    sp<IFilter> mIFilter;
+    sp<Filter> mFilter;
     jobjectArray getSectionEvent(
             jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events);
     jobjectArray getMediaEvent(
@@ -161,9 +175,11 @@
     jobjectArray getPesEvent(
             jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events);
     jobjectArray getTsRecordEvent(
-            jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events);
+            jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>&events,
+                    const std::vector<DemuxFilterEventExt::Event>& eventsExt);
     jobjectArray getMmtpRecordEvent(
-            jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events);
+            jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>&events,
+                    const std::vector<DemuxFilterEventExt::Event>& eventsExt);
     jobjectArray getDownloadEvent(
             jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events);
     jobjectArray getIpPayloadEvent(
@@ -178,6 +194,8 @@
     virtual Return<void> onEvent(FrontendEventType frontendEventType);
     virtual Return<void> onScanMessage(
             FrontendScanMessageType type, const FrontendScanMessage& message);
+    virtual Return<void> onScanMessageExt1_1(
+            FrontendScanMessageTypeExt1_1 type, const FrontendScanMessageExt1_1& messageExt);
 
     jweak mObject;
     FrontendId mId;
@@ -194,17 +212,21 @@
 struct JTuner : public RefBase {
     JTuner(JNIEnv *env, jobject thiz);
     sp<ITuner> getTunerService();
+    int getTunerVersion();
     jobject getAvSyncHwId(sp<Filter> filter);
     jobject getAvSyncTime(jint id);
     int connectCiCam(jint id);
+    int linkCiCam(jint id);
     int disconnectCiCam();
+    int unlinkCiCam(jint id);
     jobject getFrontendIds();
     jobject openFrontendById(int id);
     jint closeFrontendById(int id);
     jobject getFrontendInfo(int id);
-    int tune(const FrontendSettings& settings);
+    int tune(const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1);
     int stopTune();
-    int scan(const FrontendSettings& settings, FrontendScanType scanType);
+    int scan(const FrontendSettings& settings, FrontendScanType scanType,
+            const FrontendSettingsExt1_1& settingsExt1_1);
     int stopScan();
     int setLnb(int id);
     int setLna(bool enable);
@@ -229,8 +251,13 @@
     jclass mClass;
     jweak mObject;
     static sp<ITuner> mTuner;
+    static sp<::android::hardware::tv::tuner::V1_1::ITuner> mTuner_1_1;
+    // An integer that carries the Tuner version. The high 16 bits are the major version number
+    // while the low 16 bits are the minor version. Default value is unknown version 0.
+    static int mTunerVersion;
     hidl_vec<FrontendId> mFeIds;
     sp<IFrontend> mFe;
+    sp<::android::hardware::tv::tuner::V1_1::IFrontend> mFe_1_1;
     int mFeId;
     hidl_vec<LnbId> mLnbIds;
     sp<ILnb> mLnb;
@@ -245,6 +272,9 @@
     static jobject getIsdbs3FrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps);
     static jobject getIsdbsFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps);
     static jobject getIsdbtFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps);
+    static jobject getDtmbFrontendCaps(JNIEnv *env, int id);
+
+    bool isV1_1ExtendedStatusType(jint type);
 };
 
 class C2DataIdInfo : public C2Param {
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index 45c49e5..0d53ab1 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -331,7 +331,7 @@
     }
 
     if (deviceType != AUDIO_DEVICE_NONE) {
-        device.mType = deviceType;
+        device.mType = (audio_devices_t)deviceType;
         ScopedUtfChars address(env, deviceAddress);
         device.setAddress(address.c_str());
     }
diff --git a/media/jni/soundpool/android_media_SoundPool.cpp b/media/jni/soundpool/android_media_SoundPool.cpp
index ca3cc855..26725f8 100644
--- a/media/jni/soundpool/android_media_SoundPool.cpp
+++ b/media/jni/soundpool/android_media_SoundPool.cpp
@@ -200,7 +200,7 @@
     paa->usage = (audio_usage_t) env->GetIntField(jaa, javaAudioAttrFields.fieldUsage);
     paa->content_type =
             (audio_content_type_t) env->GetIntField(jaa, javaAudioAttrFields.fieldContentType);
-    paa->flags = env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);
+    paa->flags = (audio_flags_mask_t) env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);
 
     ALOGV("android_media_SoundPool_native_setup");
     auto *ap = new SoundPool(maxChannels, paa);
diff --git a/media/tests/AudioPolicyTest/AndroidManifest.xml b/media/tests/AudioPolicyTest/AndroidManifest.xml
index adb058c..a7ab828 100644
--- a/media/tests/AudioPolicyTest/AndroidManifest.xml
+++ b/media/tests/AudioPolicyTest/AndroidManifest.xml
@@ -25,7 +25,7 @@
     <application>
         <uses-library android:name="android.test.runner" />
         <activity android:label="@string/app_name" android:name="AudioPolicyTest"
-                  android:screenOrientation="landscape">
+                  android:screenOrientation="landscape" android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER"/>
diff --git a/media/tests/MediaRouter/Android.bp b/media/tests/MediaRouter/Android.bp
index 5a0a50c..4d0c258 100644
--- a/media/tests/MediaRouter/Android.bp
+++ b/media/tests/MediaRouter/Android.bp
@@ -11,7 +11,8 @@
     static_libs: [
         "android-support-test",
         "mockito-target-minus-junit4",
-        "testng"
+        "testng",
+        "truth-prebuilt",
     ],
 
     platform_apis: true,
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouteInfoTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouteInfoTest.java
new file mode 100644
index 0000000..92e4c95
--- /dev/null
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouteInfoTest.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.mediaroutertest;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.hardware.display.DisplayManagerGlobal;
+import android.media.MediaRouter;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.Display;
+import android.view.DisplayAddress;
+import android.view.DisplayAdjustments;
+import android.view.DisplayInfo;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class MediaRouteInfoTest {
+    private TestableRouteInfo mRouteInfo;
+    private static Display sWifiDisplay;
+    private static Display sExternalDisplay;
+    private static Display sInternalDisplay;
+    private static final String FAKE_MAC_ADDRESS = "fake MAC address";
+
+    @BeforeClass
+    public static void setUpOnce() {
+        final DisplayManagerGlobal global = DisplayManagerGlobal.getInstance();
+        final DisplayInfo wifiInfo = new DisplayInfo();
+        wifiInfo.flags = Display.FLAG_PRESENTATION;
+        wifiInfo.type = Display.TYPE_WIFI;
+        wifiInfo.address = DisplayAddress.fromMacAddress(FAKE_MAC_ADDRESS);
+        sWifiDisplay = new Display(global, 2, wifiInfo, new DisplayAdjustments());
+
+        final DisplayInfo externalInfo = new DisplayInfo();
+        externalInfo.flags = Display.FLAG_PRESENTATION;
+        externalInfo.type = Display.TYPE_EXTERNAL;
+        sExternalDisplay = new Display(global, 3, externalInfo,  new DisplayAdjustments());
+
+        final DisplayInfo internalInfo = new DisplayInfo();
+        internalInfo.flags = Display.FLAG_PRESENTATION;
+        internalInfo.type = Display.TYPE_INTERNAL;
+        sInternalDisplay = new Display(global, 4, internalInfo,  new DisplayAdjustments());
+    }
+
+    @Before
+    public void setUp() {
+        mRouteInfo = new TestableRouteInfo();
+    }
+
+    @Test
+    public void testGetPresentationDisplay_notLiveVideo() {
+        mRouteInfo.setPresentationDisplays(sWifiDisplay);
+        mRouteInfo.mSupportedType = MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY;
+
+        mRouteInfo.updatePresentationDisplay();
+
+        assertThat(mRouteInfo.getPresentationDisplay()).isNull();
+    }
+
+    @Test
+    public void testGetPresentationDisplay_includesLiveVideo() {
+        mRouteInfo.setPresentationDisplays(sWifiDisplay);
+        mRouteInfo.mSupportedType |= MediaRouter.ROUTE_TYPE_LIVE_AUDIO;
+
+        mRouteInfo.updatePresentationDisplay();
+
+        assertThat(mRouteInfo.getPresentationDisplay()).isEqualTo(sWifiDisplay);
+    }
+
+    @Test
+    public void testGetPresentationDisplay_noPresentationDisplay() {
+        mRouteInfo.updatePresentationDisplay();
+
+        assertThat(mRouteInfo.getPresentationDisplay()).isNull();
+    }
+
+    @Test
+    public void testGetPresentationDisplay_wifiDisplayOnly() {
+        mRouteInfo.setPresentationDisplays(sWifiDisplay);
+
+        mRouteInfo.updatePresentationDisplay();
+
+        assertThat(mRouteInfo.getPresentationDisplay()).isEqualTo(sWifiDisplay);
+    }
+
+    @Test
+    public void testGetPresentationDisplay_externalDisplayOnly() {
+        mRouteInfo.setPresentationDisplays(sExternalDisplay);
+
+        mRouteInfo.updatePresentationDisplay();
+
+        assertThat(mRouteInfo.getPresentationDisplay()).isEqualTo(sExternalDisplay);
+    }
+
+    @Test
+    public void testGetPresentationDisplay_internalDisplayOnly() {
+        mRouteInfo.setPresentationDisplays(sInternalDisplay);
+
+        mRouteInfo.updatePresentationDisplay();
+
+        assertThat(mRouteInfo.getPresentationDisplay()).isEqualTo(sInternalDisplay);
+    }
+
+    @Test
+    public void testGetPresentationDisplay_addressNotMatch() {
+        mRouteInfo.setPresentationDisplays(sWifiDisplay);
+        mRouteInfo.mDeviceAddress = "Not match";
+
+        mRouteInfo.updatePresentationDisplay();
+
+        assertThat(mRouteInfo.getPresentationDisplay()).isNull();
+    }
+
+    @Test
+    public void testGetPresentationDisplay_containsWifiAndExternalDisplays_returnWifiDisplay() {
+        mRouteInfo.setPresentationDisplays(sExternalDisplay, sWifiDisplay);
+
+        mRouteInfo.updatePresentationDisplay();
+
+        assertThat(mRouteInfo.getPresentationDisplay()).isEqualTo(sWifiDisplay);
+    }
+
+    @Test
+    public void testGetPresentationDisplay_containsExternalAndInternalDisplays_returnExternal() {
+        mRouteInfo.setPresentationDisplays(sExternalDisplay, sInternalDisplay);
+
+        mRouteInfo.updatePresentationDisplay();
+
+        assertThat(mRouteInfo.getPresentationDisplay()).isEqualTo(sExternalDisplay);
+    }
+
+    @Test
+    public void testGetPresentationDisplay_containsAllDisplays_returnWifiDisplay() {
+        mRouteInfo.setPresentationDisplays(sExternalDisplay, sInternalDisplay, sWifiDisplay);
+
+        mRouteInfo.updatePresentationDisplay();
+
+        assertThat(mRouteInfo.getPresentationDisplay()).isEqualTo(sWifiDisplay);
+    }
+
+    private static class TestableRouteInfo extends MediaRouter.RouteInfo {
+        private Display[] mDisplays = new Display[0];
+        private int mSupportedType = MediaRouter.ROUTE_TYPE_LIVE_VIDEO;
+        private String mDeviceAddress = FAKE_MAC_ADDRESS;
+        private MediaRouter.RouteInfo mDefaultRouteInfo = null;
+
+        private TestableRouteInfo() {
+            super(null);
+        }
+
+        private void setPresentationDisplays(Display ...displays) {
+            mDisplays = new Display[displays.length];
+            System.arraycopy(displays, 0, mDisplays, 0, displays.length);
+        }
+
+        @Override
+        public Display[] getAllPresentationDisplays() {
+            return mDisplays;
+        }
+
+        @Override
+        public int getSupportedTypes() {
+            return mSupportedType;
+        }
+
+        @Override
+        public String getDeviceAddress() {
+            return mDeviceAddress;
+        }
+
+        @Override
+        public MediaRouter.RouteInfo getDefaultAudioVideo() {
+            return mDefaultRouteInfo;
+        }
+    }
+}
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index 0a466f4..c503721 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -137,12 +137,24 @@
         return nullptr;
     }
 
+    Surface* surface = static_cast<Surface*>(window);
+    sp<IBinder> parentHandle = surface->getSurfaceControlHandle();
+
     uint32_t flags = ISurfaceComposerClient::eFXSurfaceBufferState;
-    sp<SurfaceControl> surfaceControl =
-            client->createWithSurfaceParent(String8(debug_name), 0 /* width */, 0 /* height */,
-                                            // Format is only relevant for buffer queue layers.
-                                            PIXEL_FORMAT_UNKNOWN /* format */, flags,
-                                            static_cast<Surface*>(window));
+    sp<SurfaceControl> surfaceControl;
+    if (parentHandle) {
+        surfaceControl =
+                client->createSurface(String8(debug_name), 0 /* width */, 0 /* height */,
+                                      // Format is only relevant for buffer queue layers.
+                                      PIXEL_FORMAT_UNKNOWN /* format */, flags, parentHandle);
+    } else {
+        surfaceControl =
+                client->createWithSurfaceParent(String8(debug_name), 0 /* width */, 0 /* height */,
+                                                // Format is only relevant for buffer queue layers.
+                                                PIXEL_FORMAT_UNKNOWN /* format */, flags,
+                                                static_cast<Surface*>(window));
+    }
+
     if (!surfaceControl) {
         return nullptr;
     }
@@ -164,7 +176,7 @@
             client->createSurface(String8(debug_name), 0 /* width */, 0 /* height */,
                                   // Format is only relevant for buffer queue layers.
                                   PIXEL_FORMAT_UNKNOWN /* format */, flags,
-                                  surfaceControlParent);
+                                  surfaceControlParent->getHandle());
     if (!surfaceControl) {
         return nullptr;
     }
diff --git a/native/graphics/jni/imagedecoder.cpp b/native/graphics/jni/imagedecoder.cpp
index 56f3906..ac4c16a 100644
--- a/native/graphics/jni/imagedecoder.cpp
+++ b/native/graphics/jni/imagedecoder.cpp
@@ -346,3 +346,25 @@
 void AImageDecoder_delete(AImageDecoder* decoder) {
     delete toDecoder(decoder);
 }
+
+bool AImageDecoder_isAnimated(AImageDecoder* decoder) {
+    if (!decoder) return false;
+
+    ImageDecoder* imageDecoder = toDecoder(decoder);
+    return imageDecoder->mCodec->codec()->getFrameCount() > 1;
+}
+
+int32_t AImageDecoder_getRepeatCount(AImageDecoder* decoder) {
+    if (!decoder) return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
+
+    ImageDecoder* imageDecoder = toDecoder(decoder);
+    const int count = imageDecoder->mCodec->codec()->getRepetitionCount();
+
+    // Skia should not report anything out of range, but defensively treat
+    // negative and too big as INFINITE.
+    if (count == SkCodec::kRepetitionCountInfinite || count < 0
+        || count > std::numeric_limits<int32_t>::max()) {
+        return ANDROID_IMAGE_DECODER_INFINITE;
+    }
+    return count;
+}
diff --git a/native/graphics/jni/libjnigraphics.map.txt b/native/graphics/jni/libjnigraphics.map.txt
index 01c1477..a184ab9 100644
--- a/native/graphics/jni/libjnigraphics.map.txt
+++ b/native/graphics/jni/libjnigraphics.map.txt
@@ -13,6 +13,8 @@
     AImageDecoder_setTargetSize; # introduced=30
     AImageDecoder_computeSampledSize; # introduced=30
     AImageDecoder_setCrop; # introduced=30
+    AImageDecoder_isAnimated; # introduced=31
+    AImageDecoder_getRepeatCount; # introduced=31
     AImageDecoderHeaderInfo_getWidth; # introduced=30
     AImageDecoderHeaderInfo_getHeight; # introduced=30
     AImageDecoderHeaderInfo_getMimeType; # introduced=30
diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt
index 983f20a..2ac64a3 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -1613,6 +1613,7 @@
     field public static final int windowHideAnimation = 16842935; // 0x10100b7
     field public static final int windowIsFloating = 16842839; // 0x1010057
     field public static final int windowIsTranslucent = 16842840; // 0x1010058
+    field public static final int windowLayoutAffinity = 16844313; // 0x1010619
     field public static final int windowLayoutInDisplayCutoutMode = 16844166; // 0x1010586
     field public static final int windowLightNavigationBar = 16844140; // 0x101056c
     field public static final int windowLightStatusBar = 16844000; // 0x10104e0
@@ -2850,6 +2851,7 @@
     method public int describeContents();
     method public int getDisplayId();
     method public int getGestureId();
+    method @NonNull public java.util.List<android.view.MotionEvent> getMotionEvents();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.accessibilityservice.AccessibilityGestureEvent> CREATOR;
   }
@@ -2889,6 +2891,7 @@
     field public static final int GESTURE_2_FINGER_DOUBLE_TAP = 20; // 0x14
     field public static final int GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD = 40; // 0x28
     field public static final int GESTURE_2_FINGER_SINGLE_TAP = 19; // 0x13
+    field public static final int GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD = 43; // 0x2b
     field public static final int GESTURE_2_FINGER_SWIPE_DOWN = 26; // 0x1a
     field public static final int GESTURE_2_FINGER_SWIPE_LEFT = 27; // 0x1b
     field public static final int GESTURE_2_FINGER_SWIPE_RIGHT = 28; // 0x1c
@@ -2897,11 +2900,13 @@
     field public static final int GESTURE_3_FINGER_DOUBLE_TAP = 23; // 0x17
     field public static final int GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD = 41; // 0x29
     field public static final int GESTURE_3_FINGER_SINGLE_TAP = 22; // 0x16
+    field public static final int GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD = 44; // 0x2c
     field public static final int GESTURE_3_FINGER_SWIPE_DOWN = 30; // 0x1e
     field public static final int GESTURE_3_FINGER_SWIPE_LEFT = 31; // 0x1f
     field public static final int GESTURE_3_FINGER_SWIPE_RIGHT = 32; // 0x20
     field public static final int GESTURE_3_FINGER_SWIPE_UP = 29; // 0x1d
     field public static final int GESTURE_3_FINGER_TRIPLE_TAP = 24; // 0x18
+    field public static final int GESTURE_3_FINGER_TRIPLE_TAP_AND_HOLD = 45; // 0x2d
     field public static final int GESTURE_4_FINGER_DOUBLE_TAP = 38; // 0x26
     field public static final int GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD = 42; // 0x2a
     field public static final int GESTURE_4_FINGER_SINGLE_TAP = 37; // 0x25
@@ -2928,6 +2933,7 @@
     field public static final int GESTURE_SWIPE_UP_AND_DOWN = 7; // 0x7
     field public static final int GESTURE_SWIPE_UP_AND_LEFT = 13; // 0xd
     field public static final int GESTURE_SWIPE_UP_AND_RIGHT = 14; // 0xe
+    field public static final int GESTURE_UNKNOWN = 0; // 0x0
     field public static final int GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS = 14; // 0xe
     field public static final int GLOBAL_ACTION_ACCESSIBILITY_BUTTON = 11; // 0xb
     field public static final int GLOBAL_ACTION_ACCESSIBILITY_BUTTON_CHOOSER = 12; // 0xc
@@ -3043,6 +3049,7 @@
     field public static final int FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK = 1024; // 0x400
     field public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 4; // 0x4
     field public static final int FLAG_RETRIEVE_INTERACTIVE_WINDOWS = 64; // 0x40
+    field public static final int FLAG_SEND_MOTION_EVENTS = 16384; // 0x4000
     field public static final int FLAG_SERVICE_HANDLES_DOUBLE_TAP = 2048; // 0x800
     field public int eventTypes;
     field public int feedbackType;
@@ -4376,6 +4383,7 @@
     method @Deprecated public void checkPackage(int, @NonNull String);
     method @Deprecated public void finishOp(@NonNull String, int, @NonNull String);
     method public void finishOp(@NonNull String, int, @NonNull String, @Nullable String);
+    method public void finishProxyOp(@NonNull String, int, @NonNull String, @Nullable String);
     method public boolean isOpActive(@NonNull String, int, @NonNull String);
     method @Deprecated public int noteOp(@NonNull String, int, @NonNull String);
     method public int noteOp(@NonNull String, int, @Nullable String, @Nullable String, @Nullable String);
@@ -4392,6 +4400,8 @@
     method public int startOp(@NonNull String, int, @Nullable String, @Nullable String, @Nullable String);
     method @Deprecated public int startOpNoThrow(@NonNull String, int, @NonNull String);
     method public int startOpNoThrow(@NonNull String, int, @NonNull String, @NonNull String, @Nullable String);
+    method public int startProxyOp(@NonNull String, int, @NonNull String, @Nullable String, @Nullable String);
+    method public int startProxyOpNoThrow(@NonNull String, int, @NonNull String, @Nullable String, @Nullable String);
     method public void startWatchingActive(@NonNull String[], @NonNull java.util.concurrent.Executor, @NonNull android.app.AppOpsManager.OnOpActiveChangedListener);
     method public void startWatchingMode(@NonNull String, @Nullable String, @NonNull android.app.AppOpsManager.OnOpChangedListener);
     method public void startWatchingMode(@NonNull String, @Nullable String, int, @NonNull android.app.AppOpsManager.OnOpChangedListener);
@@ -4895,7 +4905,7 @@
   @Deprecated public class Fragment implements android.content.ComponentCallbacks2 android.view.View.OnCreateContextMenuListener {
     ctor @Deprecated public Fragment();
     method @Deprecated public void dump(String, java.io.FileDescriptor, java.io.PrintWriter, String[]);
-    method @Deprecated public final boolean equals(Object);
+    method @Deprecated public final boolean equals(@Nullable Object);
     method @Deprecated public final android.app.Activity getActivity();
     method @Deprecated public boolean getAllowEnterTransitionOverlap();
     method @Deprecated public boolean getAllowReturnTransitionOverlap();
@@ -6515,6 +6525,7 @@
     method public void dropShellPermissionIdentity();
     method public android.view.accessibility.AccessibilityEvent executeAndWaitForEvent(Runnable, android.app.UiAutomation.AccessibilityEventFilter, long) throws java.util.concurrent.TimeoutException;
     method public android.os.ParcelFileDescriptor executeShellCommand(String);
+    method @NonNull public android.os.ParcelFileDescriptor[] executeShellCommandRw(@NonNull String);
     method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
     method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow();
     method public android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
@@ -9141,6 +9152,7 @@
     method public boolean getIncludeTxPowerLevel();
     method public android.util.SparseArray<byte[]> getManufacturerSpecificData();
     method public java.util.Map<android.os.ParcelUuid,byte[]> getServiceData();
+    method @Nullable public java.util.List<android.os.ParcelUuid> getServiceSolicitationUuids();
     method public java.util.List<android.os.ParcelUuid> getServiceUuids();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.le.AdvertiseData> CREATOR;
@@ -9150,6 +9162,7 @@
     ctor public AdvertiseData.Builder();
     method public android.bluetooth.le.AdvertiseData.Builder addManufacturerData(int, byte[]);
     method public android.bluetooth.le.AdvertiseData.Builder addServiceData(android.os.ParcelUuid, byte[]);
+    method @NonNull public android.bluetooth.le.AdvertiseData.Builder addServiceSolicitationUuid(@NonNull android.os.ParcelUuid);
     method public android.bluetooth.le.AdvertiseData.Builder addServiceUuid(android.os.ParcelUuid);
     method public android.bluetooth.le.AdvertiseData build();
     method public android.bluetooth.le.AdvertiseData.Builder setIncludeDeviceName(boolean);
@@ -10658,12 +10671,15 @@
     field public static final String ACTION_PACKAGE_CHANGED = "android.intent.action.PACKAGE_CHANGED";
     field public static final String ACTION_PACKAGE_DATA_CLEARED = "android.intent.action.PACKAGE_DATA_CLEARED";
     field public static final String ACTION_PACKAGE_FIRST_LAUNCH = "android.intent.action.PACKAGE_FIRST_LAUNCH";
+    field public static final String ACTION_PACKAGE_FULLY_LOADED = "android.intent.action.PACKAGE_FULLY_LOADED";
     field public static final String ACTION_PACKAGE_FULLY_REMOVED = "android.intent.action.PACKAGE_FULLY_REMOVED";
     field @Deprecated public static final String ACTION_PACKAGE_INSTALL = "android.intent.action.PACKAGE_INSTALL";
     field public static final String ACTION_PACKAGE_NEEDS_VERIFICATION = "android.intent.action.PACKAGE_NEEDS_VERIFICATION";
     field public static final String ACTION_PACKAGE_REMOVED = "android.intent.action.PACKAGE_REMOVED";
     field public static final String ACTION_PACKAGE_REPLACED = "android.intent.action.PACKAGE_REPLACED";
     field public static final String ACTION_PACKAGE_RESTARTED = "android.intent.action.PACKAGE_RESTARTED";
+    field public static final String ACTION_PACKAGE_STARTABLE = "android.intent.action.PACKAGE_STARTABLE";
+    field public static final String ACTION_PACKAGE_UNSTARTABLE = "android.intent.action.PACKAGE_UNSTARTABLE";
     field public static final String ACTION_PACKAGE_VERIFIED = "android.intent.action.PACKAGE_VERIFIED";
     field public static final String ACTION_PASTE = "android.intent.action.PASTE";
     field public static final String ACTION_PICK = "android.intent.action.PICK";
@@ -10827,7 +10843,9 @@
     field public static final String EXTRA_TIMEZONE = "time-zone";
     field public static final String EXTRA_TITLE = "android.intent.extra.TITLE";
     field public static final String EXTRA_UID = "android.intent.extra.UID";
+    field public static final String EXTRA_UNSTARTABLE_REASON = "android.intent.extra.UNSTARTABLE_REASON";
     field public static final String EXTRA_USER = "android.intent.extra.USER";
+    field public static final String EXTRA_USER_INITIATED = "android.intent.extra.USER_INITIATED";
     field public static final int FILL_IN_ACTION = 1; // 0x1
     field public static final int FILL_IN_CATEGORIES = 4; // 0x4
     field public static final int FILL_IN_CLIP_DATA = 128; // 0x80
@@ -11454,10 +11472,10 @@
 
   public final class ApkChecksum implements android.os.Parcelable {
     method public int describeContents();
-    method public int getKind();
-    method @Nullable public java.security.cert.Certificate getSourceCertificate() throws java.security.cert.CertificateException;
-    method @Nullable public String getSourcePackageName();
+    method @Nullable public java.security.cert.Certificate getInstallerCertificate() throws java.security.cert.CertificateException;
+    method @Nullable public String getInstallerPackageName();
     method @Nullable public String getSplitName();
+    method public int getType();
     method @NonNull public byte[] getValue();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.ApkChecksum> CREATOR;
@@ -11567,17 +11585,17 @@
   public final class Checksum implements android.os.Parcelable {
     ctor public Checksum(int, @NonNull byte[]);
     method public int describeContents();
-    method public int getKind();
+    method public int getType();
     method @NonNull public byte[] getValue();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.Checksum> CREATOR;
-    field public static final int PARTIAL_MERKLE_ROOT_1M_SHA256 = 32; // 0x20
-    field public static final int PARTIAL_MERKLE_ROOT_1M_SHA512 = 64; // 0x40
-    field @Deprecated public static final int WHOLE_MD5 = 2; // 0x2
-    field public static final int WHOLE_MERKLE_ROOT_4K_SHA256 = 1; // 0x1
-    field @Deprecated public static final int WHOLE_SHA1 = 4; // 0x4
-    field public static final int WHOLE_SHA256 = 8; // 0x8
-    field public static final int WHOLE_SHA512 = 16; // 0x10
+    field public static final int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256 = 32; // 0x20
+    field public static final int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512 = 64; // 0x40
+    field @Deprecated public static final int TYPE_WHOLE_MD5 = 2; // 0x2
+    field public static final int TYPE_WHOLE_MERKLE_ROOT_4K_SHA256 = 1; // 0x1
+    field @Deprecated public static final int TYPE_WHOLE_SHA1 = 4; // 0x4
+    field @Deprecated public static final int TYPE_WHOLE_SHA256 = 8; // 0x8
+    field @Deprecated public static final int TYPE_WHOLE_SHA512 = 16; // 0x10
   }
 
   public class ComponentInfo extends android.content.pm.PackageItemInfo {
@@ -12027,7 +12045,6 @@
     method @Nullable public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
     method @NonNull public CharSequence getBackgroundPermissionOptionLabel();
     method @Nullable public abstract android.content.pm.ChangedPackages getChangedPackages(@IntRange(from=0) int);
-    method public void getChecksums(@NonNull String, boolean, int, @Nullable java.util.List<java.security.cert.Certificate>, @NonNull android.content.IntentSender) throws java.security.cert.CertificateEncodingException, java.io.IOException, android.content.pm.PackageManager.NameNotFoundException;
     method public abstract int getComponentEnabledSetting(@NonNull android.content.ComponentName);
     method @NonNull public abstract android.graphics.drawable.Drawable getDefaultActivityIcon();
     method @Nullable public abstract android.graphics.drawable.Drawable getDrawable(@NonNull String, @DrawableRes int, @Nullable android.content.pm.ApplicationInfo);
@@ -12100,6 +12117,7 @@
     method @Deprecated public abstract void removePackageFromPreferred(@NonNull String);
     method public abstract void removePermission(@NonNull String);
     method @RequiresPermission(value="android.permission.WHITELIST_RESTRICTED_PERMISSIONS", conditional=true) public boolean removeWhitelistedRestrictedPermission(@NonNull String, @NonNull String, int);
+    method public void requestChecksums(@NonNull String, boolean, int, @NonNull java.util.List<java.security.cert.Certificate>, @NonNull android.content.IntentSender) throws java.security.cert.CertificateEncodingException, android.content.pm.PackageManager.NameNotFoundException;
     method @Nullable public abstract android.content.pm.ResolveInfo resolveActivity(@NonNull android.content.Intent, int);
     method @Nullable public abstract android.content.pm.ProviderInfo resolveContentProvider(@NonNull String, int);
     method @Nullable public abstract android.content.pm.ResolveInfo resolveService(@NonNull android.content.Intent, int);
@@ -12247,7 +12265,7 @@
     field @Deprecated public static final int GET_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000
     field public static final int GET_GIDS = 256; // 0x100
     field public static final int GET_INSTRUMENTATION = 16; // 0x10
-    field public static final int GET_INTENT_FILTERS = 32; // 0x20
+    field @Deprecated public static final int GET_INTENT_FILTERS = 32; // 0x20
     field public static final int GET_META_DATA = 128; // 0x80
     field public static final int GET_PERMISSIONS = 4096; // 0x1000
     field public static final int GET_PROVIDERS = 8; // 0x8
@@ -12286,6 +12304,9 @@
     field public static final int SYNCHRONOUS = 2; // 0x2
     field @Nullable public static final java.util.List<java.security.cert.Certificate> TRUST_ALL;
     field @NonNull public static final java.util.List<java.security.cert.Certificate> TRUST_NONE;
+    field public static final int UNSTARTABLE_REASON_CONNECTION_ERROR = 1; // 0x1
+    field public static final int UNSTARTABLE_REASON_INSUFFICIENT_STORAGE = 2; // 0x2
+    field public static final int UNSTARTABLE_REASON_UNKNOWN = 0; // 0x0
     field public static final int VERIFICATION_ALLOW = 1; // 0x1
     field public static final int VERIFICATION_REJECT = -1; // 0xffffffff
     field public static final int VERSION_CODE_HIGHEST = -1; // 0xffffffff
@@ -13056,38 +13077,38 @@
 
   public interface Cursor extends java.io.Closeable {
     method public void close();
-    method public void copyStringToBuffer(int, android.database.CharArrayBuffer);
+    method public void copyStringToBuffer(@IntRange(from=0) int, android.database.CharArrayBuffer);
     method @Deprecated public void deactivate();
-    method public byte[] getBlob(int);
-    method public int getColumnCount();
-    method public int getColumnIndex(String);
-    method public int getColumnIndexOrThrow(String) throws java.lang.IllegalArgumentException;
-    method public String getColumnName(int);
+    method public byte[] getBlob(@IntRange(from=0) int);
+    method @IntRange(from=0) public int getColumnCount();
+    method @IntRange(from=0xffffffff) public int getColumnIndex(String);
+    method @IntRange(from=0) public int getColumnIndexOrThrow(String) throws java.lang.IllegalArgumentException;
+    method public String getColumnName(@IntRange(from=0) int);
     method public String[] getColumnNames();
-    method public int getCount();
-    method public double getDouble(int);
+    method @IntRange(from=0) public int getCount();
+    method public double getDouble(@IntRange(from=0) int);
     method public android.os.Bundle getExtras();
-    method public float getFloat(int);
-    method public int getInt(int);
-    method public long getLong(int);
+    method public float getFloat(@IntRange(from=0) int);
+    method public int getInt(@IntRange(from=0) int);
+    method public long getLong(@IntRange(from=0) int);
     method public android.net.Uri getNotificationUri();
     method @Nullable public default java.util.List<android.net.Uri> getNotificationUris();
-    method public int getPosition();
-    method public short getShort(int);
-    method public String getString(int);
-    method public int getType(int);
+    method @IntRange(from=0xffffffff) public int getPosition();
+    method public short getShort(@IntRange(from=0) int);
+    method public String getString(@IntRange(from=0) int);
+    method public int getType(@IntRange(from=0) int);
     method public boolean getWantsAllOnMoveCalls();
     method public boolean isAfterLast();
     method public boolean isBeforeFirst();
     method public boolean isClosed();
     method public boolean isFirst();
     method public boolean isLast();
-    method public boolean isNull(int);
+    method public boolean isNull(@IntRange(from=0) int);
     method public boolean move(int);
     method public boolean moveToFirst();
     method public boolean moveToLast();
     method public boolean moveToNext();
-    method public boolean moveToPosition(int);
+    method public boolean moveToPosition(@IntRange(from=0xffffffff) int);
     method public boolean moveToPrevious();
     method public void registerContentObserver(android.database.ContentObserver);
     method public void registerDataSetObserver(android.database.DataSetObserver);
@@ -13129,33 +13150,33 @@
     ctor @Deprecated public CursorWindow(boolean);
     method public boolean allocRow();
     method public void clear();
-    method public void copyStringToBuffer(int, int, android.database.CharArrayBuffer);
+    method public void copyStringToBuffer(@IntRange(from=0) int, @IntRange(from=0) int, android.database.CharArrayBuffer);
     method public int describeContents();
     method public void freeLastRow();
-    method public byte[] getBlob(int, int);
-    method public double getDouble(int, int);
-    method public float getFloat(int, int);
-    method public int getInt(int, int);
-    method public long getLong(int, int);
-    method public int getNumRows();
-    method public short getShort(int, int);
-    method public int getStartPosition();
-    method public String getString(int, int);
-    method public int getType(int, int);
-    method @Deprecated public boolean isBlob(int, int);
-    method @Deprecated public boolean isFloat(int, int);
-    method @Deprecated public boolean isLong(int, int);
-    method @Deprecated public boolean isNull(int, int);
-    method @Deprecated public boolean isString(int, int);
+    method public byte[] getBlob(@IntRange(from=0) int, @IntRange(from=0) int);
+    method public double getDouble(@IntRange(from=0) int, @IntRange(from=0) int);
+    method public float getFloat(@IntRange(from=0) int, @IntRange(from=0) int);
+    method public int getInt(@IntRange(from=0) int, @IntRange(from=0) int);
+    method public long getLong(@IntRange(from=0) int, @IntRange(from=0) int);
+    method @IntRange(from=0) public int getNumRows();
+    method public short getShort(@IntRange(from=0) int, @IntRange(from=0) int);
+    method @IntRange(from=0) public int getStartPosition();
+    method public String getString(@IntRange(from=0) int, @IntRange(from=0) int);
+    method public int getType(@IntRange(from=0) int, @IntRange(from=0) int);
+    method @Deprecated public boolean isBlob(@IntRange(from=0) int, @IntRange(from=0) int);
+    method @Deprecated public boolean isFloat(@IntRange(from=0) int, @IntRange(from=0) int);
+    method @Deprecated public boolean isLong(@IntRange(from=0) int, @IntRange(from=0) int);
+    method @Deprecated public boolean isNull(@IntRange(from=0) int, @IntRange(from=0) int);
+    method @Deprecated public boolean isString(@IntRange(from=0) int, @IntRange(from=0) int);
     method public static android.database.CursorWindow newFromParcel(android.os.Parcel);
     method protected void onAllReferencesReleased();
-    method public boolean putBlob(byte[], int, int);
-    method public boolean putDouble(double, int, int);
-    method public boolean putLong(long, int, int);
-    method public boolean putNull(int, int);
-    method public boolean putString(String, int, int);
-    method public boolean setNumColumns(int);
-    method public void setStartPosition(int);
+    method public boolean putBlob(byte[], @IntRange(from=0) int, @IntRange(from=0) int);
+    method public boolean putDouble(double, @IntRange(from=0) int, @IntRange(from=0) int);
+    method public boolean putLong(long, @IntRange(from=0) int, @IntRange(from=0) int);
+    method public boolean putNull(@IntRange(from=0) int, @IntRange(from=0) int);
+    method public boolean putString(String, @IntRange(from=0) int, @IntRange(from=0) int);
+    method public boolean setNumColumns(@IntRange(from=0) int);
+    method public void setStartPosition(@IntRange(from=0) int);
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.database.CursorWindow> CREATOR;
   }
@@ -14356,6 +14377,7 @@
     method public void drawColor(@ColorLong long, @NonNull android.graphics.BlendMode);
     method public void drawDoubleRoundRect(@NonNull android.graphics.RectF, float, float, @NonNull android.graphics.RectF, float, float, @NonNull android.graphics.Paint);
     method public void drawDoubleRoundRect(@NonNull android.graphics.RectF, @NonNull float[], @NonNull android.graphics.RectF, @NonNull float[], @NonNull android.graphics.Paint);
+    method public void drawGlyphs(@NonNull int[], @IntRange(from=0) int, @NonNull float[], @IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.graphics.fonts.Font, @NonNull android.graphics.Paint);
     method public void drawLine(float, float, float, float, @NonNull android.graphics.Paint);
     method public void drawLines(@NonNull @Size(multiple=4) float[], int, int, @NonNull android.graphics.Paint);
     method public void drawLines(@NonNull @Size(multiple=4) float[], @NonNull android.graphics.Paint);
@@ -16496,23 +16518,6 @@
 
 package android.graphics.text {
 
-  public class GlyphStyle {
-    ctor public GlyphStyle(@ColorInt int, @FloatRange(from=0) float, @FloatRange(from=0) float, @FloatRange(from=0) float, int);
-    ctor public GlyphStyle(@NonNull android.graphics.Paint);
-    method public void applyToPaint(@NonNull android.graphics.Paint);
-    method @ColorInt public int getColor();
-    method public int getFlags();
-    method @FloatRange(from=0) public float getFontSize();
-    method @FloatRange(from=0) public float getScaleX();
-    method @FloatRange(from=0) public float getSkewX();
-    method public void setColor(@ColorInt int);
-    method public void setFlags(int);
-    method public void setFontSize(@FloatRange(from=0) float);
-    method public void setFromPaint(@NonNull android.graphics.Paint);
-    method public void setScaleX(@FloatRange(from=0) float);
-    method public void setSkewX(@FloatRange(from=0) float);
-  }
-
   public class LineBreaker {
     method @NonNull public android.graphics.text.LineBreaker.Result computeLineBreaks(@NonNull android.graphics.text.MeasuredText, @NonNull android.graphics.text.LineBreaker.ParagraphConstraints, @IntRange(from=0) int);
     field public static final int BREAK_STRATEGY_BALANCED = 2; // 0x2
@@ -16574,20 +16579,19 @@
   }
 
   public final class PositionedGlyphs {
+    method public float getAdvance();
     method public float getAscent();
     method public float getDescent();
     method @NonNull public android.graphics.fonts.Font getFont(@IntRange(from=0) int);
     method @IntRange(from=0) public int getGlyphId(@IntRange(from=0) int);
-    method public float getOriginX();
-    method public float getOriginY();
-    method public float getPositionX(@IntRange(from=0) int);
-    method public float getPositionY(@IntRange(from=0) int);
-    method @NonNull public android.graphics.text.GlyphStyle getStyle();
-    method public float getTotalAdvance();
+    method public float getGlyphX(@IntRange(from=0) int);
+    method public float getGlyphY(@IntRange(from=0) int);
+    method public float getOffsetX();
+    method public float getOffsetY();
     method @IntRange(from=0) public int glyphCount();
   }
 
-  public class TextShaper {
+  public class TextRunShaper {
     method @NonNull public static android.graphics.text.PositionedGlyphs shapeTextRun(@NonNull char[], int, int, int, int, float, float, boolean, @NonNull android.graphics.Paint);
     method @NonNull public static android.graphics.text.PositionedGlyphs shapeTextRun(@NonNull CharSequence, int, int, int, int, float, float, boolean, @NonNull android.graphics.Paint);
   }
@@ -24063,9 +24067,13 @@
     method @IntRange(from=1, to=java.lang.Integer.MAX_VALUE) public int getMaxUpdates();
     method @FloatRange(from=0, to=java.lang.Float.MAX_VALUE) public float getMinUpdateDistanceMeters();
     method @IntRange(from=0) public long getMinUpdateIntervalMillis();
+    method public int getQuality();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.location.LocationRequest> CREATOR;
     field public static final long PASSIVE_INTERVAL = 9223372036854775807L; // 0x7fffffffffffffffL
+    field public static final int QUALITY_BALANCED_POWER_ACCURACY = 102; // 0x66
+    field public static final int QUALITY_HIGH_ACCURACY = 100; // 0x64
+    field public static final int QUALITY_LOW_POWER = 104; // 0x68
   }
 
   public static final class LocationRequest.Builder {
@@ -24078,6 +24086,7 @@
     method @NonNull public android.location.LocationRequest.Builder setMaxUpdates(@IntRange(from=1, to=java.lang.Integer.MAX_VALUE) int);
     method @NonNull public android.location.LocationRequest.Builder setMinUpdateDistanceMeters(@FloatRange(from=0, to=java.lang.Float.MAX_VALUE) float);
     method @NonNull public android.location.LocationRequest.Builder setMinUpdateIntervalMillis(@IntRange(from=0) long);
+    method @NonNull public android.location.LocationRequest.Builder setQuality(int);
   }
 
   public interface OnNmeaMessageListener {
@@ -25430,6 +25439,7 @@
 
   public static final class MediaCodec.CryptoInfo {
     ctor public MediaCodec.CryptoInfo();
+    method @NonNull public android.media.MediaCodec.CryptoInfo.Pattern getPattern();
     method public void set(int, @NonNull int[], @NonNull int[], @NonNull byte[], @NonNull byte[], int);
     method public void setPattern(android.media.MediaCodec.CryptoInfo.Pattern);
     field public byte[] iv;
@@ -26358,7 +26368,7 @@
     method public boolean containsKey(String);
     method public int describeContents();
     method public android.graphics.Bitmap getBitmap(String);
-    method @IntRange(from=0) public int getBitmapDimensionLimit();
+    method @IntRange(from=1) public int getBitmapDimensionLimit();
     method @NonNull public android.media.MediaDescription getDescription();
     method public long getLong(String);
     method public android.media.Rating getRating(String);
@@ -26408,7 +26418,7 @@
     method public android.media.MediaMetadata.Builder putRating(String, android.media.Rating);
     method public android.media.MediaMetadata.Builder putString(String, String);
     method public android.media.MediaMetadata.Builder putText(String, CharSequence);
-    method @NonNull public android.media.MediaMetadata.Builder setBitmapDimensionLimit(int);
+    method @NonNull public android.media.MediaMetadata.Builder setBitmapDimensionLimit(@IntRange(from=1) int);
   }
 
   @Deprecated public abstract class MediaMetadataEditor {
@@ -30006,9 +30016,12 @@
     method public int describeContents();
     method @NonNull public byte[] getKey();
     method @NonNull public String getName();
+    method @NonNull public static java.util.Set<java.lang.String> getSupportedAlgorithms();
     method public int getTruncationLengthBits();
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final String AUTH_AES_XCBC = "xcbc(aes)";
     field public static final String AUTH_CRYPT_AES_GCM = "rfc4106(gcm(aes))";
+    field public static final String AUTH_CRYPT_CHACHA20_POLY1305 = "rfc7539esp(chacha20,poly1305)";
     field public static final String AUTH_HMAC_MD5 = "hmac(md5)";
     field public static final String AUTH_HMAC_SHA1 = "hmac(sha1)";
     field public static final String AUTH_HMAC_SHA256 = "hmac(sha256)";
@@ -30016,6 +30029,7 @@
     field public static final String AUTH_HMAC_SHA512 = "hmac(sha512)";
     field @NonNull public static final android.os.Parcelable.Creator<android.net.IpSecAlgorithm> CREATOR;
     field public static final String CRYPT_AES_CBC = "cbc(aes)";
+    field public static final String CRYPT_AES_CTR = "rfc3686(ctr(aes))";
   }
 
   public final class IpSecManager {
@@ -41421,8 +41435,16 @@
     method @NonNull public java.util.Map<android.view.autofill.AutofillId,android.service.autofill.FieldClassification> getFieldsClassification();
     method @NonNull public java.util.Set<java.lang.String> getIgnoredDatasetIds();
     method @NonNull public java.util.Map<android.view.autofill.AutofillId,java.util.Set<java.lang.String>> getManuallyEnteredField();
+    method public int getNoSaveReason();
     method @NonNull public java.util.Set<java.lang.String> getSelectedDatasetIds();
     method public int getType();
+    field public static final int NO_SAVE_REASON_DATASET_MATCH = 6; // 0x6
+    field public static final int NO_SAVE_REASON_FIELD_VALIDATION_FAILED = 5; // 0x5
+    field public static final int NO_SAVE_REASON_HAS_EMPTY_REQUIRED = 3; // 0x3
+    field public static final int NO_SAVE_REASON_NONE = 0; // 0x0
+    field public static final int NO_SAVE_REASON_NO_SAVE_INFO = 1; // 0x1
+    field public static final int NO_SAVE_REASON_NO_VALUE_CHANGED = 4; // 0x4
+    field public static final int NO_SAVE_REASON_WITH_DELAY_SAVE_FLAG = 2; // 0x2
     field public static final int TYPE_AUTHENTICATION_SELECTED = 2; // 0x2
     field public static final int TYPE_CONTEXT_COMMITTED = 4; // 0x4
     field public static final int TYPE_DATASETS_SHOWN = 5; // 0x5
@@ -42226,11 +42248,13 @@
     method public long getLastAudiblyAlertedMillis();
     method public String getOverrideGroupKey();
     method public int getRank();
+    method @Nullable public android.content.pm.ShortcutInfo getShortcutInfo();
     method @NonNull public java.util.List<android.app.Notification.Action> getSmartActions();
     method @NonNull public java.util.List<java.lang.CharSequence> getSmartReplies();
     method public int getSuppressedVisualEffects();
     method public int getUserSentiment();
     method public boolean isAmbient();
+    method public boolean isConversation();
     method public boolean isSuspended();
     method public boolean matchesInterruptionFilter();
     field public static final int USER_SENTIMENT_NEGATIVE = -1; // 0xffffffff
@@ -45192,6 +45216,7 @@
   }
 
   public static final class CarrierConfigManager.Ims {
+    field public static final String KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL = "ims.ims_single_registration_required_bool";
     field public static final String KEY_PREFIX = "ims.";
     field public static final String KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT = "ims.wifi_off_deferring_time_millis_int";
   }
@@ -46196,6 +46221,7 @@
   }
 
   public class SignalStrength implements android.os.Parcelable {
+    ctor public SignalStrength(@NonNull android.telephony.SignalStrength);
     method public int describeContents();
     method @Deprecated public int getCdmaDbm();
     method @Deprecated public int getCdmaEcio();
@@ -46223,6 +46249,7 @@
     method @NonNull public android.os.Bundle getCarrierConfigValues();
     method @Deprecated public static android.telephony.SmsManager getDefault();
     method public static int getDefaultSmsSubscriptionId();
+    method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getSmsCapacityOnIcc();
     method @Deprecated public static android.telephony.SmsManager getSmsManagerForSubscriptionId(int);
     method @RequiresPermission(android.Manifest.permission.SMS_FINANCIAL_TRANSACTIONS) public void getSmsMessagesForFinancialApp(android.os.Bundle, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.SmsManager.FinancialSmsCallback);
     method @Nullable @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getSmscAddress();
@@ -46565,6 +46592,7 @@
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getDeviceSoftwareVersion();
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>> getEmergencyNumberList();
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>> getEmergencyNumberList(int);
+    method @NonNull @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<java.lang.String> getEquivalentHomePlmns();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String[] getForbiddenPlmns();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getGroupIdLevel1();
     method public String getIccAuthentication(int, int, String);
@@ -46589,7 +46617,7 @@
     method @Deprecated 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(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_COARSE_LOCATION}) public android.telephony.ServiceState getServiceState();
+    method @Nullable @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();
@@ -46612,7 +46640,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getVoiceMailAlphaTag();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getVoiceMailNumber();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getVoiceNetworkType();
-    method public android.net.Uri getVoicemailRingtoneUri(android.telecom.PhoneAccountHandle);
+    method @Nullable public android.net.Uri getVoicemailRingtoneUri(android.telecom.PhoneAccountHandle);
     method public boolean hasCarrierPrivileges();
     method public boolean hasIccCard();
     method @Deprecated public boolean iccCloseLogicalChannel(int);
@@ -46701,6 +46729,9 @@
     field public static final int DATA_ENABLED_REASON_USER = 0; // 0x0
     field public static final int DATA_SUSPENDED = 3; // 0x3
     field public static final int DATA_UNKNOWN = -1; // 0xffffffff
+    field public static final int ERI_FLASH = 2; // 0x2
+    field public static final int ERI_OFF = 1; // 0x1
+    field public static final int ERI_ON = 0; // 0x0
     field public static final String EXTRA_ACTIVE_SIM_SUPPORTED_COUNT = "android.telephony.extra.ACTIVE_SIM_SUPPORTED_COUNT";
     field public static final String EXTRA_CALL_VOICEMAIL_INTENT = "android.telephony.extra.CALL_VOICEMAIL_INTENT";
     field public static final String EXTRA_CARRIER_ID = "android.telephony.extra.CARRIER_ID";
@@ -48177,10 +48208,6 @@
     method @NonNull public android.text.StaticLayout.Builder setUseLineSpacingFromFallbacks(boolean);
   }
 
-  public class StyledTextShaper {
-    method @NonNull public static java.util.List<android.graphics.text.PositionedGlyphs> shapeText(@NonNull CharSequence, int, int, @NonNull android.text.TextDirectionHeuristic, @NonNull android.text.TextPaint);
-  }
-
   public interface TextDirectionHeuristic {
     method public boolean isRtl(char[], int, int);
     method public boolean isRtl(CharSequence, int, int);
@@ -48210,6 +48237,14 @@
     field @Px public float underlineThickness;
   }
 
+  public class TextShaper {
+    method public static void shapeText(@NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.text.TextDirectionHeuristic, @NonNull android.text.TextPaint, @NonNull android.text.TextShaper.GlyphsConsumer);
+  }
+
+  public static interface TextShaper.GlyphsConsumer {
+    method public void accept(@IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.graphics.text.PositionedGlyphs, @NonNull android.text.TextPaint);
+  }
+
   public class TextUtils {
     method @Deprecated public static CharSequence commaEllipsize(CharSequence, android.text.TextPaint, float, String, String);
     method public static CharSequence concat(java.lang.CharSequence...);
@@ -49008,6 +49043,7 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.text.style.SuggestionSpan> CREATOR;
     field public static final int FLAG_AUTO_CORRECTION = 4; // 0x4
     field public static final int FLAG_EASY_CORRECT = 1; // 0x1
+    field public static final int FLAG_GRAMMAR_ERROR = 8; // 0x8
     field public static final int FLAG_MISSPELLED = 2; // 0x2
     field public static final int SUGGESTIONS_MAX_SIZE = 5; // 0x5
     field @Deprecated public static final String SUGGESTION_SPAN_PICKED_AFTER = "after";
@@ -51838,11 +51874,12 @@
     method @Nullable public android.net.Uri getLinkUri();
     method public int getSource();
     field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1
-    field public static final int SOURCE_AUTOFILL = 3; // 0x3
-    field public static final int SOURCE_CLIPBOARD = 0; // 0x0
-    field public static final int SOURCE_DRAG_AND_DROP = 2; // 0x2
-    field public static final int SOURCE_INPUT_METHOD = 1; // 0x1
-    field public static final int SOURCE_PROCESS_TEXT = 4; // 0x4
+    field public static final int SOURCE_APP = 0; // 0x0
+    field public static final int SOURCE_AUTOFILL = 4; // 0x4
+    field public static final int SOURCE_CLIPBOARD = 1; // 0x1
+    field public static final int SOURCE_DRAG_AND_DROP = 3; // 0x3
+    field public static final int SOURCE_INPUT_METHOD = 2; // 0x2
+    field public static final int SOURCE_PROCESS_TEXT = 5; // 0x5
   }
 
   public static final class OnReceiveContentCallback.Payload.Builder {
@@ -55499,6 +55536,7 @@
     method public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int);
     method public android.os.Handler getHandler();
     method public CharSequence getSelectedText(int);
+    method @Nullable public default android.view.inputmethod.SurroundingText getSurroundingText(@IntRange(from=0) int, @IntRange(from=0) int, int);
     method public CharSequence getTextAfterCursor(int, int);
     method public CharSequence getTextBeforeCursor(int, int);
     method public boolean performContextMenuAction(int);
@@ -55707,6 +55745,17 @@
     method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeNameResId(int);
   }
 
+  public final class SurroundingText implements android.os.Parcelable {
+    ctor public SurroundingText(@NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0xffffffff) int);
+    method public int describeContents();
+    method @IntRange(from=0xffffffff) public int getOffset();
+    method @IntRange(from=0) public int getSelectionEnd();
+    method @IntRange(from=0) public int getSelectionStart();
+    method @NonNull public CharSequence getText();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.SurroundingText> CREATOR;
+  }
+
 }
 
 package android.view.inspector {
@@ -56375,6 +56424,7 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.view.textservice.SuggestionsInfo> CREATOR;
     field public static final int RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS = 4; // 0x4
     field public static final int RESULT_ATTR_IN_THE_DICTIONARY = 1; // 0x1
+    field public static final int RESULT_ATTR_LOOKS_LIKE_GRAMMAR_ERROR = 8; // 0x8
     field public static final int RESULT_ATTR_LOOKS_LIKE_TYPO = 2; // 0x2
   }
 
diff --git a/non-updatable-api/module-lib-current.txt b/non-updatable-api/module-lib-current.txt
index 003a363..ae6ecc7 100644
--- a/non-updatable-api/module-lib-current.txt
+++ b/non-updatable-api/module-lib-current.txt
@@ -1,10 +1,20 @@
 // Signature format: 2.0
 package android.app {
 
+  public class ActivityManager {
+    method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void addHomeVisibilityListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.HomeVisibilityListener);
+    method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void removeHomeVisibilityListener(@NonNull android.app.HomeVisibilityListener);
+  }
+
   public class AppOpsManager {
     field public static final String OPSTR_NO_ISOLATED_STORAGE = "android:no_isolated_storage";
   }
 
+  public abstract class HomeVisibilityListener {
+    ctor public HomeVisibilityListener();
+    method public abstract void onHomeVisibilityChanged(boolean);
+  }
+
   public class NotificationManager {
     method public boolean hasEnabledNotificationListener(@NonNull String, @NonNull android.os.UserHandle);
     field public static final String ACTION_NOTIFICATION_LISTENER_ENABLED_CHANGED = "android.app.action.NOTIFICATION_LISTENER_ENABLED_CHANGED";
@@ -53,6 +63,10 @@
     field public static final int FLAG_FROM_KEY = 4096; // 0x1000
   }
 
+  public class MediaMetadataRetriever implements java.lang.AutoCloseable {
+    field public static final int METADATA_KEY_VIDEO_CODEC_MIME_TYPE = 40; // 0x28
+  }
+
 }
 
 package android.media.session {
@@ -72,13 +86,17 @@
   public final class MediaSessionManager {
     method public void addOnActiveSessionsChangedListener(@NonNull android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, @Nullable android.content.ComponentName, int, @Nullable android.os.Handler);
     method public void dispatchMediaKeyEventAsSystemService(@NonNull android.view.KeyEvent);
-    method public boolean dispatchMediaKeyEventAsSystemService(@NonNull android.media.session.MediaSession.Token, @NonNull android.view.KeyEvent);
+    method public boolean dispatchMediaKeyEventToSessionAsSystemService(@NonNull android.view.KeyEvent, @NonNull android.media.session.MediaSession.Token);
     method public void dispatchVolumeKeyEventAsSystemService(@NonNull android.view.KeyEvent, int);
-    method public void dispatchVolumeKeyEventAsSystemService(@NonNull android.media.session.MediaSession.Token, @NonNull android.view.KeyEvent);
+    method public void dispatchVolumeKeyEventToSessionAsSystemService(@NonNull android.view.KeyEvent, @NonNull android.media.session.MediaSession.Token);
     field public static final int RESULT_MEDIA_KEY_HANDLED = 1; // 0x1
     field public static final int RESULT_MEDIA_KEY_NOT_HANDLED = 0; // 0x0
   }
 
+  public final class PlaybackState implements android.os.Parcelable {
+    method public boolean isActiveState();
+  }
+
 }
 
 package android.os {
@@ -89,8 +107,6 @@
 
   public interface Parcelable {
     method public default int getStability();
-    field public static final int PARCELABLE_STABILITY_LOCAL = 0; // 0x0
-    field public static final int PARCELABLE_STABILITY_VINTF = 1; // 0x1
   }
 
   public class StatsServiceManager {
@@ -119,6 +135,18 @@
 
 }
 
+package android.telephony {
+
+  public abstract class CellSignalStrength {
+    method public static int getNumSignalStrengthLevels();
+  }
+
+  public class TelephonyManager {
+    method @NonNull public static int[] getAllNetworkTypes();
+  }
+
+}
+
 package android.util {
 
   public class AtomicFile {
diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt
index 04847d5..5c8c4d5 100644
--- a/non-updatable-api/system-current.txt
+++ b/non-updatable-api/system-current.txt
@@ -38,6 +38,7 @@
     field public static final String BIND_EXTERNAL_STORAGE_SERVICE = "android.permission.BIND_EXTERNAL_STORAGE_SERVICE";
     field public static final String BIND_IMS_SERVICE = "android.permission.BIND_IMS_SERVICE";
     field public static final String BIND_KEYGUARD_APPWIDGET = "android.permission.BIND_KEYGUARD_APPWIDGET";
+    field public static final String BIND_MUSIC_RECOGNITION_SERVICE = "android.permission.BIND_MUSIC_RECOGNITION_SERVICE";
     field public static final String BIND_NETWORK_RECOMMENDATION_SERVICE = "android.permission.BIND_NETWORK_RECOMMENDATION_SERVICE";
     field public static final String BIND_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE";
     field public static final String BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE = "android.permission.BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE";
@@ -120,6 +121,8 @@
     field public static final String MANAGE_DEBUGGING = "android.permission.MANAGE_DEBUGGING";
     field public static final String MANAGE_FACTORY_RESET_PROTECTION = "android.permission.MANAGE_FACTORY_RESET_PROTECTION";
     field public static final String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS";
+    field public static final String MANAGE_MUSIC_RECOGNITION = "android.permission.MANAGE_MUSIC_RECOGNITION";
+    field public static final String MANAGE_NOTIFICATION_LISTENERS = "android.permission.MANAGE_NOTIFICATION_LISTENERS";
     field public static final String MANAGE_ONE_TIME_PERMISSION_SESSIONS = "android.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS";
     field public static final String MANAGE_ROLE_HOLDERS = "android.permission.MANAGE_ROLE_HOLDERS";
     field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS";
@@ -222,6 +225,7 @@
     field public static final String SET_WALLPAPER_COMPONENT = "android.permission.SET_WALLPAPER_COMPONENT";
     field public static final String SHOW_KEYGUARD_MESSAGE = "android.permission.SHOW_KEYGUARD_MESSAGE";
     field public static final String SHUTDOWN = "android.permission.SHUTDOWN";
+    field public static final String START_ACTIVITIES_FROM_BACKGROUND = "android.permission.START_ACTIVITIES_FROM_BACKGROUND";
     field public static final String STATUS_BAR_SERVICE = "android.permission.STATUS_BAR_SERVICE";
     field public static final String STOP_APP_SWITCHES = "android.permission.STOP_APP_SWITCHES";
     field public static final String SUBSTITUTE_NOTIFICATION_APP_NAME = "android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME";
@@ -306,6 +310,7 @@
     field public static final int config_helpPackageNameKey = 17039387; // 0x104001b
     field public static final int config_helpPackageNameValue = 17039388; // 0x104001c
     field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028
+    field public static final int config_systemAutomotiveProjection = 17039402; // 0x104002a
     field public static final int config_systemGallery = 17039399; // 0x1040027
     field public static final int config_systemVideoCall = 17039401; // 0x1040029
   }
@@ -591,7 +596,7 @@
 
   public class BroadcastOptions {
     method public static android.app.BroadcastOptions makeBasic();
-    method @RequiresPermission("android.permission.START_ACTIVITIES_FROM_BACKGROUND") public void setBackgroundActivityStartsAllowed(boolean);
+    method @RequiresPermission(android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND) public void setBackgroundActivityStartsAllowed(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();
@@ -672,8 +677,10 @@
   public class NotificationManager {
     method @NonNull public java.util.List<java.lang.String> getAllowedAssistantAdjustments();
     method @Nullable public android.content.ComponentName getAllowedNotificationAssistant();
+    method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public java.util.List<android.content.ComponentName> getEnabledNotificationListeners();
     method public boolean isNotificationAssistantAccessGranted(@NonNull android.content.ComponentName);
     method public void setNotificationAssistantAccessGranted(@Nullable android.content.ComponentName, boolean);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public void setNotificationListenerAccessGranted(@NonNull android.content.ComponentName, boolean);
     field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL = "android.app.action.CLOSE_NOTIFICATION_HANDLER_PANEL";
     field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_OPEN_NOTIFICATION_HANDLER_PANEL = "android.app.action.OPEN_NOTIFICATION_HANDLER_PANEL";
     field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL = "android.app.action.TOGGLE_NOTIFICATION_HANDLER_PANEL";
@@ -858,6 +865,7 @@
     field public static final int PROVISIONING_TRIGGER_QR_CODE = 2; // 0x2
     field public static final int PROVISIONING_TRIGGER_UNSPECIFIED = 0; // 0x0
     field public static final int STATE_USER_PROFILE_COMPLETE = 4; // 0x4
+    field public static final int STATE_USER_PROFILE_FINALIZED = 5; // 0x5
     field public static final int STATE_USER_SETUP_COMPLETE = 2; // 0x2
     field public static final int STATE_USER_SETUP_FINALIZED = 3; // 0x3
     field public static final int STATE_USER_SETUP_INCOMPLETE = 1; // 0x1
@@ -1386,20 +1394,20 @@
 package android.app.usage {
 
   public final class CacheQuotaHint implements android.os.Parcelable {
-    ctor public CacheQuotaHint(android.app.usage.CacheQuotaHint.Builder);
+    ctor public CacheQuotaHint(@NonNull android.app.usage.CacheQuotaHint.Builder);
     method public int describeContents();
     method public long getQuota();
     method public int getUid();
-    method public android.app.usage.UsageStats getUsageStats();
-    method public String getVolumeUuid();
-    method public void writeToParcel(android.os.Parcel, int);
+    method @Nullable public android.app.usage.UsageStats getUsageStats();
+    method @Nullable public String getVolumeUuid();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.usage.CacheQuotaHint> CREATOR;
     field public static final long QUOTA_NOT_SET = -1L; // 0xffffffffffffffffL
   }
 
   public static final class CacheQuotaHint.Builder {
     ctor public CacheQuotaHint.Builder();
-    ctor public CacheQuotaHint.Builder(android.app.usage.CacheQuotaHint);
+    ctor public CacheQuotaHint.Builder(@NonNull android.app.usage.CacheQuotaHint);
     method @NonNull public android.app.usage.CacheQuotaHint build();
     method @NonNull public android.app.usage.CacheQuotaHint.Builder setQuota(long);
     method @NonNull public android.app.usage.CacheQuotaHint.Builder setUid(int);
@@ -1737,6 +1745,7 @@
     field public static final String APP_PREDICTION_SERVICE = "app_prediction";
     field public static final String BACKUP_SERVICE = "backup";
     field public static final String BATTERY_STATS_SERVICE = "batterystats";
+    field public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 1048576; // 0x100000
     field public static final String BUGREPORT_SERVICE = "bugreport";
     field public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions";
     field public static final String CONTEXTHUB_SERVICE = "contexthub";
@@ -4109,9 +4118,8 @@
     method @Deprecated public long getInterval();
     method @Deprecated public int getNumUpdates();
     method @Deprecated @NonNull public String getProvider();
-    method public int getQuality();
     method @Deprecated public float getSmallestDisplacement();
-    method @Nullable public android.os.WorkSource getWorkSource();
+    method @NonNull public android.os.WorkSource getWorkSource();
     method public boolean isHiddenFromAppOps();
     method public boolean isLocationSettingsIgnored();
     method public boolean isLowPower();
@@ -4128,11 +4136,11 @@
     method @Deprecated @NonNull public android.location.LocationRequest setQuality(int);
     method @Deprecated @NonNull public android.location.LocationRequest setSmallestDisplacement(float);
     method @Deprecated public void setWorkSource(@Nullable android.os.WorkSource);
-    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
-    field public static final int POWER_HIGH = 203; // 0xcb
-    field public static final int POWER_LOW = 201; // 0xc9
+    field @Deprecated public static final int ACCURACY_BLOCK = 102; // 0x66
+    field @Deprecated public static final int ACCURACY_CITY = 104; // 0x68
+    field @Deprecated public static final int ACCURACY_FINE = 100; // 0x64
+    field @Deprecated public static final int POWER_HIGH = 203; // 0xcb
+    field @Deprecated public static final int POWER_LOW = 201; // 0xc9
     field @Deprecated public static final int POWER_NONE = 200; // 0xc8
   }
 
@@ -4140,7 +4148,6 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_APP_OPS_STATS) public android.location.LocationRequest.Builder setHiddenFromAppOps(boolean);
     method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LocationRequest.Builder setLocationSettingsIgnored(boolean);
     method @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public android.location.LocationRequest.Builder setLowPower(boolean);
-    method @NonNull public android.location.LocationRequest.Builder setQuality(int);
     method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.location.LocationRequest.Builder setWorkSource(@Nullable android.os.WorkSource);
   }
 
@@ -4557,6 +4564,58 @@
 
 }
 
+package android.media.musicrecognition {
+
+  public class MusicRecognitionManager {
+    method @RequiresPermission(android.Manifest.permission.MANAGE_MUSIC_RECOGNITION) public void beginStreamingSearch(@NonNull android.media.musicrecognition.RecognitionRequest, @NonNull java.util.concurrent.Executor, @NonNull android.media.musicrecognition.MusicRecognitionManager.RecognitionCallback);
+    field public static final int RECOGNITION_FAILED_AUDIO_UNAVAILABLE = 7; // 0x7
+    field public static final int RECOGNITION_FAILED_NOT_FOUND = 1; // 0x1
+    field public static final int RECOGNITION_FAILED_NO_CONNECTIVITY = 2; // 0x2
+    field public static final int RECOGNITION_FAILED_SERVICE_KILLED = 5; // 0x5
+    field public static final int RECOGNITION_FAILED_SERVICE_UNAVAILABLE = 3; // 0x3
+    field public static final int RECOGNITION_FAILED_TIMEOUT = 6; // 0x6
+    field public static final int RECOGNITION_FAILED_UNKNOWN = -1; // 0xffffffff
+  }
+
+  public static interface MusicRecognitionManager.RecognitionCallback {
+    method public void onAudioStreamClosed();
+    method public void onRecognitionFailed(@NonNull android.media.musicrecognition.RecognitionRequest, int);
+    method public void onRecognitionSucceeded(@NonNull android.media.musicrecognition.RecognitionRequest, @NonNull android.media.MediaMetadata, @Nullable android.os.Bundle);
+  }
+
+  public abstract class MusicRecognitionService extends android.app.Service {
+    ctor public MusicRecognitionService();
+    method public abstract void onRecognize(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @NonNull android.media.musicrecognition.MusicRecognitionService.Callback);
+  }
+
+  public static interface MusicRecognitionService.Callback {
+    method public void onRecognitionFailed(int);
+    method public void onRecognitionSucceeded(@NonNull android.media.MediaMetadata, @Nullable android.os.Bundle);
+  }
+
+  public final class RecognitionRequest implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public android.media.AudioAttributes getAudioAttributes();
+    method @NonNull public android.media.AudioFormat getAudioFormat();
+    method public int getCaptureSession();
+    method public int getIgnoreBeginningFrames();
+    method public int getMaxAudioLengthSeconds();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.media.musicrecognition.RecognitionRequest> CREATOR;
+  }
+
+  public static final class RecognitionRequest.Builder {
+    ctor public RecognitionRequest.Builder();
+    method @NonNull public android.media.musicrecognition.RecognitionRequest build();
+    method @NonNull public android.media.musicrecognition.RecognitionRequest.Builder setAudioAttributes(@NonNull android.media.AudioAttributes);
+    method @NonNull public android.media.musicrecognition.RecognitionRequest.Builder setAudioFormat(@NonNull android.media.AudioFormat);
+    method @NonNull public android.media.musicrecognition.RecognitionRequest.Builder setCaptureSession(int);
+    method @NonNull public android.media.musicrecognition.RecognitionRequest.Builder setIgnoreBeginningFrames(int);
+    method @NonNull public android.media.musicrecognition.RecognitionRequest.Builder setMaxAudioLengthSeconds(int);
+  }
+
+}
+
 package android.media.session {
 
   public final class MediaSessionManager {
@@ -4655,6 +4714,22 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.DvbDeviceInfo> CREATOR;
   }
 
+  public final class TunedInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getAppTag();
+    method public int getAppType();
+    method @Nullable public android.net.Uri getChannelUri();
+    method @NonNull public String getInputId();
+    method public boolean isForeground();
+    method public boolean isRecordingSession();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field public static final int APP_TAG_SELF = 0; // 0x0
+    field public static final int APP_TYPE_NON_SYSTEM = 3; // 0x3
+    field public static final int APP_TYPE_SELF = 1; // 0x1
+    field public static final int APP_TYPE_SYSTEM = 2; // 0x2
+    field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.TunedInfo> CREATOR;
+  }
+
   public final class TvContentRatingSystemInfo implements android.os.Parcelable {
     method public static android.media.tv.TvContentRatingSystemInfo createTvContentRatingSystemInfo(int, android.content.pm.ApplicationInfo);
     method public int describeContents();
@@ -4770,6 +4845,7 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS) public void addBlockedRating(@NonNull android.media.tv.TvContentRating);
     method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public boolean captureFrame(String, android.view.Surface, android.media.tv.TvStreamConfig);
     method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public java.util.List<android.media.tv.TvStreamConfig> getAvailableTvStreamConfigList(String);
+    method @NonNull @RequiresPermission("com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS") public java.util.List<android.media.tv.TunedInfo> getCurrentTunedInfos();
     method @NonNull @RequiresPermission("android.permission.DVB_DEVICE") public java.util.List<android.media.tv.DvbDeviceInfo> getDvbDeviceList();
     method @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE) public java.util.List<android.media.tv.TvInputHardwareInfo> getHardwareList();
     method @RequiresPermission(android.Manifest.permission.READ_CONTENT_RATING_SYSTEMS) public java.util.List<android.media.tv.TvContentRatingSystemInfo> getTvContentRatingSystemList();
@@ -4795,6 +4871,10 @@
     method public abstract void onStreamConfigChanged(android.media.tv.TvStreamConfig[]);
   }
 
+  public abstract static class TvInputManager.TvInputCallback {
+    method public void onCurrentTunedInfosUpdated(@NonNull java.util.List<android.media.tv.TunedInfo>);
+  }
+
   public abstract class TvInputService extends android.app.Service {
     method @Nullable public android.media.tv.TvInputInfo onHardwareAdded(android.media.tv.TvInputHardwareInfo);
     method @Nullable public String onHardwareRemoved(android.media.tv.TvInputHardwareInfo);
@@ -4922,6 +5002,7 @@
     method @Nullable public android.media.tv.tuner.DemuxCapabilities getDemuxCapabilities();
     method @Nullable public android.media.tv.tuner.frontend.FrontendInfo getFrontendInfo();
     method @Nullable public android.media.tv.tuner.frontend.FrontendStatus getFrontendStatus(@NonNull int[]);
+    method public int linkFrontendToCiCam(int);
     method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_DESCRAMBLER) public android.media.tv.tuner.Descrambler openDescrambler();
     method @Nullable public android.media.tv.tuner.dvr.DvrPlayback openDvrPlayback(long, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.dvr.OnPlaybackStatusChangedListener);
     method @Nullable public android.media.tv.tuner.dvr.DvrRecorder openDvrRecorder(long, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.dvr.OnRecordStatusChangedListener);
@@ -4935,9 +5016,14 @@
     method public void setResourceLostListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.Tuner.OnResourceLostListener);
     method public void shareFrontendFromTuner(@NonNull android.media.tv.tuner.Tuner);
     method public int tune(@NonNull android.media.tv.tuner.frontend.FrontendSettings);
+    method public int unlinkFrontendToCiCam(int);
     method public void updateResourcePriority(int, int);
     field public static final int INVALID_AV_SYNC_ID = -1; // 0xffffffff
     field public static final int INVALID_FILTER_ID = -1; // 0xffffffff
+    field public static final long INVALID_FILTER_ID_64BIT = -1L; // 0xffffffffffffffffL
+    field public static final int INVALID_FRONTEND_SETTING_FREQUENCY = -1; // 0xffffffff
+    field public static final int INVALID_LTS_ID = -1; // 0xffffffff
+    field public static final int INVALID_MMTP_RECORD_EVENT_MPT_SEQUENCE_NUM = -1; // 0xffffffff
     field public static final int INVALID_STREAM_ID = 65535; // 0xffff
     field public static final long INVALID_TIMESTAMP = -1L; // 0xffffffffffffffffL
     field public static final int INVALID_TS_PID = 65535; // 0xffff
@@ -4957,15 +5043,22 @@
     method public void onResourceLost(@NonNull android.media.tv.tuner.Tuner);
   }
 
+  public final class TunerVersionChecker {
+    method public static int getTunerVersion();
+    field public static final int TUNER_VERSION_1_0 = 65536; // 0x10000
+    field public static final int TUNER_VERSION_1_1 = 65537; // 0x10001
+    field public static final int TUNER_VERSION_UNKNOWN = 0; // 0x0
+  }
+
 }
 
 package android.media.tv.tuner.dvr {
 
   public class DvrPlayback implements java.lang.AutoCloseable {
-    method public int attachFilter(@NonNull android.media.tv.tuner.filter.Filter);
+    method @Deprecated public int attachFilter(@NonNull android.media.tv.tuner.filter.Filter);
     method public void close();
     method public int configure(@NonNull android.media.tv.tuner.dvr.DvrSettings);
-    method public int detachFilter(@NonNull android.media.tv.tuner.filter.Filter);
+    method @Deprecated public int detachFilter(@NonNull android.media.tv.tuner.filter.Filter);
     method public int flush();
     method public long read(long);
     method public long read(@NonNull byte[], long, long);
@@ -5059,12 +5152,45 @@
 
   public class AvSettings extends android.media.tv.tuner.filter.Settings {
     method @NonNull public static android.media.tv.tuner.filter.AvSettings.Builder builder(int, boolean);
+    method public int getAudioStreamType();
+    method public int getVideoStreamType();
     method public boolean isPassthrough();
+    field public static final int AUDIO_STREAM_TYPE_AAC = 6; // 0x6
+    field public static final int AUDIO_STREAM_TYPE_AC3 = 7; // 0x7
+    field public static final int AUDIO_STREAM_TYPE_AC4 = 9; // 0x9
+    field public static final int AUDIO_STREAM_TYPE_DRA = 15; // 0xf
+    field public static final int AUDIO_STREAM_TYPE_DTS = 10; // 0xa
+    field public static final int AUDIO_STREAM_TYPE_DTS_HD = 11; // 0xb
+    field public static final int AUDIO_STREAM_TYPE_EAC3 = 8; // 0x8
+    field public static final int AUDIO_STREAM_TYPE_MP3 = 2; // 0x2
+    field public static final int AUDIO_STREAM_TYPE_MPEG1 = 3; // 0x3
+    field public static final int AUDIO_STREAM_TYPE_MPEG2 = 4; // 0x4
+    field public static final int AUDIO_STREAM_TYPE_MPEGH = 5; // 0x5
+    field public static final int AUDIO_STREAM_TYPE_OPUS = 13; // 0xd
+    field public static final int AUDIO_STREAM_TYPE_PCM = 1; // 0x1
+    field public static final int AUDIO_STREAM_TYPE_UNDEFINED = 0; // 0x0
+    field public static final int AUDIO_STREAM_TYPE_VORBIS = 14; // 0xe
+    field public static final int AUDIO_STREAM_TYPE_WMA = 12; // 0xc
+    field public static final int VIDEO_STREAM_TYPE_AV1 = 10; // 0xa
+    field public static final int VIDEO_STREAM_TYPE_AVC = 5; // 0x5
+    field public static final int VIDEO_STREAM_TYPE_AVS = 11; // 0xb
+    field public static final int VIDEO_STREAM_TYPE_AVS2 = 12; // 0xc
+    field public static final int VIDEO_STREAM_TYPE_HEVC = 6; // 0x6
+    field public static final int VIDEO_STREAM_TYPE_MPEG1 = 2; // 0x2
+    field public static final int VIDEO_STREAM_TYPE_MPEG2 = 3; // 0x3
+    field public static final int VIDEO_STREAM_TYPE_MPEG4P2 = 4; // 0x4
+    field public static final int VIDEO_STREAM_TYPE_RESERVED = 1; // 0x1
+    field public static final int VIDEO_STREAM_TYPE_UNDEFINED = 0; // 0x0
+    field public static final int VIDEO_STREAM_TYPE_VC1 = 7; // 0x7
+    field public static final int VIDEO_STREAM_TYPE_VP8 = 8; // 0x8
+    field public static final int VIDEO_STREAM_TYPE_VP9 = 9; // 0x9
   }
 
   public static class AvSettings.Builder {
     method @NonNull public android.media.tv.tuner.filter.AvSettings build();
+    method @NonNull public android.media.tv.tuner.filter.AvSettings.Builder setAudioStreamType(int);
     method @NonNull public android.media.tv.tuner.filter.AvSettings.Builder setPassthrough(boolean);
+    method @NonNull public android.media.tv.tuner.filter.AvSettings.Builder setVideoStreamType(int);
   }
 
   public class DownloadEvent extends android.media.tv.tuner.filter.FilterEvent {
@@ -5090,6 +5216,7 @@
     method public int configure(@NonNull android.media.tv.tuner.filter.FilterConfiguration);
     method public int flush();
     method public int getId();
+    method public long getId64Bit();
     method public int read(@NonNull byte[], long, long);
     method public int setDataSource(@Nullable android.media.tv.tuner.filter.Filter);
     method public int start();
@@ -5141,16 +5268,19 @@
     method @NonNull public static android.media.tv.tuner.filter.IpFilterConfiguration.Builder builder();
     method @NonNull @Size(min=4, max=16) public byte[] getDstIpAddress();
     method public int getDstPort();
+    method @IntRange(from=0, to=61439) public int getIpFilterContextId();
     method @NonNull @Size(min=4, max=16) public byte[] getSrcIpAddress();
     method public int getSrcPort();
     method public int getType();
     method public boolean isPassthrough();
+    field public static final int INVALID_IP_FILTER_CONTEXT_ID = -1; // 0xffffffff
   }
 
   public static final class IpFilterConfiguration.Builder {
     method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration build();
     method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setDstIpAddress(@NonNull byte[]);
     method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setDstPort(int);
+    method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setIpFilterContextId(int);
     method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setPassthrough(boolean);
     method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setSettings(@Nullable android.media.tv.tuner.filter.Settings);
     method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setSrcIpAddress(@NonNull byte[]);
@@ -5174,6 +5304,7 @@
     method public boolean isPrivateData();
     method public boolean isPtsPresent();
     method public boolean isSecureMemory();
+    method public void release();
   }
 
   public final class MmtpFilterConfiguration extends android.media.tv.tuner.filter.FilterConfiguration {
@@ -5190,6 +5321,8 @@
 
   public class MmtpRecordEvent extends android.media.tv.tuner.filter.FilterEvent {
     method public long getDataLength();
+    method public int getMpuSequenceNumber();
+    method public long getPts();
     method public int getScHevcIndexMask();
   }
 
@@ -5352,6 +5485,7 @@
   public class TsRecordEvent extends android.media.tv.tuner.filter.FilterEvent {
     method public long getDataLength();
     method public int getPacketId();
+    method public long getPts();
     method public int getScIndexMask();
     method public int getTsIndexMask();
   }
@@ -5367,9 +5501,13 @@
 
   public class AnalogFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings {
     method @NonNull public static android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder builder();
+    method public int getAftFlag();
     method public int getSifStandard();
     method public int getSignalType();
     method public int getType();
+    field public static final int AFT_FLAG_FALSE = 2; // 0x2
+    field public static final int AFT_FLAG_TRUE = 1; // 0x1
+    field public static final int AFT_FLAG_UNDEFINED = 0; // 0x0
     field public static final int SIF_AUTO = 1; // 0x1
     field public static final int SIF_BG = 2; // 0x2
     field public static final int SIF_BG_A2 = 4; // 0x4
@@ -5402,6 +5540,7 @@
 
   public static class AnalogFrontendSettings.Builder {
     method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings build();
+    method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setAftFlag(int);
     method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setFrequency(int);
     method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setSifStandard(int);
     method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setSignalType(int);
@@ -5517,6 +5656,69 @@
     method @NonNull public android.media.tv.tuner.frontend.AtscFrontendSettings.Builder setModulation(int);
   }
 
+  public class DtmbFrontendCapabilities extends android.media.tv.tuner.frontend.FrontendCapabilities {
+    method public int getBandwidthCapability();
+    method public int getCodeRateCapability();
+    method public int getGuardIntervalCapability();
+    method public int getModulationCapability();
+    method public int getTimeInterleaveModeCapability();
+    method public int getTransmissionModeCapability();
+  }
+
+  public class DtmbFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings {
+    method @NonNull public static android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder builder();
+    method public int getBandwidth();
+    method public int getCodeRate();
+    method public int getGuardInterval();
+    method public int getModulation();
+    method public int getTimeInterleaveMode();
+    method public int getTransmissionMode();
+    method public int getType();
+    field public static final int BANDWIDTH_6MHZ = 4; // 0x4
+    field public static final int BANDWIDTH_8MHZ = 2; // 0x2
+    field public static final int BANDWIDTH_AUTO = 1; // 0x1
+    field public static final int BANDWIDTH_UNDEFINED = 0; // 0x0
+    field public static final int CODERATE_2_5 = 2; // 0x2
+    field public static final int CODERATE_3_5 = 4; // 0x4
+    field public static final int CODERATE_4_5 = 8; // 0x8
+    field public static final int CODERATE_AUTO = 1; // 0x1
+    field public static final int CODERATE_UNDEFINED = 0; // 0x0
+    field public static final int GUARD_INTERVAL_AUTO = 1; // 0x1
+    field public static final int GUARD_INTERVAL_PN_420_CONST = 16; // 0x10
+    field public static final int GUARD_INTERVAL_PN_420_VARIOUS = 2; // 0x2
+    field public static final int GUARD_INTERVAL_PN_595_CONST = 4; // 0x4
+    field public static final int GUARD_INTERVAL_PN_945_CONST = 32; // 0x20
+    field public static final int GUARD_INTERVAL_PN_945_VARIOUS = 8; // 0x8
+    field public static final int GUARD_INTERVAL_PN_RESERVED = 64; // 0x40
+    field public static final int GUARD_INTERVAL_UNDEFINED = 0; // 0x0
+    field public static final int MODULATION_CONSTELLATION_16QAM = 8; // 0x8
+    field public static final int MODULATION_CONSTELLATION_32QAM = 16; // 0x10
+    field public static final int MODULATION_CONSTELLATION_4QAM = 2; // 0x2
+    field public static final int MODULATION_CONSTELLATION_4QAM_NR = 4; // 0x4
+    field public static final int MODULATION_CONSTELLATION_64QAM = 32; // 0x20
+    field public static final int MODULATION_CONSTELLATION_AUTO = 1; // 0x1
+    field public static final int MODULATION_CONSTELLATION_UNDEFINED = 0; // 0x0
+    field public static final int TIME_INTERLEAVE_MODE_AUTO = 1; // 0x1
+    field public static final int TIME_INTERLEAVE_MODE_TIMER_INT_240 = 2; // 0x2
+    field public static final int TIME_INTERLEAVE_MODE_TIMER_INT_720 = 4; // 0x4
+    field public static final int TIME_INTERLEAVE_MODE_UNDEFINED = 0; // 0x0
+    field public static final int TRANSMISSION_MODE_AUTO = 1; // 0x1
+    field public static final int TRANSMISSION_MODE_C1 = 2; // 0x2
+    field public static final int TRANSMISSION_MODE_C3780 = 4; // 0x4
+    field public static final int TRANSMISSION_MODE_UNDEFINED = 0; // 0x0
+  }
+
+  public static final class DtmbFrontendSettings.Builder {
+    method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings build();
+    method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setBandwidth(int);
+    method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setCodeRate(int);
+    method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setFrequency(int);
+    method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setGuardInterval(int);
+    method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setModulation(int);
+    method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setTimeInterleaveMode(int);
+    method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setTransmissionMode(int);
+  }
+
   public class DvbcFrontendCapabilities extends android.media.tv.tuner.frontend.FrontendCapabilities {
     method public int getAnnexCapability();
     method public int getFecCapability();
@@ -5531,6 +5733,7 @@
     method public int getOuterFec();
     method public int getSpectralInversion();
     method public int getSymbolRate();
+    method public int getTimeInterleaveMode();
     method public int getType();
     field public static final int ANNEX_A = 1; // 0x1
     field public static final int ANNEX_B = 2; // 0x2
@@ -5546,9 +5749,20 @@
     field public static final int OUTER_FEC_OUTER_FEC_NONE = 1; // 0x1
     field public static final int OUTER_FEC_OUTER_FEC_RS = 2; // 0x2
     field public static final int OUTER_FEC_UNDEFINED = 0; // 0x0
-    field public static final int SPECTRAL_INVERSION_INVERTED = 2; // 0x2
-    field public static final int SPECTRAL_INVERSION_NORMAL = 1; // 0x1
-    field public static final int SPECTRAL_INVERSION_UNDEFINED = 0; // 0x0
+    field @Deprecated public static final int SPECTRAL_INVERSION_INVERTED = 2; // 0x2
+    field @Deprecated public static final int SPECTRAL_INVERSION_NORMAL = 1; // 0x1
+    field @Deprecated public static final int SPECTRAL_INVERSION_UNDEFINED = 0; // 0x0
+    field public static final int TIME_INTERLEAVE_MODE_128_1_0 = 2; // 0x2
+    field public static final int TIME_INTERLEAVE_MODE_128_1_1 = 4; // 0x4
+    field public static final int TIME_INTERLEAVE_MODE_128_2 = 128; // 0x80
+    field public static final int TIME_INTERLEAVE_MODE_128_3 = 256; // 0x100
+    field public static final int TIME_INTERLEAVE_MODE_128_4 = 512; // 0x200
+    field public static final int TIME_INTERLEAVE_MODE_16_8 = 32; // 0x20
+    field public static final int TIME_INTERLEAVE_MODE_32_4 = 16; // 0x10
+    field public static final int TIME_INTERLEAVE_MODE_64_2 = 8; // 0x8
+    field public static final int TIME_INTERLEAVE_MODE_8_16 = 64; // 0x40
+    field public static final int TIME_INTERLEAVE_MODE_AUTO = 1; // 0x1
+    field public static final int TIME_INTERLEAVE_MODE_UNDEFINED = 0; // 0x0
   }
 
   public static class DvbcFrontendSettings.Builder {
@@ -5560,6 +5774,7 @@
     method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setOuterFec(int);
     method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setSpectralInversion(int);
     method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setSymbolRate(int);
+    method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setTimeInterleaveMode(int);
   }
 
   public class DvbsCodeRate {
@@ -5591,10 +5806,12 @@
     method public int getModulation();
     method public int getPilot();
     method public int getRolloff();
+    method public int getScanType();
     method public int getStandard();
     method public int getSymbolRate();
     method public int getType();
     method public int getVcmMode();
+    method public boolean isDiseqcRxMessage();
     field public static final int MODULATION_AUTO = 1; // 0x1
     field public static final int MODULATION_MOD_128APSK = 2048; // 0x800
     field public static final int MODULATION_MOD_16APSK = 256; // 0x100
@@ -5621,6 +5838,11 @@
     field public static final int ROLLOFF_0_35 = 1; // 0x1
     field public static final int ROLLOFF_0_5 = 6; // 0x6
     field public static final int ROLLOFF_UNDEFINED = 0; // 0x0
+    field public static final int SCAN_TYPE_DIRECT = 1; // 0x1
+    field public static final int SCAN_TYPE_DISEQC = 2; // 0x2
+    field public static final int SCAN_TYPE_JESS = 4; // 0x4
+    field public static final int SCAN_TYPE_UNDEFINED = 0; // 0x0
+    field public static final int SCAN_TYPE_UNICABLE = 3; // 0x3
     field public static final int STANDARD_AUTO = 1; // 0x1
     field public static final int STANDARD_S = 2; // 0x2
     field public static final int STANDARD_S2 = 4; // 0x4
@@ -5633,11 +5855,13 @@
   public static class DvbsFrontendSettings.Builder {
     method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings build();
     method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setCodeRate(@Nullable android.media.tv.tuner.frontend.DvbsCodeRate);
+    method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setDiseqcRxMessage(boolean);
     method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setFrequency(int);
     method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setInputStreamId(int);
     method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setModulation(int);
     method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setPilot(int);
     method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setRolloff(int);
+    method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setScanType(int);
     method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setStandard(int);
     method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setSymbolRate(int);
     method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setVcmMode(int);
@@ -5690,10 +5914,14 @@
     field public static final int CODERATE_AUTO = 1; // 0x1
     field public static final int CODERATE_UNDEFINED = 0; // 0x0
     field public static final int CONSTELLATION_16QAM = 4; // 0x4
+    field public static final int CONSTELLATION_16QAM_R = 64; // 0x40
     field public static final int CONSTELLATION_256QAM = 16; // 0x10
+    field public static final int CONSTELLATION_256QAM_R = 256; // 0x100
     field public static final int CONSTELLATION_64QAM = 8; // 0x8
+    field public static final int CONSTELLATION_64QAM_R = 128; // 0x80
     field public static final int CONSTELLATION_AUTO = 1; // 0x1
     field public static final int CONSTELLATION_QPSK = 2; // 0x2
+    field public static final int CONSTELLATION_QPSK_R = 32; // 0x20
     field public static final int CONSTELLATION_UNDEFINED = 0; // 0x0
     field public static final int GUARD_INTERVAL_19_128 = 64; // 0x40
     field public static final int GUARD_INTERVAL_19_256 = 128; // 0x80
@@ -5727,6 +5955,9 @@
     field public static final int TRANSMISSION_MODE_4K = 8; // 0x8
     field public static final int TRANSMISSION_MODE_8K = 4; // 0x4
     field public static final int TRANSMISSION_MODE_AUTO = 1; // 0x1
+    field public static final int TRANSMISSION_MODE_EXTENDED_16K = 256; // 0x100
+    field public static final int TRANSMISSION_MODE_EXTENDED_32K = 512; // 0x200
+    field public static final int TRANSMISSION_MODE_EXTENDED_8K = 128; // 0x80
     field public static final int TRANSMISSION_MODE_UNDEFINED = 0; // 0x0
   }
 
@@ -5764,8 +5995,12 @@
   }
 
   public abstract class FrontendSettings {
+    method public int getEndFrequency();
     method public int getFrequency();
+    method public int getFrontendSpectralInversion();
     method public abstract int getType();
+    method @IntRange(from=1) public void setEndFrequency(int);
+    method public void setSpectralInversion(int);
     field public static final long FEC_11_15 = 4194304L; // 0x400000L
     field public static final long FEC_11_20 = 8388608L; // 0x800000L
     field public static final long FEC_11_45 = 16777216L; // 0x1000000L
@@ -5803,9 +6038,13 @@
     field public static final long FEC_9_20 = 2097152L; // 0x200000L
     field public static final long FEC_AUTO = 1L; // 0x1L
     field public static final long FEC_UNDEFINED = 0L; // 0x0L
+    field public static final int FRONTEND_SPECTRAL_INVERSION_INVERTED = 2; // 0x2
+    field public static final int FRONTEND_SPECTRAL_INVERSION_NORMAL = 1; // 0x1
+    field public static final int FRONTEND_SPECTRAL_INVERSION_UNDEFINED = 0; // 0x0
     field public static final int TYPE_ANALOG = 1; // 0x1
     field public static final int TYPE_ATSC = 2; // 0x2
     field public static final int TYPE_ATSC3 = 3; // 0x3
+    field public static final int TYPE_DTMB = 10; // 0xa
     field public static final int TYPE_DVBC = 4; // 0x4
     field public static final int TYPE_DVBS = 5; // 0x5
     field public static final int TYPE_DVBT = 6; // 0x6
@@ -5818,14 +6057,21 @@
   public class FrontendStatus {
     method public int getAgc();
     method @NonNull public android.media.tv.tuner.frontend.FrontendStatus.Atsc3PlpTuningInfo[] getAtsc3PlpTuningInfo();
+    method public int getBandwidth();
     method public int getBer();
+    method @NonNull public int[] getBers();
+    method @NonNull public int[] getCodeRates();
     method public int getFreqOffset();
+    method public int getGuardInterval();
     method public int getHierarchy();
     method public long getInnerFec();
+    method @NonNull public int[] getInterleaving();
+    method @NonNull public int[] getIsdbtSegment();
     method @NonNull public boolean[] getLayerErrors();
     method public int getLnbVoltage();
     method public int getMer();
     method public int getModulation();
+    method @NonNull public int[] getModulationsExt();
     method public int getPer();
     method public int getPerBer();
     method public int getPlpId();
@@ -5834,23 +6080,34 @@
     method public int getSnr();
     method public int getSpectralInversion();
     method public int getSymbolRate();
+    method public int getSystemId();
+    method public int getTransmissionMode();
+    method @NonNull public int[] getTsDataRate();
+    method public int getUec();
     method public boolean isDemodLocked();
     method public boolean isEwbs();
     method public boolean isLnaOn();
     method public boolean isRfLocked();
     field public static final int FRONTEND_STATUS_TYPE_AGC = 14; // 0xe
     field public static final int FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO = 21; // 0x15
+    field public static final int FRONTEND_STATUS_TYPE_BANDWIDTH = 25; // 0x19
     field public static final int FRONTEND_STATUS_TYPE_BER = 2; // 0x2
+    field public static final int FRONTEND_STATUS_TYPE_BERS = 23; // 0x17
+    field public static final int FRONTEND_STATUS_TYPE_CODERATES = 24; // 0x18
     field public static final int FRONTEND_STATUS_TYPE_DEMOD_LOCK = 0; // 0x0
     field public static final int FRONTEND_STATUS_TYPE_EWBS = 13; // 0xd
     field public static final int FRONTEND_STATUS_TYPE_FEC = 8; // 0x8
     field public static final int FRONTEND_STATUS_TYPE_FREQ_OFFSET = 18; // 0x12
+    field public static final int FRONTEND_STATUS_TYPE_GUARD_INTERVAL = 26; // 0x1a
     field public static final int FRONTEND_STATUS_TYPE_HIERARCHY = 19; // 0x13
+    field public static final int FRONTEND_STATUS_TYPE_INTERLEAVINGS = 30; // 0x1e
+    field public static final int FRONTEND_STATUS_TYPE_ISDBT_SEGMENTS = 31; // 0x1f
     field public static final int FRONTEND_STATUS_TYPE_LAYER_ERROR = 16; // 0x10
     field public static final int FRONTEND_STATUS_TYPE_LNA = 15; // 0xf
     field public static final int FRONTEND_STATUS_TYPE_LNB_VOLTAGE = 11; // 0xb
     field public static final int FRONTEND_STATUS_TYPE_MER = 17; // 0x11
     field public static final int FRONTEND_STATUS_TYPE_MODULATION = 9; // 0x9
+    field public static final int FRONTEND_STATUS_TYPE_MODULATIONS_EXT = 22; // 0x16
     field public static final int FRONTEND_STATUS_TYPE_PER = 3; // 0x3
     field public static final int FRONTEND_STATUS_TYPE_PLP_ID = 12; // 0xc
     field public static final int FRONTEND_STATUS_TYPE_PRE_BER = 4; // 0x4
@@ -5860,6 +6117,10 @@
     field public static final int FRONTEND_STATUS_TYPE_SNR = 1; // 0x1
     field public static final int FRONTEND_STATUS_TYPE_SPECTRAL = 10; // 0xa
     field public static final int FRONTEND_STATUS_TYPE_SYMBOL_RATE = 7; // 0x7
+    field public static final int FRONTEND_STATUS_TYPE_T2_SYSTEM_ID = 29; // 0x1d
+    field public static final int FRONTEND_STATUS_TYPE_TRANSMISSION_MODE = 27; // 0x1b
+    field public static final int FRONTEND_STATUS_TYPE_TS_DATA_RATES = 32; // 0x20
+    field public static final int FRONTEND_STATUS_TYPE_UEC = 28; // 0x1c
   }
 
   public static class FrontendStatus.Atsc3PlpTuningInfo {
@@ -6023,7 +6284,9 @@
     method public void onHierarchyReported(int);
     method public void onInputStreamIdsReported(@NonNull int[]);
     method public void onLocked();
+    method public default void onModulationReported(int);
     method public void onPlpIdsReported(@NonNull int[]);
+    method public default void onPriorityReported(boolean);
     method public void onProgress(@IntRange(from=0, to=100) int);
     method public void onScanStopped();
     method public void onSignalTypeReported(int);
@@ -7442,6 +7705,22 @@
     method public boolean hasSingleFileDescriptor();
   }
 
+  public interface Parcelable {
+    field public static final int PARCELABLE_STABILITY_LOCAL = 0; // 0x0
+    field public static final int PARCELABLE_STABILITY_VINTF = 1; // 0x1
+  }
+
+  public final class ParcelableHolder implements android.os.Parcelable {
+    ctor public ParcelableHolder(int);
+    method public int describeContents();
+    method @Nullable public <T extends android.os.Parcelable> T getParcelable(@NonNull Class<T>);
+    method public int getStability();
+    method public void readFromParcel(@NonNull android.os.Parcel);
+    method public boolean setParcelable(@Nullable android.os.Parcelable);
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.os.ParcelableHolder> CREATOR;
+  }
+
   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();
@@ -9832,6 +10111,25 @@
     field public static final String MBMS_STREAMING_SERVICE_ACTION = "android.telephony.action.EmbmsStreaming";
   }
 
+  public final class ModemActivityInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public android.telephony.ModemActivityInfo getDelta(@NonNull android.telephony.ModemActivityInfo);
+    method public long getIdleTimeMillis();
+    method public static int getNumTxPowerLevels();
+    method public long getReceiveTimeMillis();
+    method public long getSleepTimeMillis();
+    method public long getTimestampMillis();
+    method public long getTransmitDurationMillisAtPowerLevel(int);
+    method @NonNull public android.util.Range<java.lang.Integer> getTransmitPowerRange(int);
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ModemActivityInfo> CREATOR;
+    field public static final int TX_POWER_LEVEL_0 = 0; // 0x0
+    field public static final int TX_POWER_LEVEL_1 = 1; // 0x1
+    field public static final int TX_POWER_LEVEL_2 = 2; // 0x2
+    field public static final int TX_POWER_LEVEL_3 = 3; // 0x3
+    field public static final int TX_POWER_LEVEL_4 = 4; // 0x4
+  }
+
   public final class NetworkRegistrationInfo implements android.os.Parcelable {
     method @Nullable public android.telephony.DataSpecificRegistrationInfo getDataSpecificInfo();
     method public int getRegistrationState();
@@ -10211,7 +10509,6 @@
     method public boolean disableCellBroadcastRange(int, int, int);
     method public boolean enableCellBroadcastRange(int, int, int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getPremiumSmsConsent(@NonNull String);
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getSmsCapacityOnIcc();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void sendMultipartTextMessageWithoutPersisting(String, String, java.util.List<java.lang.String>, java.util.List<android.app.PendingIntent>, java.util.List<android.app.PendingIntent>);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setPremiumSmsConsent(@NonNull String, int);
     field public static final int PREMIUM_SMS_CONSENT_ALWAYS_ALLOW = 3; // 0x3
@@ -10312,6 +10609,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCarrierPrivilegeStatus(int);
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<java.lang.String> getCarrierPrivilegedPackagesForAllActiveSubscriptions();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.CarrierRestrictionRules getCarrierRestrictionRules();
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCdmaEnhancedRoamingIndicatorIconIndex();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn(int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin();
@@ -10326,7 +10624,6 @@
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDeviceSoftwareVersion(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean getEmergencyCallbackMode();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEmergencyNumberDbVersion();
-    method @NonNull @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<java.lang.String> getEquivalentHomePlmns();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimDomain();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimIst();
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.Map<java.lang.Integer,java.lang.Integer> getLogicalToPhysicalSlotMapping();
@@ -10360,6 +10657,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean isIccLockEnabled();
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isLteCdmaEvdoGsmWcdmaEnabled();
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isMobileDataPolicyEnabled(int);
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isOpportunisticNetworkEnabled();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isPotentialEmergencyNumber(@NonNull String);
@@ -10390,6 +10688,7 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataActivationState(int);
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMobileDataPolicyEnabledStatus(int, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setPreferredNetworkTypeBitmask(long);
@@ -10440,6 +10739,8 @@
     field public static final int INVALID_EMERGENCY_NUMBER_DB_VERSION = -1; // 0xffffffff
     field public static final int KEY_TYPE_EPDG = 1; // 0x1
     field public static final int KEY_TYPE_WLAN = 2; // 0x2
+    field public static final int MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL = 1; // 0x1
+    field public static final int MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED = 2; // 0x2
     field public static final long NETWORK_TYPE_BITMASK_1xRTT = 64L; // 0x40L
     field public static final long NETWORK_TYPE_BITMASK_CDMA = 8L; // 0x8L
     field public static final long NETWORK_TYPE_BITMASK_EDGE = 2L; // 0x2L
@@ -11042,6 +11343,10 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsExternalCallState> CREATOR;
   }
 
+  public class ImsManager {
+    method @NonNull public android.telephony.ims.SipDelegateManager getSipDelegateManager(int);
+  }
+
   public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
     method @Deprecated @NonNull @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getFeatureState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>) throws android.telephony.ims.ImsException;
@@ -11077,10 +11382,13 @@
     method public void disableIms(int);
     method public void enableIms(int);
     method public android.telephony.ims.stub.ImsConfigImplBase getConfig(int);
+    method public long getImsServiceCapabilities();
     method public android.telephony.ims.stub.ImsRegistrationImplBase getRegistration(int);
+    method @Nullable public android.telephony.ims.stub.SipTransportImplBase getSipTransport(int);
     method public final void onUpdateSupportedImsFeatures(android.telephony.ims.stub.ImsFeatureConfiguration) throws android.os.RemoteException;
     method public android.telephony.ims.stub.ImsFeatureConfiguration querySupportedImsFeatures();
     method public void readyForFeatureCreation();
+    field public static final long CAPABILITY_SIP_DELEGATE_CREATION = 2L; // 0x2L
   }
 
   public final class ImsSsData implements android.os.Parcelable {
@@ -11326,6 +11634,10 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
   }
 
+  public class SipDelegateManager {
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSupported() throws android.telephony.ims.ImsException;
+  }
+
 }
 
 package android.telephony.ims.feature {
@@ -11575,6 +11887,10 @@
     method public int updateColr(int);
   }
 
+  public class SipTransportImplBase {
+    ctor public SipTransportImplBase(@NonNull java.util.concurrent.Executor);
+  }
+
 }
 
 package android.telephony.mbms {
@@ -11857,7 +12173,7 @@
     method @Nullable public String findProxyForUrl(@NonNull String);
     method @NonNull public static android.webkit.PacProcessor getInstance();
     method @Nullable public default android.net.Network getNetwork();
-    method public default void releasePacProcessor();
+    method public default void release();
     method public default void setNetwork(@Nullable android.net.Network);
     method public boolean setProxyScript(@NonNull String);
   }
diff --git a/packages/CarSystemUI/AndroidManifest.xml b/packages/CarSystemUI/AndroidManifest.xml
index c0482e1..f0cf2a9 100644
--- a/packages/CarSystemUI/AndroidManifest.xml
+++ b/packages/CarSystemUI/AndroidManifest.xml
@@ -23,6 +23,8 @@
     <uses-permission android:name="android.car.permission.CAR_POWER" />
     <!-- This permission is required to get the trusted device list of a user. -->
     <uses-permission android:name="android.car.permission.CAR_ENROLL_TRUST"/>
+    <!-- This permission is required to monitor gear state. -->
+    <uses-permission android:name="android.car.permission.CAR_POWERTRAIN" />
     <!-- This permission is required to get bluetooth broadcast. -->
     <uses-permission android:name="android.permission.BLUETOOTH" />
     <!-- These permissions are required to implement icons based on role holders. -->
diff --git a/packages/CarSystemUI/res/layout/rear_view_camera.xml b/packages/CarSystemUI/res/layout/rear_view_camera.xml
new file mode 100644
index 0000000..9b9898c
--- /dev/null
+++ b/packages/CarSystemUI/res/layout/rear_view_camera.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/rear_view_camera_container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@android:color/transparent"
+    android:orientation="vertical">
+    <Button
+        android:id="@+id/close_button"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="@color/rear_view_camera_button_background"
+        android:text="@string/rear_view_camera_close_button_text"
+        android:textAppearance="?android:attr/textAppearanceLarge"/>
+</LinearLayout>
diff --git a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
index e7295aa..980265e 100644
--- a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
+++ b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
@@ -25,8 +25,7 @@
     <ViewStub android:id="@+id/notification_panel_stub"
               android:layout_width="match_parent"
               android:layout_height="match_parent"
-              android:layout="@layout/notification_panel_container"
-              android:layout_marginBottom="@dimen/car_bottom_navigation_bar_height"/>
+              android:layout="@layout/notification_panel_container"/>
 
     <ViewStub android:id="@+id/keyguard_stub"
               android:layout_width="match_parent"
@@ -43,4 +42,10 @@
               android:layout_height="match_parent"
               android:layout="@layout/car_user_switching_dialog"/>
 
-</FrameLayout>
\ No newline at end of file
+    <!-- Should be at bottom to get the highest Z-order. -->
+    <ViewStub android:id="@+id/rear_view_camera_stub"
+              android:layout_width="@dimen/rear_view_camera_width"
+              android:layout_height="@dimen/rear_view_camera_height"
+              android:layout_gravity="center"
+              android:layout="@layout/rear_view_camera"/>
+</FrameLayout>
diff --git a/packages/CarSystemUI/res/values-af/strings.xml b/packages/CarSystemUI/res/values-af/strings.xml
index 2ab3aa4..b377ec4 100644
--- a/packages/CarSystemUI/res/values-af/strings.xml
+++ b/packages/CarSystemUI/res/values-af/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Enige gebruiker kan programme vir al die ander gebruikers opdateer."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Laai tans"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Laai tans gebruiker (van <xliff:g id="FROM_USER">%1$d</xliff:g> na <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-am/strings.xml b/packages/CarSystemUI/res/values-am/strings.xml
index d5b580e..4f2bba8 100644
--- a/packages/CarSystemUI/res/values-am/strings.xml
+++ b/packages/CarSystemUI/res/values-am/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"ማንኛውም ተጠቃሚ መተግበሪያዎችን ለሌሎች ተጠቃሚዎች ሁሉ ማዘመን ይችላል።"</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"በመጫን ላይ"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"ተጠቃሚን (ከ<xliff:g id="FROM_USER">%1$d</xliff:g> ወደ <xliff:g id="TO_USER">%2$d</xliff:g>) በመጫን ላይ"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-ar/strings.xml b/packages/CarSystemUI/res/values-ar/strings.xml
index 09b302d..61a08a4 100644
--- a/packages/CarSystemUI/res/values-ar/strings.xml
+++ b/packages/CarSystemUI/res/values-ar/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"يمكن لأي مستخدم تحديث التطبيقات لجميع المستخدمين الآخرين."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"جارٍ التحميل"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"جارٍ تحميل الملف الشخصي الجديد للمستخدم (من <xliff:g id="FROM_USER">%1$d</xliff:g> إلى <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-as/strings.xml b/packages/CarSystemUI/res/values-as/strings.xml
index a8aa74d8..edc3621 100644
--- a/packages/CarSystemUI/res/values-as/strings.xml
+++ b/packages/CarSystemUI/res/values-as/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"যিকোনো ব্যৱহাৰকাৰীয়ে অন্য ব্যৱহাৰকাৰীৰ বাবে এপ্‌সমূহ আপডে’ট কৰিব পাৰে।"</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"ল’ড হৈ আছে"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"ব্যৱহাৰকাৰী ল’ড হৈ আছে (<xliff:g id="FROM_USER">%1$d</xliff:g>ৰ পৰা to <xliff:g id="TO_USER">%2$d</xliff:g>লৈ)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-az/strings.xml b/packages/CarSystemUI/res/values-az/strings.xml
index 97facc2..398f5c3 100644
--- a/packages/CarSystemUI/res/values-az/strings.xml
+++ b/packages/CarSystemUI/res/values-az/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"İstənilən istifadəçi digər bütün istifadəçilər üçün tətbiqləri güncəlləyə bilər."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Yüklənir"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"İstifadəçi yüklənir (<xliff:g id="FROM_USER">%1$d</xliff:g>-<xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-b+sr+Latn/strings.xml b/packages/CarSystemUI/res/values-b+sr+Latn/strings.xml
index ec310be..6c1979f 100644
--- a/packages/CarSystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/CarSystemUI/res/values-b+sr+Latn/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Svaki korisnik može da ažurira aplikacije za sve ostale korisnike."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Učitava se"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Profil korisnika se učitava (iz<xliff:g id="FROM_USER">%1$d</xliff:g> u <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-be/strings.xml b/packages/CarSystemUI/res/values-be/strings.xml
index 17fc584..4e97948 100644
--- a/packages/CarSystemUI/res/values-be/strings.xml
+++ b/packages/CarSystemUI/res/values-be/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Кожны карыстальнік прылады можа абнаўляць праграмы для ўсіх іншых карыстальнікаў."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Ідзе загрузка"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Ідзе загрузка профілю карыстальніка (ад <xliff:g id="FROM_USER">%1$d</xliff:g> да <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-bg/strings.xml b/packages/CarSystemUI/res/values-bg/strings.xml
index ae2db2d..7dfab54 100644
--- a/packages/CarSystemUI/res/values-bg/strings.xml
+++ b/packages/CarSystemUI/res/values-bg/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Всеки потребител може да актуализира приложенията за всички останали потребители."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Зарежда се"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Потребителят се зарежда (от <xliff:g id="FROM_USER">%1$d</xliff:g> до <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-bn/strings.xml b/packages/CarSystemUI/res/values-bn/strings.xml
index 9a01fad..a45e66e 100644
--- a/packages/CarSystemUI/res/values-bn/strings.xml
+++ b/packages/CarSystemUI/res/values-bn/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"যেকোনও ব্যবহারকারী বাকি সব ব্যবহারকারীর জন্য অ্যাপ আপডেট করতে পারবেন।"</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"লোড হচ্ছে"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"ব্যবহারকারীর প্রোফাইল লোড করা হচ্ছে (<xliff:g id="FROM_USER">%1$d</xliff:g> থেকে <xliff:g id="TO_USER">%2$d</xliff:g>-এ)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-bs/strings.xml b/packages/CarSystemUI/res/values-bs/strings.xml
index 5ef9aeb..119f2d7b 100644
--- a/packages/CarSystemUI/res/values-bs/strings.xml
+++ b/packages/CarSystemUI/res/values-bs/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Bilo koji korisnik može ažurirati aplikacije za sve druge korisnike."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Učitavanje"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Učitavanje korisnika (od korisnika <xliff:g id="FROM_USER">%1$d</xliff:g> do korisnika <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-ca/strings.xml b/packages/CarSystemUI/res/values-ca/strings.xml
index 083f9d0..b1e722e 100644
--- a/packages/CarSystemUI/res/values-ca/strings.xml
+++ b/packages/CarSystemUI/res/values-ca/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Qualsevol usuari pot actualitzar les aplicacions de la resta d\'usuaris."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"S\'està carregant"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"S\'està carregant l\'usuari (de <xliff:g id="FROM_USER">%1$d</xliff:g> a <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-cs/strings.xml b/packages/CarSystemUI/res/values-cs/strings.xml
index 8071cef..dd4472f 100644
--- a/packages/CarSystemUI/res/values-cs/strings.xml
+++ b/packages/CarSystemUI/res/values-cs/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Každý uživatel může aktualizovat aplikace všech ostatních uživatelů."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Načítání"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Načítání uživatele (předchozí: <xliff:g id="FROM_USER">%1$d</xliff:g>, následující: <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-da/strings.xml b/packages/CarSystemUI/res/values-da/strings.xml
index b19cdcb..6c08aa5 100644
--- a/packages/CarSystemUI/res/values-da/strings.xml
+++ b/packages/CarSystemUI/res/values-da/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Alle brugere kan opdatere apps for alle andre brugere."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Indlæser"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Indlæser bruger (fra <xliff:g id="FROM_USER">%1$d</xliff:g> til <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-de/strings.xml b/packages/CarSystemUI/res/values-de/strings.xml
index 99ba13e..131dee1 100644
--- a/packages/CarSystemUI/res/values-de/strings.xml
+++ b/packages/CarSystemUI/res/values-de/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Jeder Nutzer kann Apps für alle anderen Nutzer aktualisieren."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Wird geladen"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Nutzer wird geladen (von <xliff:g id="FROM_USER">%1$d</xliff:g> bis <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-el/strings.xml b/packages/CarSystemUI/res/values-el/strings.xml
index e2d2cec..66f8d18 100644
--- a/packages/CarSystemUI/res/values-el/strings.xml
+++ b/packages/CarSystemUI/res/values-el/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Οποιοσδήποτε χρήστης μπορεί να ενημερώσει τις εφαρμογές για όλους τους άλλους χρήστες."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Φόρτωση"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Φόρτωση χρήστη (από <xliff:g id="FROM_USER">%1$d</xliff:g> έως <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-en-rAU/strings.xml b/packages/CarSystemUI/res/values-en-rAU/strings.xml
index b8bf990..b3e358f 100644
--- a/packages/CarSystemUI/res/values-en-rAU/strings.xml
+++ b/packages/CarSystemUI/res/values-en-rAU/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Any user can update apps for all other users."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Loading"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Loading user (from <xliff:g id="FROM_USER">%1$d</xliff:g> to <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-en-rCA/strings.xml b/packages/CarSystemUI/res/values-en-rCA/strings.xml
index b8bf990..b3e358f 100644
--- a/packages/CarSystemUI/res/values-en-rCA/strings.xml
+++ b/packages/CarSystemUI/res/values-en-rCA/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Any user can update apps for all other users."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Loading"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Loading user (from <xliff:g id="FROM_USER">%1$d</xliff:g> to <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-en-rGB/strings.xml b/packages/CarSystemUI/res/values-en-rGB/strings.xml
index b8bf990..b3e358f 100644
--- a/packages/CarSystemUI/res/values-en-rGB/strings.xml
+++ b/packages/CarSystemUI/res/values-en-rGB/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Any user can update apps for all other users."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Loading"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Loading user (from <xliff:g id="FROM_USER">%1$d</xliff:g> to <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-en-rIN/strings.xml b/packages/CarSystemUI/res/values-en-rIN/strings.xml
index b8bf990..b3e358f 100644
--- a/packages/CarSystemUI/res/values-en-rIN/strings.xml
+++ b/packages/CarSystemUI/res/values-en-rIN/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Any user can update apps for all other users."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Loading"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Loading user (from <xliff:g id="FROM_USER">%1$d</xliff:g> to <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-en-rXC/strings.xml b/packages/CarSystemUI/res/values-en-rXC/strings.xml
index 1ffa5eb..eaf6f51 100644
--- a/packages/CarSystemUI/res/values-en-rXC/strings.xml
+++ b/packages/CarSystemUI/res/values-en-rXC/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‎‎‎‎‎‎‎‎‎‎‏‏‎‎‎‏‎‏‎‎‎‏‏‏‎‏‎‏‏‏‎‏‎‎‏‎‏‏‏‏‏‏‏‎‏‎‎‏‏‏‎‎‎‏‏‎Any user can update apps for all other users.‎‏‎‎‏‎"</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‏‎‎‎‏‏‎‏‎‏‏‏‎‎‏‎‏‏‎‎‏‏‏‏‎‏‏‏‏‏‎‏‎‎‏‏‏‎‏‏‎‏‏‎‎‎‎‎‎‎‎‎‏‎‏‎‎Loading‎‏‎‎‏‎"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‎‎‎‎‏‏‎‎‏‏‏‎‏‏‎‎‏‏‎‏‎‏‏‎‎‎‎‏‏‎‏‎‏‏‏‎‏‎‎‏‎‏‏‎‎‎‏‎‏‎‎‎‏‎‏‏‎Loading user (from ‎‏‎‎‏‏‎<xliff:g id="FROM_USER">%1$d</xliff:g>‎‏‎‎‏‏‏‎ to ‎‏‎‎‏‏‎<xliff:g id="TO_USER">%2$d</xliff:g>‎‏‎‎‏‏‏‎)‎‏‎‎‏‎"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-es-rUS/strings.xml b/packages/CarSystemUI/res/values-es-rUS/strings.xml
index c1c21d1..6a5f8ce 100644
--- a/packages/CarSystemUI/res/values-es-rUS/strings.xml
+++ b/packages/CarSystemUI/res/values-es-rUS/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Cualquier usuario podrá actualizar las apps de otras personas."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Cargando"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Cargando usuario (de <xliff:g id="FROM_USER">%1$d</xliff:g> a <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-es/strings.xml b/packages/CarSystemUI/res/values-es/strings.xml
index fe54605..c43d7e5 100644
--- a/packages/CarSystemUI/res/values-es/strings.xml
+++ b/packages/CarSystemUI/res/values-es/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Cualquier usuario puede actualizar las aplicaciones del resto de los usuarios."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Cargando"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Cargando usuario (de <xliff:g id="FROM_USER">%1$d</xliff:g> a <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-et/strings.xml b/packages/CarSystemUI/res/values-et/strings.xml
index 2fa71a9..ad82d5f 100644
--- a/packages/CarSystemUI/res/values-et/strings.xml
+++ b/packages/CarSystemUI/res/values-et/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Iga kasutaja saab rakendusi värskendada kõigi teiste kasutajate jaoks."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Laadimine"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Kasutaja laadimine (<xliff:g id="FROM_USER">%1$d</xliff:g> &gt; <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-eu/strings.xml b/packages/CarSystemUI/res/values-eu/strings.xml
index 36bcaae..5d2ca35 100644
--- a/packages/CarSystemUI/res/values-eu/strings.xml
+++ b/packages/CarSystemUI/res/values-eu/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Edozein erabiltzailek egunera ditzake beste erabiltzaile guztien aplikazioak."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Kargatzen"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Erabiltzailea kargatzen (<xliff:g id="FROM_USER">%1$d</xliff:g> izatetik<xliff:g id="TO_USER">%2$d</xliff:g> izatera igaroko da)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-fa/strings.xml b/packages/CarSystemUI/res/values-fa/strings.xml
index 3224899..ef37b65 100644
--- a/packages/CarSystemUI/res/values-fa/strings.xml
+++ b/packages/CarSystemUI/res/values-fa/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"هر کاربری می‌تواند برنامه‌ها را برای همه کاربران دیگر به‌روزرسانی کند."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"درحال بارگیری"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"بارگیری کاربر (از <xliff:g id="FROM_USER">%1$d</xliff:g> تا <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-fi/strings.xml b/packages/CarSystemUI/res/values-fi/strings.xml
index 6068969..10bb0c5 100644
--- a/packages/CarSystemUI/res/values-fi/strings.xml
+++ b/packages/CarSystemUI/res/values-fi/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Kaikki käyttäjät voivat päivittää muiden käyttäjien sovelluksia."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Ladataan"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Ladataan käyttäjäprofiilia (<xliff:g id="FROM_USER">%1$d</xliff:g>–<xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-fr-rCA/strings.xml b/packages/CarSystemUI/res/values-fr-rCA/strings.xml
index 18e68c0..bebd3f4 100644
--- a/packages/CarSystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/CarSystemUI/res/values-fr-rCA/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Tout utilisateur peut mettre à jour les applications pour tous les autres utilisateurs."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Chargement en cours…"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Chargement de l\'utilisateur (de <xliff:g id="FROM_USER">%1$d</xliff:g> vers <xliff:g id="TO_USER">%2$d</xliff:g>) en cours…"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-fr/strings.xml b/packages/CarSystemUI/res/values-fr/strings.xml
index 954df67..3d498d2 100644
--- a/packages/CarSystemUI/res/values-fr/strings.xml
+++ b/packages/CarSystemUI/res/values-fr/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"N\'importe quel utilisateur peut mettre à jour les applications pour tous les autres utilisateurs."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Chargement…"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Chargement de l\'utilisateur (de <xliff:g id="FROM_USER">%1$d</xliff:g> à <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-gl/strings.xml b/packages/CarSystemUI/res/values-gl/strings.xml
index 95bfbd3..e4586cc 100644
--- a/packages/CarSystemUI/res/values-gl/strings.xml
+++ b/packages/CarSystemUI/res/values-gl/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Calquera usuario pode actualizar as aplicacións para o resto dos usuarios."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Cargando"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Cargando usuario (do <xliff:g id="FROM_USER">%1$d</xliff:g> ao <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-gu/strings.xml b/packages/CarSystemUI/res/values-gu/strings.xml
index dec991c..ba884e4 100644
--- a/packages/CarSystemUI/res/values-gu/strings.xml
+++ b/packages/CarSystemUI/res/values-gu/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"કોઈપણ વપરાશકર્તા અન્ય બધા વપરાશકર્તાઓ માટે ઍપને અપડેટ કરી શકે છે."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"લોડ કરી રહ્યાં છીએ"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"વપરાશકર્તાને લોડ કરી રહ્યાં છીએ (<xliff:g id="FROM_USER">%1$d</xliff:g>માંથી <xliff:g id="TO_USER">%2$d</xliff:g>માં)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-hi/strings.xml b/packages/CarSystemUI/res/values-hi/strings.xml
index 89b6960..95454a5 100644
--- a/packages/CarSystemUI/res/values-hi/strings.xml
+++ b/packages/CarSystemUI/res/values-hi/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"कोई भी उपयोगकर्ता, बाकी सभी उपयोगकर्ताओं के लिए ऐप्लिकेशन अपडेट कर सकता है."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"लोड हो रही है"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"उपयोगकर्ता को लोड किया जा रहा है (<xliff:g id="FROM_USER">%1$d</xliff:g> से <xliff:g id="TO_USER">%2$d</xliff:g> पर)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-hr/strings.xml b/packages/CarSystemUI/res/values-hr/strings.xml
index 6baec74..f3aaf63 100644
--- a/packages/CarSystemUI/res/values-hr/strings.xml
+++ b/packages/CarSystemUI/res/values-hr/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Svaki korisnik može ažurirati aplikacije za ostale korisnike."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Učitavanje"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Učitavanje korisnika (od <xliff:g id="FROM_USER">%1$d</xliff:g> do <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-hu/strings.xml b/packages/CarSystemUI/res/values-hu/strings.xml
index ffa512c..b63ba8b 100644
--- a/packages/CarSystemUI/res/values-hu/strings.xml
+++ b/packages/CarSystemUI/res/values-hu/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Bármely felhasználó frissítheti az alkalmazásokat az összes felhasználó számára."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Betöltés"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Felhasználó betöltése (<xliff:g id="FROM_USER">%1$d</xliff:g> → <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-hy/strings.xml b/packages/CarSystemUI/res/values-hy/strings.xml
index ee6f74b..e2a2c6b 100644
--- a/packages/CarSystemUI/res/values-hy/strings.xml
+++ b/packages/CarSystemUI/res/values-hy/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Ցանկացած օգտատեր կարող է թարմացնել հավելվածները բոլոր մյուս հաշիվների համար։"</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Բեռնում"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Օգտատերը բեռնվում է (<xliff:g id="FROM_USER">%1$d</xliff:g> – <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-in/strings.xml b/packages/CarSystemUI/res/values-in/strings.xml
index 901cbe7..0a70d26 100644
--- a/packages/CarSystemUI/res/values-in/strings.xml
+++ b/packages/CarSystemUI/res/values-in/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Setiap pengguna dapat mengupdate aplikasi untuk semua pengguna lain."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Memuat"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Memuat pengguna (dari <xliff:g id="FROM_USER">%1$d</xliff:g> menjadi <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-is/strings.xml b/packages/CarSystemUI/res/values-is/strings.xml
index 13e7136..ea6b031 100644
--- a/packages/CarSystemUI/res/values-is/strings.xml
+++ b/packages/CarSystemUI/res/values-is/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Allir notendur geta uppfært forrit fyrir alla aðra notendur."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Hleður"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Hleður notanda (frá <xliff:g id="FROM_USER">%1$d</xliff:g> til <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-it/strings.xml b/packages/CarSystemUI/res/values-it/strings.xml
index f4f7ab7..ecbcd5d 100644
--- a/packages/CarSystemUI/res/values-it/strings.xml
+++ b/packages/CarSystemUI/res/values-it/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Qualsiasi utente può aggiornare le app per tutti gli altri."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Caricamento"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Caricamento dell\'utente (da <xliff:g id="FROM_USER">%1$d</xliff:g> a <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-iw/strings.xml b/packages/CarSystemUI/res/values-iw/strings.xml
index a044b23..fe182a3 100644
--- a/packages/CarSystemUI/res/values-iw/strings.xml
+++ b/packages/CarSystemUI/res/values-iw/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"כל משתמש יכול לעדכן אפליקציות לכל שאר המשתמשים."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"בטעינה"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"המשתמש בטעינה (מהמשתמש <xliff:g id="FROM_USER">%1$d</xliff:g> אל <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-ja/strings.xml b/packages/CarSystemUI/res/values-ja/strings.xml
index aae7dbf..1448675 100644
--- a/packages/CarSystemUI/res/values-ja/strings.xml
+++ b/packages/CarSystemUI/res/values-ja/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"どのユーザーも他のすべてのユーザーに代わってアプリを更新できます。"</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"読み込んでいます"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"ユーザーを読み込んでいます(<xliff:g id="FROM_USER">%1$d</xliff:g>~<xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-ka/strings.xml b/packages/CarSystemUI/res/values-ka/strings.xml
index 19894d1..0fef7e5 100644
--- a/packages/CarSystemUI/res/values-ka/strings.xml
+++ b/packages/CarSystemUI/res/values-ka/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"ნებისმიერ მომხმარებელს შეუძლია აპები ყველა სხვა მომხმარებლისათვის განაახლოს."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"იტვირთება"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"იტვირთება მომხმარებელი (<xliff:g id="FROM_USER">%1$d</xliff:g>-დან <xliff:g id="TO_USER">%2$d</xliff:g>-მდე)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-kk/strings.xml b/packages/CarSystemUI/res/values-kk/strings.xml
index f9449be..a4cf787 100644
--- a/packages/CarSystemUI/res/values-kk/strings.xml
+++ b/packages/CarSystemUI/res/values-kk/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Кез келген пайдаланушы қолданбаларды басқа пайдаланушылар үшін жаңарта алады."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Жүктелуде"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Пайдаланушы профилі жүктелуде (<xliff:g id="FROM_USER">%1$d</xliff:g> – <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-km/strings.xml b/packages/CarSystemUI/res/values-km/strings.xml
index fbcab84..7b9a093 100644
--- a/packages/CarSystemUI/res/values-km/strings.xml
+++ b/packages/CarSystemUI/res/values-km/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"អ្នកប្រើប្រាស់​ណាក៏​អាច​ដំឡើងកំណែ​កម្មវិធី​សម្រាប់​អ្នកប្រើប្រាស់ទាំងអស់​ផ្សេងទៀត​បានដែរ។"</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"កំពុងផ្ទុក"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"កំពុងផ្ទុក​អ្នកប្រើប្រាស់ (ពី <xliff:g id="FROM_USER">%1$d</xliff:g> ដល់ <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-kn/strings.xml b/packages/CarSystemUI/res/values-kn/strings.xml
index 21c4cc0..34a83553 100644
--- a/packages/CarSystemUI/res/values-kn/strings.xml
+++ b/packages/CarSystemUI/res/values-kn/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"ಯಾವುದೇ ಬಳಕೆದಾರರು ಎಲ್ಲಾ ಇತರೆ ಬಳಕೆದಾರರಿಗಾಗಿ ಆ್ಯಪ್‌ಗಳನ್ನು ಅಪ್‌ಡೇಟ್‌ ಮಾಡಬಹುದು."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"ಲೋಡ್ ಆಗುತ್ತಿದೆ"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"ಬಳಕೆದಾರರನ್ನು ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ (<xliff:g id="FROM_USER">%1$d</xliff:g> ನಿಂದ <xliff:g id="TO_USER">%2$d</xliff:g> ವರೆಗೆ)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-ko/strings.xml b/packages/CarSystemUI/res/values-ko/strings.xml
index 6b670b0..b570c5c 100644
--- a/packages/CarSystemUI/res/values-ko/strings.xml
+++ b/packages/CarSystemUI/res/values-ko/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"누구나 다른 모든 사용자를 위해 앱을 업데이트할 수 있습니다."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"로드 중"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"사용자 로드 중(<xliff:g id="FROM_USER">%1$d</xliff:g>님에서 <xliff:g id="TO_USER">%2$d</xliff:g>님으로)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-ky/strings.xml b/packages/CarSystemUI/res/values-ky/strings.xml
index b093363..c66b34f 100644
--- a/packages/CarSystemUI/res/values-ky/strings.xml
+++ b/packages/CarSystemUI/res/values-ky/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Колдонмолорду бир колдонуучу калган бардык колдонуучулар үчүн да жаңырта алат."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Жүктөлүүдө"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Колдонуучу тууралуу маалымат жүктөлүүдө (<xliff:g id="FROM_USER">%1$d</xliff:g> – <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-lo/strings.xml b/packages/CarSystemUI/res/values-lo/strings.xml
index d2199e2..2bf19e0 100644
--- a/packages/CarSystemUI/res/values-lo/strings.xml
+++ b/packages/CarSystemUI/res/values-lo/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"ຜູ້ໃຊ້ຕ່າງໆສາມາດອັບເດດແອັບສຳລັບຜູ້ໃຊ້ອື່ນທັງໝົດໄດ້."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"ກຳລັງໂຫຼດ"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"ກຳລັງໂຫຼດຜູ້ໃຊ້ (ຈາກ <xliff:g id="FROM_USER">%1$d</xliff:g> ໄປຍັງ <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-lt/strings.xml b/packages/CarSystemUI/res/values-lt/strings.xml
index 2669000..1cae1e9 100644
--- a/packages/CarSystemUI/res/values-lt/strings.xml
+++ b/packages/CarSystemUI/res/values-lt/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Bet kuris naudotojas gali atnaujinti visų kitų naudotojų programas."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Įkeliama"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Įkeliamas naudotojo profilis (<xliff:g id="FROM_USER">%1$d</xliff:g> – <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-lv/strings.xml b/packages/CarSystemUI/res/values-lv/strings.xml
index 87b4ede..62b8bf8 100644
--- a/packages/CarSystemUI/res/values-lv/strings.xml
+++ b/packages/CarSystemUI/res/values-lv/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Ikviens lietotājs var atjaunināt lietotnes visu lietotāju vārdā."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Notiek ielāde…"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Notiek lietotāja profila ielāde (<xliff:g id="FROM_USER">%1$d</xliff:g>–<xliff:g id="TO_USER">%2$d</xliff:g>)…"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-mk/strings.xml b/packages/CarSystemUI/res/values-mk/strings.xml
index f8fd02c..3e7ad63 100644
--- a/packages/CarSystemUI/res/values-mk/strings.xml
+++ b/packages/CarSystemUI/res/values-mk/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Секој корисник може да ажурира апликации за сите други корисници."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Се вчитува"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Се вчитува корисникот (од <xliff:g id="FROM_USER">%1$d</xliff:g> до <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-ml/strings.xml b/packages/CarSystemUI/res/values-ml/strings.xml
index 3431571..3bc7557 100644
--- a/packages/CarSystemUI/res/values-ml/strings.xml
+++ b/packages/CarSystemUI/res/values-ml/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"ഏതൊരു ഉപയോക്താവിനും മറ്റെല്ലാ ഉപയോക്താക്കൾക്കുമായി ആപ്പുകൾ അപ്‌ഡേറ്റ് ചെയ്യാനാവും."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"ലോഡ് ചെയ്യുന്നു"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"ഉപയോക്തൃ പ്രൊഫൈൽ ലോഡ് ചെയ്യുന്നു (<xliff:g id="FROM_USER">%1$d</xliff:g> എന്നതിൽ നിന്ന് <xliff:g id="TO_USER">%2$d</xliff:g> എന്നതിലേക്ക്)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-mn/strings.xml b/packages/CarSystemUI/res/values-mn/strings.xml
index 5bd76dc..45921d2 100644
--- a/packages/CarSystemUI/res/values-mn/strings.xml
+++ b/packages/CarSystemUI/res/values-mn/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Бусад бүх хэрэглэгчийн аппыг дурын хэрэглэгч шинэчлэх боломжтой."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Ачаалж байна"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Хэрэглэгчийг ачаалж байна (<xliff:g id="FROM_USER">%1$d</xliff:g>-с <xliff:g id="TO_USER">%2$d</xliff:g> хүртэл)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-mr/strings.xml b/packages/CarSystemUI/res/values-mr/strings.xml
index 2366465..fdbab4f 100644
--- a/packages/CarSystemUI/res/values-mr/strings.xml
+++ b/packages/CarSystemUI/res/values-mr/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"कोणताही वापरकर्ता इतर सर्व वापरकर्त्यांसाठी अ‍ॅप्स अपडेट करू शकतो."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"लोड करत आहे"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"वापरकर्ता लोड करत आहे (<xliff:g id="FROM_USER">%1$d</xliff:g> पासून <xliff:g id="TO_USER">%2$d</xliff:g> पर्यंत)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-ms/strings.xml b/packages/CarSystemUI/res/values-ms/strings.xml
index 29ac83a..1a43d9c 100644
--- a/packages/CarSystemUI/res/values-ms/strings.xml
+++ b/packages/CarSystemUI/res/values-ms/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Mana-mana pengguna boleh mengemas kini apl untuk semua pengguna lain."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Memuatkan"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Memuatkan pengguna (daripada <xliff:g id="FROM_USER">%1$d</xliff:g> hingga <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-my/strings.xml b/packages/CarSystemUI/res/values-my/strings.xml
index 7f240b4..4f3922b 100644
--- a/packages/CarSystemUI/res/values-my/strings.xml
+++ b/packages/CarSystemUI/res/values-my/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"မည်သူမဆို အသုံးပြုသူအားလုံးအတွက် အက်ပ်များကို အပ်ဒိတ်လုပ်နိုင်သည်။"</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"ဖွင့်နေသည်"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"အသုံးပြုသူကို ဖွင့်နေသည် (<xliff:g id="FROM_USER">%1$d</xliff:g> မှ <xliff:g id="TO_USER">%2$d</xliff:g> သို့)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-nb/strings.xml b/packages/CarSystemUI/res/values-nb/strings.xml
index c1cfcf7..5b6166f 100644
--- a/packages/CarSystemUI/res/values-nb/strings.xml
+++ b/packages/CarSystemUI/res/values-nb/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Alle brukere kan oppdatere apper for alle andre brukere."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Laster inn"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Laster inn brukeren (fra <xliff:g id="FROM_USER">%1$d</xliff:g> til <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-ne/strings.xml b/packages/CarSystemUI/res/values-ne/strings.xml
index 70bcfc7..e9eb4d8 100644
--- a/packages/CarSystemUI/res/values-ne/strings.xml
+++ b/packages/CarSystemUI/res/values-ne/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"सबै प्रयोगकर्ताले अन्य प्रयोगकर्ताका अनुप्रयोगहरू अद्यावधिक गर्न सक्छन्।"</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"लोड गरिँदै"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"प्रयोगकर्तासम्बन्धी जानकारी लोड गरिँदै (<xliff:g id="FROM_USER">%1$d</xliff:g> बाट <xliff:g id="TO_USER">%2$d</xliff:g> मा)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-nl/strings.xml b/packages/CarSystemUI/res/values-nl/strings.xml
index 95a7646..d79f2b1 100644
--- a/packages/CarSystemUI/res/values-nl/strings.xml
+++ b/packages/CarSystemUI/res/values-nl/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Elke gebruiker kan apps updaten voor alle andere gebruikers"</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Laden"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Gebruiker laden (van <xliff:g id="FROM_USER">%1$d</xliff:g> naar <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-or/strings.xml b/packages/CarSystemUI/res/values-or/strings.xml
index b0b59b3..58f59e4 100644
--- a/packages/CarSystemUI/res/values-or/strings.xml
+++ b/packages/CarSystemUI/res/values-or/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"ଯେ କୌଣସି ଉପଯୋଗକର୍ତ୍ତା ଅନ୍ୟ ସମସ୍ତ ଉପଯୋଗକର୍ତ୍ତାଙ୍କ ପାଇଁ ଆପଗୁଡ଼ିକୁ ଅପଡେଟ୍ କରିପାରିବେ।"</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"ଲୋଡ୍ କରାଯାଉଛି"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"ଉପଯୋଗକର୍ତ୍ତାଙ୍କୁ ଲୋଡ୍ କରାଯାଉଛି (<xliff:g id="FROM_USER">%1$d</xliff:g>ଙ୍କ ଠାରୁ <xliff:g id="TO_USER">%2$d</xliff:g> ପର୍ଯ୍ୟନ୍ତ)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-pa/strings.xml b/packages/CarSystemUI/res/values-pa/strings.xml
index e7efca1..e73e20a 100644
--- a/packages/CarSystemUI/res/values-pa/strings.xml
+++ b/packages/CarSystemUI/res/values-pa/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"ਕੋਈ ਵੀ ਵਰਤੋਂਕਾਰ ਹੋਰ ਸਾਰੇ ਵਰਤੋਂਕਾਰਾਂ ਦੀਆਂ ਐਪਾਂ ਨੂੰ ਅੱਪਡੇਟ ਕਰ ਸਕਦਾ ਹੈ।"</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"ਵਰਤੋਂਕਾਰ ਨੂੰ ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ (<xliff:g id="FROM_USER">%1$d</xliff:g> ਤੋਂ <xliff:g id="TO_USER">%2$d</xliff:g> ਤੱਕ)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-pl/strings.xml b/packages/CarSystemUI/res/values-pl/strings.xml
index 37d0ef1..dd8c189 100644
--- a/packages/CarSystemUI/res/values-pl/strings.xml
+++ b/packages/CarSystemUI/res/values-pl/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Każdy użytkownik może aktualizować aplikacje wszystkich innych użytkowników."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Ładuję"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Ładuję użytkownika (od <xliff:g id="FROM_USER">%1$d</xliff:g> do <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-pt-rPT/strings.xml b/packages/CarSystemUI/res/values-pt-rPT/strings.xml
index 182f5b5..c7f5ecf 100644
--- a/packages/CarSystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/CarSystemUI/res/values-pt-rPT/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Qualquer utilizador pode atualizar apps para todos os outros utilizadores."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"A carregar…"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"A carregar o utilizador (de <xliff:g id="FROM_USER">%1$d</xliff:g> para <xliff:g id="TO_USER">%2$d</xliff:g>)…"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-pt/strings.xml b/packages/CarSystemUI/res/values-pt/strings.xml
index 5c8394a..0712fb8 100644
--- a/packages/CarSystemUI/res/values-pt/strings.xml
+++ b/packages/CarSystemUI/res/values-pt/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Qualquer usuário pode atualizar apps para os demais usuários."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Carregando"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Carregando usuário (de <xliff:g id="FROM_USER">%1$d</xliff:g> para <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-ro/strings.xml b/packages/CarSystemUI/res/values-ro/strings.xml
index 25ecbb6..60fd4fe 100644
--- a/packages/CarSystemUI/res/values-ro/strings.xml
+++ b/packages/CarSystemUI/res/values-ro/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Orice utilizator poate actualiza aplicațiile pentru toți ceilalți utilizatori."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Se încarcă"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Se încarcă utilizatorul (de la <xliff:g id="FROM_USER">%1$d</xliff:g> la <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-ru/strings.xml b/packages/CarSystemUI/res/values-ru/strings.xml
index e93d25d..a043d24 100644
--- a/packages/CarSystemUI/res/values-ru/strings.xml
+++ b/packages/CarSystemUI/res/values-ru/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Любой пользователь устройства может обновлять приложения для всех аккаунтов."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Загрузка…"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Загрузка профиля пользователя (с <xliff:g id="FROM_USER">%1$d</xliff:g> по <xliff:g id="TO_USER">%2$d</xliff:g>)…"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-si/strings.xml b/packages/CarSystemUI/res/values-si/strings.xml
index 947cb0a..e14f64a 100644
--- a/packages/CarSystemUI/res/values-si/strings.xml
+++ b/packages/CarSystemUI/res/values-si/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"සියලුම අනෙක් පරිශීලකයින් සඳහා ඕනෑම පරිශීලකයෙකුට යෙදුම් යාවත්කාලීන කළ හැක."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"පූරණය වෙමින්"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"පරිශීලකයා පූරණය වෙමින් (<xliff:g id="FROM_USER">%1$d</xliff:g> සිට <xliff:g id="TO_USER">%2$d</xliff:g> වෙත)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-sk/strings.xml b/packages/CarSystemUI/res/values-sk/strings.xml
index ea99f0f..8f98983 100644
--- a/packages/CarSystemUI/res/values-sk/strings.xml
+++ b/packages/CarSystemUI/res/values-sk/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Ktorýkoľvek používateľ môže aktualizovať aplikácie všetkých ostatných používateľov."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Načítava sa"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Načítava sa používateľ (predchádzajúci: <xliff:g id="FROM_USER">%1$d</xliff:g>, nasledujúci: <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-sl/strings.xml b/packages/CarSystemUI/res/values-sl/strings.xml
index 32fd50e..6b47184 100644
--- a/packages/CarSystemUI/res/values-sl/strings.xml
+++ b/packages/CarSystemUI/res/values-sl/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Vsak uporabnik lahko posodobi aplikacije za vse druge uporabnike."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Nalaganje"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Nalaganje uporabnika (od uporabnika <xliff:g id="FROM_USER">%1$d</xliff:g> do uporabnika <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-sq/strings.xml b/packages/CarSystemUI/res/values-sq/strings.xml
index 6fdd06c..18ea401 100644
--- a/packages/CarSystemUI/res/values-sq/strings.xml
+++ b/packages/CarSystemUI/res/values-sq/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Çdo përdorues mund t\'i përditësojë aplikacionet për të gjithë përdoruesit e tjerë."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Po ngarkohet"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Përdoruesi po ngarkohet (nga <xliff:g id="FROM_USER">%1$d</xliff:g> te <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-sr/strings.xml b/packages/CarSystemUI/res/values-sr/strings.xml
index 494aeaa..878a1c1 100644
--- a/packages/CarSystemUI/res/values-sr/strings.xml
+++ b/packages/CarSystemUI/res/values-sr/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Сваки корисник може да ажурира апликације за све остале кориснике."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Учитава се"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Профил корисника се учитава (из<xliff:g id="FROM_USER">%1$d</xliff:g> у <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-sv/strings.xml b/packages/CarSystemUI/res/values-sv/strings.xml
index 65481cd..905dd44 100644
--- a/packages/CarSystemUI/res/values-sv/strings.xml
+++ b/packages/CarSystemUI/res/values-sv/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Alla användare kan uppdatera appar för andra användare."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Läser in"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Läser in användare (från <xliff:g id="FROM_USER">%1$d</xliff:g> till <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-sw/strings.xml b/packages/CarSystemUI/res/values-sw/strings.xml
index a79d628..3cb6098 100644
--- a/packages/CarSystemUI/res/values-sw/strings.xml
+++ b/packages/CarSystemUI/res/values-sw/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Mtumiaji yeyote anaweza kusasisha programu za watumiaji wengine."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Inapakia"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Inapakia wasifu wa mtumiaji (kutoka <xliff:g id="FROM_USER">%1$d</xliff:g> kuwa <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-ta/strings.xml b/packages/CarSystemUI/res/values-ta/strings.xml
index 849b40d..de52ede 100644
--- a/packages/CarSystemUI/res/values-ta/strings.xml
+++ b/packages/CarSystemUI/res/values-ta/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"எந்தப் பயனரும் பிற பயனர்கள் சார்பாக ஆப்ஸைப் புதுப்பிக்க முடியும்."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"ஏற்றுகிறது"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"பயனர் தகவலை ஏற்றுகிறது (<xliff:g id="FROM_USER">%1$d</xliff:g>லிருந்து <xliff:g id="TO_USER">%2$d</xliff:g> வரை)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-te/strings.xml b/packages/CarSystemUI/res/values-te/strings.xml
index 83bb24a..fff0845 100644
--- a/packages/CarSystemUI/res/values-te/strings.xml
+++ b/packages/CarSystemUI/res/values-te/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"ఏ యూజర్ అయినా మిగతా యూజర్‌ల కోసం యాప్‌లను అప్‌డేట్ చేయవచ్చు."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"లోడ్ అవుతోంది"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"యూజర్‌ను లోడ్ చేస్తోంది (<xliff:g id="FROM_USER">%1$d</xliff:g> నుండి <xliff:g id="TO_USER">%2$d</xliff:g> వరకు)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-th/strings.xml b/packages/CarSystemUI/res/values-th/strings.xml
index fa99ac1..8f3012b 100644
--- a/packages/CarSystemUI/res/values-th/strings.xml
+++ b/packages/CarSystemUI/res/values-th/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"ผู้ใช้ทุกคนจะอัปเดตแอปให้แก่ผู้ใช้คนอื่นๆ ได้"</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"กำลังโหลด"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"กำลังโหลดผู้ใช้ (จาก <xliff:g id="FROM_USER">%1$d</xliff:g> ถึง <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-tl/strings.xml b/packages/CarSystemUI/res/values-tl/strings.xml
index c6f5f59..5b0e3b3 100644
--- a/packages/CarSystemUI/res/values-tl/strings.xml
+++ b/packages/CarSystemUI/res/values-tl/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Puwedeng i-update ng sinumang user ang mga app para sa lahat ng iba pang user."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Naglo-load"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Nilo-load ang user (mula kay <xliff:g id="FROM_USER">%1$d</xliff:g> papunta kay <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-tr/strings.xml b/packages/CarSystemUI/res/values-tr/strings.xml
index a768127..81fa01c 100644
--- a/packages/CarSystemUI/res/values-tr/strings.xml
+++ b/packages/CarSystemUI/res/values-tr/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Herhangi bir kullanıcı, diğer tüm kullanıcılar için uygulamaları güncelleyebilir."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Yükleniyor"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Kullanıcı yükleniyor (<xliff:g id="FROM_USER">%1$d</xliff:g> kullanıcısından <xliff:g id="TO_USER">%2$d</xliff:g> kullanıcısına)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-uk/strings.xml b/packages/CarSystemUI/res/values-uk/strings.xml
index c424d87..b7031c6 100644
--- a/packages/CarSystemUI/res/values-uk/strings.xml
+++ b/packages/CarSystemUI/res/values-uk/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Усі користувачі можуть оновлювати додатки для решти людей."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Завантаження"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Завантаження профілю користувача (від <xliff:g id="FROM_USER">%1$d</xliff:g> до <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-ur/strings.xml b/packages/CarSystemUI/res/values-ur/strings.xml
index 063dcbc..ef65c75 100644
--- a/packages/CarSystemUI/res/values-ur/strings.xml
+++ b/packages/CarSystemUI/res/values-ur/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"کوئی بھی صارف دیگر سبھی صارفین کے لیے ایپس کو اپ ڈیٹ کر سکتا ہے۔"</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"لوڈ ہو رہی ہے"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"صارف کی نئی پروفائل لوڈ ہو رہی ہے (<xliff:g id="FROM_USER">%1$d</xliff:g> سے <xliff:g id="TO_USER">%2$d</xliff:g> کو)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-uz/strings.xml b/packages/CarSystemUI/res/values-uz/strings.xml
index adef2ad..471e459 100644
--- a/packages/CarSystemUI/res/values-uz/strings.xml
+++ b/packages/CarSystemUI/res/values-uz/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Qurilmaning istalgan foydalanuvchisi ilovalarni barcha hisoblar uchun yangilashi mumkin."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Yuklanmoqda"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Foydalanuvchi profili yuklanmoqda (<xliff:g id="FROM_USER">%1$d</xliff:g> – <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-vi/strings.xml b/packages/CarSystemUI/res/values-vi/strings.xml
index 616b48f..26bdddc 100644
--- a/packages/CarSystemUI/res/values-vi/strings.xml
+++ b/packages/CarSystemUI/res/values-vi/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Bất kỳ người dùng nào cũng có thể cập nhật ứng dụng cho tất cả những người dùng khác."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Đang tải"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Đang tải hồ sơ người dùng (từ <xliff:g id="FROM_USER">%1$d</xliff:g> sang <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-zh-rCN/strings.xml b/packages/CarSystemUI/res/values-zh-rCN/strings.xml
index 6dc3738..e7ca871 100644
--- a/packages/CarSystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/CarSystemUI/res/values-zh-rCN/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"任何用户均可为所有其他用户更新应用。"</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"正在加载"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"正在加载用户(从 <xliff:g id="FROM_USER">%1$d</xliff:g> 到 <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-zh-rHK/strings.xml b/packages/CarSystemUI/res/values-zh-rHK/strings.xml
index 00ebd32..268243f 100644
--- a/packages/CarSystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/CarSystemUI/res/values-zh-rHK/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"任何使用者都可以為所有其他使用者更新應用程式。"</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"正在載入"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"正在載入使用者 (由 <xliff:g id="FROM_USER">%1$d</xliff:g> 至 <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-zh-rTW/strings.xml b/packages/CarSystemUI/res/values-zh-rTW/strings.xml
index 96d6a1d..9dc0f1a 100644
--- a/packages/CarSystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/CarSystemUI/res/values-zh-rTW/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"任何使用者都能為所有其他使用者更新應用程式。"</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"載入中"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"正在載入使用者 (從 <xliff:g id="FROM_USER">%1$d</xliff:g> 到 <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values-zu/strings.xml b/packages/CarSystemUI/res/values-zu/strings.xml
index b835f9a..8845ff7 100644
--- a/packages/CarSystemUI/res/values-zu/strings.xml
+++ b/packages/CarSystemUI/res/values-zu/strings.xml
@@ -28,4 +28,6 @@
     <string name="user_add_user_message_update" msgid="7061671307004867811">"Noma yimuphi umsebenzisi angabuyekeza izinhlelo zokusebenza zabanye abasebenzisi."</string>
     <string name="car_loading_profile" msgid="4507385037552574474">"Iyalayisha"</string>
     <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Ilayisha umsebenzisi (kusuka ku-<xliff:g id="FROM_USER">%1$d</xliff:g> kuya ku-<xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
+    <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
+    <skip />
 </resources>
diff --git a/packages/CarSystemUI/res/values/colors.xml b/packages/CarSystemUI/res/values/colors.xml
index 0181b9a..91d4166 100644
--- a/packages/CarSystemUI/res/values/colors.xml
+++ b/packages/CarSystemUI/res/values/colors.xml
@@ -66,4 +66,6 @@
 
     <color name="car_user_switching_dialog_background_color">@android:color/black</color>
     <color name="car_user_switching_dialog_loading_text_color">@*android:color/car_body1</color>
+
+    <color name="rear_view_camera_button_background">@*android:color/car_card_dark</color>
 </resources>
diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml
index 949a0fa..8667ba1 100644
--- a/packages/CarSystemUI/res/values/config.xml
+++ b/packages/CarSystemUI/res/values/config.xml
@@ -108,6 +108,7 @@
         <item>com.android.systemui.car.keyguard.CarKeyguardViewMediator</item>
         <item>com.android.systemui.car.userswitcher.FullscreenUserSwitcherViewMediator</item>
         <item>com.android.systemui.car.userswitcher.UserSwitchTransitionViewMediator</item>
+        <item>com.android.systemui.car.rvc.RearViewCameraViewMediator</item>
     </string-array>
 
     <!--
@@ -150,4 +151,7 @@
 
     <!-- How many milliseconds to wait before force hiding the UserSwitchTransitionView -->
     <integer name="config_userSwitchTransitionViewShownTimeoutMs" translatable="false">5000</integer>
+
+    <!-- The Activity name for the Rear View Camera, if empty, the feature will be disabled. -->
+    <string name="config_rearViewCameraActivity" translatable="false"></string>
 </resources>
diff --git a/packages/CarSystemUI/res/values/dimens.xml b/packages/CarSystemUI/res/values/dimens.xml
index 86bfa75..a7d8ab5 100644
--- a/packages/CarSystemUI/res/values/dimens.xml
+++ b/packages/CarSystemUI/res/values/dimens.xml
@@ -227,4 +227,8 @@
     <dimen name="car_fullscreen_user_pod_image_avatar_height">96dp</dimen>
     <dimen name="car_user_switching_dialog_loading_text_margin_top">@*android:dimen/car_padding_4</dimen>
     <dimen name="car_user_switching_dialog_loading_text_font_size">@*android:dimen/car_body1_size</dimen>
+
+    <!-- dimensions for rear view camera -->
+    <dimen name="rear_view_camera_width">600dp</dimen>
+    <dimen name="rear_view_camera_height">500dp</dimen>
 </resources>
diff --git a/packages/CarSystemUI/res/values/strings.xml b/packages/CarSystemUI/res/values/strings.xml
index 06ae7cf..2644565 100644
--- a/packages/CarSystemUI/res/values/strings.xml
+++ b/packages/CarSystemUI/res/values/strings.xml
@@ -42,4 +42,6 @@
     <string name="car_loading_profile">Loading</string>
     <!-- Message to inform user that the new user profile is loading with additional information on the previous and the next user. [CHAR LIMIT=100] -->
     <string name="car_loading_profile_developer_message">Loading user (from <xliff:g id="from_user" example="10">%1$d</xliff:g> to <xliff:g id="to_user" example="12">%2$d</xliff:g>)</string>
+    <!-- Text for the close button in Rear View Camera [CHAR LIMIT=30] -->
+    <string name="rear_view_camera_close_button_text">Close</string>
 </resources>
diff --git a/packages/CarSystemUI/samples/sample3/rro/Android.bp b/packages/CarSystemUI/samples/sample3/rro/Android.bp
new file mode 100644
index 0000000..0eae7c2
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample3/rro/Android.bp
@@ -0,0 +1,27 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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: "CarSystemUISampleThreeRRO",
+    resource_dirs: ["res"],
+    certificate: "platform",
+    platform_apis: true,
+    manifest: "AndroidManifest.xml",
+    aaptflags: [
+        "--no-resource-deduping",
+        "--no-resource-removal",
+     ]
+}
\ No newline at end of file
diff --git a/packages/CarSystemUI/samples/sample3/rro/AndroidManifest.xml b/packages/CarSystemUI/samples/sample3/rro/AndroidManifest.xml
new file mode 100644
index 0000000..5c25056
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample3/rro/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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.systemui.rro">
+    <overlay
+        android:targetPackage="com.android.systemui"
+        android:isStatic="false"
+        android:resourcesMap="@xml/car_sysui_overlays"
+    />
+</manifest>
\ No newline at end of file
diff --git a/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_apps.xml b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_apps.xml
new file mode 100644
index 0000000..a8d8a2f
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_apps.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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:viewportWidth="44"
+        android:viewportHeight="44"
+        android:width="44dp"
+        android:height="44dp">
+<path
+    android:pathData="M7.33333333 14.6666667L14.6666667 14.6666667L14.6666667 7.33333333L7.33333333 7.33333333L7.33333333 14.6666667ZM18.3333333 36.6666667L25.6666667 36.6666667L25.6666667 29.3333333L18.3333333 29.3333333L18.3333333 36.6666667ZM7.33333333 36.6666667L14.6666667 36.6666667L14.6666667 29.3333333L7.33333333 29.3333333L7.33333333 36.6666667ZM7.33333333 25.6666667L14.6666667 25.6666667L14.6666667 18.3333333L7.33333333 18.3333333L7.33333333 25.6666667ZM18.3333333 25.6666667L25.6666667 25.6666667L25.6666667 18.3333333L18.3333333 18.3333333L18.3333333 25.6666667ZM29.3333333 7.33333333L29.3333333 14.6666667L36.6666667 14.6666667L36.6666667 7.33333333L29.3333333 7.33333333ZM18.3333333 14.6666667L25.6666667 14.6666667L25.6666667 7.33333333L18.3333333 7.33333333L18.3333333 14.6666667ZM29.3333333 25.6666667L36.6666667 25.6666667L36.6666667 18.3333333L29.3333333 18.3333333L29.3333333 25.6666667ZM29.3333333 36.6666667L36.6666667 36.6666667L36.6666667 29.3333333L29.3333333 29.3333333L29.3333333 36.6666667Z"
+    android:fillColor="@color/car_nav_icon_fill_color" />
+</vector>
\ No newline at end of file
diff --git a/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_home.xml b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_home.xml
new file mode 100644
index 0000000..c78f0ed
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_home.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="@dimen/system_bar_icon_drawing_size"
+        android:height="@dimen/system_bar_icon_drawing_size"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+    <path
+        android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z"
+        android:fillColor="@color/car_nav_icon_fill_color" />
+</vector>
\ No newline at end of file
diff --git a/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_hvac.xml b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_hvac.xml
new file mode 100644
index 0000000..55c968e
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_hvac.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2018 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="@dimen/system_bar_icon_drawing_size"
+        android:height="@dimen/system_bar_icon_drawing_size"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+    <path
+        android:pathData="M16.34,8.36l-2.29,0.82c-0.18,-0.13 -0.38,-0.25 -0.58,-0.34c0.17,-0.83 0.63,-1.58 1.36,-2.06C16.85,5.44 16.18,2 13.39,2C9,2 7.16,5.01 8.36,7.66l0.82,2.29c-0.13,0.18 -0.25,0.38 -0.34,0.58c-0.83,-0.17 -1.58,-0.63 -2.06,-1.36C5.44,7.15 2,7.82 2,10.61c0,4.4 3.01,6.24 5.66,5.03l2.29,-0.82c0.18,0.13 0.38,0.25 0.58,0.34c-0.17,0.83 -0.63,1.58 -1.36,2.06C7.15,18.56 7.82,22 10.61,22c4.4,0 6.24,-3.01 5.03,-5.66l-0.82,-2.29c0.13,-0.18 0.25,-0.38 0.34,-0.58c0.83,0.17 1.58,0.63 2.06,1.36c1.34,2.01 4.77,1.34 4.77,-1.45C22,9 18.99,7.16 16.34,8.36zM12,13.5c-0.83,0 -1.5,-0.67 -1.5,-1.5c0,-0.83 0.67,-1.5 1.5,-1.5c0.83,0 1.5,0.67 1.5,1.5C13.5,12.83 12.83,13.5 12,13.5zM10.24,5.22C10.74,4.44 11.89,4 13.39,4c0.79,0 0.71,0.86 0.34,1.11c-1.22,0.81 -2,2.06 -2.25,3.44c-0.21,0.03 -0.42,0.08 -0.62,0.15l-0.68,-1.88C10,6.42 9.86,5.81 10.24,5.22zM6.83,13.82c-0.4,0.18 -1.01,0.32 -1.61,-0.06C4.44,13.26 4,12.11 4,10.61c0,-0.79 0.86,-0.71 1.11,-0.34c0.81,1.22 2.06,2 3.44,2.25c0.03,0.21 0.08,0.42 0.15,0.62L6.83,13.82zM13.76,18.78c-0.5,0.77 -1.65,1.22 -3.15,1.22c-0.79,0 -0.71,-0.86 -0.34,-1.11c1.22,-0.81 2,-2.06 2.25,-3.44c0.21,-0.03 0.42,-0.08 0.62,-0.15l0.68,1.88C14,17.58 14.14,18.18 13.76,18.78zM18.89,13.73c-0.81,-1.22 -2.06,-2 -3.44,-2.25c-0.03,-0.21 -0.08,-0.42 -0.15,-0.62l1.88,-0.68c0.4,-0.18 1.01,-0.32 1.61,0.06c0.77,0.5 1.22,1.65 1.22,3.15C20,14.19 19.14,14.11 18.89,13.73z"
+        android:fillColor="@color/car_nav_icon_fill_color" />
+</vector>
diff --git a/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_music.xml b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_music.xml
new file mode 100644
index 0000000..6339ebb
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_music.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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="44"
+    android:viewportHeight="44"
+    android:width="44dp"
+    android:height="44dp">
+    <path
+        android:pathData="M22 5.5L22 24.8416667C20.9183333 24.2183333 19.6716667 23.8333333 18.3333333 23.8333333C14.2816667 23.8333333 11 27.115 11 31.1666667C11 35.2183333 14.2816667 38.5 18.3333333 38.5C22.385 38.5 25.6666667 35.2183333 25.6666667 31.1666667L25.6666667 12.8333333L33 12.8333333L33 5.5L22 5.5Z"
+        android:fillColor="@color/car_nav_icon_fill_color" />
+</vector>
\ No newline at end of file
diff --git a/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_navigation.xml b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_navigation.xml
new file mode 100644
index 0000000..e1fabe0
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_navigation.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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="44"
+    android:viewportHeight="44"
+    android:width="44dp"
+    android:height="44dp">
+    <path
+        android:pathData="M39.8016667 20.6983333L23.3016667 4.19833333C22.5866667 3.48333333 21.4316667 3.48333333 20.7166667 4.19833333L4.21666667 20.6983333C3.50166667 21.4133333 3.50166667 22.5683333 4.21666667 23.2833333L20.7166667 39.7833333C21.4316667 40.4983333 22.5866667 40.4983333 23.3016667 39.7833333L39.8016667 23.2833333C40.5166667 22.5866667 40.5166667 21.4316667 39.8016667 20.6983333ZM25.6666667 26.5833333L25.6666667 22L18.3333333 22L18.3333333 27.5L14.6666667 27.5L14.6666667 20.1666667C14.6666667 19.1583333 15.4916667 18.3333333 16.5 18.3333333L25.6666667 18.3333333L25.6666667 13.75L32.0833333 20.1666667L25.6666667 26.5833333Z"
+        android:fillColor="@color/car_nav_icon_fill_color" />
+</vector>
\ No newline at end of file
diff --git a/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_notification.xml b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_notification.xml
new file mode 100644
index 0000000..aabf916
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_notification.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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="@dimen/system_bar_icon_drawing_size"
+        android:height="@dimen/system_bar_icon_drawing_size"
+        android:viewportWidth="44"
+        android:viewportHeight="44">
+    <path
+        android:pathData="M22 39.125C23.925 39.125 25.5 37.55 25.5 35.625L18.5 35.625C18.5 37.55 20.0575 39.125 22 39.125ZM32.5 28.625L32.5 19.875C32.5 14.5025 29.63 10.005 24.625 8.815L24.625 7.625C24.625 6.1725 23.4525 5 22 5C20.5475 5 19.375 6.1725 19.375 7.625L19.375 8.815C14.3525 10.005 11.5 14.485 11.5 19.875L11.5 28.625L8 32.125L8 33.875L36 33.875L36 32.125L32.5 28.625Z"
+        android:fillColor="@color/car_nav_icon_fill_color" />
+</vector>
\ No newline at end of file
diff --git a/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_overview.xml b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_overview.xml
new file mode 100644
index 0000000..f185eb9
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_overview.xml
@@ -0,0 +1,26 @@
+<?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:viewportWidth="44"
+    android:viewportHeight="44"
+    android:width="44dp"
+    android:height="44dp">
+    <path
+        android:pathData="M36.92857 22.39286A14.53571 14.53571 0 0 1 7.857143 22.39286A14.53571 14.53571 0 0 1 36.92857 22.39286Z"
+        android:strokeColor="@color/car_nav_icon_fill_color"
+        android:strokeWidth="4" />
+</vector>
\ No newline at end of file
diff --git a/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_phone.xml b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_phone.xml
new file mode 100644
index 0000000..50e36b5
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_phone.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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="44"
+    android:viewportHeight="44"
+    android:width="44dp"
+    android:height="44dp">
+    <path
+        android:pathData="M12.1366667 19.7816667C14.7766667 24.97 19.03 29.205 24.2183333 31.8633333L28.2516667 27.83C28.7466667 27.335 29.48 27.17 30.1216667 27.39C32.175 28.0683333 34.3933333 28.435 36.6666667 28.435C37.675 28.435 38.5 29.26 38.5 30.2683333L38.5 36.6666667C38.5 37.675 37.675 38.5 36.6666667 38.5C19.4516667 38.5 5.5 24.5483333 5.5 7.33333333C5.5 6.325 6.325 5.5 7.33333333 5.5L13.75 5.5C14.7583333 5.5 15.5833333 6.325 15.5833333 7.33333333C15.5833333 9.625 15.95 11.825 16.6283333 13.8783333C16.83 14.52 16.6833333 15.235 16.17 15.7483333L12.1366667 19.7816667Z"
+        android:fillColor="@color/car_nav_icon_fill_color" />
+</vector>
\ No newline at end of file
diff --git a/packages/CarSystemUI/samples/sample3/rro/res/drawable/system_bar_background.xml b/packages/CarSystemUI/samples/sample3/rro/res/drawable/system_bar_background.xml
new file mode 100644
index 0000000..66da21c
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample3/rro/res/drawable/system_bar_background.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" >
+    <solid
+        android:color="#404040"
+    />
+</shape>
\ No newline at end of file
diff --git a/packages/CarSystemUI/samples/sample3/rro/res/layout/car_left_navigation_bar.xml b/packages/CarSystemUI/samples/sample3/rro/res/layout/car_left_navigation_bar.xml
new file mode 100644
index 0000000..3d1cd08
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample3/rro/res/layout/car_left_navigation_bar.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<com.android.systemui.car.navigationbar.CarNavigationBarView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:layout_height="match_parent"
+    android:layout_width="match_parent"
+    android:orientation="vertical"
+    android:background="@android:color/transparent">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:gravity="bottom"
+        android:orientation="vertical">
+
+        <com.android.systemui.statusbar.policy.Clock
+            android:id="@+id/clock"
+            android:textAppearance="@style/TextAppearance.StatusBar.Clock"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:singleLine="true"
+            android:gravity="center_horizontal"
+            android:paddingBottom="20dp"
+        />
+
+        <Space
+            android:layout_height="50dp"
+            android:layout_width="match_parent"/>
+
+    </LinearLayout>
+
+</com.android.systemui.car.navigationbar.CarNavigationBarView>
diff --git a/packages/CarSystemUI/samples/sample3/rro/res/layout/car_navigation_bar.xml b/packages/CarSystemUI/samples/sample3/rro/res/layout/car_navigation_bar.xml
new file mode 100644
index 0000000..8314ba5
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample3/rro/res/layout/car_navigation_bar.xml
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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.car.navigationbar.CarNavigationBarView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@drawable/system_bar_background"
+    android:gravity="center"
+    android:orientation="horizontal">
+
+    <RelativeLayout
+        android:id="@+id/nav_buttons"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layoutDirection="ltr">
+
+        <com.android.systemui.car.hvac.AdjustableTemperatureView
+            android:id="@+id/driver_hvac"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:gravity="center_vertical"
+            systemui:hvacAreaId="49"
+            systemui:hvacTempFormat="%.0f\u00B0" />
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_centerInParent="true"
+            android:layout_weight="1"
+            android:gravity="center"
+            android:layoutDirection="ltr"
+            android:paddingEnd="@dimen/system_bar_button_group_padding"
+            android:paddingStart="@dimen/system_bar_button_group_padding">
+
+            <Space
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"/>
+
+            <com.android.systemui.car.navigationbar.CarNavigationButton
+                android:id="@+id/home"
+                style="@style/NavigationBarButton"
+                systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
+                systemui:highlightWhenSelected="true"
+                systemui:icon="@drawable/car_ic_home"
+                systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"/>
+
+            <com.android.systemui.car.navigationbar.CarNavigationButton
+                android:id="@+id/phone_nav"
+                style="@style/NavigationBarButton"
+                systemui:highlightWhenSelected="true"
+                systemui:icon="@drawable/car_ic_phone"
+                systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;package=com.android.car.dialer;launchFlags=0x10000000;end"
+                systemui:packages="com.android.car.dialer"/>
+
+            <com.android.systemui.car.navigationbar.CarNavigationButton
+                android:id="@+id/grid_nav"
+                style="@style/NavigationBarButton"
+                systemui:componentNames="com.android.car.carlauncher/.AppGridActivity"
+                systemui:highlightWhenSelected="true"
+                systemui:icon="@drawable/car_ic_apps"
+                systemui:intent="intent:#Intent;component=com.android.car.carlauncher/.AppGridActivity;launchFlags=0x24000000;end"/>
+
+            <com.android.systemui.car.navigationbar.CarNavigationButton
+                android:id="@+id/hvac"
+                style="@style/NavigationBarButton"
+                systemui:highlightWhenSelected="true"
+                systemui:icon="@drawable/car_ic_hvac"
+                systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
+                systemui:broadcast="true"/>
+
+            <com.android.systemui.car.navigationbar.CarNavigationButton
+                android:id="@+id/notifications"
+                style="@style/NavigationBarButton"
+                systemui:highlightWhenSelected="true"
+                systemui:icon="@drawable/car_ic_notification"
+                systemui:longIntent="intent:#Intent;component=com.android.car.bugreport/.BugReportActivity;end"/>
+
+            <Space
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"/>
+        </LinearLayout>
+
+        <com.android.systemui.car.hvac.AdjustableTemperatureView
+            android:id="@+id/passenger_hvac"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_alignParentEnd="true"
+            android:gravity="center_vertical"
+            systemui:hvacAreaId="68"
+            systemui:hvacTempFormat="%.0f\u00B0" />
+    </RelativeLayout>
+
+    <LinearLayout
+        android:id="@+id/lock_screen_nav_buttons"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:gravity="center"
+        android:layoutDirection="ltr"
+        android:paddingEnd="@dimen/car_keyline_1"
+        android:paddingStart="@dimen/car_keyline_1"
+        android:visibility="gone"
+    />
+</com.android.systemui.car.navigationbar.CarNavigationBarView>
\ No newline at end of file
diff --git a/packages/CarSystemUI/samples/sample3/rro/res/values/attrs.xml b/packages/CarSystemUI/samples/sample3/rro/res/values/attrs.xml
new file mode 100644
index 0000000..bc7ded2
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample3/rro/res/values/attrs.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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>
+    <attr name="broadcast" format="boolean"/>
+    <attr name="icon" format="reference"/>
+    <attr name="intent" format="string"/>
+    <attr name="longIntent" format="string"/>
+    <attr name="componentNames" format="string" />
+    <attr name="highlightWhenSelected" format="boolean" />
+    <attr name="categories" format="string"/>
+    <attr name="packages" format="string" />
+
+    <!-- Custom attributes to configure hvac values -->
+    <declare-styleable name="AnimatedTemperatureView">
+        <attr name="hvacAreaId" format="integer"/>
+        <attr name="hvacPropertyId" format="integer"/>
+        <attr name="hvacTempFormat" format="string"/>
+        <!-- how far away the animations should center around -->
+        <attr name="hvacPivotOffset" format="dimension"/>
+        <attr name="hvacMinValue" format="float"/>
+        <attr name="hvacMaxValue" format="float"/>
+        <attr name="hvacMinText" format="string|reference"/>
+        <attr name="hvacMaxText" format="string|reference"/>
+        <attr name="android:gravity"/>
+        <attr name="android:minEms"/>
+        <attr name="android:textAppearance"/>
+    </declare-styleable>
+</resources>
diff --git a/packages/CarSystemUI/samples/sample3/rro/res/values/colors.xml b/packages/CarSystemUI/samples/sample3/rro/res/values/colors.xml
new file mode 100644
index 0000000..f98cb96
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample3/rro/res/values/colors.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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">
+    <color name="car_nav_icon_fill_color">#8F8F8F</color>
+</resources>
diff --git a/packages/CarSystemUI/samples/sample3/rro/res/values/config.xml b/packages/CarSystemUI/samples/sample3/rro/res/values/config.xml
new file mode 100644
index 0000000..2148e7c
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample3/rro/res/values/config.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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>
+    <!-- Configure which system bars should be displayed. -->
+    <bool name="config_enableTopNavigationBar">false</bool>
+    <bool name="config_enableLeftNavigationBar">true</bool>
+    <bool name="config_enableRightNavigationBar">false</bool>
+    <bool name="config_enableBottomNavigationBar">true</bool>
+
+    <!-- Configure the type of each system bar. Each system bar must have a unique type. -->
+    <!--    STATUS_BAR = 0-->
+    <!--    NAVIGATION_BAR = 1-->
+    <!--    STATUS_BAR_EXTRA = 2-->
+    <!--    NAVIGATION_BAR_EXTRA = 3-->
+    <integer name="config_topSystemBarType">2</integer>
+    <integer name="config_leftSystemBarType">0</integer>
+    <integer name="config_rightSystemBarType">3</integer>
+    <integer name="config_bottomSystemBarType">1</integer>
+
+    <!-- Configure the relative z-order among the system bars. When two system bars overlap (e.g.
+         if both top bar and left bar are enabled, it creates an overlapping space in the upper left
+         corner), the system bar with the higher z-order takes the overlapping space and padding is
+         applied to the other bar.-->
+    <!-- NOTE: If two overlapping system bars have the same z-order, SystemBarConfigs will throw a
+         RuntimeException, since their placing order cannot be determined. Bars that do not overlap
+         are allowed to have the same z-order. -->
+    <!-- NOTE: If the z-order of a bar is 10 or above, it will also appear on top of HUN's.    -->
+    <integer name="config_topSystemBarZOrder">0</integer>
+    <integer name="config_leftSystemBarZOrder">10</integer>
+    <integer name="config_rightSystemBarZOrder">0</integer>
+    <integer name="config_bottomSystemBarZOrder">15</integer>
+
+    <!-- Whether heads-up notifications should be shown on the bottom. If false, heads-up
+         notifications will be shown pushed to the top of their parent container. If true, they will
+         be shown pushed to the bottom of their parent container. If true, then should override
+         config_headsUpNotificationAnimationHelper to use a different AnimationHelper, such as
+         com.android.car.notification.headsup.animationhelper.
+         CarHeadsUpNotificationBottomAnimationHelper. -->
+    <bool name="config_showHeadsUpNotificationOnBottom">false</bool>
+
+    <string name="config_notificationPanelViewMediator" translatable="false">com.android.systemui.car.notification.BottomNotificationPanelViewMediator</string>
+</resources>
\ No newline at end of file
diff --git a/packages/CarSystemUI/samples/sample3/rro/res/values/dimens.xml b/packages/CarSystemUI/samples/sample3/rro/res/values/dimens.xml
new file mode 100644
index 0000000..c89f949
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample3/rro/res/values/dimens.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>
+    <dimen name="car_left_navigation_bar_width">280dp</dimen>
+    <dimen name="car_keyline_1">24dp</dimen>
+    <dimen name="system_bar_button_group_padding">64dp</dimen>
+    <dimen name="system_bar_icon_drawing_size">44dp</dimen>
+</resources>
diff --git a/packages/CarSystemUI/samples/sample3/rro/res/values/styles.xml b/packages/CarSystemUI/samples/sample3/rro/res/values/styles.xml
new file mode 100644
index 0000000..bad3691
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample3/rro/res/values/styles.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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">
+    <style name="TextAppearance.StatusBar.Clock"
+           parent="@*android:style/TextAppearance.StatusBar.Icon">
+        <item name="android:textSize">40sp</item>
+        <item name="android:fontFamily">sans-serif-regular</item>
+        <item name="android:textColor">#FFFFFF</item>
+    </style>
+
+    <style name="NavigationBarButton">
+        <item name="android:layout_height">96dp</item>
+        <item name="android:layout_width">96dp</item>
+        <item name="android:background">?android:attr/selectableItemBackground</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/packages/CarSystemUI/samples/sample3/rro/res/xml/car_sysui_overlays.xml b/packages/CarSystemUI/samples/sample3/rro/res/xml/car_sysui_overlays.xml
new file mode 100644
index 0000000..f08d968
--- /dev/null
+++ b/packages/CarSystemUI/samples/sample3/rro/res/xml/car_sysui_overlays.xml
@@ -0,0 +1,75 @@
+
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<overlay>
+    <item target="layout/car_navigation_bar" value="@layout/car_navigation_bar"/>
+    <item target="layout/car_left_navigation_bar" value="@layout/car_left_navigation_bar"/>
+
+    <item target="bool/config_enableTopNavigationBar" value="@bool/config_enableTopNavigationBar"/>
+    <item target="bool/config_enableLeftNavigationBar" value="@bool/config_enableLeftNavigationBar"/>
+    <item target="bool/config_enableRightNavigationBar" value="@bool/config_enableRightNavigationBar"/>
+    <item target="bool/config_enableBottomNavigationBar" value="@bool/config_enableBottomNavigationBar"/>
+    <item target="bool/config_showHeadsUpNotificationOnBottom" value="@bool/config_showHeadsUpNotificationOnBottom"/>
+
+    <item target="attr/icon" value="@attr/icon"/>
+    <item target="attr/intent" value="@attr/intent"/>
+    <item target="attr/longIntent" value="@attr/longIntent"/>
+    <item target="attr/componentNames" value="@attr/componentNames"/>
+    <item target="attr/highlightWhenSelected" value="@attr/highlightWhenSelected"/>
+    <item target="attr/categories" value="@attr/categories"/>
+    <item target="attr/packages" value="@attr/packages"/>
+    <item target="attr/hvacAreaId" value="@attr/hvacAreaId"/>
+    <item target="attr/hvacPropertyId" value="@attr/hvacPropertyId"/>
+    <item target="attr/hvacTempFormat" value="@attr/hvacTempFormat"/>
+    <item target="attr/hvacPivotOffset" value="@attr/hvacPivotOffset"/>
+    <item target="attr/hvacMinValue" value="@attr/hvacMinValue"/>
+    <item target="attr/hvacMaxValue" value="@attr/hvacMaxValue"/>
+    <item target="attr/hvacMinText" value="@attr/hvacMinText"/>
+    <item target="attr/hvacMaxText" value="@attr/hvacMaxText"/>
+    <!-- start the intent as a broad cast instead of an activity if true-->
+    <item target="attr/broadcast" value="@attr/broadcast"/>
+
+    <item target="color/car_nav_icon_fill_color" value="@color/car_nav_icon_fill_color" />
+
+    <item target="drawable/car_ic_overview" value="@drawable/car_ic_overview" />
+    <item target="drawable/car_ic_home" value="@drawable/car_ic_home" />
+    <item target="drawable/car_ic_hvac" value="@drawable/car_ic_hvac" />
+    <item target="drawable/car_ic_apps" value="@drawable/car_ic_apps" />
+    <item target="drawable/car_ic_music" value="@drawable/car_ic_music" />
+    <item target="drawable/car_ic_notification" value="@drawable/car_ic_notification" />
+    <item target="drawable/car_ic_phone" value="@drawable/car_ic_phone" />
+    <item target="drawable/car_ic_navigation" value="@drawable/car_ic_navigation" />
+
+    <item target="dimen/car_left_navigation_bar_width" value="@dimen/car_left_navigation_bar_width" />
+    <item target="dimen/car_keyline_1" value="@dimen/car_keyline_1" />
+    <item target="dimen/system_bar_button_group_padding" value="@dimen/system_bar_button_group_padding" />
+    <item target="dimen/system_bar_icon_drawing_size" value="@dimen/system_bar_icon_drawing_size" />
+
+    <item target="integer/config_topSystemBarType" value="@integer/config_topSystemBarType"/>
+    <item target="integer/config_leftSystemBarType" value="@integer/config_leftSystemBarType"/>
+    <item target="integer/config_rightSystemBarType" value="@integer/config_rightSystemBarType"/>
+    <item target="integer/config_bottomSystemBarType" value="@integer/config_bottomSystemBarType"/>
+
+    <item target="integer/config_topSystemBarZOrder" value="@integer/config_topSystemBarZOrder"/>
+    <item target="integer/config_leftSystemBarZOrder" value="@integer/config_leftSystemBarZOrder"/>
+    <item target="integer/config_rightSystemBarZOrder" value="@integer/config_rightSystemBarZOrder"/>
+    <item target="integer/config_bottomSystemBarZOrder" value="@integer/config_bottomSystemBarZOrder"/>
+
+    <item target="string/config_notificationPanelViewMediator" value="@string/config_notificationPanelViewMediator"/>
+
+    <item target="style/NavigationBarButton" value="@style/NavigationBarButton"/>
+</overlay>
\ No newline at end of file
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarGlobalRootComponent.java b/packages/CarSystemUI/src/com/android/systemui/CarGlobalRootComponent.java
index b17ad0f..b056dcf 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarGlobalRootComponent.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarGlobalRootComponent.java
@@ -19,6 +19,7 @@
 import com.android.systemui.dagger.GlobalModule;
 import com.android.systemui.dagger.GlobalRootComponent;
 import com.android.systemui.dagger.WMModule;
+import com.android.systemui.wmshell.CarWMComponent;
 
 import javax.inject.Singleton;
 
@@ -41,6 +42,12 @@
         CarGlobalRootComponent build();
     }
 
+    /**
+     * Builder for a WMComponent.
+     */
+    @Override
+    CarWMComponent.Builder getWMComponentBuilder();
+
     @Override
     CarSysUIComponent.Builder getSysUIComponent();
 }
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
index 51fda96..1d35bbb 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
@@ -64,7 +64,6 @@
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.volume.VolumeDialogComponent;
-import com.android.systemui.wmshell.CarWMShellModule;
 
 import javax.inject.Named;
 
@@ -74,8 +73,7 @@
 
 @Module(
         includes = {
-                QSModule.class,
-                CarWMShellModule.class
+                QSModule.class
         })
 abstract class CarSystemUIModule {
 
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
index b647f13..4b66069 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
@@ -32,6 +32,7 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.WindowInsets;
 
 import androidx.annotation.NonNull;
 import androidx.recyclerview.widget.RecyclerView;
@@ -82,7 +83,6 @@
     private final StatusBarStateController mStatusBarStateController;
     private final boolean mEnableHeadsUpNotificationWhenNotificationShadeOpen;
     private final NotificationVisibilityLogger mNotificationVisibilityLogger;
-    private final int mNavBarHeight;
 
     private float mInitialBackgroundAlpha;
     private float mBackgroundAlphaDiff;
@@ -97,6 +97,7 @@
     private boolean mNotificationListAtEndAtTimeOfTouch;
     private boolean mIsSwipingVerticallyToClose;
     private boolean mIsNotificationCardSwiping;
+    private boolean mImeVisible = false;
 
     private OnUnseenCountUpdateListener mUnseenCountUpdateListener;
 
@@ -139,8 +140,6 @@
         mStatusBarStateController = statusBarStateController;
         mNotificationVisibilityLogger = notificationVisibilityLogger;
 
-        mNavBarHeight = mResources.getDimensionPixelSize(R.dimen.car_bottom_navigation_bar_height);
-
         mCommandQueue.addCallback(this);
 
         // Notification background setup.
@@ -189,19 +188,7 @@
         if (mContext.getDisplayId() != displayId) {
             return;
         }
-        boolean isKeyboardVisible = (vis & InputMethodService.IME_VISIBLE) != 0;
-        int bottomMargin = isKeyboardVisible ? 0 : mNavBarHeight;
-        ViewGroup container = (ViewGroup) getLayout();
-        if (container == null) {
-            // Notification panel hasn't been inflated before. We shouldn't try to update the layout
-            // params.
-            return;
-        }
-
-        ViewGroup.MarginLayoutParams params =
-                (ViewGroup.MarginLayoutParams) container.getLayoutParams();
-        params.setMargins(params.leftMargin, params.topMargin, params.rightMargin, bottomMargin);
-        container.setLayoutParams(params);
+        mImeVisible = (vis & InputMethodService.IME_VISIBLE) != 0;
     }
 
     // OverlayViewController
@@ -229,7 +216,7 @@
 
     @Override
     protected int getInsetTypesToFit() {
-        return 0;
+        return WindowInsets.Type.navigationBars();
     }
 
     @Override
@@ -237,6 +224,12 @@
         return mEnableHeadsUpNotificationWhenNotificationShadeOpen;
     }
 
+    @Override
+    protected boolean shouldUseStableInsets() {
+        // When IME is visible, then the inset from the nav bar should not be applied.
+        return !mImeVisible;
+    }
+
     /** Reinflates the view. */
     public void reinflate() {
         ViewGroup container = (ViewGroup) getLayout();
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/rvc/RearViewCameraViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/rvc/RearViewCameraViewController.java
new file mode 100644
index 0000000..d634633
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/car/rvc/RearViewCameraViewController.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.car.rvc;
+
+import android.app.ActivityView;
+import android.app.ActivityView.StateCallback;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.util.Slog;
+import android.view.ViewGroup;
+import android.widget.LinearLayout.LayoutParams;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.R;
+import com.android.systemui.car.window.OverlayViewController;
+import com.android.systemui.car.window.OverlayViewGlobalStateController;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
+
+import javax.inject.Inject;
+
+/** View controller for the rear view camera. */
+@SysUISingleton
+public class RearViewCameraViewController extends OverlayViewController {
+    private static final String TAG = "RearViewCameraView";
+    private static final boolean DBG = false;
+
+    private final ComponentName mRearViewCameraActivity;
+    private ViewGroup mRvcView;
+    private final LayoutParams mRvcViewLayoutParams = new LayoutParams(
+            LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, /* weight= */ 1.0f);
+    @VisibleForTesting
+    ActivityView mActivityView;
+    @VisibleForTesting
+    final StateCallback mActivityViewCallback = new StateCallback() {
+        @Override
+        public void onActivityViewReady(ActivityView view) {
+            Intent intent = new Intent(Intent.ACTION_MAIN)
+                    .setComponent(mRearViewCameraActivity)
+                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+                    .addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT)
+                    .addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+            // TODO(b/170899079): Migrate this to FixedActivityService.
+            view.startActivity(intent);
+        }
+
+        @Override
+        public void onActivityViewDestroyed(ActivityView view) {}
+    };
+
+    @Inject
+    public RearViewCameraViewController(
+            @Main Resources resources,
+            OverlayViewGlobalStateController overlayViewGlobalStateController) {
+        super(R.id.rear_view_camera_stub, overlayViewGlobalStateController);
+        String rearViewCameraActivityName = resources.getString(
+                R.string.config_rearViewCameraActivity);
+        if (!rearViewCameraActivityName.isEmpty()) {
+            mRearViewCameraActivity = ComponentName.unflattenFromString(rearViewCameraActivityName);
+            if (DBG) Slog.d(TAG, "mRearViewCameraActivity=" + mRearViewCameraActivity);
+        } else {
+            mRearViewCameraActivity = null;
+            Slog.e(TAG, "RearViewCameraViewController is disabled, since no Activity is defined");
+        }
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        mRvcView = (ViewGroup) getLayout().findViewById(R.id.rear_view_camera_container);
+        getLayout().findViewById(R.id.close_button).setOnClickListener(v -> {
+            stop();
+        });
+    }
+
+    @Override
+    protected void hideInternal() {
+        super.hideInternal();
+        if (DBG) Slog.d(TAG, "hideInternal: mActivityView=" + mActivityView);
+        if (mActivityView == null) return;
+        mRvcView.removeView(mActivityView);
+        // Release ActivityView since the Activity on ActivityView (with showWhenLocked flag) keeps
+        // running even if ActivityView is hidden.
+        mActivityView.release();
+        mActivityView = null;
+    }
+
+    @Override
+    protected void showInternal() {
+        super.showInternal();
+        if (DBG) Slog.d(TAG, "showInternal: mActivityView=" + mActivityView);
+        if (mActivityView != null) return;
+        mActivityView = new ActivityView(mRvcView.getContext());
+        mActivityView.setCallback(mActivityViewCallback);
+        mActivityView.setLayoutParams(mRvcViewLayoutParams);
+        mRvcView.addView(mActivityView, /* index= */ 0);
+    }
+
+    boolean isShown() {
+        return mActivityView != null;
+    }
+
+    boolean isEnabled() {
+        return mRearViewCameraActivity != null;
+    }
+
+    @Override
+    protected boolean shouldShowHUN() {
+        return false;
+    }
+
+    @Override
+    protected boolean shouldShowWhenOccluded() {
+        // Returns true to show it on top of Keylock.
+        return true;
+    }
+
+    @Override
+    protected boolean shouldShowNavigationBarInsets() {
+        return true;
+    }
+
+    @Override
+    protected boolean shouldShowStatusBarInsets() {
+        return true;
+    }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/rvc/RearViewCameraViewMediator.java b/packages/CarSystemUI/src/com/android/systemui/car/rvc/RearViewCameraViewMediator.java
new file mode 100644
index 0000000..c575c42
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/car/rvc/RearViewCameraViewMediator.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.car.rvc;
+
+import android.car.Car;
+import android.car.VehicleGear;
+import android.car.VehiclePropertyIds;
+import android.car.hardware.CarPropertyValue;
+import android.car.hardware.property.CarPropertyManager;
+import android.car.hardware.property.CarPropertyManager.CarPropertyEventCallback;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.UserHandle;
+import android.util.Slog;
+
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.car.CarServiceProvider;
+import com.android.systemui.car.window.OverlayViewMediator;
+import com.android.systemui.dagger.SysUISingleton;
+
+import javax.inject.Inject;
+
+/**
+ * View mediator for the rear view camera (RVC), which monitors the gear changes and shows
+ * the RVC when the gear position is R and otherwise it hides the RVC.
+ */
+@SysUISingleton
+public class RearViewCameraViewMediator implements OverlayViewMediator {
+    private static final String TAG = "RearViewCameraView";
+    private static final boolean DBG = false;
+
+    private final RearViewCameraViewController mRearViewCameraViewController;
+    private final CarServiceProvider mCarServiceProvider;
+    private final BroadcastDispatcher mBroadcastDispatcher;
+
+    private CarPropertyManager mCarPropertyManager;
+    // TODO(b/170792252): Replace the following with the callback from CarEvsManager if it's ready.
+    private final CarPropertyEventCallback mPropertyEventCallback = new CarPropertyEventCallback() {
+        @Override
+        public void onChangeEvent(CarPropertyValue value) {
+            if (DBG) Slog.d(TAG, "onChangeEvent value=" + value);
+            if (value.getPropertyId() != VehiclePropertyIds.GEAR_SELECTION) {
+                Slog.w(TAG, "Got the event for non-registered property: " + value.getPropertyId());
+                return;
+            }
+            if ((Integer) value.getValue() == VehicleGear.GEAR_REVERSE) {
+                mRearViewCameraViewController.start();
+            } else {
+                mRearViewCameraViewController.stop();
+            }
+        }
+        @Override
+        public void onErrorEvent(int propId, int zone) {
+            Slog.e(TAG, "onErrorEvent propId=" + propId + ", zone=" + zone);
+        }
+    };
+
+    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (DBG) Slog.d(TAG, "onReceive: " + intent);
+            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())
+                    && mRearViewCameraViewController.isShown()) {
+                mRearViewCameraViewController.stop();
+            }
+        }
+    };
+
+    @Inject
+    public RearViewCameraViewMediator(
+            RearViewCameraViewController rearViewCameraViewController,
+            CarServiceProvider carServiceProvider,
+            BroadcastDispatcher broadcastDispatcher) {
+        if (DBG) Slog.d(TAG, "RearViewCameraViewMediator:init");
+        mRearViewCameraViewController = rearViewCameraViewController;
+        mCarServiceProvider = carServiceProvider;
+        mBroadcastDispatcher = broadcastDispatcher;
+    }
+
+    @Override
+    public void registerListeners() {
+        if (DBG) Slog.d(TAG, "RearViewCameraViewMediator:registerListeners");
+        if (!mRearViewCameraViewController.isEnabled()) {
+            Slog.i(TAG, "RearViewCameraViewController isn't enabled");
+            return;
+        }
+
+        mCarServiceProvider.addListener(car -> {
+            mCarPropertyManager = (CarPropertyManager) car.getCarManager(Car.PROPERTY_SERVICE);
+            if (mCarPropertyManager == null) {
+                Slog.e(TAG, "Unable to get CarPropertyManager");
+                return;
+            }
+            if (DBG) Slog.d(TAG, "Registering mPropertyEventCallback.");
+            mCarPropertyManager.registerCallback(mPropertyEventCallback,
+                    VehiclePropertyIds.GEAR_SELECTION, CarPropertyManager.SENSOR_RATE_UI);
+        });
+        mBroadcastDispatcher.registerReceiver(mBroadcastReceiver,
+                new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS), /* executor= */ null,
+                UserHandle.ALL);
+    }
+
+    @Override
+    public void setupOverlayContentViewControllers() {}
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserGridRecyclerView.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserGridRecyclerView.java
index 023b5b4..6d63e31 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserGridRecyclerView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserGridRecyclerView.java
@@ -567,6 +567,9 @@
                         Log.e(TAG, "Failed to switch to new user: " + user.id);
                     }
                 }
+                if (mAddUserView != null) {
+                    mAddUserView.setEnabled(true);
+                }
             }
         }
 
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
index 8adc1ad..6c3a632 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
@@ -168,6 +168,19 @@
     }
 
     /**
+     * Returns {@code true} if the window should use stable insets. Using stable insets means that
+     * even when system bars are temporarily not visible, inset from the system bars will still be
+     * applied.
+     *
+     * NOTE: When system bars are hidden in transient mode, insets from them will not be applied
+     * even when the system bars become visible. Setting the return value to {@true} here can
+     * prevent the OverlayView from overlapping with the system bars when that happens.
+     */
+    protected boolean shouldUseStableInsets() {
+        return false;
+    }
+
+    /**
      * Returns the insets types to fit to the sysui overlay window when this
      * {@link OverlayViewController} is in the foreground.
      */
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
index c13e486..0da2360 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
@@ -108,6 +108,7 @@
         if (mZOrderVisibleSortedMap.isEmpty()) {
             setWindowVisible(true);
         }
+
         if (!(viewController instanceof OverlayPanelViewController)) {
             inflateView(viewController);
         }
@@ -117,6 +118,7 @@
         }
 
         updateInternalsWhenShowingView(viewController);
+        refreshUseStableInsets();
         refreshInsetTypesToFit();
         refreshWindowFocus();
         refreshNavigationBarVisibility();
@@ -190,6 +192,7 @@
 
         mZOrderVisibleSortedMap.remove(mZOrderMap.get(viewController));
         refreshHighestZOrderWhenHidingView(viewController);
+        refreshUseStableInsets();
         refreshInsetTypesToFit();
         refreshWindowFocus();
         refreshNavigationBarVisibility();
@@ -247,6 +250,11 @@
         setWindowFocusable(mHighestZOrder == null ? false : mHighestZOrder.shouldFocusWindow());
     }
 
+    private void refreshUseStableInsets() {
+        mSystemUIOverlayWindowController.setUsingStableInsets(
+                mHighestZOrder == null ? false : mHighestZOrder.shouldUseStableInsets());
+    }
+
     private void refreshInsetTypesToFit() {
         if (mZOrderVisibleSortedMap.isEmpty()) {
             setFitInsetsTypes(statusBars());
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayWindowModule.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayWindowModule.java
index 5a16efa..fcbb0b8 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayWindowModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayWindowModule.java
@@ -20,6 +20,7 @@
 import com.android.systemui.car.notification.BottomNotificationPanelViewMediator;
 import com.android.systemui.car.notification.NotificationPanelViewMediator;
 import com.android.systemui.car.notification.TopNotificationPanelViewMediator;
+import com.android.systemui.car.rvc.RearViewCameraViewMediator;
 import com.android.systemui.car.userswitcher.FullscreenUserSwitcherViewMediator;
 import com.android.systemui.car.userswitcher.UserSwitchTransitionViewMediator;
 
@@ -75,4 +76,11 @@
     @ClassKey(UserSwitchTransitionViewMediator.class)
     public abstract OverlayViewMediator bindUserSwitchTransitionViewMediator(
             UserSwitchTransitionViewMediator userSwitchTransitionViewMediator);
+
+    /** Injects RearViewCameraViewMediator. */
+    @Binds
+    @IntoMap
+    @ClassKey(RearViewCameraViewMediator.class)
+    public abstract OverlayViewMediator bindRearViewCameraViewMediator(
+            RearViewCameraViewMediator overlayViewsMediator);
 }
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java
index 8873293..b22de84 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java
@@ -53,6 +53,7 @@
     private boolean mIsAttached = false;
     private boolean mVisible = false;
     private boolean mFocusable = false;
+    private boolean mUsingStableInsets = false;
 
     @Inject
     public SystemUIOverlayWindowController(
@@ -115,6 +116,7 @@
     /** Sets the types of insets to fit. Note: This should be rarely used. */
     public void setFitInsetsTypes(@WindowInsets.Type.InsetsType int types) {
         mLpChanged.setFitInsetsTypes(types);
+        mLpChanged.setFitInsetsIgnoringVisibility(mUsingStableInsets);
         updateWindow();
     }
 
@@ -159,6 +161,10 @@
         return mFocusable;
     }
 
+    protected void setUsingStableInsets(boolean useStableInsets) {
+        mUsingStableInsets = useStableInsets;
+    }
+
     private void updateWindow() {
         if (mLp != null && mLp.copyFrom(mLpChanged) != 0) {
             if (isAttached()) {
diff --git a/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMComponent.java b/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMComponent.java
new file mode 100644
index 0000000..c6a7fd2
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMComponent.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.wmshell;
+
+import com.android.systemui.dagger.WMComponent;
+import com.android.systemui.dagger.WMSingleton;
+
+import dagger.Subcomponent;
+
+
+/**
+ * Dagger Subcomponent for WindowManager.
+ */
+@WMSingleton
+@Subcomponent(modules = {CarWMShellModule.class})
+public interface CarWMComponent extends WMComponent {
+
+    /**
+     * Builder for a SysUIComponent.
+     */
+    @Subcomponent.Builder
+    interface Builder extends WMComponent.Builder {
+        CarWMComponent build();
+    }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java b/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java
index 6d31a8d..27aabff 100644
--- a/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java
@@ -20,25 +20,30 @@
 import android.os.Handler;
 import android.view.IWindowManager;
 
-import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.WMSingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.wm.DisplaySystemBarsController;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayImeController;
 import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.pip.Pip;
 
+import dagger.BindsOptionalOf;
 import dagger.Module;
 import dagger.Provides;
 
 /** Provides dependencies from {@link com.android.wm.shell} for CarSystemUI. */
 @Module(includes = WMShellBaseModule.class)
-public class CarWMShellModule {
-    @SysUISingleton
+public abstract class CarWMShellModule {
+    @WMSingleton
     @Provides
-    DisplayImeController provideDisplayImeController(Context context,
+    static DisplayImeController provideDisplayImeController(Context context,
             IWindowManager wmService, DisplayController displayController,
             @Main Handler mainHandler, TransactionPool transactionPool) {
         return new DisplaySystemBarsController(context, wmService, displayController,
                 mainHandler, transactionPool);
     }
+
+    @BindsOptionalOf
+    abstract Pip optionalPip();
 }
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/rvc/RearViewCameraViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/rvc/RearViewCameraViewControllerTest.java
new file mode 100644
index 0000000..a6160ec
--- /dev/null
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/rvc/RearViewCameraViewControllerTest.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.car.rvc;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+
+import android.app.ActivityView;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.testing.TestableLooper;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.car.CarSystemUiTest;
+import com.android.systemui.car.window.OverlayViewGlobalStateController;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@CarSystemUiTest
+@RunWith(MockitoJUnitRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class RearViewCameraViewControllerTest extends SysuiTestCase {
+    private static final String TEST_ACTIVITY_NAME = "testPackage/testActivity";
+    private RearViewCameraViewController mRearViewCameraViewController;
+
+    @Mock
+    private OverlayViewGlobalStateController mOverlayViewGlobalStateController;
+    @Mock
+    private ActivityView mMockActivityView;
+    @Captor
+    private ArgumentCaptor<Intent> mIntentCaptor;
+
+    private void setUpRearViewCameraViewController(String testActivityName) {
+        mContext.getOrCreateTestableResources().addOverride(
+                R.string.config_rearViewCameraActivity, testActivityName);
+        mRearViewCameraViewController = new RearViewCameraViewController(
+                mContext.getOrCreateTestableResources().getResources(),
+                mOverlayViewGlobalStateController);
+        mRearViewCameraViewController.inflate((ViewGroup) LayoutInflater.from(mContext).inflate(
+                R.layout.sysui_overlay_window, /* root= */ null));
+    }
+
+    @Test
+    public void testEmptyResourceDisablesController() {
+        setUpRearViewCameraViewController("");
+
+        assertThat(mRearViewCameraViewController.isEnabled()).isFalse();
+    }
+
+    @Test
+    public void testNonEmptyResourceEnablesController() {
+        setUpRearViewCameraViewController(TEST_ACTIVITY_NAME);
+
+        assertThat(mRearViewCameraViewController.isEnabled()).isTrue();
+    }
+
+    @Test
+    public void testShowInternal() {
+        setUpRearViewCameraViewController(TEST_ACTIVITY_NAME);
+        assertThat(mRearViewCameraViewController.isShown()).isFalse();
+        assertThat(mRearViewCameraViewController.mActivityView).isNull();
+
+        mRearViewCameraViewController.showInternal();
+
+        assertThat(mRearViewCameraViewController.isShown()).isTrue();
+        assertThat(mRearViewCameraViewController.mActivityView).isNotNull();
+    }
+
+    @Test
+    public void testHideInternal() {
+        setUpRearViewCameraViewController(TEST_ACTIVITY_NAME);
+        assertThat(mRearViewCameraViewController.isShown()).isFalse();
+        mRearViewCameraViewController.showInternal();
+        assertThat(mRearViewCameraViewController.isShown()).isTrue();
+
+        mRearViewCameraViewController.hideInternal();
+
+        assertThat(mRearViewCameraViewController.isShown()).isFalse();
+        assertThat(mRearViewCameraViewController.mActivityView).isNull();
+    }
+
+    @Test
+    public void testOnActivityViewReady_fireIntent() {
+        setUpRearViewCameraViewController(TEST_ACTIVITY_NAME);
+        mRearViewCameraViewController.mActivityViewCallback.onActivityViewReady(mMockActivityView);
+
+        verify(mMockActivityView).startActivity(mIntentCaptor.capture());
+        ComponentName expectedComponent = ComponentName.unflattenFromString(TEST_ACTIVITY_NAME);
+        assertThat(mIntentCaptor.getValue().getComponent()).isEqualTo(expectedComponent);
+    }
+}
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/rvc/RearViewCameraViewMediatorTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/rvc/RearViewCameraViewMediatorTest.java
new file mode 100644
index 0000000..5be8f91
--- /dev/null
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/rvc/RearViewCameraViewMediatorTest.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.car.rvc;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.car.Car;
+import android.car.VehicleAreaType;
+import android.car.VehicleGear;
+import android.car.VehiclePropertyIds;
+import android.car.hardware.CarPropertyValue;
+import android.car.hardware.property.CarPropertyManager;
+import android.car.hardware.property.CarPropertyManager.CarPropertyEventCallback;
+import android.content.BroadcastReceiver;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.testing.TestableLooper;
+import android.util.Log;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.car.CarServiceProvider;
+import com.android.systemui.car.CarServiceProvider.CarServiceOnConnectedListener;
+import com.android.systemui.car.CarSystemUiTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@CarSystemUiTest
+@RunWith(MockitoJUnitRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class RearViewCameraViewMediatorTest extends SysuiTestCase {
+    private static final String TAG = RearViewCameraViewMediatorTest.class.getSimpleName();
+
+    private RearViewCameraViewMediator mRearViewCameraViewMediator;
+
+    @Mock
+    private CarServiceProvider mCarServiceProvider;
+    @Mock
+    private Car mCar;
+    @Mock
+    private CarPropertyManager mCarPropertyManager;
+    @Captor
+    private ArgumentCaptor<CarPropertyEventCallback> mCarPropertyEventCallbackCaptor;
+
+    @Mock
+    private BroadcastDispatcher mBroadcastDispatcher;
+    @Captor
+    private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor;
+    @Captor
+    private ArgumentCaptor<IntentFilter> mIntentFilterCaptor;
+
+    @Mock
+    private RearViewCameraViewController mRearViewCameraViewController;
+
+    @Before
+    public void setUp() throws Exception {
+        mRearViewCameraViewMediator = new RearViewCameraViewMediator(
+                mRearViewCameraViewController, mCarServiceProvider, mBroadcastDispatcher);
+    }
+
+    public void setUpListener() {
+        doAnswer(invocation -> {
+            CarServiceOnConnectedListener listener = invocation.getArgument(0);
+            listener.onConnected(mCar);
+            return null;
+        }).when(mCarServiceProvider).addListener(any(CarServiceOnConnectedListener.class));
+        when(mCar.getCarManager(Car.PROPERTY_SERVICE)).thenReturn(mCarPropertyManager);
+        when(mRearViewCameraViewController.isEnabled()).thenReturn(true);
+
+        mRearViewCameraViewMediator.registerListeners();
+
+        verify(mCarPropertyManager).registerCallback(mCarPropertyEventCallbackCaptor.capture(),
+                eq(VehiclePropertyIds.GEAR_SELECTION), anyFloat());
+        verify(mBroadcastDispatcher).registerReceiver(mBroadcastReceiverCaptor.capture(),
+                mIntentFilterCaptor.capture(), any(), any());
+        assertThat(mIntentFilterCaptor.getValue().getAction(0)).isEqualTo(
+                Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+    }
+
+    @Test
+    public void testDoesnNotRegisterListenersWhenRearViewCameraViewControllerIsDisabled() {
+        when(mRearViewCameraViewController.isEnabled()).thenReturn(false);
+
+        mRearViewCameraViewMediator.registerListeners();
+
+        verify(mCarPropertyManager, never()).registerCallback(any(), anyInt(), anyFloat());
+        verify(mBroadcastDispatcher, never()).registerReceiver(any(), any(), any());
+    }
+
+    @Test
+    public void testGearReverseStartsRearViewCamera() {
+        setUpListener();
+
+        CarPropertyValue<Integer> gearReverse = new CarPropertyValue(
+                VehiclePropertyIds.GEAR_SELECTION, VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL,
+                VehicleGear.GEAR_REVERSE);
+        mCarPropertyEventCallbackCaptor.getValue().onChangeEvent(gearReverse);
+
+        verify(mRearViewCameraViewController, times(1)).start();
+    }
+
+    @Test
+    public void testGearNonReverseStopsRearViewCamera() {
+        setUpListener();
+
+        int[] nonReverseVehicleGears = new int[]{
+                VehicleGear.GEAR_NEUTRAL, VehicleGear.GEAR_PARK, VehicleGear.GEAR_DRIVE,
+                VehicleGear.GEAR_FIRST
+        };
+        for (int i = 0; i < nonReverseVehicleGears.length; ++i) {
+            Log.i(TAG, "testGearNonReverseStopsRearViewCamera: gear=" + nonReverseVehicleGears[i]);
+            CarPropertyValue<Integer> propertyGear = new CarPropertyValue(
+                    VehiclePropertyIds.GEAR_SELECTION, VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL,
+                    nonReverseVehicleGears[i]);
+            mCarPropertyEventCallbackCaptor.getValue().onChangeEvent(propertyGear);
+
+            verify(mRearViewCameraViewController, times(i + 1)).stop();
+        }
+    }
+
+    @Test
+    public void testBroadcastIntentStopsRearViewCamera() {
+        setUpListener();
+        when(mRearViewCameraViewController.isShown()).thenReturn(true);
+
+        Intent randomIntent = new Intent(Intent.ACTION_MAIN);
+        mBroadcastReceiverCaptor.getValue().onReceive(mContext, randomIntent);
+
+        verify(mRearViewCameraViewController, never()).stop();
+
+        Intent actionCloseSystemDialogs = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+        mBroadcastReceiverCaptor.getValue().onReceive(mContext, actionCloseSystemDialogs);
+
+        verify(mRearViewCameraViewController, times(1)).stop();
+    }
+}
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
index 9ff8684..f7f3cbb 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
@@ -321,6 +321,9 @@
 
         if (!isDynamicSystemInstalled() && (getStatus() != STATUS_READY)) {
             Log.e(TAG, "Trying to discard AOT while there is no complete installation");
+            // Stop foreground state and dismiss stale notification.
+            stopForeground(STOP_FOREGROUND_REMOVE);
+            resetTaskAndStop();
             return;
         }
 
diff --git a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
index 900e68d..6827d6e 100644
--- a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
+++ b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
@@ -19,6 +19,9 @@
 import static android.content.Intent.ACTION_USER_SWITCHED;
 import static android.location.LocationManager.GPS_PROVIDER;
 import static android.location.LocationManager.NETWORK_PROVIDER;
+import static android.location.LocationRequest.QUALITY_LOW_POWER;
+
+import static com.android.location.provider.ProviderRequestUnbundled.INTERVAL_DISABLED;
 
 import android.annotation.Nullable;
 import android.content.BroadcastReceiver;
@@ -35,7 +38,6 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.location.ProviderRequest;
 import com.android.location.provider.LocationProviderBase;
-import com.android.location.provider.LocationRequestUnbundled;
 import com.android.location.provider.ProviderPropertiesUnbundled;
 import com.android.location.provider.ProviderRequestUnbundled;
 
@@ -147,8 +149,8 @@
 
         mRequest = new ProviderRequestUnbundled(ProviderRequest.EMPTY_REQUEST);
         mWorkSource = new WorkSource();
-        mGpsInterval = Long.MAX_VALUE;
-        mNetworkInterval = Long.MAX_VALUE;
+        mGpsInterval = INTERVAL_DISABLED;
+        mNetworkInterval = INTERVAL_DISABLED;
     }
 
     void start() {
@@ -175,30 +177,9 @@
 
     @GuardedBy("mLock")
     private void updateRequirementsLocked() {
-        long gpsInterval = Long.MAX_VALUE;
-        long networkInterval = Long.MAX_VALUE;
-        if (mRequest.getReportLocation()) {
-            for (LocationRequestUnbundled request : mRequest.getLocationRequests()) {
-                switch (request.getQuality()) {
-                    case LocationRequestUnbundled.ACCURACY_FINE:
-                    case LocationRequestUnbundled.ACCURACY_BLOCK:
-                    case LocationRequestUnbundled.POWER_HIGH:
-                        if (request.getInterval() < gpsInterval) {
-                            gpsInterval = request.getInterval();
-                        }
-                        if (request.getInterval() < networkInterval) {
-                            networkInterval = request.getInterval();
-                        }
-                        break;
-                    case LocationRequestUnbundled.ACCURACY_CITY:
-                    case LocationRequestUnbundled.POWER_LOW:
-                        if (request.getInterval() < networkInterval) {
-                            networkInterval = request.getInterval();
-                        }
-                        break;
-                }
-            }
-        }
+        long gpsInterval = mRequest.getQuality() < QUALITY_LOW_POWER ? mRequest.getInterval()
+                : INTERVAL_DISABLED;
+        long networkInterval = mRequest.getInterval();
 
         if (gpsInterval != mGpsInterval) {
             resetProviderRequestLocked(GPS_PROVIDER, mGpsInterval, gpsInterval, mGpsListener);
@@ -214,11 +195,12 @@
     @GuardedBy("mLock")
     private void resetProviderRequestLocked(String provider, long oldInterval, long newInterval,
             LocationListener listener) {
-        if (oldInterval != Long.MAX_VALUE) {
+        if (oldInterval != INTERVAL_DISABLED && newInterval == INTERVAL_DISABLED) {
             mLocationManager.removeUpdates(listener);
         }
-        if (newInterval != Long.MAX_VALUE) {
+        if (newInterval != INTERVAL_DISABLED) {
             LocationRequest request = new LocationRequest.Builder(newInterval)
+                    .setQuality(mRequest.getQuality())
                     .setLocationSettingsIgnored(mRequest.isLocationSettingsIgnored())
                     .setWorkSource(mWorkSource)
                     .build();
@@ -254,10 +236,10 @@
     void dump(PrintWriter writer) {
         synchronized (mLock) {
             writer.println("request: " + mRequest);
-            if (mGpsInterval != Long.MAX_VALUE) {
+            if (mGpsInterval != INTERVAL_DISABLED) {
                 writer.println("  gps interval: " + mGpsInterval);
             }
-            if (mNetworkInterval != Long.MAX_VALUE) {
+            if (mNetworkInterval != INTERVAL_DISABLED) {
                 writer.println("  network interval: " + mNetworkInterval);
             }
             if (mGpsLocation != null) {
diff --git a/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java b/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java
index d3aa977..61349d9 100644
--- a/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java
+++ b/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java
@@ -48,7 +48,6 @@
 import java.io.ByteArrayOutputStream;
 import java.io.FileInputStream;
 import java.io.IOException;
-import java.util.Collections;
 import java.util.Random;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
@@ -60,7 +59,6 @@
 
     private static final long TIMEOUT_MS = 5000;
 
-    private Context mContext;
     private Random mRandom;
     private LocationManager mLocationManager;
 
@@ -72,15 +70,15 @@
         long seed = System.currentTimeMillis();
         Log.i(TAG, "location seed: " + seed);
 
-        mContext = InstrumentationRegistry.getTargetContext();
+        Context context = InstrumentationRegistry.getTargetContext();
         mRandom = new Random(seed);
-        mLocationManager = mContext.getSystemService(LocationManager.class);
+        mLocationManager = context.getSystemService(LocationManager.class);
 
         setMockLocation(true);
 
         mManager = new LocationProviderManagerCapture();
         mProvider = ILocationProvider.Stub.asInterface(
-                new FusedLocationProvider(mContext).getBinder());
+                new FusedLocationProvider(context).getBinder());
         mProvider.setLocationProviderManager(mManager);
 
         mLocationManager.addTestProvider(NETWORK_PROVIDER,
@@ -118,12 +116,9 @@
 
     @Test
     public void testNetworkRequest() throws Exception {
-        LocationRequest request = new LocationRequest.Builder(1000).build();
-
         mProvider.setRequest(
                         new ProviderRequest.Builder()
                                 .setIntervalMillis(1000)
-                                .setLocationRequests(Collections.singletonList(request))
                                 .build(),
                 new WorkSource());
 
@@ -135,14 +130,10 @@
 
     @Test
     public void testGpsRequest() throws Exception {
-        LocationRequest request = new LocationRequest.Builder(1000)
-                .setQuality(LocationRequest.POWER_HIGH)
-                .build();
-
         mProvider.setRequest(
                 new ProviderRequest.Builder()
+                        .setQuality(LocationRequest.QUALITY_HIGH_ACCURACY)
                         .setIntervalMillis(1000)
-                        .setLocationRequests(Collections.singletonList(request))
                         .build(),
                 new WorkSource());
 
diff --git a/packages/InputDevices/res/raw/keyboard_layout_turkish_f.kcm b/packages/InputDevices/res/raw/keyboard_layout_turkish_f.kcm
new file mode 100644
index 0000000..5b96da0
--- /dev/null
+++ b/packages/InputDevices/res/raw/keyboard_layout_turkish_f.kcm
@@ -0,0 +1,366 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Turkish F keyboard layout.
+#
+
+type OVERLAY
+
+map key 12 SLASH
+map key 13 MINUS
+map key 43 COMMA
+map key 51 EQUALS
+map key 52 BACKSLASH
+map key 53 PERIOD
+map key 86 PLUS
+
+### ROW 1
+
+key GRAVE {
+    label:                              '+'
+    base:                               '+'
+    shift:                              '*'
+    ralt:                               '\u00ac'
+}
+
+key 1 {
+    label:                              '1'
+    base:                               '1'
+    shift:                              '!'
+    ralt:                               '\u00b9'
+}
+
+key 2 {
+    label:                              '2'
+    base:                               '2'
+    shift:                              '"'
+    ralt:                               '\u00b2'
+}
+
+key 3 {
+    label:                              '3'
+    base:                               '3'
+    shift:                              '^'
+    ralt:                               '#'
+}
+
+key 4 {
+    label:                              '4'
+    base:                               '4'
+    shift:                              '$'
+    ralt:                               '\u00bc'
+}
+
+key 5 {
+    label:                              '5'
+    base:                               '5'
+    shift:                              '%'
+    ralt:                               '\u00bd'
+}
+
+key 6 {
+    label:                              '6'
+    base:                               '6'
+    shift:                              '&'
+    ralt:                               '\u00be'
+}
+
+key 7 {
+    label:                              '7'
+    base:                               '7'
+    shift:                              '\''
+    ralt:                               '{'
+}
+
+key 8 {
+    label:                              '8'
+    base:                               '8'
+    shift:                              '('
+    ralt:                               '['
+}
+
+key 9 {
+    label:                              '9'
+    base:                               '9'
+    shift:                              ')'
+    ralt:                               ']'
+}
+
+key 0 {
+    label:                              '0'
+    base:                               '0'
+    shift:                              '='
+    ralt:                               '}'
+}
+
+key SLASH {
+    label:                              '/'
+    base:                               '/'
+    shift:                              '?'
+    ralt:                               '\\'
+}
+
+key MINUS {
+    label:                              '-'
+    base:                               '-'
+    shift:                              '_'
+    ralt:                               '|'
+}
+
+### ROW 2
+
+key Q {
+    label:                              'F'
+    base:                               'f'
+    shift, capslock:                    'F'
+    ralt:                               '@'
+}
+
+key W {
+    label:                              'G'
+    base:                               'g'
+    shift, capslock:                    'G'
+}
+
+key E {
+    label:                              '\u011f'
+    base:                               '\u011f'
+    shift, capslock:                    '\u011e'
+}
+
+key R {
+    label:                              '\u0131'
+    base:                               '\u0131'
+    shift, capslock:                    'I'
+    ralt:                               '\u00b6'
+    ralt+shift, ralt+capslock:          '\u00ae'
+}
+
+key T {
+    label:                              'O'
+    base:                               'o'
+    shift, capslock:                    'O'
+}
+
+key Y {
+    label:                              'D'
+    base:                               'd'
+    shift, capslock:                    'D'
+    ralt:                               '\u00a5'
+}
+
+key U {
+    label:                              'R'
+    base:                               'r'
+    shift, capslock:                    'R'
+}
+
+key I {
+    label:                              'N'
+    base:                               'n'
+    shift, capslock:                    'N'
+}
+
+key O {
+    label:                              'H'
+    base:                               'h'
+    shift, capslock:                    'H'
+    ralt:                               '\u00f8'
+    ralt+shift, ralt+capslock:          '\u00d8'
+}
+
+key P {
+    label:                              'P'
+    base:                               'p'
+    shift, capslock:                    'P'
+    ralt:                               '\u00a3'
+}
+
+key LEFT_BRACKET {
+    label:                              'Q'
+    base:                               'q'
+    shift, capslock:                    'Q'
+    ralt:                               '"'
+}
+
+key RIGHT_BRACKET {
+    label:                              'W'
+    base:                               'w'
+    shift, capslock:                    'W'
+    ralt:                               '~'
+}
+
+### ROW 3
+
+key A {
+    label:                              '\u0075'
+    base:                               '\u0075'
+    shift, capslock:                    '\u0055'
+    ralt:                               '\u00e6'
+    ralt+shift, ralt+capslock:          '\u00c6'
+}
+
+key S {
+    label:                              'i'
+    base:                               'i'
+    shift, capslock:                    '\u0130'
+    ralt:                               '\u00df'
+    ralt+shift, ralt+capslock:          '\u00a7'
+}
+
+key D {
+    label:                              'E'
+    base:                               'e'
+    shift, capslock:                    'E'
+    ralt:                               '\u20ac'
+}
+
+key F {
+    label:                              'A'
+    base:                               'a'
+    shift, capslock:                    'A'
+    ralt:                               '\u00aa'
+}
+
+key G {
+    label:                              '\u00fc'
+    base:                               '\u00fc'
+    shift, capslock:                    '\u00dc'
+}
+
+key H {
+    label:                              'T'
+    base:                               't'
+    shift, capslock:                    'T'
+    ralt:                               '\u20ba'
+}
+
+key J {
+    label:                              'K'
+    base:                               'k'
+    shift, capslock:                    'K'
+}
+
+key K {
+    label:                              'M'
+    base:                               'm'
+    shift, capslock:                    'M'
+}
+
+key L {
+    label:                              'L'
+    base:                               'l'
+    shift, capslock:                    'L'
+}
+
+key SEMICOLON {
+    label:                              'Y'
+    base:                               'y'
+    shift, capslock:                    'Y'
+    ralt:                               '\u00b4'
+}
+
+key APOSTROPHE {
+    label:                              '\u015f'
+    base:                               '\u015f'
+    shift, capslock:                    '\u015e'
+}
+
+key COMMA {
+    label:                              'X'
+    base:                               'x'
+    shift:                              'X'
+    ralt:                               '\u0060'
+}
+
+### ROW 4
+
+key PLUS {
+    label:                              '<'
+    base:                               '<'
+    shift:                              '>'
+    ralt:                               '|'
+    ralt+shift, ralt+capslock:          '\u00a6'
+}
+
+key Z {
+    label:                              'J'
+    base:                               'j'
+    shift, capslock:                    'J'
+    ralt:                               '\u00ab'
+    ralt+shift, ralt+capslock:          '<'
+}
+
+key X {
+    label:                              '\u00f6'
+    base:                               '\u00f6'
+    shift, capslock:                    '\u00d6'
+    ralt:                               '\u00bb'
+    ralt+shift, ralt+capslock:          '>'
+}
+
+key C {
+    label:                              'V'
+    base:                               'v'
+    shift, capslock:                    'V'
+    ralt:                               '\u00a2'
+    ralt+shift, ralt+capslock:          '\u00a9'
+}
+
+key V {
+    label:                              'C'
+    base:                               'c'
+    shift, capslock:                    'C'
+}
+
+key B {
+    label:                              '\u00e7'
+    base:                               '\u00e7'
+    shift, capslock:                    '\u00c7'
+}
+
+key N {
+    label:                              'Z'
+    base:                               'z'
+    shift, capslock:                    'Z'
+}
+
+key M {
+    label:                              'S'
+    base:                               's'
+    shift, capslock:                    'S'
+    ralt:                               '\u00b5'
+    ralt+shift, ralt+capslock:          '\u00ba'
+}
+
+key EQUALS {
+    label:                              'B'
+    base:                               'b'
+    shift, capslock:                    'B'
+    ralt:                               '\u00d7'
+}
+
+key BACKSLASH {
+    label:                              '.'
+    base:                               '.'
+    shift, capslock:                    ':'
+    ralt:                               '\u00f7'
+}
+
+key PERIOD {
+    label:                              ','
+    base:                               ','
+    shift:                              ';'
+}
diff --git a/packages/InputDevices/res/values-af/strings.xml b/packages/InputDevices/res/values-af/strings.xml
index 462a6a9..7208894 100644
--- a/packages/InputDevices/res/values-af/strings.xml
+++ b/packages/InputDevices/res/values-af/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slowaaks"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Sloweens"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turks"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turks-F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Oekraïens"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabies"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Grieks"</string>
diff --git a/packages/InputDevices/res/values-am/strings.xml b/packages/InputDevices/res/values-am/strings.xml
index 1559fa8..ff9f652 100644
--- a/packages/InputDevices/res/values-am/strings.xml
+++ b/packages/InputDevices/res/values-am/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"ስሎቫክ"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"ስሎቫኒያ"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"ቱርክኛ"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"ቱርክኛ F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ዩክሬን"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"አረብኛ"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"ግሪክኛ"</string>
diff --git a/packages/InputDevices/res/values-ar/strings.xml b/packages/InputDevices/res/values-ar/strings.xml
index bf508b2..8ed1972 100644
--- a/packages/InputDevices/res/values-ar/strings.xml
+++ b/packages/InputDevices/res/values-ar/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"السلوفاكية"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"السلوفينية"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"التركية"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"‏لوحة المفاتيح باللغة التركية F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"الأوكرانية"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"العربية"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"اليونانية"</string>
diff --git a/packages/InputDevices/res/values-as/strings.xml b/packages/InputDevices/res/values-as/strings.xml
index 49fbef9..9744a7d 100644
--- a/packages/InputDevices/res/values-as/strings.xml
+++ b/packages/InputDevices/res/values-as/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"শ্ল\'ভাক"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"শ্ল\'ভেনিয়া"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"তুৰ্কী"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"টুৰ্কিছ F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ইউক্ৰেনিয়ান"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"আৰবী"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"গ্ৰীক"</string>
diff --git a/packages/InputDevices/res/values-az/strings.xml b/packages/InputDevices/res/values-az/strings.xml
index c5a1e1e..ee3a337 100644
--- a/packages/InputDevices/res/values-az/strings.xml
+++ b/packages/InputDevices/res/values-az/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovak"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Sloven"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Türk"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Türkcə F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrayna"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Ərəb"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Yunan"</string>
diff --git a/packages/InputDevices/res/values-b+sr+Latn/strings.xml b/packages/InputDevices/res/values-b+sr+Latn/strings.xml
index 16f1cb2..1fc84f3 100644
--- a/packages/InputDevices/res/values-b+sr+Latn/strings.xml
+++ b/packages/InputDevices/res/values-b+sr+Latn/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"slovačka"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"slovenačka"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"turska"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"turska F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukrajinska"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"arapski"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"grčki"</string>
diff --git a/packages/InputDevices/res/values-be/strings.xml b/packages/InputDevices/res/values-be/strings.xml
index 6b0523f..50c5910 100644
--- a/packages/InputDevices/res/values-be/strings.xml
+++ b/packages/InputDevices/res/values-be/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Славацкая"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Славенская"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Турэцкая"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Турэцкая-F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Украінская"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Арабская"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Грэчаская"</string>
diff --git a/packages/InputDevices/res/values-bg/strings.xml b/packages/InputDevices/res/values-bg/strings.xml
index a7088c9..5c25f97 100644
--- a/packages/InputDevices/res/values-bg/strings.xml
+++ b/packages/InputDevices/res/values-bg/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"словашки"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"словенски"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"турски"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Турски (тип F)"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"украински"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Арабска клавиатурна подредба"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Гръцка клавиатурна подредба"</string>
diff --git a/packages/InputDevices/res/values-bn/strings.xml b/packages/InputDevices/res/values-bn/strings.xml
index f387414..a996538 100644
--- a/packages/InputDevices/res/values-bn/strings.xml
+++ b/packages/InputDevices/res/values-bn/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"স্লোভাক"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"স্লোভেনিয়ান"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"তুর্কি"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkish F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ইউক্রেনীয়"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"আরবি"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"গ্রীক"</string>
diff --git a/packages/InputDevices/res/values-bs/strings.xml b/packages/InputDevices/res/values-bs/strings.xml
index b92ac8c..df58464 100644
--- a/packages/InputDevices/res/values-bs/strings.xml
+++ b/packages/InputDevices/res/values-bs/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"slovački"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"slovenački"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"turski"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"turski F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukrajinski"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"arapski"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"grčki"</string>
diff --git a/packages/InputDevices/res/values-ca/strings.xml b/packages/InputDevices/res/values-ca/strings.xml
index a5b5e10..761c248 100644
--- a/packages/InputDevices/res/values-ca/strings.xml
+++ b/packages/InputDevices/res/values-ca/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Eslovac"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Eslovè"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turc"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turc F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ucraïnès"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Àrab"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Grec"</string>
diff --git a/packages/InputDevices/res/values-cs/strings.xml b/packages/InputDevices/res/values-cs/strings.xml
index 6b4f7ebf..3f1b3d0 100644
--- a/packages/InputDevices/res/values-cs/strings.xml
+++ b/packages/InputDevices/res/values-cs/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"slovenské"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"slovinské"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"turecké"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"turečtina F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukrajinské"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"arabština"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"řečtina"</string>
diff --git a/packages/InputDevices/res/values-da/strings.xml b/packages/InputDevices/res/values-da/strings.xml
index cf2aecf..b160341 100644
--- a/packages/InputDevices/res/values-da/strings.xml
+++ b/packages/InputDevices/res/values-da/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovakisk"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovensk"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Tyrkisk"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Tyrkisk F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainsk"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabisk"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Græsk"</string>
diff --git a/packages/InputDevices/res/values-de/strings.xml b/packages/InputDevices/res/values-de/strings.xml
index 1e78685..95bd806 100644
--- a/packages/InputDevices/res/values-de/strings.xml
+++ b/packages/InputDevices/res/values-de/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slowakisch"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slowenisch"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Türkisch"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Türkisch F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainisch"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabisch"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Griechisch"</string>
diff --git a/packages/InputDevices/res/values-el/strings.xml b/packages/InputDevices/res/values-el/strings.xml
index eb2cc9b..1b9b42e 100644
--- a/packages/InputDevices/res/values-el/strings.xml
+++ b/packages/InputDevices/res/values-el/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Σλοβακικά"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Σλοβενικά"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Τουρκικά"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Τουρκικά F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ουκρανικά"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Αραβικά"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Ελληνικά"</string>
diff --git a/packages/InputDevices/res/values-en-rAU/strings.xml b/packages/InputDevices/res/values-en-rAU/strings.xml
index f79c3da..ab48729 100644
--- a/packages/InputDevices/res/values-en-rAU/strings.xml
+++ b/packages/InputDevices/res/values-en-rAU/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovak"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovenian"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turkish"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkish F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainian"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabic"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Greek"</string>
diff --git a/packages/InputDevices/res/values-en-rCA/strings.xml b/packages/InputDevices/res/values-en-rCA/strings.xml
index f79c3da..ab48729 100644
--- a/packages/InputDevices/res/values-en-rCA/strings.xml
+++ b/packages/InputDevices/res/values-en-rCA/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovak"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovenian"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turkish"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkish F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainian"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabic"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Greek"</string>
diff --git a/packages/InputDevices/res/values-en-rGB/strings.xml b/packages/InputDevices/res/values-en-rGB/strings.xml
index f79c3da..ab48729 100644
--- a/packages/InputDevices/res/values-en-rGB/strings.xml
+++ b/packages/InputDevices/res/values-en-rGB/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovak"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovenian"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turkish"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkish F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainian"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabic"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Greek"</string>
diff --git a/packages/InputDevices/res/values-en-rIN/strings.xml b/packages/InputDevices/res/values-en-rIN/strings.xml
index f79c3da..ab48729 100644
--- a/packages/InputDevices/res/values-en-rIN/strings.xml
+++ b/packages/InputDevices/res/values-en-rIN/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovak"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovenian"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turkish"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkish F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainian"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabic"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Greek"</string>
diff --git a/packages/InputDevices/res/values-en-rXC/strings.xml b/packages/InputDevices/res/values-en-rXC/strings.xml
index d088186..92c5a5c 100644
--- a/packages/InputDevices/res/values-en-rXC/strings.xml
+++ b/packages/InputDevices/res/values-en-rXC/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‎‏‎‎‎‏‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‏‏‏‎‏‏‎‏‎‎‏‏‎‏‏‎‏‏‎‏‎‏‎‏‏‎‎‎‎‎Slovak‎‏‎‎‏‎"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‎‎‎‏‎‏‏‏‎‏‎‎‎‏‎‏‏‏‏‎‎‏‎‎‎‏‏‏‎‏‏‎‏‏‏‏‎‎‎‏‎‎‎‎‎‏‎‎‎‏‏‎‎‎‎‎‎Slovenian‎‏‎‎‏‎"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‏‏‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‏‏‎‏‎‎‎‏‏‏‏‎‏‏‎‎‏‏‎‎‏‏‏‎‎‏‎‏‏‏‏‎‎‎‏‎‎Turkish‎‏‎‎‏‎"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‏‎‏‏‎‏‎‏‎‎‏‏‏‎‎‎‏‏‎‏‎‏‎‏‏‏‏‎‏‏‏‎‎‏‏‎‏‏‏‎‏‏‏‎‏‎‎‏‎‎Turkish F‎‏‎‎‏‎"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‏‏‎‎‏‎‏‎‎‎‎‎‎‏‎‏‏‎‏‎‎‏‏‏‏‎‎‏‏‎‏‏‎‎‎‏‎‏‏‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎Ukrainian‎‏‎‎‏‎"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‎‏‏‎‏‏‎‏‏‏‎‎‎‏‏‎‎‎‏‎‏‏‎‎‎‏‏‎‎‎‏‎‏‎‎‎‎‏‎‏‏‎‏‏‎‎‏‏‎‎‎‏‎‎‎‎Arabic‎‏‎‎‏‎"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‏‎‏‎‎‎‏‎‏‎‎‎‎‏‎‏‏‏‏‏‏‎‎‎‏‎‏‎‏‏‎‎‏‏‏‎‏‏‎‎‎‎‎‎‎‎‎‏‏‏‏‎‎‎‎Greek‎‏‎‎‏‎"</string>
diff --git a/packages/InputDevices/res/values-es-rUS/strings.xml b/packages/InputDevices/res/values-es-rUS/strings.xml
index e8d6597..b9fe046 100644
--- a/packages/InputDevices/res/values-es-rUS/strings.xml
+++ b/packages/InputDevices/res/values-es-rUS/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Eslovaco"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Esloveno"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turco"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turco F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ucraniano"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Árabe"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Griego"</string>
diff --git a/packages/InputDevices/res/values-es/strings.xml b/packages/InputDevices/res/values-es/strings.xml
index 9396e46..77b896b 100644
--- a/packages/InputDevices/res/values-es/strings.xml
+++ b/packages/InputDevices/res/values-es/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Eslovaco"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Esloveno"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turco"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turco F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ucraniano"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Árabe"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Griego"</string>
diff --git a/packages/InputDevices/res/values-et/strings.xml b/packages/InputDevices/res/values-et/strings.xml
index cf28e9f..c835522 100644
--- a/packages/InputDevices/res/values-et/strings.xml
+++ b/packages/InputDevices/res/values-et/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovaki"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Sloveenia"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Türgi"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Türgi F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukraina"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Araabia"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Kreeka"</string>
diff --git a/packages/InputDevices/res/values-eu/strings.xml b/packages/InputDevices/res/values-eu/strings.xml
index 1e080fc..77d252d 100644
--- a/packages/InputDevices/res/values-eu/strings.xml
+++ b/packages/InputDevices/res/values-eu/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Eslovakiarra"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Esloveniarra"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turkiarra"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkiarra (F teklatua)"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainarra"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabiarra"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Greziarra"</string>
diff --git a/packages/InputDevices/res/values-fa/strings.xml b/packages/InputDevices/res/values-fa/strings.xml
index 5cb237e..a086060 100644
--- a/packages/InputDevices/res/values-fa/strings.xml
+++ b/packages/InputDevices/res/values-fa/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"اسلوواکی"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"اسلوونیایی"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"ترکی"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkish F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"اوکراینی"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"عربی"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"یونانی"</string>
diff --git a/packages/InputDevices/res/values-fi/strings.xml b/packages/InputDevices/res/values-fi/strings.xml
index da72106..a20416f 100644
--- a/packages/InputDevices/res/values-fi/strings.xml
+++ b/packages/InputDevices/res/values-fi/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"slovakki"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"sloveeni"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"turkki"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"turkki (F)"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukraina"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"arabia"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"kreikka"</string>
diff --git a/packages/InputDevices/res/values-fr-rCA/strings.xml b/packages/InputDevices/res/values-fr-rCA/strings.xml
index 45aca35..63635824 100644
--- a/packages/InputDevices/res/values-fr-rCA/strings.xml
+++ b/packages/InputDevices/res/values-fr-rCA/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovaque"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovène"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turc"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turc F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainien"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabe"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Grec"</string>
diff --git a/packages/InputDevices/res/values-fr/strings.xml b/packages/InputDevices/res/values-fr/strings.xml
index b55a3c9..0e41a2e 100644
--- a/packages/InputDevices/res/values-fr/strings.xml
+++ b/packages/InputDevices/res/values-fr/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovaque"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovène"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turc"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Clavier turc en F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainien"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabe"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Grec"</string>
diff --git a/packages/InputDevices/res/values-gl/strings.xml b/packages/InputDevices/res/values-gl/strings.xml
index 40ede04..9995f79 100644
--- a/packages/InputDevices/res/values-gl/strings.xml
+++ b/packages/InputDevices/res/values-gl/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Eslovaco"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Esloveno"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turco"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turco F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ucraíno"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Árabe"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Grego"</string>
diff --git a/packages/InputDevices/res/values-gu/strings.xml b/packages/InputDevices/res/values-gu/strings.xml
index 631fc14..de50fb9 100644
--- a/packages/InputDevices/res/values-gu/strings.xml
+++ b/packages/InputDevices/res/values-gu/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"સ્લોવૅક"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"સ્લોવેનિયન"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"ટર્કીશ"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"ટર્કિશ F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"યુક્રેનિયન"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"અરબી"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"ગ્રીક"</string>
diff --git a/packages/InputDevices/res/values-hi/strings.xml b/packages/InputDevices/res/values-hi/strings.xml
index 7d1e2f8..55fc5bf 100644
--- a/packages/InputDevices/res/values-hi/strings.xml
+++ b/packages/InputDevices/res/values-hi/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"स्लोवाक"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"स्लोवेनियाई"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"तुर्की"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkish F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"यूक्रेनियाई"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"अरबी"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"ग्रीक"</string>
diff --git a/packages/InputDevices/res/values-hr/strings.xml b/packages/InputDevices/res/values-hr/strings.xml
index aff2a37..6832437 100644
--- a/packages/InputDevices/res/values-hr/strings.xml
+++ b/packages/InputDevices/res/values-hr/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"slovačka"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"slovenska"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"turska"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"turski F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukrajinska"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"arapski"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"grčki"</string>
diff --git a/packages/InputDevices/res/values-hu/strings.xml b/packages/InputDevices/res/values-hu/strings.xml
index 50d667b4..f2ac8a2 100644
--- a/packages/InputDevices/res/values-hu/strings.xml
+++ b/packages/InputDevices/res/values-hu/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"szlovák"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"szlovén"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"török"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"török F-billentyűzet"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukrán"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"arab"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"görög"</string>
diff --git a/packages/InputDevices/res/values-hy/strings.xml b/packages/InputDevices/res/values-hy/strings.xml
index 4a6fe2b..a6676fe 100644
--- a/packages/InputDevices/res/values-hy/strings.xml
+++ b/packages/InputDevices/res/values-hy/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Սլովակերեն"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Սլովեներեն"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Թուրքերեն"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"թուրքերեն F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ուկրաիներեն"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Արաբերեն"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Հունարեն"</string>
diff --git a/packages/InputDevices/res/values-in/strings.xml b/packages/InputDevices/res/values-in/strings.xml
index 90ba97d..41bf2de 100644
--- a/packages/InputDevices/res/values-in/strings.xml
+++ b/packages/InputDevices/res/values-in/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovakia"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovenia"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turki"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turki F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukraina"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arab"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Yunani"</string>
diff --git a/packages/InputDevices/res/values-is/strings.xml b/packages/InputDevices/res/values-is/strings.xml
index 0889b21..b761a7e 100644
--- a/packages/InputDevices/res/values-is/strings.xml
+++ b/packages/InputDevices/res/values-is/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slóvaskt"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slóvenskt"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Tyrkneskt"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Tyrkneskt F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Úkranískt"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabískt"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Grískt"</string>
diff --git a/packages/InputDevices/res/values-it/strings.xml b/packages/InputDevices/res/values-it/strings.xml
index 77f78c6..ed1ec55 100644
--- a/packages/InputDevices/res/values-it/strings.xml
+++ b/packages/InputDevices/res/values-it/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovacco"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Sloveno"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turco"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turco F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ucraino"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabo"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Greco"</string>
diff --git a/packages/InputDevices/res/values-iw/strings.xml b/packages/InputDevices/res/values-iw/strings.xml
index 52641b2..de9b276 100644
--- a/packages/InputDevices/res/values-iw/strings.xml
+++ b/packages/InputDevices/res/values-iw/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"סלובקית"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"סלובנית"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"טורקית"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"‏טורקית F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"אוקראינית"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"ערבית"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"יוונית"</string>
diff --git a/packages/InputDevices/res/values-ja/strings.xml b/packages/InputDevices/res/values-ja/strings.xml
index 2961548..6be0b4c 100644
--- a/packages/InputDevices/res/values-ja/strings.xml
+++ b/packages/InputDevices/res/values-ja/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"スロバキア語"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"スロベニア語"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"トルコ語"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"トルコ語 F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ウクライナ語"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"アラビア語"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"ギリシャ語"</string>
diff --git a/packages/InputDevices/res/values-ka/strings.xml b/packages/InputDevices/res/values-ka/strings.xml
index 2ccfeb2..92d9470 100644
--- a/packages/InputDevices/res/values-ka/strings.xml
+++ b/packages/InputDevices/res/values-ka/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"სლოვაკური"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"სლოვენური"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"თურქული"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"თურქული F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"უკრაინული"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"არაბული"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"ბერძნული"</string>
diff --git a/packages/InputDevices/res/values-kk/strings.xml b/packages/InputDevices/res/values-kk/strings.xml
index dfe8c56..c8ab796 100644
--- a/packages/InputDevices/res/values-kk/strings.xml
+++ b/packages/InputDevices/res/values-kk/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Словак"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Словен"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Түрік"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Түрік тілі, F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Украин"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Араб"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Грек"</string>
diff --git a/packages/InputDevices/res/values-km/strings.xml b/packages/InputDevices/res/values-km/strings.xml
index 3bd7f20..4b12321 100644
--- a/packages/InputDevices/res/values-km/strings.xml
+++ b/packages/InputDevices/res/values-km/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"ស្លូវ៉ាគី"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"ស្លូវ៉ានី"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"ទួរគី"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"តួកគី F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"អ៊ុយក្រែន"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"អារ៉ាប់"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"ក្រិក"</string>
diff --git a/packages/InputDevices/res/values-kn/strings.xml b/packages/InputDevices/res/values-kn/strings.xml
index 1e3c693..761b7cc 100644
--- a/packages/InputDevices/res/values-kn/strings.xml
+++ b/packages/InputDevices/res/values-kn/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"ಸ್ಲೋವಾಕ್"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"ಸ್ಲೋವೇನಿಯನ್"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"ಟರ್ಕಿಶ್‌"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"ಟರ್ಕಿಶ್ F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ಉಕ್ರೇನಿಯನ್"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"ಅರೇಬಿಕ್"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"ಗ್ರೀಕ್"</string>
diff --git a/packages/InputDevices/res/values-ko/strings.xml b/packages/InputDevices/res/values-ko/strings.xml
index 1470504..2a1cbb0 100644
--- a/packages/InputDevices/res/values-ko/strings.xml
+++ b/packages/InputDevices/res/values-ko/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"슬로바키아어"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"슬로베니아어"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"터키어"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"터키어 F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"우크라이나어"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"아랍어"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"그리스어"</string>
diff --git a/packages/InputDevices/res/values-ky/strings.xml b/packages/InputDevices/res/values-ky/strings.xml
index cb9dbb2..70295e1 100644
--- a/packages/InputDevices/res/values-ky/strings.xml
+++ b/packages/InputDevices/res/values-ky/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Словак"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Словен"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"түркчө"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Түркчө F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Украин"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Арабча"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Грекче"</string>
diff --git a/packages/InputDevices/res/values-lo/strings.xml b/packages/InputDevices/res/values-lo/strings.xml
index 4ae4b7d..2b8946b 100644
--- a/packages/InputDevices/res/values-lo/strings.xml
+++ b/packages/InputDevices/res/values-lo/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"ສະໂລແວັກ"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"ສະໂລເວນຽນ"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"ເຕີກິສ"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"ເທືຄິຊ-F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ຢູເຄຣນຽນ"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"ອາຣັບ"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"ກ​ຣີກ"</string>
diff --git a/packages/InputDevices/res/values-lt/strings.xml b/packages/InputDevices/res/values-lt/strings.xml
index d2aef7f..5fca46b 100644
--- a/packages/InputDevices/res/values-lt/strings.xml
+++ b/packages/InputDevices/res/values-lt/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovakų k."</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovėnų k."</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turkų k."</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkų F k."</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainiečių k."</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabų"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Graikų"</string>
diff --git a/packages/InputDevices/res/values-lv/strings.xml b/packages/InputDevices/res/values-lv/strings.xml
index 8f3ff0a..d225329 100644
--- a/packages/InputDevices/res/values-lv/strings.xml
+++ b/packages/InputDevices/res/values-lv/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovāku"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovēņu"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turku"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turku F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukraiņu"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arābu"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Grieķu"</string>
diff --git a/packages/InputDevices/res/values-mk/strings.xml b/packages/InputDevices/res/values-mk/strings.xml
index 9584e15..d98b58b 100644
--- a/packages/InputDevices/res/values-mk/strings.xml
+++ b/packages/InputDevices/res/values-mk/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Словачки"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Словенечки"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Турски"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Турски F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Украински"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"арапски"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"грчки"</string>
diff --git a/packages/InputDevices/res/values-ml/strings.xml b/packages/InputDevices/res/values-ml/strings.xml
index 9e53443..f346881 100644
--- a/packages/InputDevices/res/values-ml/strings.xml
+++ b/packages/InputDevices/res/values-ml/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"സ്ലോവാക്"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"സ്ലോവേനിയൻ"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"ടർക്കിഷ്"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"ടർക്കിഷ്-എഫ്"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ഉക്രേനിയന്‍"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"അറബിക്"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"ഗ്രീക്ക്"</string>
diff --git a/packages/InputDevices/res/values-mn/strings.xml b/packages/InputDevices/res/values-mn/strings.xml
index 18c2faf..fcaf321 100644
--- a/packages/InputDevices/res/values-mn/strings.xml
+++ b/packages/InputDevices/res/values-mn/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Словак"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Словени"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Турк"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Турк F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Украйн"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Араб"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Грек"</string>
diff --git a/packages/InputDevices/res/values-mr/strings.xml b/packages/InputDevices/res/values-mr/strings.xml
index d8788c9..c04006d 100644
--- a/packages/InputDevices/res/values-mr/strings.xml
+++ b/packages/InputDevices/res/values-mr/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"स्लोव्हाक"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"स्लोव्हेनियन"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"तुर्की"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"तुर्कीश एफ"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"यूक्रेनियन"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"अरबी"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"ग्रीक"</string>
diff --git a/packages/InputDevices/res/values-ms/strings.xml b/packages/InputDevices/res/values-ms/strings.xml
index 8bc9b2a..9bff171 100644
--- a/packages/InputDevices/res/values-ms/strings.xml
+++ b/packages/InputDevices/res/values-ms/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Bahasa Slovakia"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Bahasa Slovenia"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Bahasa Turki"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turki F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Bahasa Ukraine"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Bahasa Arab"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Bahasa Greek"</string>
diff --git a/packages/InputDevices/res/values-my/strings.xml b/packages/InputDevices/res/values-my/strings.xml
index 2672057..01b5507 100644
--- a/packages/InputDevices/res/values-my/strings.xml
+++ b/packages/InputDevices/res/values-my/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"စလိုဗက်"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"စလိုဗေးနီးယန်း"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"တူရကီ"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"တူရကီ F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ယူကရိန်း"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"အာရပ်"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"ဂရိ"</string>
diff --git a/packages/InputDevices/res/values-nb/strings.xml b/packages/InputDevices/res/values-nb/strings.xml
index 83b87e5..60cac3d 100644
--- a/packages/InputDevices/res/values-nb/strings.xml
+++ b/packages/InputDevices/res/values-nb/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovakisk"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovensk"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Tyrkisk"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Tyrkisk F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainsk"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabisk"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Gresk"</string>
diff --git a/packages/InputDevices/res/values-ne/strings.xml b/packages/InputDevices/res/values-ne/strings.xml
index 4801f75..13740e7 100644
--- a/packages/InputDevices/res/values-ne/strings.xml
+++ b/packages/InputDevices/res/values-ne/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"स्लोवाक"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"स्लोवेनियाई"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"टर्किश"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"तुर्किस-F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"युक्रेनी"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"अरबी"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"ग्रीक"</string>
diff --git a/packages/InputDevices/res/values-nl/strings.xml b/packages/InputDevices/res/values-nl/strings.xml
index 6e58490..f3a5814 100644
--- a/packages/InputDevices/res/values-nl/strings.xml
+++ b/packages/InputDevices/res/values-nl/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slowaaks"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Sloveens"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turks"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turks F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Oekraïens"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabisch"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Grieks"</string>
diff --git a/packages/InputDevices/res/values-or/strings.xml b/packages/InputDevices/res/values-or/strings.xml
index aa16151..52556ef 100644
--- a/packages/InputDevices/res/values-or/strings.xml
+++ b/packages/InputDevices/res/values-or/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"ସ୍ଲୋଭାକ୍"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"ସ୍ଲୋଭେନିଆନ୍"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"ତୁର୍କିସ୍"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"ତୁର୍କିଶ୍ F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ୟୁକ୍ରାନିଆନ୍"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"ଆରବିକ୍‍"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"ଗ୍ରୀକ୍"</string>
diff --git a/packages/InputDevices/res/values-pa/strings.xml b/packages/InputDevices/res/values-pa/strings.xml
index 7e5a03c..f261fb52 100644
--- a/packages/InputDevices/res/values-pa/strings.xml
+++ b/packages/InputDevices/res/values-pa/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"ਸਲੋਵਾਕ"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"ਸਲੋਵੀਅਨ"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"ਤੁਰਕੀ"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"ਤੁਰਕੀ F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ਯੂਕਰੇਨੀਅਨ"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"ਅਰਬੀ"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"ਯੂਨਾਨੀ"</string>
diff --git a/packages/InputDevices/res/values-pl/strings.xml b/packages/InputDevices/res/values-pl/strings.xml
index 51755e2..25a3a90 100644
--- a/packages/InputDevices/res/values-pl/strings.xml
+++ b/packages/InputDevices/res/values-pl/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Słowacki"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Słoweński"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turecki"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turecka F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukraiński"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"arabski"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"grecki"</string>
diff --git a/packages/InputDevices/res/values-pt-rBR/strings.xml b/packages/InputDevices/res/values-pt-rBR/strings.xml
index 1288688..e44f4ae 100644
--- a/packages/InputDevices/res/values-pt-rBR/strings.xml
+++ b/packages/InputDevices/res/values-pt-rBR/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Eslovaco"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Esloveno"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turco"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turco F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ucraniano"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Árabe"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Grego"</string>
diff --git a/packages/InputDevices/res/values-pt-rPT/strings.xml b/packages/InputDevices/res/values-pt-rPT/strings.xml
index 89bb3e3..3ad3e63 100644
--- a/packages/InputDevices/res/values-pt-rPT/strings.xml
+++ b/packages/InputDevices/res/values-pt-rPT/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Eslovaco"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Esloveno"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turco"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turco F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ucraniano"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Árabe"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Grego"</string>
diff --git a/packages/InputDevices/res/values-pt/strings.xml b/packages/InputDevices/res/values-pt/strings.xml
index 1288688..e44f4ae 100644
--- a/packages/InputDevices/res/values-pt/strings.xml
+++ b/packages/InputDevices/res/values-pt/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Eslovaco"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Esloveno"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turco"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turco F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ucraniano"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Árabe"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Grego"</string>
diff --git a/packages/InputDevices/res/values-ro/strings.xml b/packages/InputDevices/res/values-ro/strings.xml
index f7ff250..3867e1c 100644
--- a/packages/InputDevices/res/values-ro/strings.xml
+++ b/packages/InputDevices/res/values-ro/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovacă"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovenă"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turcă"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turcă F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ucraineană"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabă"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Greacă"</string>
diff --git a/packages/InputDevices/res/values-ru/strings.xml b/packages/InputDevices/res/values-ru/strings.xml
index 7066813..7c5c95a 100644
--- a/packages/InputDevices/res/values-ru/strings.xml
+++ b/packages/InputDevices/res/values-ru/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"словацкий"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"словенский"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"турецкий"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"турецкий (тип F)"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"украинский"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"арабский"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"греческий"</string>
diff --git a/packages/InputDevices/res/values-si/strings.xml b/packages/InputDevices/res/values-si/strings.xml
index eb3c446..f4147f3 100644
--- a/packages/InputDevices/res/values-si/strings.xml
+++ b/packages/InputDevices/res/values-si/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"ස්ලෝවැක්"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"ස්ලෝවේනියානු"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"තුර්කි"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"තුර්කි F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"යුක්රේනියානු"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"අරාබි"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"ග්‍රීක"</string>
diff --git a/packages/InputDevices/res/values-sk/strings.xml b/packages/InputDevices/res/values-sk/strings.xml
index e7e15b0..301c800 100644
--- a/packages/InputDevices/res/values-sk/strings.xml
+++ b/packages/InputDevices/res/values-sk/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"slovenské"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"slovinské"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"turecké"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turečtina F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukrajinské"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabčina"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Gréčtina"</string>
diff --git a/packages/InputDevices/res/values-sl/strings.xml b/packages/InputDevices/res/values-sl/strings.xml
index f4d1e57..09b3c31 100644
--- a/packages/InputDevices/res/values-sl/strings.xml
+++ b/packages/InputDevices/res/values-sl/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"slovaška"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"slovenska"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"turška"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turščina F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukrajinska"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"arabščina"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"grščina"</string>
diff --git a/packages/InputDevices/res/values-sq/strings.xml b/packages/InputDevices/res/values-sq/strings.xml
index 9b4fe9e..0863138 100644
--- a/packages/InputDevices/res/values-sq/strings.xml
+++ b/packages/InputDevices/res/values-sq/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"sllovakisht"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"sllovenisht"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"turqisht"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turqisht me F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukrainisht"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"arabisht"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"greqisht"</string>
diff --git a/packages/InputDevices/res/values-sr/strings.xml b/packages/InputDevices/res/values-sr/strings.xml
index e3a2043..973b833 100644
--- a/packages/InputDevices/res/values-sr/strings.xml
+++ b/packages/InputDevices/res/values-sr/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"словачка"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"словеначка"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"турска"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"турска F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"украјинска"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"арапски"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"грчки"</string>
diff --git a/packages/InputDevices/res/values-sv/strings.xml b/packages/InputDevices/res/values-sv/strings.xml
index 097ada4..a08a74b 100644
--- a/packages/InputDevices/res/values-sv/strings.xml
+++ b/packages/InputDevices/res/values-sv/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovakiskt"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovenskt"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turkiskt"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"turkiska, F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainskt"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabiska"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Grekiska"</string>
diff --git a/packages/InputDevices/res/values-sw/strings.xml b/packages/InputDevices/res/values-sw/strings.xml
index 3257962..0f4c846 100644
--- a/packages/InputDevices/res/values-sw/strings.xml
+++ b/packages/InputDevices/res/values-sw/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Kislovakia"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Kislovenia"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Kituruki"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Kituruki F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Kiukrania"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Kiarabu"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Kigiriki"</string>
diff --git a/packages/InputDevices/res/values-ta/strings.xml b/packages/InputDevices/res/values-ta/strings.xml
index d3c6000..f596b40 100644
--- a/packages/InputDevices/res/values-ta/strings.xml
+++ b/packages/InputDevices/res/values-ta/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"ஸ்லோவாக்"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"ஸ்லோவேனியன்"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"டர்கிஷ்"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"டர்கிஷ் F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"உக்ரைனியன்"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"அரபிக்"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"கிரேக்கம்"</string>
diff --git a/packages/InputDevices/res/values-te/strings.xml b/packages/InputDevices/res/values-te/strings.xml
index c0253e5..5a10f6e 100644
--- a/packages/InputDevices/res/values-te/strings.xml
+++ b/packages/InputDevices/res/values-te/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"స్లోవక్"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"స్లోవేనియన్"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"టర్కిష్"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"టర్కిష్ ఎఫ్"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ఉక్రెయినియన్"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"అరబిక్"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"గ్రీక్"</string>
diff --git a/packages/InputDevices/res/values-th/strings.xml b/packages/InputDevices/res/values-th/strings.xml
index c8e0e62..5f60c3d 100644
--- a/packages/InputDevices/res/values-th/strings.xml
+++ b/packages/InputDevices/res/values-th/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"สโลวัก"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"สโลวีเนีย"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"ตุรกี"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"ภาษาตุรกี F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ยูเครน"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"ภาษาอารบิค"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"กรีก"</string>
diff --git a/packages/InputDevices/res/values-tl/strings.xml b/packages/InputDevices/res/values-tl/strings.xml
index b9aee76..c42adbc 100644
--- a/packages/InputDevices/res/values-tl/strings.xml
+++ b/packages/InputDevices/res/values-tl/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovak"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovenian"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turkish"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkish F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainian"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabic"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Greek"</string>
diff --git a/packages/InputDevices/res/values-tr/strings.xml b/packages/InputDevices/res/values-tr/strings.xml
index f093abb..2877cb7 100644
--- a/packages/InputDevices/res/values-tr/strings.xml
+++ b/packages/InputDevices/res/values-tr/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovakça"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovence"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Türkçe"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Türkçe F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukraynaca"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arapça"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Yunanca"</string>
diff --git a/packages/InputDevices/res/values-uk/strings.xml b/packages/InputDevices/res/values-uk/strings.xml
index d9b58d2..3b0de34 100644
--- a/packages/InputDevices/res/values-uk/strings.xml
+++ b/packages/InputDevices/res/values-uk/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"словацька"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"словенська"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"турецька"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Турецька-F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"українська"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Арабська"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Грецька"</string>
diff --git a/packages/InputDevices/res/values-ur/strings.xml b/packages/InputDevices/res/values-ur/strings.xml
index 2bff7ed..0cc9b61 100644
--- a/packages/InputDevices/res/values-ur/strings.xml
+++ b/packages/InputDevices/res/values-ur/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"سلوووک"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"سلووینیائی"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"ترکش"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"‏ترکی-F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"يُوکرينی"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"عربی"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"يونانی"</string>
diff --git a/packages/InputDevices/res/values-uz/strings.xml b/packages/InputDevices/res/values-uz/strings.xml
index 9245aeb..161bd0d 100644
--- a/packages/InputDevices/res/values-uz/strings.xml
+++ b/packages/InputDevices/res/values-uz/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovakcha"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovyancha"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turkcha"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkcha F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukraincha"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arab"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Grek"</string>
diff --git a/packages/InputDevices/res/values-vi/strings.xml b/packages/InputDevices/res/values-vi/strings.xml
index 76fd0bf..0c638fa 100644
--- a/packages/InputDevices/res/values-vi/strings.xml
+++ b/packages/InputDevices/res/values-vi/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Tiếng Slovak"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Tiếng Sloven"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Tiếng Thổ Nhĩ Kỳ"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Tiếng Thổ Nhĩ Kỳ F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Tiếng Ukraina"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Tiếng Ả rập"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Tiếng Hy Lạp"</string>
diff --git a/packages/InputDevices/res/values-zh-rCN/strings.xml b/packages/InputDevices/res/values-zh-rCN/strings.xml
index aa75605..b249779 100644
--- a/packages/InputDevices/res/values-zh-rCN/strings.xml
+++ b/packages/InputDevices/res/values-zh-rCN/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"斯洛伐克语"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"斯洛文尼亚语"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"土耳其语"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"土耳其语 F 型键盘"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"乌克兰语"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"阿拉伯语"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"希腊语"</string>
diff --git a/packages/InputDevices/res/values-zh-rHK/strings.xml b/packages/InputDevices/res/values-zh-rHK/strings.xml
index dc824b9..60a52e9 100644
--- a/packages/InputDevices/res/values-zh-rHK/strings.xml
+++ b/packages/InputDevices/res/values-zh-rHK/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"斯洛伐克文"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"斯洛文尼亞文"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"土耳其文"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"土耳其文 F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"烏克蘭文"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"阿拉伯文"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"希臘文"</string>
diff --git a/packages/InputDevices/res/values-zh-rTW/strings.xml b/packages/InputDevices/res/values-zh-rTW/strings.xml
index c2714da..c3217e3 100644
--- a/packages/InputDevices/res/values-zh-rTW/strings.xml
+++ b/packages/InputDevices/res/values-zh-rTW/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"斯洛伐克文"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"斯洛維尼亞文"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"土耳其文"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"土耳其文 F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"烏克蘭文"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"阿拉伯文"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"希臘文"</string>
diff --git a/packages/InputDevices/res/values-zu/strings.xml b/packages/InputDevices/res/values-zu/strings.xml
index 3af1da1..2c53626 100644
--- a/packages/InputDevices/res/values-zu/strings.xml
+++ b/packages/InputDevices/res/values-zu/strings.xml
@@ -36,6 +36,7 @@
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Isi-Slovak"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Isi-Slovenian"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Isi-Turkish"</string>
+    <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"I-Turkish-F"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Isi-Ukrainian"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Isi-Arabic"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Isi-Greek"</string>
diff --git a/packages/InputDevices/res/values/strings.xml b/packages/InputDevices/res/values/strings.xml
index 9d068fe..1e13940 100644
--- a/packages/InputDevices/res/values/strings.xml
+++ b/packages/InputDevices/res/values/strings.xml
@@ -105,6 +105,9 @@
     <!-- Turkish keyboard layout label. [CHAR LIMIT=35] -->
     <string name="keyboard_layout_turkish">Turkish</string>
 
+    <!-- Turkish keyboard layout label. [CHAR LIMIT=35] -->
+    <string name="keyboard_layout_turkish_f">Turkish F</string>
+
     <!-- Ukrainian keyboard layout label. [CHAR LIMIT=35] -->
     <string name="keyboard_layout_ukrainian">Ukrainian</string>
 
diff --git a/packages/InputDevices/res/xml/keyboard_layouts.xml b/packages/InputDevices/res/xml/keyboard_layouts.xml
index 6b78b68..976a279 100644
--- a/packages/InputDevices/res/xml/keyboard_layouts.xml
+++ b/packages/InputDevices/res/xml/keyboard_layouts.xml
@@ -132,6 +132,10 @@
             android:label="@string/keyboard_layout_turkish"
             android:keyboardLayout="@raw/keyboard_layout_turkish" />
 
+    <keyboard-layout android:name="keyboard_layout_turkish_f"
+            android:label="@string/keyboard_layout_turkish_f"
+            android:keyboardLayout="@raw/keyboard_layout_turkish_f" />
+
     <keyboard-layout android:name="keyboard_layout_ukrainian"
             android:label="@string/keyboard_layout_ukrainian"
             android:keyboardLayout="@raw/keyboard_layout_ukrainian" />
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
index be778e9..2674eaf 100755
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
@@ -339,8 +339,9 @@
             broadcastIntent.putExtra(UninstallFinish.EXTRA_APP_LABEL, label);
             broadcastIntent.putExtra(UninstallFinish.EXTRA_UNINSTALL_ID, uninstallId);
 
-            PendingIntent pendingIntent = PendingIntent.getBroadcast(this, uninstallId,
-                    broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+            PendingIntent pendingIntent =
+                    PendingIntent.getBroadcast(this, uninstallId, broadcastIntent,
+                            PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
 
             NotificationManager notificationManager = getSystemService(NotificationManager.class);
             NotificationChannel uninstallingChannel = new NotificationChannel(UNINSTALLING_CHANNEL,
diff --git a/packages/PrintSpooler/res/values-mr/strings.xml b/packages/PrintSpooler/res/values-mr/strings.xml
index 4d7e919..8119439 100644
--- a/packages/PrintSpooler/res/values-mr/strings.xml
+++ b/packages/PrintSpooler/res/values-mr/strings.xml
@@ -63,7 +63,7 @@
     <string name="printer_info_desc" msgid="7181988788991581654">"या प्रिंटर विषयी अधिक माहिती"</string>
     <string name="notification_channel_progress" msgid="872788690775721436">"प्रिंट कार्ये चालवणे"</string>
     <string name="notification_channel_failure" msgid="9042250774797916414">"अयशस्वी प्रिंट कार्ये"</string>
-    <string name="could_not_create_file" msgid="3425025039427448443">"फाईल तयार करणेे शक्य झाले नाही"</string>
+    <string name="could_not_create_file" msgid="3425025039427448443">"फाइल तयार करणेे शक्य झाले नाही"</string>
     <string name="print_services_disabled_toast" msgid="9089060734685174685">"काही प्रिंट सेवा अक्षम केल्या आहेत"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"प्रिंटर शोधत आहे"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"कोणत्याही प्रिंट सेवा सक्षम केलेल्या नाहीत"</string>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java b/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
index 8e140ca..83974af 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
@@ -254,7 +254,7 @@
         mLocationManager.requestLocationUpdates(
                 LocationManager.FUSED_PROVIDER,
                 new LocationRequest.Builder(LOCATION_UPDATE_MS)
-                        .setQuality(LocationRequest.POWER_LOW)
+                        .setQuality(LocationRequest.QUALITY_LOW_POWER)
                         .build(),
                 new HandlerExecutor(new Handler(Looper.getMainLooper())),
                 this);
diff --git a/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml b/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml
new file mode 100644
index 0000000..f66ff00
--- /dev/null
+++ b/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml
@@ -0,0 +1,51 @@
+<!--
+     Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:baselineAligned="false"
+    android:orientation="horizontal"
+    android:padding="16dp">
+
+    <ImageView
+        android:id="@+id/user_photo"
+        android:layout_width="56dp"
+        android:layout_height="56dp"
+        android:layout_gravity="bottom"
+        android:contentDescription="@string/user_image_photo_selector"
+        android:background="@*android:drawable/spinner_background_holo_dark"
+        android:scaleType="fitCenter"/>
+
+    <EditText
+        android:id="@+id/user_name"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_gravity="bottom"
+        android:layout_weight="1"
+        android:minWidth="200dp"
+        android:layout_marginStart="6dp"
+        android:minHeight="@dimen/min_tap_target_size"
+        android:ellipsize="end"
+        android:singleLine="true"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textAlignment="viewStart"
+        android:inputType="text|textCapWords"
+        android:selectAllOnFocus="true"
+        android:hint="@string/user_nickname"
+        android:maxLength="100"/>
+
+</LinearLayout>
diff --git a/packages/SettingsLib/res/layout/restricted_popup_menu_item.xml b/packages/SettingsLib/res/layout/restricted_popup_menu_item.xml
new file mode 100644
index 0000000..923d022
--- /dev/null
+++ b/packages/SettingsLib/res/layout/restricted_popup_menu_item.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** 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.
+*/
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:gravity="center_vertical"
+    android:minHeight="?android:attr/listPreferredItemHeightSmall"
+    android:paddingEnd="16dp"
+    android:paddingStart="16dp">
+
+    <TextView
+        android:id="@+id/text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentLeft="true"
+        android:ellipsize="marquee"
+        android:textAppearance="?android:attr/textAppearanceListItemSmall"
+        android:textColor="?android:attr/textColorAlertDialogListItem" />
+
+    <ImageView
+        android:id="@+id/restricted_icon"
+        android:layout_width="@*android:dimen/config_restrictedIconSize"
+        android:layout_height="@*android:dimen/config_restrictedIconSize"
+        android:layout_alignParentRight="true"
+        android:scaleType="centerInside"
+        android:src="@*android:drawable/ic_info"
+        android:tint="?android:attr/colorAccent"
+        android:visibility="gone" />
+
+</RelativeLayout>
diff --git a/packages/SettingsLib/res/layout/user_creation_progress_dialog.xml b/packages/SettingsLib/res/layout/user_creation_progress_dialog.xml
new file mode 100644
index 0000000..fe09aac
--- /dev/null
+++ b/packages/SettingsLib/res/layout/user_creation_progress_dialog.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/message"
+    style="?android:attr/textAppearanceListItem"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:gravity="center"
+    android:paddingStart="?android:attr/dialogPreferredPadding"
+    android:paddingEnd="?android:attr/dialogPreferredPadding"
+    android:paddingTop="24dp"
+    android:paddingBottom="24dp" />
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 6751fa4..e2b04d4 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Voordat jy \'n beperkte profiel kan skep, moet jy \'n skermslot opstel om jou programme en persoonlike data te beskerm."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Stel slot op"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Skakel oor na <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Skep tans nuwe gebruiker …"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Bynaam"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Voeg gas by"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Verwyder gas"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gas"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Neem \'n foto"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Kies \'n prent"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Kies foto"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Toestelverstek"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Gedeaktiveer"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Geaktiveer"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 470e780..0e3684e 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"የተገደበ መገለጫ መፍጠር ከመቻልዎ በፊት መተግበሪያዎችዎን እና የግል ውሂብዎን ለመጠበቅ ቁልፍ ማያ ገጽ ማዋቀር አለብዎት።"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"ቁልፍ አዘጋጅ"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"ወደ <xliff:g id="USER_NAME">%s</xliff:g> ቀይር"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"አዲስ ተጠቃሚ በመፍጠር ላይ…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"ቅጽል ስም"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"እንግዳን አክል"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"እንግዳን አስወግድ"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"እንግዳ"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"ፎቶ አንሳ"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"ምስል ይምረጡ"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"ፎቶ ይምረጡ"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"የመሣሪያ ነባሪ"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ተሰናክሏል"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ነቅቷል"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 0b45380..7ec46d2 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -549,9 +549,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"قبل أن تتمكن من إنشاء ملف شخصي مقيد، يلزمك إعداد تأمين للشاشة لحماية تطبيقاتك وبياناتك الشخصية."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"تعيين التأمين"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"التبديل إلى <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"جارٍ إنشاء مستخدم جديد…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"اللقب"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"إضافة ضيف"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"إزالة جلسة الضيف"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"ضيف"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"التقاط صورة"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"اختيار صورة"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"اختيار صورة"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"الإعداد التلقائي للجهاز"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"غير مفعّل"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"مفعّل"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index f993dba..fe04094 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"আপুনি সীমিত প্ৰ\'ফাইল এটা সৃষ্টি কৰাৰ আগেয়ে, আপোনাৰ ব্যক্তিগত ডেটা আৰু এপবিলাকক সুৰক্ষিত কৰিবলৈ স্ক্ৰীণ লক এটা নিৰ্ধাৰণ কৰিব লাগিব।"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"লক ছেট কৰক"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>লৈ সলনি কৰক"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"নতুন ব্যৱহাৰকাৰী সৃষ্টি কৰি থকা হৈছে…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"উপনাম"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"অতিথি যোগ কৰক"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"অতিথি আঁতৰাওক"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"অতিথি"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"এখন ফট’ তোলক"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"এখন প্ৰতিচ্ছবি বাছনি কৰক"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"ফট’ বাছনি কৰক"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ডিভাইচ ডিফ’ল্ট"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"অক্ষম কৰা আছে"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"সক্ষম কৰা আছে"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 6a50661..bd10cfb 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Məhdudlaşdırılmış profil yaratmadan öncə, Siz tətbiqlərinizi və şəxsi datanızı qorumaq üçün ekran kilidi quraşdırmalısınız."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Kilid ayarlayın"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> adlı istifadəçiyə keçin"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Yeni istifadəçi yaradılır…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Ləqəb"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Qonaq əlavə edin"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Qonağı silin"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Qonaq"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Foto çəkin"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Şəkil seçin"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Foto seçin"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Cihaz defoltu"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Deaktiv"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiv"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index cf988ab..0deb927 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -546,9 +546,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Da biste mogli da napravite ograničeni profil, treba da podesite zaključavanje ekrana da biste zaštitili aplikacije i lične podatke."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Podesi zaključavanje"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Pređi na korisnika <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Pravi se novi korisnik…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Nadimak"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gosta"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Ukloni gosta"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gost"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Slikaj"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberi sliku"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Izaberite sliku"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Podrazumevano za uređaj"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Onemogućeno"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogućeno"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 7f6237d..9aad825 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -547,9 +547,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Перш чым вы зможаце стварыць профіль з абмежаваннямi, вам трэба наладзіць блакiроўку экрана для абароны сваiх дадаткаў і асабістай інфармацыі."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Усталёўка блакiроўкi"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Пераключыцца на карыстальніка <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Ствараецца новы карыстальнік…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Псеўданім"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Дадаць госця"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Выдаліць госця"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Госць"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Зрабіць фота"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Выбраць відарыс"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Выбраць фота"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Стандартная прылада"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Выключана"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Уключана"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 747cb26..8bf13fe 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Преди да можете да създадете потребителски профил с ограничена функционалност, трябва да настроите заключения екран, за да защитите приложенията и личните си данни."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Задаване на заключване"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Превключване към <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Създава се нов потребител…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Псевдоним"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Добавяне на гост"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Премахване на госта"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Гост"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Правене на снимка"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Избиране на изображение"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Избиране на снимката"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Стандартна настройка за у-вото"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Деактивирано"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Активирано"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 1ab88ed..8bcf9a6 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"আপনি একটি সীমাবদ্ধযুক্ত প্রোফাইল তৈরি করার আগে, আপনাকে আপনার অ্যাপ্লিকেশন এবং ব্যক্তিগত ডেটা সুরক্ষিত করার জন্য একটি স্ক্রিন লক সেট-আপ করতে হবে।"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"লক সেট করুন"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>-এ পাল্টান"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"নতুন ব্যবহারকারী তৈরি করা হচ্ছে…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"বিশেষ নাম"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"অতিথি যোগ করুন"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"অতিথি সরান"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"অতিথি"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"ফটো তুলুন"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"একটি ইমেজ বেছে নিন"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"ফটো বেছে নিন"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ডিভাইসের ডিফল্ট"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"বন্ধ করা আছে"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"চালু করা আছে"</string>
diff --git a/packages/SettingsLib/res/values-bs/arrays.xml b/packages/SettingsLib/res/values-bs/arrays.xml
index 775cd15..6d2f1f3 100644
--- a/packages/SettingsLib/res/values-bs/arrays.xml
+++ b/packages/SettingsLib/res/values-bs/arrays.xml
@@ -27,7 +27,7 @@
     <item msgid="8356618438494652335">"Autentifikacija…"</item>
     <item msgid="2837871868181677206">"Dobivanje IP adrese…"</item>
     <item msgid="4613015005934755724">"Povezano"</item>
-    <item msgid="3763530049995655072">"Suspendirano"</item>
+    <item msgid="3763530049995655072">"Obustavljeno"</item>
     <item msgid="7852381437933824454">"Prekidanje veze…"</item>
     <item msgid="5046795712175415059">"Isključen"</item>
     <item msgid="2473654476624070462">"Neuspješno"</item>
@@ -41,7 +41,7 @@
     <item msgid="3028983857109369308">"Autentifikacija s mrežom <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
     <item msgid="4287401332778341890">"Dobivanje IP adrese iz mreže <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
     <item msgid="1043944043827424501">"Povezano s mrežom <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
-    <item msgid="7445993821842009653">"Suspendirano"</item>
+    <item msgid="7445993821842009653">"Obustavljeno"</item>
     <item msgid="1175040558087735707">"Prekidanje veze s mrežom <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
     <item msgid="699832486578171722">"Isključen"</item>
     <item msgid="522383512264986901">"Neuspješno"</item>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index e329c99..32f6aa7 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -546,9 +546,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Prije nego vam se omogući kreiranje ograničenog profila, morate postaviti zaključavanje ekrana da biste zaštitili svoje aplikacije i lične podatke."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Postaviti zaključavanje"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Prebaci na korisnika <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Kreiranje novog korisnika…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Nadimak"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gosta"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Ukloni gosta"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gost"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Snimite fotografiju"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberite sliku"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Odabir fotografije"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Zadana postavka uređaja"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Onemogućeno"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogućeno"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 5ffdacd..525a187 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Per crear un perfil restringit, has de configurar una pantalla de bloqueig per protegir les aplicacions i les dades personals."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Defineix un bloqueig"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Canvia a <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"S\'està creant l\'usuari…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Àlies"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Afegeix un convidat"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Suprimeix el convidat"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Convidat"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Fes una foto"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Tria una imatge"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Selecciona una foto"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Opció predeter. del dispositiu"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Desactivat"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activat"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 0aef99f..64ad700 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -547,9 +547,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Před vytvořením omezeného profilu je nutné nejprve nastavit zámek obrazovky k ochraně aplikací a dat."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Nastavit zámek"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Přepnout na uživatele <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Vytváření nového uživatele…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Přezdívka"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Přidat hosta"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Odstranit hosta"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Host"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Pořídit fotku"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Vybrat obrázek"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Vybrat fotku"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Výchozí nastavení zařízení"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Vypnuto"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Zapnuto"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 98068cb..433368b 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Før du kan oprette en begrænset profil, skal du oprette en skærmlås for at beskytte dine apps og personlige data."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Konfigurer låseskærmen"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Skift til <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Opretter ny bruger…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Kaldenavn"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Tilføj gæsten"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Fjern gæsten"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gæst"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Tag et billede"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Vælg et billede"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Vælg billede"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Enhedens standardindstilling"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Deaktiveret"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiveret"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 0837ad3..9946eed 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Vor dem Erstellen eines eingeschränkten Profils musst du eine Displaysperre einrichten, um deine Apps und personenbezogenen Daten zu schützen."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Sperre einrichten"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Zu <xliff:g id="USER_NAME">%s</xliff:g> wechseln"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Neuer Nutzer wird erstellt…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Alias"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Gast hinzufügen"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Gast entfernen"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gast"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Foto machen"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Bild auswählen"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Foto auswählen"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Gerätestandard"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Deaktiviert"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiviert"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 1f9d977..c265dbc 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Προκειμένου να μπορέσετε να δημιουργήσετε ένα περιορισμένο προφίλ, θα πρέπει να δημιουργήσετε ένα κλείδωμα οθόνης για την προστασία των εφαρμογών και των προσωπικών δεδομένων σας."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Ορισμός κλειδώματος"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Εναλλαγή σε <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Δημιουργία νέου χρήστη…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Ψευδώνυμο"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Προσθήκη επισκέπτη"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Κατάργηση επισκέπτη"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Επισκέπτης"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Λήψη φωτογραφίας"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Επιλογή εικόνας"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Επιλογή φωτογραφίας"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Προεπιλογή συσκευής"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Ανενεργή"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ενεργή"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index abe61a9..285c615 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Before you can create a restricted profile, you\'ll need to set up a screen lock to protect your apps and personal data."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Set lock"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Switch to <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creating new user…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Nickname"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Guest"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Select photo"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Device default"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Disabled"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index 959ad46..bbd5c76 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Before you can create a restricted profile, you\'ll need to set up a screen lock to protect your apps and personal data."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Set lock"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Switch to <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creating new user…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Nickname"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Guest"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Select photo"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Device default"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Disabled"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index abe61a9..285c615 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Before you can create a restricted profile, you\'ll need to set up a screen lock to protect your apps and personal data."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Set lock"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Switch to <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creating new user…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Nickname"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Guest"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Select photo"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Device default"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Disabled"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index abe61a9..285c615 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Before you can create a restricted profile, you\'ll need to set up a screen lock to protect your apps and personal data."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Set lock"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Switch to <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creating new user…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Nickname"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Guest"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Select photo"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Device default"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Disabled"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index 738dd2a..699ad42 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‎‏‏‏‏‎‏‎‏‎‏‎‏‎‎‎‎‎‏‏‎‎‏‎‏‏‏‎‏‎‎‎‎‏‏‏‎‏‏‏‏‎‏‏‏‏‏‏‎‏‏‏‎‏‏‎‏‎‏‎Before you can create a restricted profile, you’ll need to set up a screen lock to protect your apps and personal data.‎‏‎‎‏‎"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‎‏‏‏‏‎‎‏‏‏‎‎‎‏‎‏‏‎‏‏‎‎‎‎‏‎‏‏‏‏‎‎‎‏‎‏‏‎‏‎‏‎‏‏‏‏‎‎‏‎‏‎‎‏‎‏‎‎‎‎Set lock‎‏‎‎‏‎"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‎‎‏‏‎‏‏‎‏‏‎‎‏‏‎‎‎‏‎‏‎‎‎‎‏‏‎‎‎‎‎‎‏‎‎‎‎‏‎‎‏‏‏‎‏‏‏‏‎‏‏‏‏‏‎Switch to ‎‏‎‎‏‏‎<xliff:g id="USER_NAME">%s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‏‎‎‎‎‎‎‏‎‏‏‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‎‏‎‏‏‎‎‎‏‎‏‏‏‏‎‏‎‎‏‏‎‎‎‎‎‏‏‏‎Creating new user…‎‏‎‎‏‎"</string>
+    <string name="user_nickname" msgid="262624187455825083">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‎‏‏‏‎‏‎‎‏‎‏‎‎‎‎‎‏‏‏‎‏‎‎‏‏‏‎‏‎‎‏‏‎‏‎‏‎‏‏‏‎‏‎‏‏‏‎‎‏‎‎‏‎‏‏‏‎‏‏‎Nickname‎‏‎‎‏‎"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‎‎‎‎‎‏‎‏‎‎‏‎‏‎‏‎‎‏‎‎‎‏‎‎‏‏‏‏‎‏‏‎‏‏‎‎‎‎‎‎‎‏‏‏‏‏‏‎‎‏‏‎‏‏‏‎‎‏‎‏‎Add guest‎‏‎‎‏‎"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‎‏‎‎‏‎‏‎‏‏‏‎‏‎‏‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‎‎‏‎‎‏‏‏‎‎‎‎Remove guest‎‏‎‎‏‎"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‎‏‏‏‏‏‏‎‎‎‎‎‏‏‎‎‎‏‎‎‎‏‎‏‎‎‎‏‏‏‎‎‎‏‏‎‎‏‏‎‏‏‎‎‏‏‏‎‎‎‏‏‎‎‏‎‏‏‎‏‎Guest‎‏‎‎‏‎"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‏‏‏‎‎‏‏‏‏‏‎‎‏‏‏‏‎‎‎‎‏‎‎‎‏‏‏‎‎‎‏‎‎‎‎‎‏‎‎‎‏‎‏‎‎‎‏‏‎‎‎‎‏‎‎‎‎‎‏‎‎Take a photo‎‏‎‎‏‎"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‎‏‎‏‏‏‎‏‏‎‏‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‎‏‏‎‏‏‏‎‏‏‏‏‏‏‎‏‏‎‏‎‎‎‎‎‏‎‏‏‏‏‎‎‎Choose an image‎‏‎‎‏‎"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‏‏‏‎‎‎‎‎‎‏‎‎‏‎‏‎‏‎‎‏‏‏‏‏‎‎‏‎‏‎‎‎‏‎‏‎‏‎‎‏‎‏‎‏‎‏‎‎‎‎‎‎‎‎‎‏‎‏‎‏‎Select photo‎‏‎‎‏‎"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‏‎‎‎‏‎‏‎‎‎‎‎‎‎‎‎‎‏‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‎‏‎‏‏‎‏‏‎‎‏‎‎‏‎‏‎‏‎‏‏‎‎Device default‎‏‎‎‏‎"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‎‏‎‏‏‎‏‎‏‏‏‎‎‏‏‏‎‏‎‎‎‏‎‎‏‎‏‏‏‎‏‎‏‎‏‎‎‏‎‏‎‎‏‎‎‎‎‏‎‎‎‏‏‏‎‏‎‏‎‎Disabled‎‏‎‎‏‎"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‎‏‏‎‏‏‎‏‏‏‏‎‏‏‎‏‏‎‎‎‎‏‎‎‏‎‎‎‏‏‏‏‏‏‎‎‏‏‏‎‏‎‎‎‏‏‎‏‎‎Enabled‎‏‎‎‏‎"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index d1e4fb5..ab421f6 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Para poder crear un perfil restringido, debes configurar un bloqueo de pantalla que proteja tus aplicaciones y datos personales."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Configurar bloqueo"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Cambiar a <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creando usuario nuevo…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Sobrenombre"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Agregar invitado"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Quitar invitado"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Invitado"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Tomar una foto"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Elegir una imagen"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Seleccionar foto"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Predeterminado del dispositivo"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Inhabilitado"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Habilitado"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 9ad71e2..f6157b4 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Para poder crear un perfil restringido, debes configurar una pantalla de bloqueo que proteja tus aplicaciones y datos personales."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Establecer bloqueo"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Cambiar a <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creando usuario…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Apodo"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Añadir invitado"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Quitar invitado"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Invitado"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Hacer foto"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Seleccionar una imagen"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Seleccionar foto"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Predeterminado por el dispositivo"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Inhabilitado"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Habilitado"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index 14d3b57..72766c0 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Enne piiratud profiili loomist peate seadistama lukustusekraani, et oma rakendusi ja isiklikke andmeid kaitsta."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Määra lukk"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Lülita kasutajale <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Uue kasutaja loomine …"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Hüüdnimi"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Lisa külaline"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Eemalda külaline"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Külaline"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Pildistage"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Valige pilt"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Valige foto"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Seadme vaikeseade"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Keelatud"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Lubatud"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 4a11aa7..c7462ce 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Profil murriztua sortu aurretik, aplikazioak eta datu pertsonalak babesteko, pantaila blokeatzeko metodo bat konfiguratu beharko duzu."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Ezarri blokeoa"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Aldatu <xliff:g id="USER_NAME">%s</xliff:g> erabiltzailera"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Beste erabiltzaile bat sortzen…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Goitizena"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Gehitu gonbidatua"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Kendu gonbidatua"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gonbidatua"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Atera argazki bat"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Aukeratu irudi bat"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Hautatu argazki bat"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Gailuaren balio lehenetsia"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Desgaituta"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Gaituta"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 3373f81..0b21a2d 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -28,7 +28,7 @@
     <string name="wifi_disabled_network_failure" msgid="2660396183242399585">"‏پیکربندی IP انجام نشد"</string>
     <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"اتصال ناموفق به دلیل شبکه با کیفیت پایین"</string>
     <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"‏اتصال Wi-Fi برقرار نشد"</string>
-    <string name="wifi_disabled_password_failure" msgid="6892387079613226738">"مشکل احراز هویت"</string>
+    <string name="wifi_disabled_password_failure" msgid="6892387079613226738">"مشکل اصالت‌سنجی"</string>
     <string name="wifi_cant_connect" msgid="5718417542623056783">"برقراری اتصال ممکن نیست"</string>
     <string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"برقراری اتصال به «<xliff:g id="AP_NAME">%1$s</xliff:g>» ممکن نیست"</string>
     <string name="wifi_check_password_try_again" msgid="8817789642851605628">"گذرواژه را بررسی و دوباره امتحان کنید"</string>
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"قبل از ایجاد یک نمایه محدود، باید یک قفل صفحه را برای محافظت از برنامه‌ها و داده‌های شخصی خود تنظیم کنید."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"تنظیم قفل"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"رفتن به <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"درحال ایجاد کاربر جدید…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"نام مستعار"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"افزودن مهمان"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"حذف مهمان"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"مهمان"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"عکس گرفتن"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"انتخاب تصویر"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"انتخاب عکس"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"پیش‌فرض دستگاه"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"غیرفعال"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"فعال"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 3d28f1d..dd1c12a 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Ennen kuin voit luoda rajoitetun profiilin, määritä näytön lukitus, joka suojelee sovelluksiasi ja henkilökohtaisia tietojasi."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Aseta lukitus"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Vaihda tähän käyttäjään: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Luodaan uutta käyttäjää…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Lempinimi"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Lisää vieras"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Poista vieras"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Vieras"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Ota valokuva"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Valitse kuva"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Valitse valokuva"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Laitteen oletusasetus"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Ei käytössä"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Käytössä"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 87d3de1..f0364c6 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Avant de créer un profil limité, vous devez définir un écran de verrouillage pour protéger vos applications et vos données personnelles."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Définir verrouillage écran"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Passer à <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Créer un utilisateur…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Pseudo"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Ajouter un invité"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Supprimer l\'invité"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Invité"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Prendre une photo"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Sélectionner une image"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Sélectionnez une photo"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Valeur par défaut de l\'appareil"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Désactivé"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activé"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 5f5be97..17d13c1 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Avant de créer un profil limité, vous devez définir un écran de verrouillage pour protéger vos applications et vos données personnelles."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Définir verrouillage écran"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Passer à <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Création d\'un nouvel utilisateur…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Pseudo"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Ajouter un invité"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Supprimer l\'invité"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Invité"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Prendre une photo"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Choisir une image"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Sélectionner une photo"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Paramètre par défaut"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Désactivé"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activé"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index af43099..e0b21c3 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Para poder crear un perfil restrinxido, precisarás configurar un bloqueo da pantalla para protexer as túas aplicacións e datos persoais."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Establecer bloqueo"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Cambiar a <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creando usuario novo…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Alcume"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Engadir convidado"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Quitar convidado"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Tirar foto"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Escoller imaxe"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Seleccionar foto"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Funcionamento predeterminado"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Desactivado"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activado"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 894a14a..08a2b99 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"તમે પ્રતિબંધિત પ્રોફાઇલ બનાવી શકો તે પહેલાં, તમારે તમારી ઍપ્લિકેશનો અને વ્યક્તિગત ડેટાની સુરક્ષા માટે એક લૉક સ્ક્રીન સેટ કરવાની જરૂર પડશે."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"લૉક સેટ કરો"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> પર સ્વિચ કરો"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"નવા વપરાશકર્તા બનાવી રહ્યાં છીએ…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"ઉપનામ"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"અતિથિ ઉમેરો"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"અતિથિને કાઢી નાખો"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"અતિથિ"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"ફોટો લો"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"છબી પસંદ કરો"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"ફોટો પસંદ કરો"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ડિવાઇસ ડિફૉલ્ટ"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"બંધ છે"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ચાલુ છે"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 6e734bc..8486e34 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"इससे पहले कि आप कोई प्रतिबंधित प्रोफ़ाइल बनाएं, आपको अपने ऐप्लिकेशन  और व्यक्तिगत डेटा की सुरक्षा करने के लिए एक स्क्रीन लॉक सेट करना होगा."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"लॉक सेट करें"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> पर जाएं"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"नया उपयोगकर्ता बनाया जा रहा है…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"प्रचलित नाम"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"मेहमान जोड़ें"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"मेहमान हटाएं"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"मेहमान"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"फ़ोटो खींचें"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"कोई इमेज चुनें"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"फ़ोटो चुनें"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"डिवाइस की डिफ़ॉल्ट सेटिंग"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"बंद है"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"चालू है"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 3edc452..a4ef323 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -546,9 +546,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Prije izrade ograničenog profila trebate postaviti zaključavanje zaslona radi zaštite svojih aplikacija i osobnih podataka."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Postavi zaključavanje"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Prelazak na korisnika <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Izrada novog korisnika…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Nadimak"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Dodavanje gosta"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Uklanjanje gosta"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gost"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Fotografiraj"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberi sliku"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Odabir slike"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Zadana postavka uređaja"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Onemogućeno"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogućeno"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index d86d88b..480552b 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Mielőtt létrehozhatna egy korlátozott profilt, be kell állítania egy képernyőzárat, hogy megvédje alkalmazásait és személyes adatait."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Képernyőzár beállítása"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Váltás erre: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Új felhasználó létrehozása…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Becenév"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Vendég hozzáadása"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Vendég munkamenet eltávolítása"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Vendég"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Fotó készítése"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Kép kiválasztása"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Fotó kiválasztása"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Alapértelmezett"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Letiltva"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Engedélyezve"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index e434cac..9fbf998 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Նախքան դուք կկարողանաք ստեղծել սահմանափակ պրոֆիլ, դուք պետք է կարգավորեք էկրանի կողպումը` ձեր ծրագրերը և անձնական տվյալները պաշտպանելու համար:"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Կարգավորել կողպումը"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Անցնել <xliff:g id="USER_NAME">%s</xliff:g> պրոֆիլին"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Ստեղծվում է օգտատիրոջ նոր պրոֆիլ…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Կեղծանուն"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Ավելացնել հյուր"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Հեռացնել հյուրին"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Հյուր"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Լուսանկարել"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Ընտրել պատկեր"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Ընտրեք լուսանկար"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Կանխադրված տարբերակ"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Անջատված է"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Միացված է"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index a6f8846..b1391ec 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -156,7 +156,7 @@
     <string name="launch_defaults_none" msgid="8049374306261262709">"Tidak ada setelan default"</string>
     <string name="tts_settings" msgid="8130616705989351312">"Setelan text-to-speech"</string>
     <string name="tts_settings_title" msgid="7602210956640483039">"Keluaran text-to-speech"</string>
-    <string name="tts_default_rate_title" msgid="3964187817364304022">"Laju bicara"</string>
+    <string name="tts_default_rate_title" msgid="3964187817364304022">"Kecepatan ucapan"</string>
     <string name="tts_default_rate_summary" msgid="3781937042151716987">"Kecepatan teks diucapkan"</string>
     <string name="tts_default_pitch_title" msgid="6988592215554485479">"Tinggi nada"</string>
     <string name="tts_default_pitch_summary" msgid="9132719475281551884">"Memengaruhi nada ucapan yang disintesis"</string>
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Sebelum dapat membuat profil yang dibatasi, Anda perlu menyiapkan kunci layar untuk melindungi aplikasi dan data pribadi Anda."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Setel kunci"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Beralih ke <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Membuat pengguna baru …"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Nama panggilan"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Tambahkan tamu"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Hapus tamu"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Tamu"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Ambil foto"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Pilih gambar"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Pilih foto"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Default perangkat"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Nonaktif"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktif"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index caf2323..1636c55 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Áður en þú getur búið til takmarkað snið þarftu að setja upp skjálás til að vernda forritin þín og persónuleg gögn."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Velja lás"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Skipta yfir í <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Stofnar nýjan notanda…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Gælunafn"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Bæta gesti við"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Fjarlægja gest"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gestur"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Taka mynd"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Velja mynd"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Velja mynd"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Sjálfgefin stilling tækis"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Slökkt"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Virkt"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 8d18727..b358d65 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Prima di poter creare un profilo con limitazioni, devi impostare un blocco schermo per proteggere le tue app e i tuoi dati personali."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Imposta blocco"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Passa a <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creazione nuovo utente…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Nickname"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Aggiungi ospite"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Rimuovi ospite"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Ospite"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Scatta una foto"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Scegli un\'immagine"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Seleziona la foto"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Parametro predefinito"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Non attivo"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Attivo"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index fff881c..0f2eb4b 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -547,9 +547,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"לפני שתוכל ליצור פרופיל מוגבל, תצטרך להגדיר נעילת מסך כדי להגן על האפליקציות ועל הנתונים האישיים שלך."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"הגדרת נעילה"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"מעבר אל <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"בתהליך יצירה של משתמש חדש…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"כינוי"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"הוספת אורח"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"הסרת אורח"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"אורח"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"צילום תמונה"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"לבחירת תמונה"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"בחירת תמונה"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ברירת המחדל של המכשיר"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"מושבת"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"מופעל"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 5e579b7..dba1c95 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"制限付きプロファイルを作成する場合は、アプリや個人データを保護するように画面ロックを設定しておく必要があります。"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"ロックを設定"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> に切り替え"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"新しいユーザーを作成しています…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"ニックネーム"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"ゲストを追加"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"ゲストを削除"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"ゲスト"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"写真を撮る"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"画像を選択"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"写真を選択"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"デバイスのデフォルト"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"無効"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"有効"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 1b5fae9..8302b8d 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"შეზღუდული პროფილის შექმნამდე, საკუთარი აპლიკაციებისა და პირადი მონაცემების დასაცავად, უნდა დაბლოკოთ ეკრანი."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"საკეტის დაყენება"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>-ზე გადართვა"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"მიმდინარეობს ახალი მომხმარებლის შექმნა…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"მეტსახელი"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"სტუმრის დამატება"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"სტუმრის ამოშლა"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"სტუმარი"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"ფოტოს გადაღება"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"აირჩიეთ სურათი"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"ფოტოს არჩევა"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"მოწყობილობის ნაგულისხმევი"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"გათიშული"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ჩართული"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 9c290e9..ffb5c9a 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Шектелген профайл жасақтауға дейін қолданбалар мен жеке деректерді қорғау үшін экран бекітпесін тағайындау қажет."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Бекітпе тағайындау"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> пайдаланушысына ауысу"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Жаңа пайдаланушы профилі жасалуда…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Лақап ат"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Қонақты енгізу"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Қонақты өшіру"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Қонақ"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Фотосуретке түсіру"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Сурет таңдау"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Фотосурет таңдау"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Құрылғыны әдепкісінше реттеу"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Өшірулі"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Қосулы"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 2878db1..6fbd2d1 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"មុន​ពេល​អ្នក​អាច​បង្កើត​ប្រវត្តិ​រូប​បាន​ដាក់​កម្រិត អ្នក​ត្រូវ​រៀបចំ​ការ​ចាក់​សោ​អេក្រង់ ដើម្បី​ការពារ​កម្មវិធី និង​ទិន្នន័យ​ផ្ទាល់ខ្លួន​របស់​អ្នក។"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"កំណត់​ការ​ចាក់​សោ"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"ប្ដូរទៅ <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"កំពុងបង្កើត​អ្នកប្រើប្រាស់ថ្មី…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"ឈ្មោះ​ហៅក្រៅ"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"បញ្ចូល​ភ្ញៀវ"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"លុប​​​ភ្ញៀវ"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"ភ្ញៀវ"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"ថតរូប"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"ជ្រើសរើស​រូបភាព"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"ជ្រើសរើស​​រូបថត"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"លំនាំដើម​របស់ឧបករណ៍"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"បានបិទ"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"បានបើក"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 7b01056..653d8ba 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"ನೀವು ನಿರ್ಬಂಧಿತ ಪ್ರೊಫೈಲ್ ಅನ್ನು ರಚಿಸಬಹುದಾದರ ಮೊದಲು, ನಿಮ್ಮ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಮತ್ತು ವೈಯಕ್ತಿಕ ಡೇಟಾವನ್ನು ರಕ್ಷಿಸಲು ನೀವು ಪರದೆಯ ಲಾಕ್‌ ಹೊಂದಿಸುವ ಅಗತ್ಯವಿದೆ."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"ಲಾಕ್ ಹೊಂದಿಸಿ"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> ಗೆ ಬದಲಿಸಿ"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"ಹೊಸ ಬಳಕೆದಾರರನ್ನು ರಚಿಸಲಾಗುತ್ತಿದೆ…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"ಅಡ್ಡ ಹೆಸರು"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"ಅತಿಥಿಯನ್ನು ಸೇರಿಸಿ"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"ಅತಿಥಿಯನ್ನು ತೆಗೆದುಹಾಕಿ"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"ಅತಿಥಿ"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"ಫೋಟೋ ತೆಗೆದುಕೊಳ್ಳಿ"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"ಚಿತ್ರವನ್ನು ಆರಿಸಿ"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"ಫೋಟೋ ಆಯ್ಕೆಮಾಡಿ"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ಸಾಧನದ ಡೀಫಾಲ್ಟ್"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index f13d9b8..761f813 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"제한된 프로필을 만들기 전에 화면 잠금을 설정하여 앱과 개인 데이터를 보호해야 합니다."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"잠금 설정"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>(으)로 전환"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"새로운 사용자를 만드는 중…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"닉네임"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"게스트 추가"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"게스트 삭제"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"게스트"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"사진 찍기"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"이미지 선택"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"사진 선택"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"기기 기본값"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"사용 중지됨"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"사용 설정됨"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 419c18f..c72b93f 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Чектелген профайл түзөөрдөн мурун, сиз өзүңүздүн колдонмолоруңузду жана жеке маалыматтарыңызды коргош үчүн, бөгөттөө көшөгөсүн орнотушуңуз керек болот."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Бөгөт коюу"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> аккаунтуна которулуу"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Жаңы колдонуучу түзүлүүдө…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Ылакап аты"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Конок кошуу"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Конокту өчүрүү"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Конок"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Сүрөткө тартуу"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Сүрөт тандаңыз"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Сүрөт тандаңыз"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Түзмөктүн демейки параметри"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Өчүк"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Күйүк"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index a72861e..7883308 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"ກ່ອນທ່ານຈະສ້າງໂປຣໄຟລ໌ທີ່ຖືກຈຳກັດນັ້ນ, ທ່ານຈະຕ້ອງຕັ້ງຄ່າການລັອກໜ້າຈໍ ເພື່ອປ້ອງກັນແອັບຯ ແລະຂໍ້ມູນສ່ວນໂຕຂອງທ່ານກ່ອນ."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"ຕັ້ງການລັອກ"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"ສະຫຼັບໄປ <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"ກຳລັງສ້າງຜູ້ໃຊ້ໃໝ່…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"ຊື່ຫຼິ້ນ"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"ເພີ່ມແຂກ"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"ລຶບແຂກອອກ"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"ແຂກ"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"ຖ່າຍຮູບ"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"ເລືອກຮູບ"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"ເລືອກຮູບ"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ຄ່າເລີ່ມຕົ້ນອຸປະກອນ"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ປິດການນຳໃຊ້ແລ້ວ"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ເປີດການນຳໃຊ້ແລ້ວ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index c72bf21..e3aa368 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -547,9 +547,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Prieš kuriant apribotą profilį reikės nustatyti ekrano užraktą, kad apsaugotumėte programas ir asmeninius duomenis."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Nustatyti užraktą"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Perjungti į <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Kuriamas naujas naudotojas…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Slapyvardis"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Pridėti svečią"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Pašalinti svečią"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Svečias"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Fotografuoti"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Pasirinkti vaizdą"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Pasirinkti nuotrauką"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Numatyt. įrenginio nustatymas"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Išjungta"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Įgalinta"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index d95e57d..e994974 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -546,9 +546,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Lai varētu izveidot ierobežotu profilu, jums jāiestata ekrāna bloķēšana, kas aizsargās jūsu lietotni un personas datus."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Iestatīt bloķēšanu"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Pārslēgties uz: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Notiek jauna lietotāja izveide…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Segvārds"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Pievienot viesi"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Noņemt viesi"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Viesis"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Uzņemt fotoattēlu"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Izvēlēties attēlu"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Atlasīt fotoattēlu"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Ierīces noklusējums"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Atspējots"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Iespējots"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 58d4af1..e711747 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Пред да може да создадете ограничен профил, треба да поставите заклучување на екранот за да ги заштити вашите апликации и лични податоци."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Постави заклучување"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Префрли на <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Се создава нов корисник…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Прекар"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Додај гостин"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Отстрани гостин"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Гостин"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Фотографирајте"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Одберете слика"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Изберете фотографија"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Стандардно за уредот"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Оневозможено"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Овозможено"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 3c281c8..cebd552 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"ഒരു നിയന്ത്രിത പ്രൊഫൈൽ സൃഷ്‌ടിക്കുന്നതിനുമുമ്പ്, നിങ്ങളുടെ അപ്ലിക്കേഷനുകളും വ്യക്തിഗത ഡാറ്റയും പരിരക്ഷിക്കുന്നതിന് ഒരു സ്‌ക്രീൻ ലോക്ക് സജ്ജീകരിക്കേണ്ടതുണ്ട്."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"ലോക്ക് സജ്ജീകരിക്കുക"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> എന്നതിലേക്ക് മാറുക"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"പുതിയ ഉപയോക്താവിനെ സൃഷ്‌ടിക്കുന്നു…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"വിളിപ്പേര്"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"അതിഥിയെ ചേർക്കുക"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"അതിഥിയെ നീക്കം ചെയ്യുക"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"അതിഥി"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"ഒരു ഫോട്ടോ എടുക്കുക"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"ഒരു ചിത്രം തിരഞ്ഞെടുക്കുക"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"ഫോട്ടോ തിരഞ്ഞെടുക്കുക"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ഉപകരണത്തിന്റെ ഡിഫോൾട്ട് പ്രവർത്തനം"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"പ്രവർത്തനരഹിതമാക്കി"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"പ്രവർത്തനക്ഷമമാക്കി"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 37fc5b4..8074366 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Та хязгаарлагдсан профайл үүсгэхийн өмнө өөрийн апп-ууд болон хувийн өгөгдлийг хамгаалахын тулд дэлгэцийн түгжээг тохируулах шаардлагатай."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Түгжээг тохируулах"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> руу сэлгэх"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Шинэ хэрэглэгч үүсгэж байна…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Хоч"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Зочин нэмэх"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Зочин хасах"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Зочин"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Зураг авах"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Зураг сонгох"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Зураг сонгох"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Төхөөрөмжийн өгөгдмөл"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Идэвхгүй болгосон"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Идэвхжүүлсэн"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 360f158..8c55328 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -25,7 +25,7 @@
     <string name="wifi_remembered" msgid="3266709779723179188">"सेव्ह केले"</string>
     <string name="wifi_disconnected" msgid="7054450256284661757">"डिस्कनेक्ट केले"</string>
     <string name="wifi_disabled_generic" msgid="2651916945380294607">"अक्षम"</string>
-    <string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP कॉन्फिगरेशन अयशस्वी"</string>
+    <string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP कॉंफिगरेशन अयशस्वी"</string>
     <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"कमी दर्जाच्या नेटवर्कमुळे कनेक्ट केलेले नाही"</string>
     <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"WiFi कनेक्शन अयशस्वी"</string>
     <string name="wifi_disabled_password_failure" msgid="6892387079613226738">"प्रमाणीकरण समस्या"</string>
@@ -97,7 +97,7 @@
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"श्रवण यंत्रांशी कनेक्ट केले आहे"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"मीडिया ऑडिओवर कनेक्ट केले"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"फोन ऑडिओ वर कनेक्ट केले"</string>
-    <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"फाईल स्थानांतर सर्व्हरवर कनेक्ट केले"</string>
+    <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"फाइल स्थानांतर सर्व्हरवर कनेक्ट केले"</string>
     <string name="bluetooth_map_profile_summary_connected" msgid="4141725591784669181">"नकाशाशी कनेक्ट केले"</string>
     <string name="bluetooth_sap_profile_summary_connected" msgid="1280297388033001037">"SAP शी कनेक्‍ट केले"</string>
     <string name="bluetooth_opp_profile_summary_not_connected" msgid="3959741824627764954">"फाइल स्थानांतर सर्व्हरशी कनेक्ट केले नाही"</string>
@@ -109,7 +109,7 @@
     <string name="bluetooth_sap_profile_summary_use_for" msgid="6204902866176714046">"SIM प्रवेशासाठी वापरा"</string>
     <string name="bluetooth_a2dp_profile_summary_use_for" msgid="7324694226276491807">"मीडिया ऑडिओसाठी वापरा"</string>
     <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"फोन ऑडिओसाठी वापरा"</string>
-    <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"फाईल स्थानांतरणासाठी वापरा"</string>
+    <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"फाइल स्थानांतरणासाठी वापरा"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"इनपुट साठी वापरा"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"श्रवण यंत्रांसाठी वापरा"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"पेअर करा"</string>
@@ -268,8 +268,8 @@
     <string name="bluetooth_select_a2dp_codec_type_help_info" msgid="8647200416514412338">"फोन किंवा हेडसेटला सपोर्ट करत नाही म्हणजे ती निकामी झाली आहे"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="6253965294594390806">"प्रति पॅटर्न ब्लूटूध ऑडिओ बिट"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4898693684282596143">"ब्लूटूथ ऑडिओ Codec ट्रिगर करा\nनिवड: बिट प्रति नमुना"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="364277285688014427">"ब्लूटूथ ऑडिओ चॅनेल मोड"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="2076949781460359589">"ब्लूटूथ ऑडिओ Codec ट्रिगर करा\nनिवड: चॅनेल मोड"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="364277285688014427">"ब्लूटूथ ऑडिओ चॅनल मोड"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="2076949781460359589">"ब्लूटूथ ऑडिओ Codec ट्रिगर करा\nनिवड: चॅनल मोड"</string>
     <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3233402355917446304">"ब्लूटूथ ऑडिओ LDAC कोडेक: प्लेबॅक गुणवत्ता"</string>
     <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="7274396574659784285">"ब्लूटूथ ऑडिओ LDAC\nकोडेक निवड ट्रिगर करा: प्लेबॅक गुणवत्ता"</string>
     <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="2040810756832027227">"स्ट्रीमिंग: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
@@ -370,7 +370,7 @@
     <string name="app_process_limit_title" msgid="8361367869453043007">"पार्श्वभूमी प्रक्रिया मर्यादा"</string>
     <string name="show_all_anrs" msgid="9160563836616468726">"बॅकग्राउंड ANR दाखवा"</string>
     <string name="show_all_anrs_summary" msgid="8562788834431971392">"बॅकग्राउंड अ‍ॅप्ससाठी अ‍ॅप प्रतिसाद देत नाही दाखवते"</string>
-    <string name="show_notification_channel_warnings" msgid="3448282400127597331">"सूचना चॅनेल चेतावण्या दाखवा"</string>
+    <string name="show_notification_channel_warnings" msgid="3448282400127597331">"सूचना चॅनल चेतावण्या दाखवा"</string>
     <string name="show_notification_channel_warnings_summary" msgid="68031143745094339">"एखादे अ‍ॅप वैध चॅनेलशिवाय सूचना पोस्ट करते तेव्हा स्क्रीनवर चेतावणी देते"</string>
     <string name="force_allow_on_external" msgid="9187902444231637880">"बाह्यवर ॲप्सना अनुमती देण्याची सक्ती करा"</string>
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"manifest मूल्यांकडे दुर्लक्ष करून, कोणत्याही ॲपला बाह्य स्टोरेजवर लेखन केले जाण्यासाठी पात्र बनविते"</string>
@@ -404,11 +404,11 @@
     <string name="select_webview_provider_title" msgid="3917815648099445503">"वेबदृश्य अंमलबजावणी"</string>
     <string name="select_webview_provider_dialog_title" msgid="2444261109877277714">"वेबदृश्य अंमलबजावणी सेट करा"</string>
     <string name="select_webview_provider_toast_text" msgid="8512254949169359848">"ही निवड यापुढे वैध असणार नाही. पुन्हा प्रयत्न करा."</string>
-    <string name="convert_to_file_encryption" msgid="2828976934129751818">"फाईल कूटबद्धीकरणावर रूपांतरित करा"</string>
+    <string name="convert_to_file_encryption" msgid="2828976934129751818">"फाइल कूटबद्धीकरणावर रूपांतरित करा"</string>
     <string name="convert_to_file_encryption_enabled" msgid="840757431284311754">"रूपांतरित करा..."</string>
-    <string name="convert_to_file_encryption_done" msgid="8965831011811180627">"फाईल आधीपासून एंक्रिप्ट होती"</string>
-    <string name="title_convert_fbe" msgid="5780013350366495149">"फाईल आधारित कूटबद्धीकरणावर रूपांतरित करणे"</string>
-    <string name="convert_to_fbe_warning" msgid="34294381569282109">"फाईल आधारित कूटबद्धीकरणावर डेटा विभाजक रूपांतरित करा.\n !!चेतावणी!! हे आपल्‍या सर्व डेटास मिटवेल.\n हे वैशिष्ट्य अल्‍फा आहे आणि कदाचित योग्यरित्या कार्य करू शकत नाही.\n सुरू ठेवण्‍यासाठी \'पुसा आणि रूपांतरित करा...\' दाबा."</string>
+    <string name="convert_to_file_encryption_done" msgid="8965831011811180627">"फाइल आधीपासून एंक्रिप्ट होती"</string>
+    <string name="title_convert_fbe" msgid="5780013350366495149">"फाइल आधारित कूटबद्धीकरणावर रूपांतरित करणे"</string>
+    <string name="convert_to_fbe_warning" msgid="34294381569282109">"फाइल आधारित कूटबद्धीकरणावर डेटा विभाजक रूपांतरित करा.\n !!चेतावणी!! हे आपल्‍या सर्व डेटास मिटवेल.\n हे वैशिष्ट्य अल्‍फा आहे आणि कदाचित योग्यरित्या कार्य करू शकत नाही.\n सुरू ठेवण्‍यासाठी \'पुसा आणि रूपांतरित करा...\' दाबा."</string>
     <string name="button_convert_fbe" msgid="1159861795137727671">"पुसा आणि रुपांतरित करा..."</string>
     <string name="picture_color_mode" msgid="1013807330552931903">"चित्र रंग मोड"</string>
     <string name="picture_color_mode_desc" msgid="151780973768136200">"sRGB वापरा"</string>
@@ -525,7 +525,7 @@
     <string name="accessor_expires_text" msgid="4625619273236786252">"भाडेपट्टी <xliff:g id="DATE">%s</xliff:g> रोजी एक्स्पायर होईल"</string>
     <string name="delete_blob_text" msgid="2819192607255625697">"शेअर केलेला डेटा हटवा"</string>
     <string name="delete_blob_confirmation_text" msgid="7807446938920827280">"तुम्हाला नक्की हा शेअर केलेला डेटा हटवायचा आहे का?"</string>
-    <string name="user_add_user_item_summary" msgid="5748424612724703400">"वापरकर्त्यांकडे त्यांचे स्वत:चे अ‍ॅप्स आणि सामग्री आहे"</string>
+    <string name="user_add_user_item_summary" msgid="5748424612724703400">"वापरकर्त्यांकडे त्यांचे स्वत:चे अ‍ॅप्स आणि आशय आहे"</string>
     <string name="user_add_profile_item_summary" msgid="5418602404308968028">"तुम्ही आपल्या खात्यावरुन अ‍ॅप्स आणि सामग्रीमध्ये प्रवेश करण्यास प्रतिबंध करु शकता"</string>
     <string name="user_add_user_item_title" msgid="2394272381086965029">"वापरकर्ता"</string>
     <string name="user_add_profile_item_title" msgid="3111051717414643029">"प्रतिबंधित प्रोफाईल"</string>
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"तुम्ही एक प्रतिबंधित प्रोफाईल तयार करु शकण्यापूर्वी तुम्हाला तुमचे अ‍ॅप्स आणि वैयक्तिक डेटा संरक्षित करण्यासाठी एक स्क्रीन लॉक सेट करण्याची आवश्यकता राहील."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"लॉक सेट करा"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> वर स्विच करा"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"नवीन वापरकर्ता तयार करत आहे…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"टोपणनाव"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"अतिथी जोडा"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"अतिथी काढून टाका"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"अतिथी"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"फोटो काढा"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"इमेज निवडा"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"फोटो निवडा"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"डिव्हाइस डीफॉल्ट"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"बंद केले आहे"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"सुरू केले आहे"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index 68356df2..d988dd0 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Sebelum anda boleh membuat profil yang terhad, anda perlu menyediakan kunci skrin untuk melindungi apl dan data peribadi anda."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Tetapkan kunci"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Tukar kepada <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Mencipta pengguna baharu…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Nama panggilan"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Tambah tetamu"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Alih keluar tetamu"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Tetamu"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Ambil foto"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Pilih imej"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Pilih foto"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Lalai peranti"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Dilumpuhkan"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Didayakan"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 3729a83..5a16743 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"ကန့်သတ်ကိုယ်ရေးအချက်အလက်တစ်ခုကို မပြုလုပ်မီ သင်၏ အပလီကေးရှင်းများနှင့် ကိုယ်ပိုင်အချက်အလက်များကို ကာကွယ်ရန် မျက်နှာပြင်သော့ချခြင်းကို စီမံရန် လိုအပ်လိမ့်မည်"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"သော့ချရန် သတ်မှတ်ပါ"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> သို့ ပြောင်းရန်"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"အသုံးပြုသူအသစ် ပြုလုပ်နေသည်…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"နာမည်ပြောင်"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"ဧည့်သည့် ထည့်ရန်"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"ဧည့်သည်ကို ဖယ်ထုတ်ရန်"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"ဧည့်သည်"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"ဓာတ်ပုံရိုက်ရန်"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"ပုံရွေးရန်"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"ဓာတ်ပုံရွေးရန်"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"စက်ပစ္စည်းမူရင်း"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ပိတ်ထားသည်"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ဖွင့်ထားသည်"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 0e0e761..df96ea6 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Før du kan opprette en begrenset profil, må du konfigurere skjermlåsen for å beskytte appene og de personlige dataene dine."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Angi lås"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Bytt til <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Oppretter en ny bruker …"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Kallenavn"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Legg til en gjest"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Fjern gjesten"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gjest"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Ta et bilde"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Velg et bilde"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Velg et bilde"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Standard for enheten"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Slått av"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Slått på"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index ee368fd..e34732f 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -89,7 +89,7 @@
     <string name="bluetooth_profile_pbap" msgid="7064307749579335765">"सम्पर्क साझेदारी"</string>
     <string name="bluetooth_profile_pbap_summary" msgid="2955819694801952056">"सम्पर्क साझेदारीका लागि प्रयोग"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"इन्टरनेट जडान साझेदारी गर्दै"</string>
-    <string name="bluetooth_profile_map" msgid="8907204701162107271">"पाठ सन्देशहरू"</string>
+    <string name="bluetooth_profile_map" msgid="8907204701162107271">"टेक्स्ट म्यासेजहरू"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM पहुँच"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD अडियो: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD अडियो"</string>
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"निषेधयुक्त प्रोफाइल बनाउनु अघि तपाईँको एप र व्यक्तिगत डेटा सुरक्षा गर्नाका लागि तपाईँले स्क्रिन लक सेटअप गर्नु पर्दछ ।"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"लक सेट गर्नुहोस्"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"प्रयोगकर्ता बदलेर <xliff:g id="USER_NAME">%s</xliff:g> पार्नुहोस्"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"नयाँ प्रयोगकर्ता बनाउँदै…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"उपनाम"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"अतिथि थप्नुहोस्"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"अतिथि हटाउनुहोस्"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"अतिथि"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"फोटो खिच्नुहोस्"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"कुनै फोटो छनौट गर्नुहोस्"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"फोटो चयन गर्नुहोस्"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"पूर्वनिर्धारित यन्त्र"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"असक्षम पारिएको छ"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"सक्षम पारिएको छ"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 27f5dc9..729d57b 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Voordat je een beperkt profiel kunt maken, moet je een schermvergrendeling instellen om je apps en persoonsgegevens te beschermen."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Vergrendeling instellen"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Overschakelen naar <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Nieuwe gebruiker maken…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Bijnaam"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Gast toevoegen"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Gast verwijderen"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gast"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Foto maken"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Afbeelding kiezen"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Foto selecteren"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Apparaatstandaard"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Uitgeschakeld"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ingeschakeld"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index d200f50..1ce7d56e 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"ପ୍ରତିବନ୍ଧିତ ପ୍ରୋଫାଇଲ୍‌ ତିଆରି କରିବାବେଳେ, ନିଜ ଆପ୍‌ ଓ ବ୍ୟକ୍ତିଗତ ତଥ୍ୟର ସୁରକ୍ଷା ପାଇଁ ଏକ ସ୍କ୍ରୀନ୍‌ ଲକ୍‌ ସେଟ୍‌ କରନ୍ତୁ।"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"ଲକ୍‌ ସେଟ୍‌ କରନ୍ତୁ"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>କୁ ସ୍ୱିଚ୍ କରନ୍ତୁ"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"ନୂଆ ଉପଯୋଗକର୍ତ୍ତା ତିଆରି କରାଯାଉଛି…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"ଡାକନାମ"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"ଅତିଥି ଯୋଗ କରନ୍ତୁ"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"ଅତିଥିଙ୍କୁ କାଢ଼ି ଦିଅନ୍ତୁ"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"ଅତିଥି"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"ଗୋଟିଏ ଫଟୋ ଉଠାନ୍ତୁ"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"ଏକ ଛବି ବାଛନ୍ତୁ"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"ଫଟୋ ବାଛନ୍ତୁ"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ଡିଭାଇସ୍ ଡିଫଲ୍ଟ"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ଅକ୍ଷମ କରାଯାଇଛି"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ସକ୍ଷମ କରାଯାଇଛି"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 354ee12..c122a28 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"ਇਸਤੋਂ ਪਹਿਲਾਂ ਕਿ ਤੁਸੀਂ ਇੱਕ ਪ੍ਰਤਿਬੰਧਿਤ ਪ੍ਰੋਫਾਈਲ ਬਣਾ ਸਕੋ, ਤੁਹਾਨੂੰ ਆਪਣੀਆਂ ਐਪਾਂ ਅਤੇ ਨਿੱਜੀ ਡਾਟਾ ਸੁਰੱਖਿਅਤ ਕਰਨ ਲਈ ਇੱਕ ਸਕ੍ਰੀਨ  ਲਾਕ  ਸੈੱਟ ਅੱਪ ਕਰਨ ਦੀ ਲੋੜ ਹੈ।"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">" ਲਾਕ  ਸੈੱਟ ਕਰੋ"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> \'ਤੇ ਜਾਓ"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"ਨਵਾਂ ਵਰਤੋਂਕਾਰ ਬਣਾਇਆ ਜਾ ਰਿਹਾ ਹੈ…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"ਉਪਨਾਮ"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"ਮਹਿਮਾਨ ਸ਼ਾਮਲ ਕਰੋ"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"ਮਹਿਮਾਨ ਹਟਾਓ"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"ਮਹਿਮਾਨ"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"ਇੱਕ ਫ਼ੋਟੋ ਖਿੱਚੋ"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"ਕੋਈ ਚਿੱਤਰ ਚੁਣੋ"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"ਫ਼ੋਟੋ ਚੁਣੋ"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ਡੀਵਾਈਸ ਪੂਰਵ-ਨਿਰਧਾਰਤ"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ਬੰਦ ਕੀਤਾ ਗਿਆ"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ਚਾਲੂ ਕੀਤਾ ਗਿਆ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 095412c..112ae47 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -547,9 +547,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Zanim utworzysz profil z ograniczeniami, musisz skonfigurować ekran blokady, by chronić aplikacje i osobiste dane."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Ustaw blokadę"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Przełącz na: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Tworzę nowego użytkownika…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Pseudonim"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gościa"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Usuń gościa"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gość"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Zrób zdjęcie"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Wybierz obraz"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Wybierz zdjęcie"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Ustawienie domyślne urządzenia"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Wyłączono"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Włączono"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 895a987..102961a 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Antes de criar um perfil restrito, configure um bloqueio de tela para proteger seus apps e seus dados pessoais."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Definir bloqueio"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Mudar para <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Criando novo usuário…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Apelido"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Adicionar convidado"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Remover convidado"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Selecionar foto"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Padrão do dispositivo"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Desativado"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ativado"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 2d9f037..7354835 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Antes de poder criar um perfil restrito, tem de configurar um bloqueio de ecrã para proteger as suas aplicações e dados pessoais."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Definir bloqueio"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Mudar para <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"A criar novo utilizador…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Pseudónimo"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Adicionar convidado"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Remover convidado"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Selecionar foto"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Predefinição do dispositivo"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Desativada"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ativada"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 895a987..102961a 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Antes de criar um perfil restrito, configure um bloqueio de tela para proteger seus apps e seus dados pessoais."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Definir bloqueio"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Mudar para <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Criando novo usuário…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Apelido"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Adicionar convidado"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Remover convidado"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Selecionar foto"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Padrão do dispositivo"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Desativado"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ativado"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 728db17..32e70da 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -546,9 +546,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Înainte de a putea crea un profil cu permisiuni limitate, va trebui să configurați blocarea ecranului pentru a vă proteja aplicațiile și datele personale."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Configurați blocarea"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Treceți la <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Se creează un utilizator nou…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Pseudonim"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Adăugați un invitat"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Ștergeți invitatul"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Invitat"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Faceți o fotografie"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Alegeți o imagine"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Selectați fotografia"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Prestabilit pentru dispozitiv"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Dezactivat"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activat"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index ff2115e..b04d93b 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -547,9 +547,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Чтобы создать профиль с ограниченным доступом, необходимо предварительно настроить блокировку экрана для защиты приложений и личных данных"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Включить блокировку"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Переключиться на этот аккаунт: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Создаем нового пользователя…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Псевдоним"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Добавить аккаунт гостя"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Удалить аккаунт гостя"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Гость"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Сделать снимок"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Выбрать фото"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Выбрать фотографию"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Вариант по умолчанию"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Отключено"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Включено"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index a883cc6..86725c2 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"සීමිත පැතිකඩක් නිර්මාණය කිරීමට කලින්. ඔබගේ යෙදුම් සහ පෞද්ගලික දත්ත ආරක්ෂා කිරීමට තිර අගුලක් සැකසිය යුතුයි."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"අගුල සකසන්න"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> වෙත මාරු වන්න"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"නව පරිශීලක තනමින්…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"අපනාමය"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"අමුත්තා එක් කරන්න"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"අමුත්තා ඉවත් කරන්න"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"අමුත්තා"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"ඡායාරූපයක් ගන්න"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"රූපයක් තෝරන්න"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"ඡායාරූපය තෝරන්න"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"උපාංගයේ පෙරනිමිය"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"අබල කළා"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"සබලයි"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 05c6379..91e9564 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -547,9 +547,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Pred vytvorením obmedzeného profilu je nutné najprv nastaviť zámku obrazovky na ochranu aplikácií a osobných údajov."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Nastaviť uzamknutie"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Prepnúť na používateľa <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Vytvára sa nový používateľ…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Prezývka"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Pridať hosťa"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Odobrať hosťa"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Hosť"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Odfotiť"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Vybrať obrázok"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Vybrať fotku"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Predvol. nastavenie zariadenia"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Vypnuté"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Zapnuté"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index fd216e8..45ffcde 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -547,9 +547,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Preden lahko ustvarite profil z omejitvami, morate nastaviti zaklepanje zaslona, da zaščitite aplikacije in osebne podatke."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Nastavi zaklepanje"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Preklop na račun <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Ustvarjanje novega uporabnika …"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Vzdevek"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Dodajanje gosta"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Odstranitev gosta"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gost"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Fotografiranje"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Izberi sliko"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Izbira fotografije"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Privzeta nastavitev naprave"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Onemogočeno"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogočeno"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 002c7fc..c398363 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Para se të mund të krijosh një profil të kufizuar, duhet të konfigurosh një kyçje të ekranit për të mbrojtur aplikacionet dhe të dhënat e tua personale."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Cakto kyçjen"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Kalo te <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Po krijohet një përdorues i ri…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Pseudonimi"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Shto të ftuar"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Hiq të ftuarin"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"I ftuar"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Bëj një fotografi"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Zgjidh një imazh"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Zgjidh një fotografi"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Parazgjedhja e pajisjes"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Joaktiv"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiv"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 25a1beb7..bb59bd1 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -546,9 +546,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Да бисте могли да направите ограничени профил, треба да подесите закључавање екрана да бисте заштитили апликације и личне податке."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Подеси закључавање"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Пређи на корисника <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Прави се нови корисник…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Надимак"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Додај госта"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Уклони госта"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Гост"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Сликај"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Одабери слику"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Изаберите слику"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Подразумевано за уређај"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Онемогућено"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Омогућено"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 352cb0a..197655e 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -451,7 +451,7 @@
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Laddas snabbt"</string>
     <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Laddas långsamt"</string>
     <string name="battery_info_status_discharging" msgid="6962689305413556485">"Laddar inte"</string>
-    <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Inkopplad, kan inte laddas just nu"</string>
+    <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Ansluten, kan inte laddas just nu"</string>
     <string name="battery_info_status_full" msgid="4443168946046847468">"Fullt"</string>
     <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Strys av administratören"</string>
     <string name="disabled" msgid="8017887509554714950">"Inaktiverad"</string>
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Innan du skapar en begränsad profil måste du konfigurera ett skärmlås för att skydda dina appar och personliga data."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Konfigurera lås"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Byt till <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Skapar ny användare …"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Smeknamn"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Lägg till gäst"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Ta bort gäst"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gäst"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Ta ett foto"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Välj en bild"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Välj foto"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Enhetens standardinställning"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Inaktiverat"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiverat"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index c5d70ac..1f0d167 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Kabla uunde wasifu uliowekekwa vikwazo, utahitajika kuweka skrini iliyofungwa ili kulinda programu zako na data binafsi."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Weka ufunguo"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Badili utumie <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Inaweka mtumiaji mpya…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Jina wakilishi"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Weka mgeni"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Ondoa mgeni"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Mgeni"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Piga picha"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Chagua picha"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Chagua picha"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Hali chaguomsingi ya kifaa"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Imezimwa"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Imewashwa"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 7837dd8..b6d8be8 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"நீங்கள் வரையறுக்கப்பட்டச் சுயவிவரத்தை உருவாக்குவதற்கு முன்பு, உங்கள் ஆப்ஸ் மற்றும் தனிப்பட்ட தரவைப் பாதுகாக்கும் வகையில் நீங்கள் திரைப் பூட்டை அமைக்க வேண்டும்."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"பூட்டை அமை"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>க்கு மாறு"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"புதிய பயனரை உருவாக்குகிறது…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"புனைப்பெயர்"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"கெஸ்ட்டைச் சேர்"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"கெஸ்ட்டை அகற்று"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"கெஸ்ட்"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"படமெடுங்கள்"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"படத்தைத் தேர்வுசெய்யுங்கள்"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"படத்தைத் தேர்ந்தெடுங்கள்"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"சாதனத்தின் இயல்புநிலை"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"முடக்கப்பட்டது"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"இயக்கப்பட்டது"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index e252eca..5a8e734 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"మీరు పరిమితం చేయబడిన ప్రొఫైల్‌ను సృష్టించడానికి ముందు, మీ అనువర్తనాలు మరియు వ్యక్తిగత డేటాను రక్షించడానికి స్క్రీన్ లాక్‌ను సెటప్ చేయాల్సి ఉంటుంది."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"లాక్‌ను సెట్ చేయి"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>కు మార్చు"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"కొత్త యూజర్‌ను క్రియేట్ చేస్తోంది…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"మారుపేరు"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"అతిథిని జోడించండి"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"అతిథిని తీసివేయండి"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"అతిథి"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"ఒక ఫోటో తీయండి"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"ఇమేజ్‌ను ఎంచుకోండి"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"ఫోటోను ఎంచుకోండి"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"పరికర ఆటోమేటిక్ సెట్టింగ్"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"డిజేబుల్ చేయబడింది"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ఎనేబుల్ చేయబడింది"</string>
diff --git a/packages/SettingsLib/res/values-th/arrays.xml b/packages/SettingsLib/res/values-th/arrays.xml
index 58d8a45..21fe6e4 100644
--- a/packages/SettingsLib/res/values-th/arrays.xml
+++ b/packages/SettingsLib/res/values-th/arrays.xml
@@ -138,15 +138,15 @@
     <item msgid="1333279807604675720">"สเตอริโอ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="1241278021345116816">"เพิ่มประสิทธิภาพสำหรับคุณภาพเสียง (990 kbps/909 kbps)"</item>
+    <item msgid="1241278021345116816">"เพิ่มประสิทธิภาพเพื่อคุณภาพเสียง (990 kbps/909 kbps)"</item>
     <item msgid="3523665555859696539">"คุณภาพเสียงและการเชื่อมต่อที่สมดุล (660 kbps/606 kbps)"</item>
-    <item msgid="886408010459747589">"เพิ่มประสิทธิภาพสำหรับคุณภาพการเชื่อมต่อ (330 kbps/303 kbps)"</item>
+    <item msgid="886408010459747589">"เพิ่มประสิทธิภาพเพื่อคุณภาพการเชื่อมต่อ (330 kbps/303 kbps)"</item>
     <item msgid="3808414041654351577">"ดีที่สุดเท่าที่ทำได้ (ปรับอัตราบิตอัตโนมัติ)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="804499336721569838">"เพิ่มประสิทธิภาพสำหรับคุณภาพเสียง"</item>
+    <item msgid="804499336721569838">"เพิ่มประสิทธิภาพเพื่อคุณภาพเสียง"</item>
     <item msgid="7451422070435297462">"คุณภาพเสียงและการเชื่อมต่อที่สมดุล"</item>
-    <item msgid="6173114545795428901">"เพิ่มประสิทธิภาพสำหรับคุณภาพการเชื่อมต่อ"</item>
+    <item msgid="6173114545795428901">"เพิ่มประสิทธิภาพเพื่อคุณภาพการเชื่อมต่อ"</item>
     <item msgid="4349908264188040530">"ดีที่สุดเท่าที่ทำได้ (ปรับอัตราบิตอัตโนมัติ)"</item>
   </string-array>
   <string-array name="bluetooth_audio_active_device_summaries">
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 7468d04..2ae4131 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"ก่อนที่คุณจะสามารถสร้างโปรไฟล์ที่ถูกจำกัดได้ คุณจะต้องตั้งค่าล็อกหน้าจอเพื่อปกป้องแอปและข้อมูลส่วนตัวของคุณ"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"ตั้งค่าล็อก"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"เปลี่ยนเป็น <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"กำลังสร้างผู้ใช้ใหม่…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"ชื่อเล่น"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"เพิ่มผู้เข้าร่วม"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"นำผู้เข้าร่วมออก"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"ผู้ใช้ชั่วคราว"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"ถ่ายรูป"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"เลือกรูปภาพ"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"เลือกรูปภาพ"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ค่าเริ่มต้นของอุปกรณ์"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ปิดใช้"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"เปิดใช้"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 5d4e975..e9f1da3 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Bago ka makakalikha ng pinaghihigpitang profile, kakailanganin mong mag-set up ng screen lock upang protektahan ang iyong apps at personal na data."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Itakda ang lock"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Lumipat sa <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Gumagawa ng bagong user…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Nickname"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Magdagdag ng bisita"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Alisin ang bisita"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Bisita"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Kumuha ng larawan"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Pumili ng larawan"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Pumili ng larawan"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Default ng device"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Naka-disable"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Na-enable"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index f01f3fa..0502359 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Kısıtlanmış bir profil oluşturabilmeniz için uygulamalarınızı ve kişisel verilerinizi korumak üzere bir ekran kilidi oluşturmanız gerekir."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Kilidi ayarla"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> hesabına geç"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Yeni kullanıcı oluşturuluyor…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Takma ad"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Misafir ekle"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Misafir oturumunu kaldır"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Misafir"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Fotoğraf çek"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Resim seç"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Fotoğraf seç"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Cihaz varsayılanı"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Devre dışı"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Etkin"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 9ca2f06..b68aab4 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -547,9 +547,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Перш ніж створювати обмежений профіль, потрібно налаштувати блокування екрана, щоб захистити свої програми та особисті дані."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Налаштувати блокування"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Перейти до користувача <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Створення нового користувача…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Псевдонім"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Додати гостя"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Видалити гостя"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Гість"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Зробити фотографію"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Вибрати зображення"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Вибрати фотографію"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"За умовчанням для пристрою"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Вимкнено"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Увімкнено"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 8953f50..b971aee 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -197,9 +197,9 @@
     <string name="category_personal" msgid="6236798763159385225">"ذاتی"</string>
     <string name="category_work" msgid="4014193632325996115">"دفتر"</string>
     <string name="development_settings_title" msgid="140296922921597393">"ڈویلپر کے اختیارات"</string>
-    <string name="development_settings_enable" msgid="4285094651288242183">"ڈیولپر کے اختیارات فعال کریں"</string>
+    <string name="development_settings_enable" msgid="4285094651288242183">"ڈویلپر کے اختیارات فعال کریں"</string>
     <string name="development_settings_summary" msgid="8718917813868735095">"ایپ ڈویلپمنٹ کیلئے اختیارات سیٹ کریں"</string>
-    <string name="development_settings_not_available" msgid="355070198089140951">"اس صارف کیلئے ڈیولپر کے اختیارات دستیاب نہیں ہیں"</string>
+    <string name="development_settings_not_available" msgid="355070198089140951">"اس صارف کیلئے ڈویلپر کے اختیارات دستیاب نہیں ہیں"</string>
     <string name="vpn_settings_not_available" msgid="2894137119965668920">"‏VPN ترتیبات اس صارف کیلئے دستیاب نہیں ہیں"</string>
     <string name="tethering_settings_not_available" msgid="266821736434699780">"ٹیدرنگ ترتیبات اس صارف کیلئے دستیاب نہیں ہیں"</string>
     <string name="apn_settings_not_available" msgid="1147111671403342300">"رسائی کی جگہ کے نام کی ترتیبات اس صارف کیلئے دستیاب نہیں ہیں"</string>
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"ایک محدود پروفائل بنانے سے پہلے، آپ کو اپنی ایپس اور ذاتی ڈیٹا کو محفوظ کرنے کیلئے ایک اسکرین لاک سیٹ اپ کرنا ہوگا۔"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"لاک سیٹ کریں"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"‫<xliff:g id="USER_NAME">%s</xliff:g> پر سوئچ کریں"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"نیا صارف تخلیق کرنا…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"عرفی نام"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"مہمان کو شامل کریں"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"مہمان کو ہٹائیں"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"مہمان"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"ایک تصویر لیں"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"ایک تصویر منتخب کریں"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"تصویر منتخب کریں"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"آلہ ڈیفالٹ"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"غیر فعال"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"فعال"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index f25b3ac..3df33b6 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Cheklangan profil yaratish uchun, shaxsiy ilovlar va ma‘lumotlarni himoyalash maqsadida avval ekran qulfini yaratish lozim."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Qulf o‘rnatish"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Bunga almashish: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Yangi foydalanuvchi yaratilmoqda…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Nik"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Mehmon kiritish"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Mehmon rejimini olib tashlash"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Mehmon"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Suratga olish"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Rasm tanlash"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Surat tanlash"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Qurilma standarti"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Yoqilmagan"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Yoniq"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index b5798f3..4d4808f 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Trước khi bạn có thể tạo tiểu sử bị hạn chế, bạn sẽ cần thiết lập một màn hình khóa để bảo vệ các ứng dụng và dữ liệu cá nhân của bạn."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Thiết lập khóa"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Chuyển sang <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Đang tạo người dùng mới…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Biệt hiệu"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Thêm khách"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Xóa phiên khách"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Khách"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Chụp ảnh"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Chọn một hình ảnh"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Chọn ảnh"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Theo giá trị mặc định của thiết bị"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Đã tắt"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Đã bật"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index c4dcfff..b6e8eba2 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"您需要先设置锁定屏幕来保护您的应用和个人数据,然后才可以创建受限个人资料。"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"设置屏幕锁定方式"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"切换到<xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"正在创建新用户…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"昵称"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"添加访客"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"移除访客"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"访客"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"拍摄照片"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"选择图片"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"选择照片"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"设备默认设置"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"已停用"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"已启用"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index e04651c..07bc887 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"建立限制存取的個人檔案前,您必須先設定上鎖畫面來保護您的應用程式和個人資料。"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"設定上鎖畫面"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"切換至<xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"正在建立新使用者…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"暱稱"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"新增訪客"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"移除訪客"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"訪客"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"拍照"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"選擇圖片"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"揀相"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"裝置預設設定"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"已停用"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"已啟用"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index a1ae6b6..833a82f 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"如要建立設有限制的個人資料,你必須先設定螢幕鎖定來保護你的應用程式和個人資料。"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"設定鎖定"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"切換至<xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"正在建立新使用者…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"暱稱"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"新增訪客"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"移除訪客"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"訪客"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"拍照"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"選擇圖片"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"選取相片"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"裝置預設設定"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"已停用"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"已啟用"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 2dafad8..0065eb6 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Ngaphambi kokuthi ungadala iphrofayela ekhawulelwe, kuzomele usethe ukukhiya isikrini ukuze uvikele izinhlelo zakho zokusebenza nedatha yakho yomuntu siqu."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Setha ukukhiya"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Shintshela ku-<xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Idala umsebenzisi omusha…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Isiteketiso"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Engeza isivakashi"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Susa isihambeli"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Isihambeli"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Thatha isithombe"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Khetha isithombe"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Khetha isithombe"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Idivayisi ezenzakalelayo"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Ikhutshaziwe"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Inikwe amandla"</string>
diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml
index e552d78..ef4b97f 100644
--- a/packages/SettingsLib/res/values/dimens.xml
+++ b/packages/SettingsLib/res/values/dimens.xml
@@ -97,4 +97,8 @@
 
     <!-- Size of advanced icon -->
     <dimen name="advanced_icon_size">18dp</dimen>
+
+    <!-- Minimum width for the popup for updating a user's photo. -->
+    <dimen name="update_user_photo_popup_min_width">300dp</dimen>
+
 </resources>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 03161d0..6a4c8c3 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1354,6 +1354,11 @@
     <string name="user_set_lock_button">Set lock</string>
     <!-- Label for switching to other user in the user switcher [CHAR LIMIT=35] -->
     <string name="user_switch_to_user">Switch to <xliff:g id="user_name" example="John Doe">%s</xliff:g></string>
+    <!-- Dialog message when creating a new user [CHAR LIMIT=40] -->
+    <string name="creating_new_user_dialog_message">Creating new user…</string>
+
+    <!-- Title for the preference to enter the nickname of the user to display in the user switcher [CHAR LIMIT=25]-->
+    <string name="user_nickname">Nickname</string>
 
     <!-- Label for adding a new guest in the user switcher [CHAR LIMIT=35] -->
     <string name="guest_new_guest">Add guest</string>
@@ -1362,6 +1367,13 @@
     <!-- Name for the guest user [CHAR LIMIT=35] -->
     <string name="guest_nickname">Guest</string>
 
+    <!-- An option in a photo selection dialog to take a new photo [CHAR LIMIT=50] -->
+    <string name="user_image_take_photo">Take a photo</string>
+    <!-- An option in a photo selection dialog to choose a pre-existing image [CHAR LIMIT=50] -->
+    <string name="user_image_choose_photo">Choose an image</string>
+    <!-- Accessibility message for the photo selector which is a button/popup with the current photo [CHAR LIMIT=50] -->
+    <string name="user_image_photo_selector">Select photo</string>
+
     <!-- List entry in developer settings to choose default device/system behavior for the app freezer [CHAR LIMIT=30]-->
     <string name="cached_apps_freezer_device_default">Device default</string>
     <!-- List entry in developer settings to disable the app freezer in developer settings [CHAR LIMIT=30]-->
diff --git a/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java b/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java
index 450bdb1..a0c8663 100644
--- a/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java
+++ b/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java
@@ -129,10 +129,14 @@
         if (Log.isLoggable(TAG, Log.DEBUG)) {
             Log.d(TAG, "Found services for profile id " + profileId + ": " + resolveInfos);
         }
+
+        final PackageManager userPackageManager = mContext.createContextAsUser(
+                userHandle, /* flags */ 0).getPackageManager();
         List<InjectedSetting> settings = new ArrayList<InjectedSetting>(resolveInfos.size());
         for (ResolveInfo resolveInfo : resolveInfos) {
             try {
-                InjectedSetting setting = parseServiceInfo(resolveInfo, userHandle, pm);
+                InjectedSetting setting = parseServiceInfo(resolveInfo, userHandle,
+                        userPackageManager);
                 if (setting == null) {
                     Log.w(TAG, "Unable to load service info " + resolveInfo);
                 } else {
@@ -248,8 +252,7 @@
                         + SettingInjectorService.ATTRIBUTES_NAME + " tag");
             }
 
-            Resources res = pm.getResourcesForApplicationAsUser(si.packageName,
-                    userHandle.getIdentifier());
+            Resources res = pm.getResourcesForApplication(si.packageName);
             return parseAttributes(si.packageName, si.name, userHandle, res, attrs);
         } catch (PackageManager.NameNotFoundException e) {
             throw new XmlPullParserException(
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index 6c7e03f..f83c7a2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -361,6 +361,36 @@
         return null;
     }
 
+    boolean shouldDisableMediaOutput(String packageName) {
+        boolean shouldDisableMediaOutput = false;
+        if (TextUtils.isEmpty(packageName)) {
+            Log.w(TAG, "shouldDisableMediaOutput() package name is null or empty!");
+            return false;
+        }
+        final List<MediaRoute2Info> infos = mRouterManager.getAvailableRoutes(packageName);
+        if (infos.size() == 1) {
+            final MediaRoute2Info info = infos.get(0);
+            final int deviceType = info.getType();
+            switch (deviceType) {
+                case TYPE_UNKNOWN:
+                case TYPE_REMOTE_TV:
+                case TYPE_REMOTE_SPEAKER:
+                case TYPE_GROUP:
+                    shouldDisableMediaOutput = true;
+                    break;
+                default:
+                    shouldDisableMediaOutput = false;
+                    break;
+            }
+        }
+        if (DEBUG) {
+            Log.d(TAG, "shouldDisableMediaOutput() MediaRoute2Info size : " + infos.size()
+                    + ", package name : " + packageName + ", shouldDisableMediaOutput : "
+                    + shouldDisableMediaOutput);
+        }
+        return shouldDisableMediaOutput;
+    }
+
     private void refreshDevices() {
         mMediaDevices.clear();
         mCurrentConnectedDevice = null;
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index 72a6074..32419f4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -402,6 +402,13 @@
         return mPackageName;
     }
 
+    /**
+     * Returns {@code true} if needed to disable media output, otherwise returns {@code false}.
+     */
+    public boolean shouldDisableMediaOutput(String packageName) {
+        return mInfoMediaManager.shouldDisableMediaOutput(packageName);
+    }
+
     @VisibleForTesting
     MediaDevice updateCurrentConnectedDevice() {
         MediaDevice connectedDevice = null;
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java
index 28e993d..fc16eb6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java
@@ -92,4 +92,10 @@
      * SystemUi package name.
      */
     public static final String SYSTEMUI_PACKAGE_NAME = "com.android.systemui";
+
+    /**
+     * An intent action to close settings panel.
+     */
+    public static final String ACTION_CLOSE_PANEL =
+            "com.android.settings.panel.action.CLOSE_PANEL";
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/ActivityStarter.java b/packages/SettingsLib/src/com/android/settingslib/users/ActivityStarter.java
new file mode 100644
index 0000000..081b507
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/users/ActivityStarter.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.users;
+
+import android.content.Intent;
+
+/**
+ * An interface to start activities for result. This is used as a callback from controllers where
+ * activity starting isn't possible but we want to keep the intent building logic there.
+ */
+public interface ActivityStarter {
+
+    /**
+     * Launch an activity for which you would like a result when it finished.
+     */
+    void startActivityForResult(Intent intent, int requestCode);
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java b/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java
new file mode 100644
index 0000000..5859953
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.users;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.EditText;
+import android.widget.ImageView;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.internal.util.UserIcons;
+import com.android.settingslib.R;
+import com.android.settingslib.drawable.CircleFramedDrawable;
+
+import java.io.File;
+import java.util.function.BiConsumer;
+
+/**
+ * This class encapsulates a Dialog for editing the user nickname and photo.
+ */
+public class EditUserInfoController {
+
+    private static final String KEY_AWAITING_RESULT = "awaiting_result";
+    private static final String KEY_SAVED_PHOTO = "pending_photo";
+
+    private Dialog mEditUserInfoDialog;
+    private Bitmap mSavedPhoto;
+    private EditUserPhotoController mEditUserPhotoController;
+    private boolean mWaitingForActivityResult = false;
+    private final String mFileAuthority;
+
+    public EditUserInfoController(String fileAuthority) {
+        mFileAuthority = fileAuthority;
+    }
+
+    private void clear() {
+        if (mEditUserPhotoController != null) {
+            mEditUserPhotoController.removeNewUserPhotoBitmapFile();
+        }
+        mEditUserInfoDialog = null;
+        mSavedPhoto = null;
+    }
+
+    /**
+     * This should be called when the container activity/fragment got re-initialized from a
+     * previously saved state.
+     */
+    public void onRestoreInstanceState(Bundle icicle) {
+        String pendingPhoto = icicle.getString(KEY_SAVED_PHOTO);
+        if (pendingPhoto != null) {
+            mSavedPhoto = EditUserPhotoController.loadNewUserPhotoBitmap(new File(pendingPhoto));
+        }
+        mWaitingForActivityResult = icicle.getBoolean(KEY_AWAITING_RESULT, false);
+    }
+
+    /**
+     * Should be called from the container activity/fragment when it's onSaveInstanceState is
+     * called.
+     */
+    public void onSaveInstanceState(Bundle outState) {
+        if (mEditUserInfoDialog != null && mEditUserPhotoController != null) {
+            // Bitmap cannot be stored into bundle because it may exceed parcel limit
+            // Store it in a temporary file instead
+            File file = mEditUserPhotoController.saveNewUserPhotoBitmap();
+            if (file != null) {
+                outState.putString(KEY_SAVED_PHOTO, file.getPath());
+            }
+        }
+        outState.putBoolean(KEY_AWAITING_RESULT, mWaitingForActivityResult);
+    }
+
+    /**
+     * Should be called from the container activity/fragment when an activity has started for
+     * take/choose/crop photo actions.
+     */
+    public void startingActivityForResult() {
+        mWaitingForActivityResult = true;
+    }
+
+    /**
+     * Should be called from the container activity/fragment after it receives a result from
+     * take/choose/crop photo activity.
+     */
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        mWaitingForActivityResult = false;
+
+        if (mEditUserPhotoController != null && mEditUserInfoDialog != null) {
+            mEditUserPhotoController.onActivityResult(requestCode, resultCode, data);
+        }
+    }
+
+    /**
+     * Creates a user edit dialog with option to change the user's name and photo.
+     *
+     * @param activityStarter - ActivityStarter is called with appropriate intents and request
+     *                        codes to take photo/choose photo/crop photo.
+     */
+    public Dialog createDialog(Activity activity, ActivityStarter activityStarter,
+            @Nullable Drawable oldUserIcon, String defaultUserName, String title,
+            BiConsumer<String, Drawable> successCallback, Runnable cancelCallback) {
+        LayoutInflater inflater = LayoutInflater.from(activity);
+        View content = inflater.inflate(R.layout.edit_user_info_dialog_content, null);
+
+        EditText userNameView = content.findViewById(R.id.user_name);
+        userNameView.setText(defaultUserName);
+
+        ImageView userPhotoView = content.findViewById(R.id.user_photo);
+
+        // if oldUserIcon param is null then we use a default gray user icon
+        Drawable defaultUserIcon = oldUserIcon != null ? oldUserIcon : UserIcons.getDefaultUserIcon(
+                activity.getResources(), UserHandle.USER_NULL, false);
+        // in case a new photo was selected and the activity got recreated we have to load the image
+        Drawable userIcon = getUserIcon(activity, defaultUserIcon);
+        userPhotoView.setImageDrawable(userIcon);
+
+        if (canChangePhoto(activity)) {
+            mEditUserPhotoController = createEditUserPhotoController(activity, activityStarter,
+                    userPhotoView);
+        } else {
+            // some users can't change their photos so we need to remove suggestive
+            // background from the photoView
+            userPhotoView.setBackground(null);
+        }
+
+        mEditUserInfoDialog = buildDialog(activity, content, userNameView, oldUserIcon,
+                defaultUserName, title, successCallback, cancelCallback);
+
+        // Make sure the IME is up.
+        mEditUserInfoDialog.getWindow()
+                .setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
+
+        return mEditUserInfoDialog;
+    }
+
+    private Drawable getUserIcon(Activity activity, Drawable defaultUserIcon) {
+        if (mSavedPhoto != null) {
+            return CircleFramedDrawable.getInstance(activity, mSavedPhoto);
+        }
+        return defaultUserIcon;
+    }
+
+    private Dialog buildDialog(Activity activity, View content, EditText userNameView,
+            @Nullable Drawable oldUserIcon, String defaultUserName, String title,
+            BiConsumer<String, Drawable> successCallback, Runnable cancelCallback) {
+        return new AlertDialog.Builder(activity)
+                .setTitle(title)
+                .setView(content)
+                .setCancelable(true)
+                .setPositiveButton(android.R.string.ok, (dialog, which) -> {
+                    Drawable newUserIcon = mEditUserPhotoController != null
+                            ? mEditUserPhotoController.getNewUserPhotoDrawable()
+                            : null;
+                    Drawable userIcon = newUserIcon != null
+                            ? newUserIcon
+                            : oldUserIcon;
+
+                    String newName = userNameView.getText().toString().trim();
+                    String userName = !newName.isEmpty() ? newName : defaultUserName;
+
+                    clear();
+                    if (successCallback != null) {
+                        successCallback.accept(userName, userIcon);
+                    }
+                })
+                .setNegativeButton(android.R.string.cancel, (dialog, which) -> {
+                    clear();
+                    if (cancelCallback != null) {
+                        cancelCallback.run();
+                    }
+                })
+                .setOnCancelListener(dialog -> {
+                    clear();
+                    if (cancelCallback != null) {
+                        cancelCallback.run();
+                    }
+                })
+                .create();
+    }
+
+    @VisibleForTesting
+    boolean canChangePhoto(Context context) {
+        return (PhotoCapabilityUtils.canCropPhoto(context)
+                && PhotoCapabilityUtils.canChoosePhoto(context))
+                || PhotoCapabilityUtils.canTakePhoto(context);
+    }
+
+    @VisibleForTesting
+    EditUserPhotoController createEditUserPhotoController(Activity activity,
+            ActivityStarter activityStarter, ImageView userPhotoView) {
+        return new EditUserPhotoController(activity, activityStarter, userPhotoView,
+                mSavedPhoto, mWaitingForActivityResult, mFileAuthority);
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java b/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java
new file mode 100644
index 0000000..ecd4066
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java
@@ -0,0 +1,509 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.users;
+
+import android.app.Activity;
+import android.content.ClipData;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+import android.media.ExifInterface;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.StrictMode;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.ContactsContract.DisplayPhoto;
+import android.provider.MediaStore;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.ListPopupWindow;
+import android.widget.TextView;
+
+import androidx.core.content.FileProvider;
+
+import com.android.settingslib.R;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.RestrictedLockUtilsInternal;
+import com.android.settingslib.drawable.CircleFramedDrawable;
+
+import libcore.io.Streams;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class contains logic for starting activities to take/choose/crop photo, reads and transforms
+ * the result image.
+ */
+public class EditUserPhotoController {
+    private static final String TAG = "EditUserPhotoController";
+
+    // It seems that this class generates custom request codes and they may
+    // collide with ours, these values are very unlikely to have a conflict.
+    private static final int REQUEST_CODE_CHOOSE_PHOTO = 1001;
+    private static final int REQUEST_CODE_TAKE_PHOTO = 1002;
+    private static final int REQUEST_CODE_CROP_PHOTO = 1003;
+    // in rare cases we get a null Cursor when querying for DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI
+    // so we need a default photo size
+    private static final int DEFAULT_PHOTO_SIZE = 500;
+
+    private static final String IMAGES_DIR = "multi_user";
+    private static final String CROP_PICTURE_FILE_NAME = "CropEditUserPhoto.jpg";
+    private static final String TAKE_PICTURE_FILE_NAME = "TakeEditUserPhoto.jpg";
+    private static final String NEW_USER_PHOTO_FILE_NAME = "NewUserPhoto.png";
+
+    private final int mPhotoSize;
+
+    private final Activity mActivity;
+    private final ActivityStarter mActivityStarter;
+    private final ImageView mImageView;
+    private final String mFileAuthority;
+
+    private final File mImagesDir;
+    private final Uri mCropPictureUri;
+    private final Uri mTakePictureUri;
+
+    private Bitmap mNewUserPhotoBitmap;
+    private Drawable mNewUserPhotoDrawable;
+
+    public EditUserPhotoController(Activity activity, ActivityStarter activityStarter,
+            ImageView view, Bitmap bitmap, boolean waiting, String fileAuthority) {
+        mActivity = activity;
+        mActivityStarter = activityStarter;
+        mImageView = view;
+        mFileAuthority = fileAuthority;
+
+        mImagesDir = new File(activity.getCacheDir(), IMAGES_DIR);
+        mImagesDir.mkdir();
+        mCropPictureUri = createTempImageUri(activity, CROP_PICTURE_FILE_NAME, !waiting);
+        mTakePictureUri = createTempImageUri(activity, TAKE_PICTURE_FILE_NAME, !waiting);
+        mPhotoSize = getPhotoSize(activity);
+        mImageView.setOnClickListener(v -> showUpdatePhotoPopup());
+        mNewUserPhotoBitmap = bitmap;
+    }
+
+    /**
+     * Handles activity result from containing activity/fragment after a take/choose/crop photo
+     * action result is received.
+     */
+    public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (resultCode != Activity.RESULT_OK) {
+            return false;
+        }
+        final Uri pictureUri = data != null && data.getData() != null
+                ? data.getData() : mTakePictureUri;
+        switch (requestCode) {
+            case REQUEST_CODE_CROP_PHOTO:
+                onPhotoCropped(pictureUri);
+                return true;
+            case REQUEST_CODE_TAKE_PHOTO:
+            case REQUEST_CODE_CHOOSE_PHOTO:
+                if (mTakePictureUri.equals(pictureUri)) {
+                    if (PhotoCapabilityUtils.canCropPhoto(mActivity)) {
+                        cropPhoto();
+                    } else {
+                        onPhotoNotCropped(pictureUri);
+                    }
+                } else {
+                    copyAndCropPhoto(pictureUri);
+                }
+                return true;
+        }
+        return false;
+    }
+
+    public Drawable getNewUserPhotoDrawable() {
+        return mNewUserPhotoDrawable;
+    }
+
+    private void showUpdatePhotoPopup() {
+        final Context context = mImageView.getContext();
+        final boolean canTakePhoto = PhotoCapabilityUtils.canTakePhoto(context);
+        final boolean canChoosePhoto = PhotoCapabilityUtils.canChoosePhoto(context);
+
+        if (!canTakePhoto && !canChoosePhoto) {
+            return;
+        }
+
+        final List<EditUserPhotoController.RestrictedMenuItem> items = new ArrayList<>();
+
+        if (canTakePhoto) {
+            final String title = context.getString(R.string.user_image_take_photo);
+            items.add(new RestrictedMenuItem(context, title, UserManager.DISALLOW_SET_USER_ICON,
+                    this::takePhoto));
+        }
+
+        if (canChoosePhoto) {
+            final String title = context.getString(R.string.user_image_choose_photo);
+            items.add(new RestrictedMenuItem(context, title, UserManager.DISALLOW_SET_USER_ICON,
+                    this::choosePhoto));
+        }
+
+        final ListPopupWindow listPopupWindow = new ListPopupWindow(context);
+
+        listPopupWindow.setAnchorView(mImageView);
+        listPopupWindow.setModal(true);
+        listPopupWindow.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
+        listPopupWindow.setAdapter(new RestrictedPopupMenuAdapter(context, items));
+
+        final int width = Math.max(mImageView.getWidth(), context.getResources()
+                .getDimensionPixelSize(R.dimen.update_user_photo_popup_min_width));
+        listPopupWindow.setWidth(width);
+        listPopupWindow.setDropDownGravity(Gravity.START);
+
+        listPopupWindow.setOnItemClickListener((parent, view, position, id) -> {
+            listPopupWindow.dismiss();
+            final RestrictedMenuItem item =
+                    (RestrictedMenuItem) parent.getAdapter().getItem(position);
+            item.doAction();
+        });
+
+        listPopupWindow.show();
+    }
+
+    private void takePhoto() {
+        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE_SECURE);
+        appendOutputExtra(intent, mTakePictureUri);
+        mActivityStarter.startActivityForResult(intent, REQUEST_CODE_TAKE_PHOTO);
+    }
+
+    private void choosePhoto() {
+        Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
+        intent.setType("image/*");
+        appendOutputExtra(intent, mTakePictureUri);
+        mActivityStarter.startActivityForResult(intent, REQUEST_CODE_CHOOSE_PHOTO);
+    }
+
+    private void copyAndCropPhoto(final Uri pictureUri) {
+        // TODO: Replace AsyncTask
+        new AsyncTask<Void, Void, Void>() {
+            @Override
+            protected Void doInBackground(Void... params) {
+                final ContentResolver cr = mActivity.getContentResolver();
+                try (InputStream in = cr.openInputStream(pictureUri);
+                     OutputStream out = cr.openOutputStream(mTakePictureUri)) {
+                    Streams.copy(in, out);
+                } catch (IOException e) {
+                    Log.w(TAG, "Failed to copy photo", e);
+                }
+                return null;
+            }
+
+            @Override
+            protected void onPostExecute(Void result) {
+                if (!mActivity.isFinishing() && !mActivity.isDestroyed()) {
+                    cropPhoto();
+                }
+            }
+        }.execute();
+    }
+
+    private void cropPhoto() {
+        // TODO: Use a public intent, when there is one.
+        Intent intent = new Intent("com.android.camera.action.CROP");
+        intent.setDataAndType(mTakePictureUri, "image/*");
+        appendOutputExtra(intent, mCropPictureUri);
+        appendCropExtras(intent);
+        if (intent.resolveActivity(mActivity.getPackageManager()) != null) {
+            try {
+                StrictMode.disableDeathOnFileUriExposure();
+                mActivityStarter.startActivityForResult(intent, REQUEST_CODE_CROP_PHOTO);
+            } finally {
+                StrictMode.enableDeathOnFileUriExposure();
+            }
+        } else {
+            onPhotoNotCropped(mTakePictureUri);
+        }
+    }
+
+    private void appendOutputExtra(Intent intent, Uri pictureUri) {
+        intent.putExtra(MediaStore.EXTRA_OUTPUT, pictureUri);
+        intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+                | Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        intent.setClipData(ClipData.newRawUri(MediaStore.EXTRA_OUTPUT, pictureUri));
+    }
+
+    private void appendCropExtras(Intent intent) {
+        intent.putExtra("crop", "true");
+        intent.putExtra("scale", true);
+        intent.putExtra("scaleUpIfNeeded", true);
+        intent.putExtra("aspectX", 1);
+        intent.putExtra("aspectY", 1);
+        intent.putExtra("outputX", mPhotoSize);
+        intent.putExtra("outputY", mPhotoSize);
+    }
+
+    private void onPhotoCropped(final Uri data) {
+        // TODO: Replace AsyncTask to avoid possible memory leaks and handle configuration change
+        new AsyncTask<Void, Void, Bitmap>() {
+            @Override
+            protected Bitmap doInBackground(Void... params) {
+                InputStream imageStream = null;
+                try {
+                    imageStream = mActivity.getContentResolver()
+                            .openInputStream(data);
+                    return BitmapFactory.decodeStream(imageStream);
+                } catch (FileNotFoundException fe) {
+                    Log.w(TAG, "Cannot find image file", fe);
+                    return null;
+                } finally {
+                    if (imageStream != null) {
+                        try {
+                            imageStream.close();
+                        } catch (IOException ioe) {
+                            Log.w(TAG, "Cannot close image stream", ioe);
+                        }
+                    }
+                }
+            }
+
+            @Override
+            protected void onPostExecute(Bitmap bitmap) {
+                onPhotoProcessed(bitmap);
+
+            }
+        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
+    }
+
+    private void onPhotoNotCropped(final Uri data) {
+        // TODO: Replace AsyncTask to avoid possible memory leaks and handle configuration change
+        new AsyncTask<Void, Void, Bitmap>() {
+            @Override
+            protected Bitmap doInBackground(Void... params) {
+                // Scale and crop to a square aspect ratio
+                Bitmap croppedImage = Bitmap.createBitmap(mPhotoSize, mPhotoSize,
+                        Config.ARGB_8888);
+                Canvas canvas = new Canvas(croppedImage);
+                Bitmap fullImage;
+                try {
+                    InputStream imageStream = mActivity.getContentResolver()
+                            .openInputStream(data);
+                    fullImage = BitmapFactory.decodeStream(imageStream);
+                } catch (FileNotFoundException fe) {
+                    return null;
+                }
+                if (fullImage != null) {
+                    int rotation = getRotation(mActivity, data);
+                    final int squareSize = Math.min(fullImage.getWidth(),
+                            fullImage.getHeight());
+                    final int left = (fullImage.getWidth() - squareSize) / 2;
+                    final int top = (fullImage.getHeight() - squareSize) / 2;
+
+                    Matrix matrix = new Matrix();
+                    RectF rectSource = new RectF(left, top,
+                            left + squareSize, top + squareSize);
+                    RectF rectDest = new RectF(0, 0, mPhotoSize, mPhotoSize);
+                    matrix.setRectToRect(rectSource, rectDest, Matrix.ScaleToFit.CENTER);
+                    matrix.postRotate(rotation, mPhotoSize / 2f, mPhotoSize / 2f);
+                    canvas.drawBitmap(fullImage, matrix, new Paint());
+                    return croppedImage;
+                } else {
+                    // Bah! Got nothin.
+                    return null;
+                }
+            }
+
+            @Override
+            protected void onPostExecute(Bitmap bitmap) {
+                onPhotoProcessed(bitmap);
+            }
+        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
+    }
+
+    /**
+     * Reads the image's exif data and determines the rotation degree needed to display the image
+     * in portrait mode.
+     */
+    private int getRotation(Context context, Uri selectedImage) {
+        int rotation = -1;
+        try {
+            InputStream imageStream = context.getContentResolver().openInputStream(selectedImage);
+            ExifInterface exif = new ExifInterface(imageStream);
+            rotation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, -1);
+        } catch (IOException exception) {
+            Log.e(TAG, "Error while getting rotation", exception);
+        }
+
+        switch (rotation) {
+            case ExifInterface.ORIENTATION_ROTATE_90:
+                return 90;
+            case ExifInterface.ORIENTATION_ROTATE_180:
+                return 180;
+            case ExifInterface.ORIENTATION_ROTATE_270:
+                return 270;
+            default:
+                return 0;
+        }
+    }
+
+    private void onPhotoProcessed(Bitmap bitmap) {
+        if (bitmap != null) {
+            mNewUserPhotoBitmap = bitmap;
+            mNewUserPhotoDrawable = CircleFramedDrawable
+                    .getInstance(mImageView.getContext(), mNewUserPhotoBitmap);
+            mImageView.setImageDrawable(mNewUserPhotoDrawable);
+        }
+        new File(mImagesDir, TAKE_PICTURE_FILE_NAME).delete();
+        new File(mImagesDir, CROP_PICTURE_FILE_NAME).delete();
+    }
+
+    private static int getPhotoSize(Context context) {
+        try (Cursor cursor = context.getContentResolver().query(
+                DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI,
+                new String[]{DisplayPhoto.DISPLAY_MAX_DIM}, null, null, null)) {
+            if (cursor != null) {
+                cursor.moveToFirst();
+                return cursor.getInt(0);
+            } else {
+                return DEFAULT_PHOTO_SIZE;
+            }
+        }
+    }
+
+    private Uri createTempImageUri(Context context, String fileName, boolean purge) {
+        final File fullPath = new File(mImagesDir, fileName);
+        if (purge) {
+            fullPath.delete();
+        }
+        return FileProvider.getUriForFile(context, mFileAuthority, fullPath);
+    }
+
+    File saveNewUserPhotoBitmap() {
+        if (mNewUserPhotoBitmap == null) {
+            return null;
+        }
+        try {
+            File file = new File(mImagesDir, NEW_USER_PHOTO_FILE_NAME);
+            OutputStream os = new FileOutputStream(file);
+            mNewUserPhotoBitmap.compress(Bitmap.CompressFormat.PNG, 100, os);
+            os.flush();
+            os.close();
+            return file;
+        } catch (IOException e) {
+            Log.e(TAG, "Cannot create temp file", e);
+        }
+        return null;
+    }
+
+    static Bitmap loadNewUserPhotoBitmap(File file) {
+        return BitmapFactory.decodeFile(file.getAbsolutePath());
+    }
+
+    void removeNewUserPhotoBitmapFile() {
+        new File(mImagesDir, NEW_USER_PHOTO_FILE_NAME).delete();
+    }
+
+    private static final class RestrictedMenuItem {
+        private final Context mContext;
+        private final String mTitle;
+        private final Runnable mAction;
+        private final RestrictedLockUtils.EnforcedAdmin mAdmin;
+        // Restriction may be set by system or something else via UserManager.setUserRestriction().
+        private final boolean mIsRestrictedByBase;
+
+        /**
+         * The menu item, used for popup menu. Any element of such a menu can be disabled by admin.
+         *
+         * @param context     A context.
+         * @param title       The title of the menu item.
+         * @param restriction The restriction, that if is set, blocks the menu item.
+         * @param action      The action on menu item click.
+         */
+        RestrictedMenuItem(Context context, String title, String restriction,
+                Runnable action) {
+            mContext = context;
+            mTitle = title;
+            mAction = action;
+
+            final int myUserId = UserHandle.myUserId();
+            mAdmin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced(context,
+                    restriction, myUserId);
+            mIsRestrictedByBase = RestrictedLockUtilsInternal.hasBaseUserRestriction(mContext,
+                    restriction, myUserId);
+        }
+
+        @Override
+        public String toString() {
+            return mTitle;
+        }
+
+        void doAction() {
+            if (isRestrictedByBase()) {
+                return;
+            }
+
+            if (isRestrictedByAdmin()) {
+                RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext, mAdmin);
+                return;
+            }
+
+            mAction.run();
+        }
+
+        boolean isRestrictedByAdmin() {
+            return mAdmin != null;
+        }
+
+        boolean isRestrictedByBase() {
+            return mIsRestrictedByBase;
+        }
+    }
+
+    /**
+     * Provide this adapter to ListPopupWindow.setAdapter() to have a popup window menu, where
+     * any element can be restricted by admin (profile owner or device owner).
+     */
+    private static final class RestrictedPopupMenuAdapter extends ArrayAdapter<RestrictedMenuItem> {
+        RestrictedPopupMenuAdapter(Context context, List<RestrictedMenuItem> items) {
+            super(context, R.layout.restricted_popup_menu_item, R.id.text, items);
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            final View view = super.getView(position, convertView, parent);
+            final RestrictedMenuItem item = getItem(position);
+            final TextView text = (TextView) view.findViewById(R.id.text);
+            final ImageView image = (ImageView) view.findViewById(R.id.restricted_icon);
+
+            text.setEnabled(!item.isRestrictedByAdmin() && !item.isRestrictedByBase());
+            image.setVisibility(item.isRestrictedByAdmin() && !item.isRestrictedByBase()
+                    ? ImageView.VISIBLE : ImageView.GONE);
+
+            return view;
+        }
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/PhotoCapabilityUtils.java b/packages/SettingsLib/src/com/android/settingslib/users/PhotoCapabilityUtils.java
new file mode 100644
index 0000000..165c280
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/users/PhotoCapabilityUtils.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.users;
+
+import android.app.KeyguardManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.provider.MediaStore;
+
+/**
+ * Utility class that contains helper methods to determine if the current user has permission and
+ * the device is in a proper state to start an activity for a given action.
+ */
+public class PhotoCapabilityUtils {
+
+    /**
+     * Check if the current user can perform any activity for
+     * android.media.action.IMAGE_CAPTURE action.
+     */
+    public static boolean canTakePhoto(Context context) {
+        return context.getPackageManager().queryIntentActivities(
+                new Intent(MediaStore.ACTION_IMAGE_CAPTURE),
+                PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
+    }
+
+    /**
+     * Check if the current user can perform any activity for
+     * android.intent.action.GET_CONTENT action for images.
+     * Returns false if the device is currently locked and
+     * requires a PIN, pattern or password to unlock.
+     */
+    public static boolean canChoosePhoto(Context context) {
+        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+        intent.setType("image/*");
+        boolean canPerformActivityForGetImage =
+                context.getPackageManager().queryIntentActivities(intent, 0).size() > 0;
+        // on locked device we can't access the images
+        return canPerformActivityForGetImage && !isDeviceLocked(context);
+    }
+
+    /**
+     * Check if the current user can perform any activity for
+     * com.android.camera.action.CROP action for images.
+     * Returns false if the device is currently locked and
+     * requires a PIN, pattern or password to unlock.
+     */
+    public static boolean canCropPhoto(Context context) {
+        Intent intent = new Intent("com.android.camera.action.CROP");
+        intent.setType("image/*");
+        boolean canPerformActivityForCropping =
+                context.getPackageManager().queryIntentActivities(intent, 0).size() > 0;
+        // on locked device we can't start a cropping activity
+        return canPerformActivityForCropping && !isDeviceLocked(context);
+    }
+
+    private static boolean isDeviceLocked(Context context) {
+        KeyguardManager keyguardManager = context.getSystemService(KeyguardManager.class);
+        return keyguardManager == null || keyguardManager.isDeviceLocked();
+    }
+
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/UserCreatingDialog.java b/packages/SettingsLib/src/com/android/settingslib/users/UserCreatingDialog.java
new file mode 100644
index 0000000..075635c
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/users/UserCreatingDialog.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.users;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.TextView;
+
+import com.android.settingslib.R;
+
+/**
+ * Dialog to show when a user creation is in progress.
+ */
+public class UserCreatingDialog extends AlertDialog {
+
+    public UserCreatingDialog(Context context) {
+        // hardcoding theme to be consistent with UserSwitchingDialog's theme
+        // todo replace both to adapt to the device's theme
+        super(context, com.android.internal.R.style.Theme_DeviceDefault_Light_Dialog_Alert);
+
+        inflateContent();
+        getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
+
+        WindowManager.LayoutParams attrs = getWindow().getAttributes();
+        attrs.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR
+                | WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+        getWindow().setAttributes(attrs);
+    }
+
+    private void inflateContent() {
+        // using the same design as UserSwitchingDialog
+        setCancelable(false);
+        View view = LayoutInflater.from(getContext())
+                .inflate(R.layout.user_creation_progress_dialog, null);
+        String message = getContext().getString(R.string.creating_new_user_dialog_message);
+        view.setAccessibilityPaneTitle(message);
+        ((TextView) view.findViewById(R.id.message)).setText(message);
+        setView(view);
+    }
+
+}
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 81cf118..b0a9136 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
@@ -1090,7 +1090,7 @@
 
         // Verify second update AP is the same object as the first update AP
         assertThat(passpointAccessPointsFirstUpdate.get(0))
-                .isSameAs(passpointAccessPointsSecondUpdate.get(0));
+                .isSameInstanceAs(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);
@@ -1210,7 +1210,8 @@
                 providersAndScans, cachedAccessPoints);
 
         // Verify second update AP is the same object as the first update AP
-        assertThat(osuAccessPointsFirstUpdate.get(0)).isSameAs(osuAccessPointsSecondUpdate.get(0));
+        assertThat(osuAccessPointsFirstUpdate.get(0))
+                .isSameInstanceAs(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/deviceinfo/IpAddressPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java
index a83d7e0..b392c5e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java
@@ -69,7 +69,7 @@
 
         assertWithMessage("Intent filter should contain expected intents")
                 .that(ipAddressPreferenceController.getConnectivityIntents())
-                .asList().containsAllIn(expectedIntents);
+                .asList().containsAtLeastElementsIn(expectedIntents);
     }
 
     private static class ConcreteIpAddressPreferenceController extends
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
index 40b9b13..3705267 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
@@ -90,7 +90,7 @@
 
         assertWithMessage("Intent filter should contain expected intents")
                 .that(mController.getConnectivityIntents())
-                .asList().containsAllIn(expectedIntents);
+                .asList().containsAtLeastElementsIn(expectedIntents);
     }
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
index 1769053..906e06e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
@@ -284,7 +284,7 @@
 
         assertThat(outTiles).hasSize(1);
         final Bundle newMetaData = outTiles.get(0).getMetaData();
-        assertThat(newMetaData).isNotSameAs(oldMetadata);
+        assertThat(newMetaData).isNotSameInstanceAs(oldMetadata);
     }
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index 68f162e..58ca734 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -27,6 +27,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
@@ -721,4 +722,47 @@
 
         assertThat(mInfoMediaManager.mMediaDevices.size()).isEqualTo(0);
     }
+
+    @Test
+    public void shouldDisableMediaOutput_infosSizeEqual1_returnsTrue() {
+        final MediaRoute2Info info = mock(MediaRoute2Info.class);
+        final List<MediaRoute2Info> infos = new ArrayList<>();
+        infos.add(info);
+        mShadowRouter2Manager.setAvailableRoutes(infos);
+
+        when(mRouterManager.getAvailableRoutes(anyString())).thenReturn(infos);
+        when(info.getType()).thenReturn(TYPE_REMOTE_SPEAKER);
+
+        assertThat(mInfoMediaManager.shouldDisableMediaOutput("test")).isTrue();
+    }
+
+    @Test
+    public void shouldDisableMediaOutput_infosSizeEqual1AndNotCastDevice_returnsFalse() {
+        final MediaRoute2Info info = mock(MediaRoute2Info.class);
+        final List<MediaRoute2Info> infos = new ArrayList<>();
+        infos.add(info);
+        mShadowRouter2Manager.setAvailableRoutes(infos);
+
+        when(mRouterManager.getAvailableRoutes(anyString())).thenReturn(infos);
+        when(info.getType()).thenReturn(TYPE_BUILTIN_SPEAKER);
+
+        assertThat(mInfoMediaManager.shouldDisableMediaOutput("test")).isFalse();
+    }
+
+
+    @Test
+    public void shouldDisableMediaOutput_infosSizeOverThan1_returnsFalse() {
+        final MediaRoute2Info info = mock(MediaRoute2Info.class);
+        final MediaRoute2Info info2 = mock(MediaRoute2Info.class);
+        final List<MediaRoute2Info> infos = new ArrayList<>();
+        infos.add(info);
+        infos.add(info2);
+        mShadowRouter2Manager.setAvailableRoutes(infos);
+
+        when(mRouterManager.getAvailableRoutes(anyString())).thenReturn(infos);
+        when(info.getType()).thenReturn(TYPE_REMOTE_SPEAKER);
+        when(info2.getType()).thenReturn(TYPE_REMOTE_SPEAKER);
+
+        assertThat(mInfoMediaManager.shouldDisableMediaOutput("test")).isFalse();
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java
new file mode 100644
index 0000000..d6c8816
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.users;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.widget.EditText;
+import android.widget.ImageView;
+
+import androidx.fragment.app.FragmentActivity;
+
+import com.android.settingslib.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.android.controller.ActivityController;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowDialog;
+
+import java.util.function.BiConsumer;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+@RunWith(RobolectricTestRunner.class)
+public class EditUserInfoControllerTest {
+    private static final int MAX_USER_NAME_LENGTH = 100;
+
+    @Mock
+    private Drawable mCurrentIcon;
+    @Mock
+    private ActivityStarter mActivityStarter;
+
+    private boolean mCanChangePhoto;
+    private Activity mActivity;
+    private TestEditUserInfoController mController;
+
+    public class TestEditUserInfoController extends EditUserInfoController {
+        private EditUserPhotoController mPhotoController;
+
+        TestEditUserInfoController() {
+            super("file_authority");
+        }
+
+        private EditUserPhotoController getPhotoController() {
+            return mPhotoController;
+        }
+
+        @Override
+        EditUserPhotoController createEditUserPhotoController(Activity activity,
+                ActivityStarter activityStarter, ImageView userPhotoView) {
+            mPhotoController = mock(EditUserPhotoController.class, Answers.RETURNS_DEEP_STUBS);
+            return mPhotoController;
+        }
+
+        @Override
+        boolean canChangePhoto(Context context) {
+            return mCanChangePhoto;
+        }
+    }
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mActivity = spy(ActivityController.of(new FragmentActivity()).get());
+        mActivity.setTheme(R.style.Theme_AppCompat_DayNight);
+        mController = new TestEditUserInfoController();
+        mCanChangePhoto = true;
+    }
+
+    @Test
+    public void photoControllerOnActivityResult_whenWaiting_isCalled() {
+        mController.createDialog(mActivity, mActivityStarter, mCurrentIcon, "test user",
+                "title", null, null);
+        mController.startingActivityForResult();
+        Intent resultData = new Intent();
+        mController.onActivityResult(0, 0, resultData);
+        EditUserPhotoController photoController = mController.getPhotoController();
+
+        assertThat(photoController).isNotNull();
+        verify(photoController).onActivityResult(0, 0, resultData);
+    }
+
+    @Test
+    @Config(shadows = ShadowDialog.class)
+    public void userNameView_inputLongName_shouldBeConstrained() {
+        // generate a string of 200 'A's
+        final String longName = Stream.generate(
+                () -> String.valueOf('A')).limit(200).collect(Collectors.joining());
+
+        final AlertDialog dialog = (AlertDialog) mController.createDialog(mActivity,
+                mActivityStarter, mCurrentIcon,
+                "test user", "title", null,
+                null);
+        dialog.show();
+        final EditText userNameEditText = dialog.findViewById(R.id.user_name);
+        userNameEditText.setText(longName);
+
+        assertThat(userNameEditText.getText().length()).isEqualTo(MAX_USER_NAME_LENGTH);
+    }
+
+    @Test
+    public void cancelCallback_isCalled_whenCancelled() {
+        BiConsumer<String, Drawable> successCallback = mock(BiConsumer.class);
+        Runnable cancelCallback = mock(Runnable.class);
+
+        AlertDialog dialog = (AlertDialog) mController.createDialog(
+                mActivity, mActivityStarter, mCurrentIcon, "test",
+                "title", successCallback, cancelCallback);
+        dialog.show();
+        dialog.cancel();
+
+        verifyZeroInteractions(successCallback);
+        verify(cancelCallback, times(1))
+                .run();
+    }
+
+    @Test
+    public void cancelCallback_isCalled_whenNegativeClicked() {
+        BiConsumer<String, Drawable> successCallback = mock(BiConsumer.class);
+        Runnable cancelCallback = mock(Runnable.class);
+
+        AlertDialog dialog = (AlertDialog) mController.createDialog(
+                mActivity, mActivityStarter, mCurrentIcon, "test",
+                "title", successCallback, cancelCallback);
+        dialog.show();
+        dialog.getButton(Dialog.BUTTON_NEGATIVE).performClick();
+
+        verifyZeroInteractions(successCallback);
+        verify(cancelCallback, times(1))
+                .run();
+    }
+
+    @Test
+    public void successCallback_isCalled_whenNothingChanged() {
+        BiConsumer<String, Drawable> successCallback = mock(BiConsumer.class);
+        Runnable cancelCallback = mock(Runnable.class);
+
+        Drawable oldUserIcon = mCurrentIcon;
+        AlertDialog dialog = (AlertDialog) mController.createDialog(
+                mActivity, mActivityStarter, oldUserIcon, "test",
+                "title", successCallback, cancelCallback);
+        // No change to the photo.
+        when(mController.getPhotoController().getNewUserPhotoDrawable()).thenReturn(null);
+        dialog.show();
+        dialog.getButton(Dialog.BUTTON_POSITIVE).performClick();
+
+        verify(successCallback, times(1))
+                .accept("test", oldUserIcon);
+        verifyZeroInteractions(cancelCallback);
+    }
+
+    @Test
+    public void successCallback_calledWithNullIcon_whenOldIconIsNullAndNothingChanged() {
+        BiConsumer<String, Drawable> successCallback = mock(BiConsumer.class);
+        Runnable cancelCallback = mock(Runnable.class);
+
+        AlertDialog dialog = (AlertDialog) mController.createDialog(
+                mActivity, mActivityStarter, null, "test",
+                "title", successCallback, cancelCallback);
+        // No change to the photo.
+        when(mController.getPhotoController().getNewUserPhotoDrawable()).thenReturn(null);
+        dialog.show();
+        dialog.getButton(Dialog.BUTTON_POSITIVE).performClick();
+
+        verify(successCallback, times(1))
+                .accept("test", null);
+        verifyZeroInteractions(cancelCallback);
+    }
+
+    @Test
+    public void successCallback_isCalled_whenLabelChanges() {
+        BiConsumer<String, Drawable> successCallback = mock(BiConsumer.class);
+        Runnable cancelCallback = mock(Runnable.class);
+
+        AlertDialog dialog = (AlertDialog) mController.createDialog(
+                mActivity, mActivityStarter, mCurrentIcon, "test",
+                "title", successCallback, cancelCallback);
+        // No change to the photo.
+        when(mController.getPhotoController().getNewUserPhotoDrawable()).thenReturn(null);
+        dialog.show();
+        String expectedNewName = "new test user";
+        EditText editText = (EditText) dialog.findViewById(R.id.user_name);
+        editText.setText(expectedNewName);
+        dialog.getButton(Dialog.BUTTON_POSITIVE).performClick();
+
+        verify(successCallback, times(1))
+                .accept(expectedNewName, mCurrentIcon);
+        verifyZeroInteractions(cancelCallback);
+    }
+
+    @Test
+    public void successCallback_isCalled_whenPhotoChanges() {
+        BiConsumer<String, Drawable> successCallback = mock(BiConsumer.class);
+        Runnable cancelCallback = mock(Runnable.class);
+
+        AlertDialog dialog = (AlertDialog) mController.createDialog(
+                mActivity, mActivityStarter, mCurrentIcon, "test",
+                "title", successCallback, cancelCallback);
+        // A different drawable.
+        Drawable newPhoto = mock(Drawable.class);
+        when(mController.getPhotoController().getNewUserPhotoDrawable()).thenReturn(newPhoto);
+        dialog.show();
+        dialog.getButton(Dialog.BUTTON_POSITIVE).performClick();
+
+        verify(successCallback, times(1))
+                .accept("test", newPhoto);
+        verifyZeroInteractions(cancelCallback);
+    }
+
+    @Test
+    public void successCallback_isCalledWithChangedPhoto_whenOldIconIsNullAndPhotoChanges() {
+        BiConsumer<String, Drawable> successCallback = mock(BiConsumer.class);
+        Runnable cancelCallback = mock(Runnable.class);
+
+        AlertDialog dialog = (AlertDialog) mController.createDialog(
+                mActivity, mActivityStarter, null, "test",
+                "title", successCallback, cancelCallback);
+        // A different drawable.
+        Drawable newPhoto = mock(Drawable.class);
+        when(mController.getPhotoController().getNewUserPhotoDrawable()).thenReturn(newPhoto);
+        dialog.show();
+        dialog.getButton(Dialog.BUTTON_POSITIVE).performClick();
+
+        verify(successCallback, times(1))
+                .accept("test", newPhoto);
+        verifyZeroInteractions(cancelCallback);
+    }
+
+    @Test
+    public void createDialog_canNotChangePhoto_nullPhotoController() {
+        mCanChangePhoto = false;
+
+        mController.createDialog(mActivity, mActivityStarter, mCurrentIcon,
+                "test", "title", null, null);
+
+        assertThat(mController.mPhotoController).isNull();
+    }
+}
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 51f69a9..5ad43e3 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -240,4 +240,7 @@
 
     <!-- Default for setting for Settings.Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED -->
     <bool name="def_hdmiControlAutoDeviceOff">false</bool>
+
+    <!-- Default for Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY -->
+    <integer name="def_accessibility_magnification_capabilities">3</integer>
 </resources>
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 4db61b0..c1bdb56 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -60,6 +60,9 @@
         Settings.Secure.ACCESSIBILITY_CAPTIONING_TYPEFACE,
         Settings.Secure.ACCESSIBILITY_CAPTIONING_FONT_SCALE,
         Settings.Secure.ACCESSIBILITY_CAPTIONING_WINDOW_COLOR,
+        Settings.Secure.FORCE_BOLD_TEXT,
+        Settings.Secure.REDUCE_BRIGHT_COLORS_LEVEL,
+        Settings.Secure.REDUCE_BRIGHT_COLORS_PERSIST_ACROSS_REBOOTS,
         Settings.Secure.TTS_DEFAULT_RATE,
         Settings.Secure.TTS_DEFAULT_PITCH,
         Settings.Secure.TTS_DEFAULT_SYNTH,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 1fde40c..388bf28 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -29,6 +29,7 @@
 import static android.provider.settings.validators.SettingsValidators.NON_NEGATIVE_INTEGER_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.NULLABLE_COMPONENT_NAME_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.PACKAGE_NAME_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.PERCENTAGE_INTEGER_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.TILE_LIST_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.TTS_LIST_VALIDATOR;
 
@@ -101,6 +102,9 @@
                 Secure.ACCESSIBILITY_CAPTIONING_FONT_SCALE,
                 new InclusiveFloatRangeValidator(0.5f, 2.0f));
         VALIDATORS.put(Secure.ACCESSIBILITY_CAPTIONING_WINDOW_COLOR, ANY_INTEGER_VALIDATOR);
+        VALIDATORS.put(Secure.FORCE_BOLD_TEXT, new DiscreteValueValidator(new String[] {"1", "2"}));
+        VALIDATORS.put(Secure.REDUCE_BRIGHT_COLORS_LEVEL, PERCENTAGE_INTEGER_VALIDATOR);
+        VALIDATORS.put(Secure.REDUCE_BRIGHT_COLORS_PERSIST_ACROSS_REBOOTS, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.TTS_DEFAULT_RATE, NON_NEGATIVE_INTEGER_VALIDATOR);
         VALIDATORS.put(Secure.TTS_DEFAULT_PITCH, NON_NEGATIVE_INTEGER_VALIDATOR);
         VALIDATORS.put(Secure.TTS_DEFAULT_SYNTH, PACKAGE_NAME_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 506b608..94de485 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -749,25 +749,25 @@
                 Settings.Global.GPU_DEBUG_LAYERS,
                 GlobalSettingsProto.Gpu.DEBUG_LAYERS);
         dumpSetting(s, p,
-                Settings.Global.GLOBAL_SETTINGS_ANGLE_DEBUG_PACKAGE,
+                Settings.Global.ANGLE_DEBUG_PACKAGE,
                 GlobalSettingsProto.Gpu.ANGLE_DEBUG_PACKAGE);
         dumpSetting(s, p,
-                Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE,
+                Settings.Global.ANGLE_GL_DRIVER_ALL_ANGLE,
                 GlobalSettingsProto.Gpu.ANGLE_GL_DRIVER_ALL_ANGLE);
         dumpSetting(s, p,
-                Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS,
+                Settings.Global.ANGLE_GL_DRIVER_SELECTION_PKGS,
                 GlobalSettingsProto.Gpu.ANGLE_GL_DRIVER_SELECTION_PKGS);
         dumpSetting(s, p,
-                Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES,
+                Settings.Global.ANGLE_GL_DRIVER_SELECTION_VALUES,
                 GlobalSettingsProto.Gpu.ANGLE_GL_DRIVER_SELECTION_VALUES);
         dumpSetting(s, p,
-                Settings.Global.GLOBAL_SETTINGS_ANGLE_ALLOWLIST,
+                Settings.Global.ANGLE_ALLOWLIST,
                 GlobalSettingsProto.Gpu.ANGLE_ALLOWLIST);
         dumpSetting(s, p,
                 Settings.Global.ANGLE_EGL_FEATURES,
                 GlobalSettingsProto.Gpu.ANGLE_EGL_FEATURES);
         dumpSetting(s, p,
-                Settings.Global.GLOBAL_SETTINGS_SHOW_ANGLE_IN_USE_DIALOG_BOX,
+                Settings.Global.SHOW_ANGLE_IN_USE_DIALOG_BOX,
                 GlobalSettingsProto.Gpu.SHOW_ANGLE_IN_USE_DIALOG);
         dumpSetting(s, p,
                 Settings.Global.GPU_DEBUG_LAYER_APP,
@@ -1494,9 +1494,6 @@
                 Settings.Global.WEBVIEW_DATA_REDUCTION_PROXY_KEY,
                 GlobalSettingsProto.Webview.DATA_REDUCTION_PROXY_KEY);
         dumpSetting(s, p,
-                Settings.Global.WEBVIEW_FALLBACK_LOGIC_ENABLED,
-                GlobalSettingsProto.Webview.FALLBACK_LOGIC_ENABLED);
-        dumpSetting(s, p,
                 Settings.Global.WEBVIEW_PROVIDER,
                 GlobalSettingsProto.Webview.PROVIDER);
         dumpSetting(s, p,
@@ -2343,6 +2340,18 @@
                 SecureSettingsProto.QuickSettings.AUTO_ADDED_TILES);
         p.end(qsToken);
 
+        final long reduceBrightColorsToken = p.start(SecureSettingsProto.REDUCE_BRIGHT_COLORS);
+        dumpSetting(s, p,
+                Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED,
+                SecureSettingsProto.ReduceBrightColors.ACTIVATED);
+        dumpSetting(s, p,
+                Settings.Secure.REDUCE_BRIGHT_COLORS_LEVEL,
+                SecureSettingsProto.ReduceBrightColors.LEVEL);
+        dumpSetting(s, p,
+                Settings.Secure.REDUCE_BRIGHT_COLORS_PERSIST_ACROSS_REBOOTS,
+                SecureSettingsProto.ReduceBrightColors.PERSIST_ACROSS_REBOOTS);
+        p.end(reduceBrightColorsToken);
+
         final long rotationToken = p.start(SecureSettingsProto.ROTATION);
         dumpSetting(s, p,
                 Settings.Secure.SHOW_ROTATION_SUGGESTIONS,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 710c016..6dd2936 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -25,6 +25,7 @@
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY;
 
+import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
 import static com.android.providers.settings.SettingsState.FALLBACK_FILE_SUFFIX;
 
 import android.Manifest;
@@ -3341,7 +3342,7 @@
         }
 
         private final class UpgradeController {
-            private static final int SETTINGS_VERSION = 192;
+            private static final int SETTINGS_VERSION = 193;
 
             private final int mUserId;
 
@@ -4724,6 +4725,35 @@
                     currentVersion = 192;
                 }
 
+                if (currentVersion == 192) {
+                    // Version 192: set the default value for magnification capabilities. If
+                    // magnification is enabled by the user, set it to full-screen, and set a value
+                    // to show a prompt when using the magnification first time after upgrading.
+                    final SettingsState secureSettings = getSecureSettingsLocked(userId);
+                    final Setting magnificationCapabilities = secureSettings.getSettingLocked(
+                            Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY);
+                    if (magnificationCapabilities.isNull()) {
+                        secureSettings.insertSettingLocked(
+                                Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY,
+                                String.valueOf(getContext().getResources().getInteger(
+                                        R.integer.def_accessibility_magnification_capabilities)),
+                                null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+
+                        if (isMagnificationSettingsOn(secureSettings)) {
+                            secureSettings.insertSettingLocked(
+                                    Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY, String.valueOf(
+                                            Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN),
+                                    null, false  /* makeDefault */,
+                                    SettingsState.SYSTEM_PACKAGE_NAME);
+                            secureSettings.insertSettingLocked(
+                                    Secure.ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT, "1",
+                                    null, false /* makeDefault */,
+                                    SettingsState.SYSTEM_PACKAGE_NAME);
+                        }
+                    }
+                    currentVersion = 193;
+                }
+
                 // vXXX: Add new settings above this point.
 
                 if (currentVersion != newVersion) {
@@ -4862,5 +4892,45 @@
                 }
             }
         }
+
+        private boolean isMagnificationSettingsOn(SettingsState secureSettings) {
+            if ("1".equals(secureSettings.getSettingLocked(
+                    Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED).getValue())) {
+                return true;
+            }
+
+            final Set<String> a11yButtonTargets = transformColonDelimitedStringToSet(
+                    secureSettings.getSettingLocked(
+                            Secure.ACCESSIBILITY_BUTTON_TARGETS).getValue());
+            if (a11yButtonTargets != null && a11yButtonTargets.contains(
+                    MAGNIFICATION_CONTROLLER_NAME)) {
+                return true;
+            }
+
+            final Set<String> a11yShortcutServices = transformColonDelimitedStringToSet(
+                    secureSettings.getSettingLocked(
+                            Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE).getValue());
+            if (a11yShortcutServices != null && a11yShortcutServices.contains(
+                    MAGNIFICATION_CONTROLLER_NAME)) {
+                return true;
+            }
+            return false;
+        }
+
+        @Nullable
+        private Set<String> transformColonDelimitedStringToSet(String value) {
+            if (TextUtils.isEmpty(value)) return null;
+            final TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(':');
+            splitter.setString(value);
+            final Set<String> items = new HashSet<>();
+            while (splitter.hasNext()) {
+                final String str = splitter.next();
+                if (TextUtils.isEmpty(str)) {
+                    continue;
+                }
+                items.add(str);
+            }
+            return items;
+        }
     }
 }
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index e027fd3..10d2eab 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -159,6 +159,7 @@
                     Settings.Global.BLE_SCAN_LOW_LATENCY_WINDOW_MS,
                     Settings.Global.BLE_SCAN_LOW_LATENCY_INTERVAL_MS,
                     Settings.Global.BLE_SCAN_BACKGROUND_MODE,
+                    Settings.Global.BLOCK_UNTRUSTED_TOUCHES_MODE,
                     Settings.Global.BLOCKED_SLICES,
                     Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT,
                     Settings.Global.BLOCKING_HELPER_STREAK_LIMIT,
@@ -313,6 +314,7 @@
                     Settings.Global.KERNEL_CPU_THREAD_READER,
                     Settings.Global.LANG_ID_UPDATE_CONTENT_URL,
                     Settings.Global.LANG_ID_UPDATE_METADATA_URL,
+                    Settings.Global.LATENCY_TRACKER,
                     Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
                     Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS,
                     Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
@@ -332,6 +334,7 @@
                     Settings.Global.MAX_ERROR_BYTES_PREFIX,
                     Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
                     Settings.Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY,
+                    Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH,
                     Settings.Global.MDC_INITIAL_MAX_RETRY,
                     Settings.Global.MHL_INPUT_SWITCHING_ENABLED,
                     Settings.Global.MHL_POWER_CHARGE_ENABLED,
@@ -405,6 +408,7 @@
                     Settings.Global.PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT,
                     Settings.Global.PDP_WATCHDOG_POLL_INTERVAL_MS,
                     Settings.Global.PDP_WATCHDOG_TRIGGER_PACKET_COUNT,
+                    Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE,
                     Settings.Global.POLICY_CONTROL,
                     Settings.Global.POWER_BUTTON_SUPPRESSION_DELAY_AFTER_GESTURE_WAKE,
                     Settings.Global.POWER_MANAGER_CONSTANTS,
@@ -438,6 +442,7 @@
                     Settings.Global.SHOW_NEW_APP_INSTALLED_NOTIFICATION_ENABLED,
                     Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS,
                     Settings.Global.SHOW_PEOPLE_SPACE,
+                    Settings.Global.SHOW_NEW_LOCKSCREEN,
                     Settings.Global.SHOW_RESTART_IN_CRASH_DIALOG,
                     Settings.Global.SHOW_TEMPERATURE_WARNING,
                     Settings.Global.SHOW_USB_TEMPERATURE_ALARM,
@@ -498,11 +503,11 @@
                     Settings.Global.GPU_DEBUG_APP,
                     Settings.Global.GPU_DEBUG_LAYERS,
                     Settings.Global.GPU_DEBUG_LAYERS_GLES,
-                    Settings.Global.GLOBAL_SETTINGS_ANGLE_DEBUG_PACKAGE,
-                    Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE,
-                    Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS,
-                    Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES,
-                    Settings.Global.GLOBAL_SETTINGS_ANGLE_ALLOWLIST,
+                    Settings.Global.ANGLE_DEBUG_PACKAGE,
+                    Settings.Global.ANGLE_GL_DRIVER_ALL_ANGLE,
+                    Settings.Global.ANGLE_GL_DRIVER_SELECTION_PKGS,
+                    Settings.Global.ANGLE_GL_DRIVER_SELECTION_VALUES,
+                    Settings.Global.ANGLE_ALLOWLIST,
                     Settings.Global.ANGLE_EGL_FEATURES,
                     Settings.Global.UPDATABLE_DRIVER_ALL_APPS,
                     Settings.Global.UPDATABLE_DRIVER_PRODUCTION_OPT_IN_APPS,
@@ -512,7 +517,7 @@
                     Settings.Global.UPDATABLE_DRIVER_PRODUCTION_DENYLIST,
                     Settings.Global.UPDATABLE_DRIVER_PRODUCTION_ALLOWLIST,
                     Settings.Global.UPDATABLE_DRIVER_SPHAL_LIBRARIES,
-                    Settings.Global.GLOBAL_SETTINGS_SHOW_ANGLE_IN_USE_DIALOG_BOX,
+                    Settings.Global.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,
@@ -521,7 +526,6 @@
                     Settings.Global.NETWORK_ACCESS_TIMEOUT_MS,
                     Settings.Global.WARNING_TEMPERATURE,
                     Settings.Global.WEBVIEW_DATA_REDUCTION_PROXY_KEY,
-                    Settings.Global.WEBVIEW_FALLBACK_LOGIC_ENABLED,
                     Settings.Global.WEBVIEW_MULTIPROCESS,
                     Settings.Global.WEBVIEW_PROVIDER,
                     Settings.Global.WFC_IMS_ENABLED,
@@ -743,7 +747,9 @@
                  Settings.Secure.NEARBY_SHARING_COMPONENT, // not user configurable
                  Settings.Secure.WINDOW_MAGNIFICATION,
                  Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER,
-                 Settings.Secure.SUPPRESS_DOZE);
+                 Settings.Secure.SUPPRESS_DOZE,
+                 Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED,
+                 Settings.Secure.ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT);
 
     @Test
     public void systemSettingsBackedUpOrDenied() {
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index a927997..5f018a0 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -329,6 +329,15 @@
     <!-- Permission needed for CTS test - TimeManagerTest -->
     <uses-permission android:name="android.permission.MANAGE_TIME_AND_ZONE_DETECTION" />
 
+    <!-- Permission required for CTS test - android.server.biometrics -->
+    <uses-permission android:name="android.permission.USE_BIOMETRIC" />
+
+    <!-- Permission required for CTS test - android.server.biometrics -->
+    <uses-permission android:name="android.permission.TEST_BIOMETRIC" />
+
+    <!-- Permissions required for CTS test - NotificationManagerTest -->
+    <uses-permission android:name="android.permission.MANAGE_NOTIFICATION_LISTENERS" />
+
     <application android:label="@string/app_label"
                 android:theme="@android:style/Theme.DeviceDefault.DayNight"
                 android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/Shell/res/values-mr/strings.xml b/packages/Shell/res/values-mr/strings.xml
index 9595e28..a957184 100644
--- a/packages/Shell/res/values-mr/strings.xml
+++ b/packages/Shell/res/values-mr/strings.xml
@@ -31,8 +31,8 @@
     <string name="bugreport_confirm" msgid="5917407234515812495">"बग रीपोर्टांमध्ये तुम्ही संवेदनशील (अ‍ॅप-वापर आणि स्थान डेटा यासारखा) डेटा म्हणून विचार करता त्या डेटाच्या समावेशासह सिस्टीमच्या विविध लॉग फायलींमधील डेटा असतो. ज्या लोकांवर आणि अ‍ॅपवर तुमचा विश्वास आहे केवळ त्यांच्यासह हा बग रीपोर्ट शेअर करा."</string>
     <string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"पुन्हा दर्शवू नका"</string>
     <string name="bugreport_storage_title" msgid="5332488144740527109">"बग रीपोर्ट"</string>
-    <string name="bugreport_unreadable_text" msgid="586517851044535486">"बग रीपोर्ट फाईल वाचणे शक्य झाले नाही"</string>
-    <string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"झिप फाईल मध्ये बग रीपोर्ट तपशील जोडणे शक्य झाले नाही"</string>
+    <string name="bugreport_unreadable_text" msgid="586517851044535486">"बग रीपोर्ट फाइल वाचणे शक्य झाले नाही"</string>
+    <string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"झिप फाइल मध्ये बग रीपोर्ट तपशील जोडणे शक्य झाले नाही"</string>
     <string name="bugreport_unnamed" msgid="2800582406842092709">"अनामित"</string>
     <string name="bugreport_info_action" msgid="2158204228510576227">"तपशील"</string>
     <string name="bugreport_screenshot_action" msgid="8677781721940614995">"स्क्रीनशॉट"</string>
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 0eac4ad..02751e2 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -737,18 +737,20 @@
             final Intent infoIntent = new Intent(mContext, BugreportProgressService.class);
             infoIntent.setAction(INTENT_BUGREPORT_INFO_LAUNCH);
             infoIntent.putExtra(EXTRA_ID, info.id);
+            // Simple notification action button clicks are immutable
             final PendingIntent infoPendingIntent =
                     PendingIntent.getService(mContext, info.id, infoIntent,
-                    PendingIntent.FLAG_UPDATE_CURRENT);
+                    PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
             final Action infoAction = new Action.Builder(null,
                     mContext.getString(R.string.bugreport_info_action),
                     infoPendingIntent).build();
             final Intent screenshotIntent = new Intent(mContext, BugreportProgressService.class);
             screenshotIntent.setAction(INTENT_BUGREPORT_SCREENSHOT);
             screenshotIntent.putExtra(EXTRA_ID, info.id);
+            // Simple notification action button clicks are immutable
             PendingIntent screenshotPendingIntent = mTakingScreenshot ? null : PendingIntent
                     .getService(mContext, info.id, screenshotIntent,
-                            PendingIntent.FLAG_UPDATE_CURRENT);
+                            PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
             final Action screenshotAction = new Action.Builder(null,
                     mContext.getString(R.string.bugreport_screenshot_action),
                     screenshotPendingIntent).build();
diff --git a/packages/SoundPicker/res/values-uz/strings.xml b/packages/SoundPicker/res/values-uz/strings.xml
index 9018e66..a617733 100644
--- a/packages/SoundPicker/res/values-uz/strings.xml
+++ b/packages/SoundPicker/res/values-uz/strings.xml
@@ -21,7 +21,7 @@
     <string name="alarm_sound_default" msgid="4787646764557462649">"Standart signal tovushi"</string>
     <string name="add_ringtone_text" msgid="6642389991738337529">"Rington qo‘shish"</string>
     <string name="add_alarm_text" msgid="3545497316166999225">"Signal qo‘shish"</string>
-    <string name="add_notification_text" msgid="4431129543300614788">"Bildirishnoma qo‘shish"</string>
+    <string name="add_notification_text" msgid="4431129543300614788">"Bildirishnoma kiritish"</string>
     <string name="delete_ringtone_text" msgid="201443984070732499">"O‘chirish"</string>
     <string name="unable_to_add_ringtone" msgid="4583511263449467326">"Maxsus rington qo‘shib bo‘lmadi"</string>
     <string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Maxsus ringtonni o‘chirib bo‘lmadi"</string>
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index a9a5671..80a6257 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -139,7 +139,9 @@
         "SystemUI-tags",
         "SystemUI-proto",
         "metrics-helper-lib",
-        "androidx.test.rules", "hamcrest-library",
+        "hamcrest-library",
+        "androidx.test.rules",
+        "androidx.test.uiautomator",
         "mockito-target-extended-minus-junit4",
         "testables",
         "truth-prebuilt",
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 505ef7a..4071045 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -572,6 +572,38 @@
             </intent-filter>
         </activity>
 
+        <!-- People Space UI Screen -->
+        <activity android:name=".people.PeopleSpaceActivity"
+            android:label="People"
+            android:icon="@drawable/ic_music_note"
+            android:exported="true"
+            android:enabled="false"
+            android:theme="@android:style/Theme.Material.NoActionBar"
+            android:launchMode="singleInstance">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+
+        <!-- People Space Widget -->
+        <receiver
+            android:name=".people.widget.PeopleSpaceWidgetProvider"
+            android:label="People Space"
+            android:enabled="true"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+            </intent-filter>
+            <meta-data android:name="android.appwidget.provider"
+                android:resource="@xml/people_space_widget_info" />
+        </receiver>
+
+        <!-- Widget service -->
+        <service android:name=".people.widget.PeopleSpaceWidgetService"
+            android:permission="android.permission.BIND_REMOTEVIEWS"
+            android:exported="false" />
+
         <!-- a gallery of delicious treats -->
         <service
             android:name=".DessertCaseDream"
@@ -606,6 +638,14 @@
             </intent-filter>
         </activity>
 
+        <activity
+            android:name=".user.CreateUserActivity"
+            android:excludeFromRecents="true"
+            android:exported="false"
+            android:finishOnCloseSystemDialogs="true"
+            android:launchMode="singleInstance"
+            android:theme="@style/Theme.CreateUser" />
+
         <activity android:name=".Somnambulator"
             android:label="@string/start_dreams"
             android:icon="@mipmap/ic_launcher_dreams"
diff --git a/packages/SystemUI/docs/broadcasts.md b/packages/SystemUI/docs/broadcasts.md
index 8ec20f5..e709278 100644
--- a/packages/SystemUI/docs/broadcasts.md
+++ b/packages/SystemUI/docs/broadcasts.md
@@ -62,17 +62,17 @@
  * @param executor An executor to dispatch [BroadcastReceiver.onReceive]. Pass null to use an
  *                 executor in the main thread (default).
  * @param user A user handle to determine which broadcast should be dispatched to this receiver.
- *             By default, it is the user of the context (system user in SystemUI).
+ *             Pass `null` to use the user of the context (system user in SystemUI).
  * @throws IllegalArgumentException if the filter has other constraints that are not actions or
  *                                  categories or the filter has no actions.
  */
 @JvmOverloads
-fun registerReceiver(
-        BroadcastReceiver, 
-        IntentFilter, 
-        Executor? = context.mainExecutor,
-        UserHandle = context.user
-) {
+open fun registerReceiver(
+    receiver: BroadcastReceiver,
+    filter: IntentFilter,
+    executor: Executor? = null,
+    user: UserHandle? = null
+)
 ```
 
 All subscriptions are done with the same overloaded method. As specified in the doc, in order to pass a `UserHandle` with the default `Executor`, pass `null` for the `Executor`.
diff --git a/packages/SystemUI/docs/plugin_hooks.md b/packages/SystemUI/docs/plugin_hooks.md
index 9fe2e18..cde5094 100644
--- a/packages/SystemUI/docs/plugin_hooks.md
+++ b/packages/SystemUI/docs/plugin_hooks.md
@@ -1,33 +1,34 @@
 # Plugin hooks
 ### Action: com.android.systemui.action.PLUGIN_OVERLAY
-Expected interface: [OverlayPlugin](/packages/SystemUI/plugin/src/com/android/systemui/plugins/OverlayPlugin.java)
+Expected interface: [OverlayPlugin](/frameworks/base/packages/SystemUI/plugin/src/com/android
+/systemui/plugins/OverlayPlugin.java)
 
 Use: Allows plugin access to the status bar and nav bar window for whatever nefarious purposes you can imagine.
 
 ### Action: com.android.systemui.action.PLUGIN_QS
-Expected interface: [QS](/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java)
+Expected interface: [QS](/frameworks/base/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java)
 
 Use: Allows the entire QS panel to be replaced with something else that is optionally expandable.
 
 Notes: To not mess up the notification panel interaction, much of the QSContainer interface needs to actually be implemented.
 
 ### Action: com.android.systemui.action.PLUGIN_QS_FACTORY
-Expected interface: [QSFactory](/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSFactory.java)
+Expected interface: [QSFactory](/frameworks/base/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSFactory.java)
 
 Use: Controls the creation of QS Tiles and their views, can used to add or change system QS tiles, can also be used to change the layout/interaction of the tile views.
 
 ### Action: com.android.systemui.action.PLUGIN_NAV_BUTTON
-Expected interface: [NavBarButtonProvider](/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavBarButtonProvider.java)
+Expected interface: [NavBarButtonProvider](/frameworks/base/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavBarButtonProvider.java)
 
 Use: Allows a plugin to create a new nav bar button, or override an existing one with a view of its own.
 
 ### Action: com.android.systemui.action.PLUGIN_NAV_GESTURE
-Expected interface: [NavGesture](/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java)
+Expected interface: [NavGesture](/frameworks/base/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java)
 
 Use: Allows touch events from the nav bar to be intercepted and used for other gestures.
 
 ### Action: com.android.systemui.action.PLUGIN_LOCKSCREEN_RIGHT_BUTTON
-Expected interface: [IntentButtonProvider](/packages/SystemUI/plugin/src/com/android/systemui/plugins/IntentButtonProvider.java)
+Expected interface: [IntentButtonProvider](/frameworks/base/packages/SystemUI/plugin/src/com/android/systemui/plugins/IntentButtonProvider.java)
 
 Use: Allows a plugin to specify the icon for the bottom right lock screen button, and the intent that gets launched when it is activated.
 
@@ -37,28 +38,33 @@
 Use: Allows a plugin to specify the icon for the bottom left lock screen button, and the intent that gets launched when it is activated.
 
 ### Action: com.android.systemui.action.PLUGIN_GLOBAL_ACTIONS
-Expected interface: [GlobalActions](/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActions.java)
+Expected interface: [GlobalActions](/frameworks/base/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActions.java)
 
 Use: Allows the long-press power menu to be completely replaced.
 
 ### Action: com.android.systemui.action.PLUGIN_VOLUME
-Expected interface: [VolumeDialog](/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialog.java)
+Expected interface: [VolumeDialog](/frameworks/base/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialog.java)
 
 Use: Allows replacement of the volume dialog.
 
 ### Action: com.android.systemui.action.PLUGIN_NOTIFICATION_SWIPE_ACTION
-Expected interface: [NotificationSwipeActionHelper](/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java)
+Expected interface: [NotificationSwipeActionHelper](/frameworks/base/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java)
 
 Use: Control over swipes/input for notification views, can be used to control what happens when you swipe/long-press
 
 ### Action: com.android.systemui.action.PLUGIN_CLOCK
-Expected interface: [ClockPlugin](/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java)
+Expected interface: [ClockPlugin](/frameworks/base/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java)
 
 Use: Allows replacement of the keyguard main clock.
 
+### Action: com.android.systemui.action.PLUGIN_TOAST
+Expected interface: [ToastPlugin](/frameworks/base/packages/SystemUI/plugin/src/com/android/systemui/plugins/ToastPlugin.java)
+
+Use: Allows replacement of uncustomized toasts created via Toast.makeText().
+
 # Global plugin dependencies
 These classes can be accessed by any plugin using PluginDependency as long as they @Requires them.
 
-[VolumeDialogController](/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java) - Mostly just API for the volume plugin
+[VolumeDialogController](/frameworks/base/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java) - Mostly just API for the volume plugin
 
-[ActivityStarter](/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java) - Allows starting of intents while co-operating with keyguard unlocks.
+[ActivityStarter](/frameworks/base/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java) - Allows starting of intents while co-operating with keyguard unlocks.
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ToastPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ToastPlugin.java
new file mode 100644
index 0000000..0831e0e
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ToastPlugin.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.plugins;
+
+import android.animation.Animator;
+import android.annotation.NonNull;
+import android.view.View;
+
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+/**
+ * Customize toasts displayed by SystemUI (via Toast#makeText)
+ */
+@ProvidesInterface(action = ToastPlugin.ACTION, version = ToastPlugin.VERSION)
+public interface ToastPlugin extends Plugin {
+
+    String ACTION = "com.android.systemui.action.PLUGIN_TOAST";
+    int VERSION = 1;
+
+    /**
+     * Creates a CustomPluginToast.
+     */
+    @NonNull Toast createToast(CharSequence text, String packageName, int userId);
+
+    /**
+     * Custom Toast with the ability to change toast positioning, styling and animations.
+     */
+    interface Toast {
+        /**
+         * Retrieve the Toast view's gravity.
+         * If no changes, returns null.
+         */
+        default Integer getGravity() {
+            return null;
+        }
+
+        /**
+         * Retrieve the Toast view's X-offset.
+         * If no changes, returns null.
+         */
+        default Integer getXOffset() {
+            return null;
+        }
+
+        /**
+         * Retrieve the Toast view's Y-offset.
+         * If no changes, returns null.
+         */
+        default Integer getYOffset() {
+            return null;
+        }
+
+        /**
+         * Retrieve the Toast view's horizontal margin.
+         * If no changes, returns null.
+         */
+        default Integer getHorizontalMargin()  {
+            return null;
+        }
+
+        /**
+         * Retrieve the Toast view's vertical margin.
+         * If no changes, returns null.
+         */
+        default Integer getVerticalMargin()  {
+            return null;
+        }
+
+        /**
+         * Retrieve the Toast view to show.
+         * If no changes, returns null.
+         */
+        default View getView() {
+            return null;
+        }
+
+        /**
+         * Retrieve the Toast's animate in.
+         * If no changes, returns null.
+         */
+        default Animator getInAnimation() {
+            return null;
+        }
+
+        /**
+         * Retrieve the Toast's animate out.
+         * If no changes, returns null.
+         */
+        default Animator getOutAnimation() {
+            return null;
+        }
+    }
+}
diff --git a/packages/SystemUI/res-keyguard/font/clock.xml b/packages/SystemUI/res-keyguard/font/clock.xml
new file mode 100644
index 0000000..008b322
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/font/clock.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!--
+** AOD/LockScreen Clock font.
+** Should include all numeric glyphs in all supported locales.
+** Recommended: font with variable width to support AOD => LS animations
+-->
+<font-family xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- TODO (b/171376810): switch this font with a variable width clock font for AOSP -->
+    <font android:typeface="monospace" android:font="@*android:string/config_headlineFontFamily" />
+</font-family>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index bf2963c..f22c729 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -61,6 +61,28 @@
              android:visibility="invisible"
              />
     </FrameLayout>
+    <FrameLayout
+        android:id="@+id/new_lockscreen_clock_view"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentEnd="true"
+        android:layout_alignParentTop="true"
+        android:visibility="gone">
+        <com.android.keyguard.GradientTextClock
+            android:id="@+id/gradient_clock_view"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="100dp"
+            android:letterSpacing="0.02"
+            android:lineSpacingMultiplier=".8"
+            android:includeFontPadding="false"
+            android:fontFamily="@font/clock"
+            android:typeface="monospace"
+            android:format12Hour="hh\nmm"
+            android:format24Hour="HH\nmm"
+            android:elegantTextHeight="false"
+        />
+    </FrameLayout>
     <include layout="@layout/keyguard_status_area"
         android:id="@+id/keyguard_status_area"
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/drawable/ic_check_box.xml b/packages/SystemUI/res/drawable/ic_check_box.xml
new file mode 100644
index 0000000..a8d1a65
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_check_box.xml
@@ -0,0 +1,26 @@
+<!--
+  Copyright (C) 2020 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:id="@+id/checked"
+        android:state_checked="true"
+        android:drawable="@drawable/ic_check_box_blue_24dp" />
+    <item
+        android:id="@+id/unchecked"
+        android:state_checked="false"
+        android:drawable="@drawable/ic_check_box_outline_24dp" />
+</selector>
diff --git a/packages/SystemUI/res/drawable/ic_check_box_blue_24dp.xml b/packages/SystemUI/res/drawable/ic_check_box_blue_24dp.xml
new file mode 100644
index 0000000..43cae69
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_check_box_blue_24dp.xml
@@ -0,0 +1,26 @@
+<!--
+  Copyright (C) 2020 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="M19,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.11,0 2,-0.9 2,-2L21,5c0,-1.1 -0.89,-2 -2,-2zM10,17l-5,-5 1.41,-1.41L10,14.17l7.59,-7.59L19,8l-9,9z"
+        android:fillColor="#4285F4"/>
+</vector>
+
diff --git a/packages/SystemUI/res/drawable/ic_check_box_outline_24dp.xml b/packages/SystemUI/res/drawable/ic_check_box_outline_24dp.xml
new file mode 100644
index 0000000..f6f453a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_check_box_outline_24dp.xml
@@ -0,0 +1,26 @@
+<!--
+  Copyright (C) 2020 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="M19,5v14H5V5h14m0,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2z"
+        android:fillColor="#757575"/>
+</vector>
+
diff --git a/packages/SystemUI/res/drawable/ic_speaker_group_black_24dp.xml b/packages/SystemUI/res/drawable/ic_speaker_group_black_24dp.xml
new file mode 100644
index 0000000..ae0d562
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_speaker_group_black_24dp.xml
@@ -0,0 +1,31 @@
+<!--
+  Copyright (C) 2020 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="M18.2,1L9.8,1C8.81,1 8,1.81 8,2.8v14.4c0,0.99 0.81,1.79 1.8,1.79l8.4,0.01c0.99,0 1.8,-0.81 1.8,-1.8L20,2.8c0,-0.99 -0.81,-1.8 -1.8,-1.8zM14,3c1.1,0 2,0.89 2,2s-0.9,2 -2,2 -2,-0.89 -2,-2 0.9,-2 2,-2zM14,16.5c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4 4,1.79 4,4 -1.79,4 -4,4z"
+        android:fillColor="#000000"/>
+    <path
+        android:pathData="M14,12.5m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0"
+        android:fillColor="#000000"/>
+    <path
+        android:pathData="M6,5H4v16c0,1.1 0.89,2 2,2h10v-2H6V5z"
+        android:fillColor="#000000"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/people_space_tile_view_card.xml b/packages/SystemUI/res/drawable/people_space_tile_view_card.xml
new file mode 100644
index 0000000..6b81703
--- /dev/null
+++ b/packages/SystemUI/res/drawable/people_space_tile_view_card.xml
@@ -0,0 +1,28 @@
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="@android:color/white" />
+    <padding
+        android:left="1dp"
+        android:top="1dp"
+        android:right="1dp"
+        android:bottom="1dp" />
+    <corners
+        android:bottomRightRadius="7dp"
+        android:bottomLeftRadius="7dp"
+        android:topRightRadius="7dp"
+        android:topLeftRadius="7dp" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-land/global_screenshot_preview.xml b/packages/SystemUI/res/layout-land/global_screenshot_preview.xml
index b1f4cb7..040303a 100644
--- a/packages/SystemUI/res/layout-land/global_screenshot_preview.xml
+++ b/packages/SystemUI/res/layout-land/global_screenshot_preview.xml
@@ -28,6 +28,6 @@
     android:visibility="gone"
     android:background="@drawable/screenshot_rounded_corners"
     android:adjustViewBounds="true"
-    android:contentDescription="@string/screenshot_preview_description"
+    android:contentDescription="@string/screenshot_edit"
     app:layout_constraintBottom_toBottomOf="parent"
     app:layout_constraintStart_toStartOf="parent"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/activity_create_new_user.xml b/packages/SystemUI/res/layout/activity_create_new_user.xml
new file mode 100644
index 0000000..ba434e6
--- /dev/null
+++ b/packages/SystemUI/res/layout/activity_create_new_user.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@android:color/transparent"
+    android:orientation="vertical">
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/global_screenshot.xml b/packages/SystemUI/res/layout/global_screenshot.xml
index ef7325e..e0333eb 100644
--- a/packages/SystemUI/res/layout/global_screenshot.xml
+++ b/packages/SystemUI/res/layout/global_screenshot.xml
@@ -14,7 +14,7 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<FrameLayout
+<com.android.systemui.screenshot.ScreenshotView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/global_screenshot_frame"
     android:layout_width="match_parent"
@@ -62,4 +62,4 @@
             android:layout_margin="@dimen/screenshot_dismiss_button_margin"
             android:src="@drawable/screenshot_cancel"/>
     </FrameLayout>
-</FrameLayout>
+</com.android.systemui.screenshot.ScreenshotView>
diff --git a/packages/SystemUI/res/layout/global_screenshot_action_chip.xml b/packages/SystemUI/res/layout/global_screenshot_action_chip.xml
index 4b3534b..1021186d 100644
--- a/packages/SystemUI/res/layout/global_screenshot_action_chip.xml
+++ b/packages/SystemUI/res/layout/global_screenshot_action_chip.xml
@@ -19,7 +19,7 @@
     android:id="@+id/global_screenshot_action_chip"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:layout_marginEnd="@dimen/screenshot_action_chip_margin_right"
+    android:layout_marginStart="@dimen/screenshot_action_chip_margin_start"
     android:paddingVertical="@dimen/screenshot_action_chip_margin_vertical"
     android:layout_gravity="center"
     android:gravity="center"
diff --git a/packages/SystemUI/res/layout/global_screenshot_preview.xml b/packages/SystemUI/res/layout/global_screenshot_preview.xml
index e6295f5..c745854 100644
--- a/packages/SystemUI/res/layout/global_screenshot_preview.xml
+++ b/packages/SystemUI/res/layout/global_screenshot_preview.xml
@@ -28,6 +28,6 @@
     android:visibility="gone"
     android:background="@drawable/screenshot_rounded_corners"
     android:adjustViewBounds="true"
-    android:contentDescription="@string/screenshot_preview_description"
+    android:contentDescription="@string/screenshot_edit"
     app:layout_constraintBottom_toBottomOf="parent"
     app:layout_constraintStart_toStartOf="parent"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/global_screenshot_static.xml b/packages/SystemUI/res/layout/global_screenshot_static.xml
index 9ec2f20..26edf3a 100644
--- a/packages/SystemUI/res/layout/global_screenshot_static.xml
+++ b/packages/SystemUI/res/layout/global_screenshot_static.xml
@@ -51,7 +51,12 @@
         <LinearLayout
             android:id="@+id/global_screenshot_actions"
             android:layout_width="wrap_content"
-            android:layout_height="wrap_content"/>
+            android:layout_height="wrap_content">
+            <include layout="@layout/global_screenshot_action_chip"
+                     android:id="@+id/screenshot_share_chip"/>
+            <include layout="@layout/global_screenshot_action_chip"
+                     android:id="@+id/screenshot_edit_chip"/>
+        </LinearLayout>
     </HorizontalScrollView>
     <include layout="@layout/global_screenshot_preview"/>
 </androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/media_output_list_item.xml b/packages/SystemUI/res/layout/media_output_list_item.xml
index ac8b7b5..c98c3a0 100644
--- a/packages/SystemUI/res/layout/media_output_list_item.xml
+++ b/packages/SystemUI/res/layout/media_output_list_item.xml
@@ -15,97 +15,124 @@
   ~ limitations under the License.
   -->
 
-<FrameLayout
-    android:id="@+id/device_container"
+<LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/device_container"
     android:layout_width="match_parent"
-    android:layout_height="64dp">
-
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
     <FrameLayout
-        android:layout_width="36dp"
-        android:layout_height="36dp"
-        android:layout_gravity="center_vertical"
-        android:layout_marginStart="16dp">
-        <ImageView
-            android:id="@+id/title_icon"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center"/>
-    </FrameLayout>
+        android:layout_width="match_parent"
+        android:layout_height="64dp">
 
-    <TextView
-        android:id="@+id/title"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical"
-        android:layout_marginStart="68dp"
-        android:ellipsize="end"
-        android:maxLines="1"
-        android:textColor="?android:attr/textColorPrimary"
-        android:textSize="14sp"/>
+        <FrameLayout
+            android:layout_width="36dp"
+            android:layout_height="36dp"
+            android:layout_gravity="center_vertical"
+            android:layout_marginStart="16dp">
+            <ImageView
+                android:id="@+id/title_icon"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center"/>
+        </FrameLayout>
 
-    <RelativeLayout
-        android:id="@+id/two_line_layout"
-        android:layout_width="wrap_content"
-        android:layout_height="48dp"
-        android:layout_marginStart="52dp"
-        android:layout_marginEnd="69dp"
-        android:layout_marginTop="10dp">
         <TextView
-            android:id="@+id/two_line_title"
+            android:id="@+id/title"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_marginStart="16dp"
-            android:layout_marginEnd="15dp"
+            android:layout_gravity="center_vertical"
+            android:layout_marginStart="68dp"
             android:ellipsize="end"
             android:maxLines="1"
             android:textColor="?android:attr/textColorPrimary"
             android:textSize="14sp"/>
-        <TextView
-            android:id="@+id/subtitle"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginStart="16dp"
-            android:layout_marginEnd="15dp"
-            android:layout_marginBottom="7dp"
-            android:layout_alignParentBottom="true"
-            android:ellipsize="end"
-            android:maxLines="1"
-            android:textColor="?android:attr/textColorSecondary"
-            android:textSize="12sp"
-            android:fontFamily="roboto-regular"
-            android:visibility="gone"/>
-        <SeekBar
-            android:id="@+id/volume_seekbar"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_alignParentBottom="true"/>
-    </RelativeLayout>
 
-    <ProgressBar
-        android:id="@+id/volume_indeterminate_progress"
-        style="@*android:style/Widget.Material.ProgressBar.Horizontal"
-        android:layout_width="258dp"
-        android:layout_height="18dp"
-        android:layout_marginStart="68dp"
-        android:layout_marginTop="40dp"
-        android:indeterminate="true"
-        android:indeterminateOnly="true"
-        android:visibility="gone"/>
+        <RelativeLayout
+            android:id="@+id/two_line_layout"
+            android:layout_width="wrap_content"
+            android:layout_height="48dp"
+            android:layout_marginStart="52dp"
+            android:layout_marginEnd="69dp"
+            android:layout_marginTop="10dp">
+            <TextView
+                android:id="@+id/two_line_title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="16dp"
+                android:layout_marginEnd="15dp"
+                android:ellipsize="end"
+                android:maxLines="1"
+                android:textColor="?android:attr/textColorPrimary"
+                android:textSize="14sp"/>
+            <TextView
+                android:id="@+id/subtitle"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="16dp"
+                android:layout_marginEnd="15dp"
+                android:layout_marginBottom="7dp"
+                android:layout_alignParentBottom="true"
+                android:ellipsize="end"
+                android:maxLines="1"
+                android:textColor="?android:attr/textColorSecondary"
+                android:textSize="12sp"
+                android:fontFamily="roboto-regular"
+                android:visibility="gone"/>
+            <SeekBar
+                android:id="@+id/volume_seekbar"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_alignParentBottom="true"/>
+        </RelativeLayout>
+
+        <ProgressBar
+            android:id="@+id/volume_indeterminate_progress"
+            style="@*android:style/Widget.Material.ProgressBar.Horizontal"
+            android:layout_width="258dp"
+            android:layout_height="18dp"
+            android:layout_marginStart="68dp"
+            android:layout_marginTop="40dp"
+            android:indeterminate="true"
+            android:indeterminateOnly="true"
+            android:visibility="gone"/>
+
+        <View
+            android:id="@+id/end_divider"
+            android:layout_width="1dp"
+            android:layout_height="36dp"
+            android:layout_marginEnd="68dp"
+            android:layout_gravity="right|center_vertical"
+            android:background="?android:attr/listDivider"
+            android:visibility="gone"/>
+
+        <ImageView
+            android:id="@+id/add_icon"
+            android:layout_width="24dp"
+            android:layout_height="24dp"
+            android:layout_gravity="right|center_vertical"
+            android:layout_marginEnd="24dp"
+            android:src="@drawable/ic_add"
+            android:tint="?android:attr/colorAccent"
+            android:visibility="gone"/>
+
+        <CheckBox
+            android:id="@+id/check_box"
+            android:layout_width="24dp"
+            android:layout_height="24dp"
+            android:layout_gravity="right|center_vertical"
+            android:layout_marginEnd="24dp"
+            android:button="@drawable/ic_check_box"
+            android:visibility="gone"/>
+    </FrameLayout>
 
     <View
-        android:layout_width="1dp"
-        android:layout_height="36dp"
-        android:layout_marginEnd="68dp"
-        android:layout_gravity="right|center_vertical"
+        android:id="@+id/bottom_divider"
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:layout_marginTop="12dp"
+        android:layout_marginBottom="12dp"
+        android:layout_gravity="bottom"
         android:background="?android:attr/listDivider"
         android:visibility="gone"/>
-
-    <ImageView
-        android:id="@+id/end_icon"
-        android:layout_width="24dp"
-        android:layout_height="24dp"
-        android:layout_gravity="right|center_vertical"
-        android:layout_marginEnd="24dp"
-        android:visibility="gone"/>
-</FrameLayout>
\ No newline at end of file
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/people_space_activity.xml b/packages/SystemUI/res/layout/people_space_activity.xml
new file mode 100644
index 0000000..67ecdaa
--- /dev/null
+++ b/packages/SystemUI/res/layout/people_space_activity.xml
@@ -0,0 +1,45 @@
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<androidx.core.widget.NestedScrollView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/scroll"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@android:color/holo_blue_light">
+
+    <LinearLayout
+        android:id="@+id/people_space_layout"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="16dp"
+        android:orientation="vertical"
+        android:padding="16dp"
+        android:clipChildren="false"
+        android:clipToPadding="false">
+
+        <ImageView
+            android:id="@+id/people_space_cloud"
+            android:layout_width="67dp"
+            android:layout_height="67dp"
+            android:layout_gravity="center_horizontal"
+            android:scaleType="fitCenter"
+            android:focusable="false"
+            android:paddingBottom="16dp"
+            android:tint="?android:attr/colorControlNormal"
+            android:src="@drawable/cloud" />
+
+    </LinearLayout>
+</androidx.core.widget.NestedScrollView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/people_space_tile_view.xml b/packages/SystemUI/res/layout/people_space_tile_view.xml
new file mode 100644
index 0000000..80bb070
--- /dev/null
+++ b/packages/SystemUI/res/layout/people_space_tile_view.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/tile_view"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:background="@drawable/people_space_tile_view_card"
+        android:orientation="vertical"
+        android:padding="16dp"
+        android:layout_marginBottom="24dp"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:elevation="4dp"
+        android:gravity="start">
+
+        <ImageView
+            android:id="@+id/tile_view_package_icon"
+            android:layout_width="32dp"
+            android:layout_height="32dp"
+            android:layout_gravity="end" />
+
+        <ImageView
+            android:id="@+id/tile_view_person_icon"
+            android:layout_width="32dp"
+            android:layout_height="32dp"
+            android:layout_gravity="start" />
+
+        <TextView
+            android:id="@+id/tile_view_name"
+            android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+            android:paddingVertical="4dp"
+            android:textSize="22sp"
+            android:textColor="?android:attr/textColorPrimary"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="start" />
+
+        <TextView
+            android:id="@+id/tile_view_status"
+            android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+            android:paddingVertical="4dp"
+            android:textSize="16sp"
+            android:textColor="?android:attr/textColorSecondary"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:ellipsize="end"
+            android:layout_gravity="start" />
+    </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/people_space_widget.xml b/packages/SystemUI/res/layout/people_space_widget.xml
new file mode 100644
index 0000000..6020099
--- /dev/null
+++ b/packages/SystemUI/res/layout/people_space_widget.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<ListView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/widget_list_view"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@android:color/holo_blue_light"
+    android:clipChildren="false"
+    android:clipToPadding="false"
+    android:padding="5dp"
+    android:divider="@null"
+    android:dividerHeight="0dp"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/people_space_widget_item.xml b/packages/SystemUI/res/layout/people_space_widget_item.xml
new file mode 100644
index 0000000..a40bfff
--- /dev/null
+++ b/packages/SystemUI/res/layout/people_space_widget_item.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/item"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+    <LinearLayout
+        android:background="@drawable/people_space_tile_view_card"
+        android:orientation="vertical"
+        android:padding="6dp"
+        android:layout_marginBottom="6dp"
+        android:elevation="4dp"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="start">
+        <ImageView
+            android:id="@+id/package_icon"
+            android:layout_width="30dp"
+            android:layout_height="30dp"
+            android:layout_gravity="end" />
+        <ImageView
+            android:id="@+id/person_icon"
+            android:layout_width="30dp"
+            android:layout_height="30dp"
+            android:layout_gravity="start" />
+        <TextView
+            android:id="@+id/name"
+            android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+            android:textSize="18sp"
+            android:textColor="?android:attr/textColorPrimary"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="start" />
+        <TextView
+            android:id="@+id/status"
+            android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+            android:paddingVertical="2dp"
+            android:textSize="14sp"
+            android:textColor="?android:attr/textColorSecondary"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="start" />
+    </LinearLayout>
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index 436188a..0822947 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -16,7 +16,7 @@
 -->
 
 <!-- Extends FrameLayout -->
-<com.android.systemui.qs.QSFooterImpl
+<com.android.systemui.qs.QSFooterView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/qs_footer"
     android:layout_width="match_parent"
@@ -130,4 +130,4 @@
             </com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
         </com.android.keyguard.AlphaOptimizedLinearLayout>
     </LinearLayout>
-</com.android.systemui.qs.QSFooterImpl>
+</com.android.systemui.qs.QSFooterView>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 7d3390d..220a773 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Probeer weer skermkiekie neem"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Kan weens beperkte bergingspasie nie skermkiekie stoor nie"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Die program of jou organisasie laat nie toe dat skermkiekies geneem word nie"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Wysig skermkiekie"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Maak skermkiekie toe"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Skermkiekievoorskou"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Skermopnemer"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Instellings"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Vergrotingvenster"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Vergrotingvensterkontroles"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Zoem in"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Zoem uit"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Skuif op"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Skuif af"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Beweeg links"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Beweeg regs"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Vergrotingwisselaar"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Vergroot die hele skerm"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Vergroot \'n deel van die skerm"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Wissel"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Toestelkontroles"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Voeg kontroles vir jou gekoppelde toestelle by"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Stel toestelkontroles op"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Laai tans aanbevelings"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Versteek die huidige sessie."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Huidige sessie kan nie versteek word nie."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Maak toe"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Hervat"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Instellings"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ontkoppel)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Kon nie koppel nie. Probeer weer."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Bind nuwe toestel saam"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Bounommer"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Bounommer is na knipbord gekopieer."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index a91182e..b2a7c16 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ቅጽበታዊ ገጽ ዕይታን እንደገና ማንሳት ይሞክሩ"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"ባለው ውሱን የማከማቻ ቦታ ምክንያት ቅጽበታዊ ገጽ ዕይታን ማስቀመጥ አይችልም"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ቅጽበታዊ ገጽ እይታዎችን ማንሳት በመተግበሪያው ወይም በእርስዎ ድርጅት አይፈቀድም"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"ቅጽበታዊ ገጽ ዕይታን አርትዕ ያድርጉ"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ቅጽበታዊ ገጽ እይታን አሰናብት"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"የቅጽበታዊ ገጽ ዕይታ ቅድመ-ዕይታ"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"የማያ መቅጃ"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"ቅንብሮች"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"የማጉያ መስኮት"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"የማጉያ መስኮት መቆጣጠሪያዎች"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"አጉላ"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"አሳንስ"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"ወደ ላይ ውሰድ"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"ወደ ታች ውሰድ"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"ወደ ግራ ውሰድ"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"ወደ ቀኝ ውሰድ"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"የማጉላት ማብሪያ/ማጥፊያ"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"ሙሉውን ማያ ገጽ አጉላ"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"የማያ ገጹን ክፍል አጉላ"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ማብሪያ/ማጥፊያ"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"የመሣሪያ መቆጣጠሪያዎች"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"ለእርስዎ የተገናኙ መሣሪያዎች መቆጣጠሪያዎችን ያክሉ"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"የመሣሪያ መቆጣጠሪያዎችን ያቀናብሩ"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"ምክሮችን በመጫን ላይ"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"ሚዲያ"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"የአሁኑን ክፍለ-ጊዜ ደብቅ።"</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"የአሁኑ ክፍለ ጊዜ መደበቅ አይችልም።"</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"አሰናብት"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"ከቆመበት ቀጥል"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ቅንብሮች"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ግንኙነት ተቋርጧል)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"ማገናኘት አልተቻለም። እንደገና ይሞክሩ።"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"አዲስ መሣሪያ ያጣምሩ"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"የግንብ ቁጥር"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"የገንባ ቁጥር ወደ ቅንጥብ ሰሌዳ ተቀድቷል።"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 74a9b64..2cced23 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"جرّب أخذ لقطة الشاشة مرة أخرى"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"يتعذر حفظ لقطة الشاشة لأن مساحة التخزين المتاحة محدودة."</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"يحظر التطبيق أو تحظر مؤسستك التقاط لقطات شاشة"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"تعديل لقطة الشاشة"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"إغلاق لقطة الشاشة"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"معاينة لقطة الشاشة"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"مسجّل الشاشة"</string>
@@ -1032,18 +1033,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"الإعدادات"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"نافذة التكبير"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"عناصر التحكم في نافذة التكبير"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"تكبير"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"تصغير"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"نقل للأعلى"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"نقل للأسفل"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"نقل لليسار"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"نقل لليمين"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"مفتاح تبديل وضع التكبير"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"تكبير الشاشة بالكامل"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"تكبير جزء من الشاشة"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"تبديل"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"أدوات التحكم بالأجهزة"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"إضافة عناصر تحكّم لأجهزتك المتصلة"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"إعداد أدوات التحكم بالجهاز"</string>
@@ -1089,6 +1088,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"جارٍ تحميل الاقتراحات"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"الوسائط"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"إخفاء الجلسة الحالية"</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"لا يمكن إخفاء الجلسة الحالية."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"إغلاق"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"استئناف التشغيل"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"الإعدادات"</string>
@@ -1111,8 +1111,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (غير متّصل)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"تعذّر الاتصال. يُرجى إعادة المحاولة."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"إقران جهاز جديد"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"رقم الإصدار"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"تم نسخ رقم الإصدار إلى الحافظة."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 297d774..4c43e4e 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"স্ক্ৰীণশ্বট আকৌ ল\'বলৈ চেষ্টা কৰক"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"সঞ্চয়াগাৰত সীমিত খালী ঠাই থকাৰ বাবে স্ক্ৰীণশ্বট ছেভ কৰিব পৰা নগ\'ল"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"এপটোৱে বা আপোনাৰ প্ৰতিষ্ঠানে স্ক্ৰীণশ্বট ল\'বলৈ অনুমতি নিদিয়ে"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"স্ক্ৰীনশ্বট সম্পাদনা কৰক"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"স্ক্ৰীনশ্বট অগ্ৰাহ্য কৰক"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"স্ক্ৰীনশ্বটৰ পূৰ্বদৰ্শন"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"স্ক্ৰীন ৰেকৰ্ডাৰ"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"ছেটিংসমূহ"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"বিবৰ্ধন ৱিণ্ড’"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"বিবৰ্ধন ৱিণ্ড’ৰ নিয়ন্ত্ৰণসমূহ"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"জুম ইন কৰক"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"জুম আউট কৰক"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"ওপৰলৈ নিয়ক"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"তললৈ নিয়ক"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"বাওঁফাললৈ নিয়ক"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"সোঁফাললৈ নিয়ক"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"বিবৰ্ধনৰ ছুইচ"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"গোটেই স্ক্ৰীনখন বিবৰ্ধন কৰক"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"স্ক্ৰীনৰ কিছু অংশ বিবৰ্ধন কৰক"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ছুইচ"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"ডিভাইচৰ নিয়ন্ত্ৰণসমূহ"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"আপোনাৰ সংযোজিত ডিভাইচসমূহৰ বাবে নিয়ন্ত্ৰণসমূহ যোগ কৰক"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"ডিভাইচৰ নিয়ন্ত্ৰণসমূহ ছেট আপ কৰক"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"চুপাৰিছসমূহ ল’ড কৰি থকা হৈছে"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"মিডিয়া"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"বৰ্তমানৰ ছেশ্বনটো লুকুৱাওক।"</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"বৰ্তমান ছেশ্বনটো লুকুৱাব নোৱাৰি।"</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"অগ্ৰাহ্য কৰক"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"পুনৰ আৰম্ভ কৰক"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ছেটিংসমূহ"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (সংযোগ বিচ্ছিন্ন হৈছে)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"সংযোগ কৰিব পৰা নগ’ল। পুনৰ চেষ্টা কৰক।"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"নতুন ডিভাইচ পেয়াৰ কৰক"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"বিল্ডৰ নম্বৰ"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"ক্লিপব’ৰ্ডলৈ বিল্ডৰ নম্বৰ প্ৰতিলিপি কৰা হ’ল।"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index debad02..4cefd84 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Skrinşotu yenidən çəkin"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Yaddaş ehtiyatının az olması səbəbindən skrinşotu yadda saxlamaq olmur"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Skrinşot çəkməyə tətbiq və ya təşkilat tərəfindən icazə verilmir"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Skrinşota düzəliş edin"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Ekran şəklini ötürün"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Ekran şəklinə önbaxış"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Ekran Yazıcısı"</string>
@@ -797,7 +798,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Yuxarı Səhifə"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Aşağı Səhifə"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Silin"</string>
-    <string name="keyboard_key_move_home" msgid="3496502501803911971">"Əsas səhifə"</string>
+    <string name="keyboard_key_move_home" msgid="3496502501803911971">"Home"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"Son"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Daxil edin"</string>
     <string name="keyboard_key_num_lock" msgid="7209960042043090548">"Nömrələr"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Ayarlar"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Böyütmə Pəncərəsi"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Böyütmə Pəncərəsi Kontrolları"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Yaxınlaşdırın"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Uzaqlaşdırın"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Yuxarı köçürün"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Aşağı köçürün"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Sola köçürün"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Sağa köçürün"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Böyütmə dəyişdiricisi"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Bütün ekranı böyüdün"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekranın bir hissəsini böyüdün"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Dəyişdirici"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Cihaz idarəetmələri"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Qoşulmuş cihazlarınız üçün nizamlayıcılar əlavə edin"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Cihaz idarəetmələrini ayarlayın"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Tövsiyələr yüklənir"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Cari sessiyanı gizlədin."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Cari sessiyanı gizlətmək olmur."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"İmtina edin"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Davam edin"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Ayarlar"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (bağlantı kəsilib)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Qoşulmaq alınmadı. Yenə cəhd edin."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Yeni cihazı qoşalaşdırın"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Versiya nömrəsi"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Versiya nömrəsi mübadilə buferinə kopyalandı."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index be31c2d..2bc6c90 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Probajte da ponovo napravite snimak ekrana"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Čuvanje snimka ekrana nije uspelo zbog ograničenog memorijskog prostora"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Aplikacija ili organizacija ne dozvoljavaju pravljenje snimaka ekrana"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Izmenite snimak ekrana"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Odbacite snimak ekrana"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Pregled snimka ekrana"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Snimač ekrana"</string>
@@ -1017,18 +1018,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Podešavanja"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Prozor za uvećanje"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Kontrole prozora za uvećanje"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Uvećajte"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Umanjite"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Pomerite nagore"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Pomerite nadole"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Pomerite nalevo"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Pomerite nadesno"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Prelazak na drugi režim uvećanja"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Uvećajte ceo ekran"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Uvećajte deo ekrana"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Pređi"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Kontrole uređaja"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Dodajte kontrole za povezane uređaje"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Podesite kontrole uređaja"</string>
@@ -1071,6 +1070,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Učitavaju se preporuke"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Mediji"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Sakrijte aktuelnu sesiju."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Aktuelna sesija ne može da se sakrije."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Odbaci"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Nastavi"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Podešavanja"</string>
@@ -1093,8 +1093,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (veza je prekinuta)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Povezivanje nije uspelo. Probajte ponovo."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Upari novi uređaj"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Broj verzije"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Broj verzije je kopiran u privremenu memoriju."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index e3831a8..6396cbe 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Паспрабуйце зрабіць здымак экрана яшчэ раз"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Немагчыма захаваць здымак экрана, бо мала месца ў сховішчы"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Рабіць здымкі экрана не дазваляе праграма ці ваша арганізацыя"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Змяніць здымак экрана"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Адхіліць здымак экрана"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Перадпрагляд здымка экрана"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Запіс экрана"</string>
@@ -1022,18 +1023,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Налады"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Акно павелічэння"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Налады акна павелічэння"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Павялічыць маштаб"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Паменшыць маштаб"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Перамясціць уверх"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Перамясціць ніжэй"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Перамясціць улева"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Перамясціць управа"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Пераключальнік павелічэння"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Павялічыць на ўвесь экран"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Павялічыць частку экрана"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Пераключальнік"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Элементы кіравання прыладай"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Дадайце элементы кіравання для падключаных прылад"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Наладзіць элементы кіравання прыладай"</string>
@@ -1077,6 +1076,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Загружаюцца рэкамендацыі"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Мультымедыя"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Схаваць цяперашні сеанс."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Гэты сеанс не можа быць схаваны."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Адхіліць"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Узнавіць"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Налады"</string>
@@ -1099,8 +1099,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (адключана)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Не ўдалося падключыцца. Паўтарыце спробу."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Спалучыць з новай прыладай"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Нумар зборкі"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Нумар зборкі скапіраваны ў буфер абмену."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index d959178..26a313a 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Опитайте да направите екранна снимка отново"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Екранната снимка не може да се запази поради ограничено място в хранилището"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Правенето на екранни снимки не е разрешено от приложението или организацията ви"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Редактиране на екранната снимка"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Отхвърляне на екранната снимка"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Визуализация на екранната снимка"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Запис на екрана"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Настройки"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Прозорец за ниво на мащаба"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Контроли за прозореца за ниво на мащаба"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Увеличаване на мащаба"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Намаляване на мащаба"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Преместване нагоре"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Преместване надолу"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Преместване наляво"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Преместване надясно"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Превключване на увеличението"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Увеличаване на целия екран"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Увеличаване на част от екрана"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Превключване"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Контроли за устройството"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Добавяне на контроли за свързаните ви устройства"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Настройване на контролите за устройството"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Препоръките се зареждат"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Мултимедия"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Скриване на текущата сесия."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Текущата сесия не може да бъде скрита."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Отхвърляне"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Възобновяване"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Настройки"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (връзката е прекратена)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Неуспешно свързване. Опитайте отново."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Сдвояване на ново устройство"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Номер на компилацията"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Номерът на компилацията е копиран в буферната памет."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index fffa555..c4c6a0c 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"আবার স্ক্রিনশট নেওয়ার চেষ্টা করুন"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"বেশি জায়গা নেই তাই স্ক্রিনশটটি সেভ করা যাবে না৷"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"এই অ্যাপ বা আপনার প্রতিষ্ঠান স্ক্রিনশট নেওয়ার অনুমতি দেয়নি"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"স্ক্রিনশট এডিট করুন"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"স্ক্রিনশট বাতিল করুন"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"স্ক্রিনশটের প্রিভিউ"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"স্ক্রিন রেকর্ডার"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"সেটিংস"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"উইন্ডো বড় করে দেখা"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"উইন্ডো কন্ট্রোল বড় করে দেখা"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"বড় করুন"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"ছোট করুন"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"উপরে তুলুন"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"নিচে নামান"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"বাঁদিকে সরান"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"ডানদিকে সরান"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"বড় করে দেখার সুইচ"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"ফুল-স্ক্রিন মোডে দেখুন"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"স্ক্রিনের কিছুটা অংশ বড় করুন"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"বদল করুন"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"ডিভাইস কন্ট্রোল"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"আপনার কানেক্ট করা ডিভাইসের জন্য কন্ট্রোল যোগ করুন"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"ডিভাইস কন্ট্রোল সেট-আপ করুন"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"সাজেশন লোড করা হচ্ছে"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"মিডিয়া"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"বর্তমান সেশন লুকান।"</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"বর্তমান সেশন লুকানো যাবে না।"</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"খারিজ করুন"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"আবার চালু করুন"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"সেটিংস"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (কানেক্ট করা নেই)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"কানেক্ট করা যায়নি। আবার চেষ্টা করুন।"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"নতুন ডিভাইস পেয়ার করুন"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"বিল্ড নম্বর"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"বিল্ড নম্বর ক্লিপবোর্ডে কপি করা হয়েছে।"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index f5410cf..4ce5496 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Pokušajte ponovo snimiti ekran"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Snimak ekrana se ne može sačuvati zbog manjka prostora za pohranu"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Ova aplikacija ili vaša organizacija ne dozvoljavaju snimanje ekrana"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Uredite snimak ekrana"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Odbacite snimak ekrana"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Pregled snimka ekrana"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Snimač ekrana"</string>
@@ -726,7 +727,7 @@
     <string name="bubble_overflow_empty_title" msgid="3120029421991510842">"Nema nedavnih oblačića"</string>
     <string name="bubble_overflow_empty_subtitle" msgid="2030874469510497397">"Nedavni i odbačeni oblačići će se pojaviti ovdje"</string>
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Ta obavještenja se ne mogu izmijeniti."</string>
-    <string name="notification_multichannel_desc" msgid="7414593090056236179">"Ovdje nije moguće konfigurirati ovu grupu obavještenja"</string>
+    <string name="notification_multichannel_desc" msgid="7414593090056236179">"Ovu grupu obavještenja nije moguće konfigurirati ovdje"</string>
     <string name="notification_delegate_header" msgid="1264510071031479920">"Obavještenje preko proksi servera"</string>
     <string name="notification_channel_dialog_title" msgid="6856514143093200019">"Sva obavještenja aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="see_more_title" msgid="7409317011708185729">"Prikaži više"</string>
@@ -1017,18 +1018,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Postavke"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Prozor za uvećavanje"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Kontrole prozora za uvećavanje"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Uvećavanje"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Umanjivanje"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Pomjeranje prema gore"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Pomjeranje prema dolje"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Pomjeranje lijevo"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Pomjeranje desno"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Prekidač za uvećavanje"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Uvećavanje cijelog ekrana"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Uvećavanje dijela ekrana"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Prekidač"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Kontrole uređaja"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Dodajte kontrole za povezane uređaje"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Postavite kontrole uređaja"</string>
@@ -1071,6 +1070,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Učitavanje preporuka"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Mediji"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Sakrijte trenutnu sesiju."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Nije moguće sakriti trenutnu sesiju."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Odbaci"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Nastavi"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Postavke"</string>
@@ -1093,8 +1093,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (veza je prekinuta)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Povezivanje nije uspjelo. Pokušajte ponovo."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Uparite novi uređaj"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Broj verzije"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Broj verzije je kopiran u međumemoriju."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 28bf3bb..2b6dfd5 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Prova de tornar a fer una captura de pantalla"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"La captura de pantalla no es pot desar perquè no hi ha prou espai d\'emmagatzematge"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"L\'aplicació o la teva organització no permeten fer captures de pantalla"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Edita la captura de pantalla"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Ignora la captura de pantalla"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Previsualització de la captura de pantalla"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Gravació de pantalla"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Configuració"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Finestra d\'ampliació"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Finestra de controls d\'ampliació"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Amplia"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Redueix"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Mou cap amunt"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Mou cap avall"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Mou cap a l\'esquerra"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Mou cap a la dreta"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Canvia al mode d\'ampliació"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Amplia tota la pantalla"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Amplia una part de la pantalla"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Canvia"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Controls de dispositius"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Afegeix controls per als teus dispositius connectats"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Configura els controls de dispositius"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Carregant les recomanacions"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Multimèdia"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Amaga la sessió actual."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"La sessió actual no es pot amagar."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ignora"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Reprèn"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Configuració"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (desconnectat)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"No s\'ha pogut connectar. Torna-ho a provar."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Vincula un dispositiu nou"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número de compilació"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"El número de compilació s\'ha copiat al porta-retalls."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index c66f200..f00c279 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Zkuste snímek pořídit znovu"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Snímek obrazovky kvůli nedostatku místa v úložišti nelze uložit"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Aplikace nebo organizace zakazuje pořizování snímků obrazovky"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Upravit snímek obrazovky"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Zavřít snímek obrazovky"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Náhled snímku obrazovky"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Rekordér obrazovky"</string>
@@ -1022,18 +1023,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Nastavení"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Zvětšovací okno"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Ovládací prvky zvětšovacího okna"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Přiblížit"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Oddálit"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Přesunout nahoru"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Přesunout dolů"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Přesunout doleva"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Přesunout doprava"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Přepínač zvětšení"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Zvětšit celou obrazovku"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Zvětšit část obrazovky"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Přepnout"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Ovládání zařízení"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Přidejte ovládací prvky pro připojená zařízení"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Nastavení ovládání zařízení"</string>
@@ -1077,6 +1076,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Načítání doporučení"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Média"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Skrýt aktuální relaci."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Aktuální relaci nelze skrýt."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Zavřít"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Pokračovat"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Nastavení"</string>
@@ -1099,8 +1099,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (odpojeno)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Spojení se nezdařilo. Zkuste to znovu."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Spárovat nové zařízení"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Číslo sestavení"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Číslo sestavení bylo zkopírováno do schránky."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 5b0566d..9d365c3 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Prøv at tage et screenshot igen"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Screenshottet kan ikke gemmes, fordi der er begrænset lagerplads"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Appen eller din organisation tillader ikke, at du tager screenshots"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Rediger dit screenshot"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Luk screenshot"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Forhåndsvisning af screenshot"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Skærmoptagelse"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Indstillinger"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Vindue med forstørrelse"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Vindue med forstørrelsesstyring"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Zoom ind"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Zoom ud"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Flyt op"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Flyt ned"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Flyt til venstre"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Flyt til højre"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Skift forstørrelsestilstand"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Forstør hele skærmen"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Forstør en del af skærmen"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Skift"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Enhedsstyring"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Tilføj styring af dine tilsluttede enheder"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Konfigurer enhedsstyring"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Indlæser anbefalinger"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Medie"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Skjul den aktuelle session."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Den nuværende session kan ikke skjules."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Luk"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Genoptag"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Indstillinger"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ingen forbindelse)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Der kunne ikke oprettes forbindelse. Prøv igen."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Par ny enhed"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Buildnummer"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Buildnummeret blev kopieret til udklipsholderen."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 0ab1f0e..55e7d17 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -28,15 +28,15 @@
     <string name="battery_low_percent_format" msgid="4276661262843170964">"<xliff:g id="PERCENTAGE">%s</xliff:g> verbleibend"</string>
     <string name="battery_low_percent_format_hybrid" msgid="3985614339605686167">"Noch <xliff:g id="PERCENTAGE">%1$s</xliff:g> übrig; bei deinem Nutzungsmuster hast du noch ca. <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="battery_low_percent_format_hybrid_short" msgid="5917433188456218857">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> ausstehend; noch ca. <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <string name="battery_low_percent_format_saver_started" msgid="4968468824040940688">"Noch <xliff:g id="PERCENTAGE">%s</xliff:g>. Der Stromsparmodus ist aktiviert."</string>
+    <string name="battery_low_percent_format_saver_started" msgid="4968468824040940688">"Noch <xliff:g id="PERCENTAGE">%s</xliff:g>. Der Energiesparmodus ist aktiviert."</string>
     <string name="invalid_charger" msgid="4370074072117767416">"Aufladen über USB nicht möglich. Verwende das mit dem Gerät gelieferte Ladegerät."</string>
     <string name="invalid_charger_title" msgid="938685362320735167">"Aufladen über USB nicht möglich"</string>
     <string name="invalid_charger_text" msgid="2339310107232691577">"Verwende das mit dem Gerät gelieferte Ladegerät"</string>
     <string name="battery_low_why" msgid="2056750982959359863">"Einstellungen"</string>
-    <string name="battery_saver_confirmation_title" msgid="1234998463717398453">"Stromsparmodus aktivieren?"</string>
-    <string name="battery_saver_confirmation_title_generic" msgid="2299231884234959849">"Über den Stromsparmodus"</string>
+    <string name="battery_saver_confirmation_title" msgid="1234998463717398453">"Energiesparmodus aktivieren?"</string>
+    <string name="battery_saver_confirmation_title_generic" msgid="2299231884234959849">"Über den Energiesparmodus"</string>
     <string name="battery_saver_confirmation_ok" msgid="5042136476802816494">"Aktivieren"</string>
-    <string name="battery_saver_start_action" msgid="4553256017945469937">"Stromsparmodus aktivieren"</string>
+    <string name="battery_saver_start_action" msgid="4553256017945469937">"Energiesparmodus aktivieren"</string>
     <string name="status_bar_settings_settings_button" msgid="534331565185171556">"Einstellungen"</string>
     <string name="status_bar_settings_wifi_button" msgid="7243072479837270946">"WLAN"</string>
     <string name="status_bar_settings_auto_rotation" msgid="8329080442278431708">"Bildschirm automatisch drehen"</string>
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Versuche noch einmal, den Screenshot zu erstellen"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Speichern des Screenshots aufgrund von zu wenig Speicher nicht möglich"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Die App oder deine Organisation lässt das Erstellen von Screenshots nicht zu"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Screenshot bearbeiten"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Screenshot schließen"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Screenshotvorschau"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Bildschirmaufzeichnung"</string>
@@ -419,7 +420,7 @@
     <string name="quick_settings_night_secondary_label_on_at" msgid="3584738542293528235">"An um <xliff:g id="TIME">%s</xliff:g>"</string>
     <string name="quick_settings_secondary_label_until" msgid="1883981263191927372">"Bis <xliff:g id="TIME">%s</xliff:g>"</string>
     <string name="quick_settings_ui_mode_night_label" msgid="1398928270610780470">"Dunkles Design"</string>
-    <string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Stromsparmodus"</string>
+    <string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Energiesparmodus"</string>
     <string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"An bei Sonnenuntergang"</string>
     <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Bis Sonnenaufgang"</string>
     <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"An um <xliff:g id="TIME">%s</xliff:g>"</string>
@@ -497,9 +498,9 @@
     <string name="user_remove_user_title" msgid="9124124694835811874">"Nutzer entfernen?"</string>
     <string name="user_remove_user_message" msgid="6702834122128031833">"Alle Apps und Daten dieses Nutzers werden gelöscht."</string>
     <string name="user_remove_user_remove" msgid="8387386066949061256">"Entfernen"</string>
-    <string name="battery_saver_notification_title" msgid="8419266546034372562">"Stromsparmodus ist aktiviert"</string>
+    <string name="battery_saver_notification_title" msgid="8419266546034372562">"Energiesparmodus ist aktiviert"</string>
     <string name="battery_saver_notification_text" msgid="2617841636449016951">"Reduzierung der Leistung und Hintergrunddaten"</string>
-    <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Stromsparmodus deaktivieren"</string>
+    <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Energiesparmodus deaktivieren"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"Die App \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\" erhält Zugriff auf alle Informationen, die auf deinem Bildschirm sichtbar sind oder von deinem Gerät wiedergegeben werden, während du aufnimmst oder streamst. Dazu gehören beispielsweise angezeigte Passwörter und Zahlungsdetails, Fotos, Nachrichten und Audioinhalte."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Der Anbieter dieser App erhält Zugriff auf alle Informationen, die auf deinem Bildschirm sichtbar sind oder von deinem Gerät wiedergegeben werden, während du aufnimmst oder streamst. Dazu gehören beispielsweise Passwörter, Zahlungsdetails, Fotos, Nachrichten und Audioinhalte."</string>
     <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Aufnahme oder Stream starten?"</string>
@@ -773,8 +774,8 @@
       <item quantity="one">%d Minute</item>
     </plurals>
     <string name="battery_panel_title" msgid="5931157246673665963">"Akkunutzung"</string>
-    <string name="battery_detail_charging_summary" msgid="8821202155297559706">"Der Stromsparmodus ist beim Aufladen nicht verfügbar."</string>
-    <string name="battery_detail_switch_title" msgid="6940976502957380405">"Stromsparmodus"</string>
+    <string name="battery_detail_charging_summary" msgid="8821202155297559706">"Der Energiesparmodus ist beim Aufladen nicht verfügbar."</string>
+    <string name="battery_detail_switch_title" msgid="6940976502957380405">"Energiesparmodus"</string>
     <string name="battery_detail_switch_summary" msgid="3668748557848025990">"Reduzierung der Leistung und Hintergrunddaten"</string>
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Taste <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Pos1"</string>
@@ -961,11 +962,11 @@
     <string name="slice_permission_checkbox" msgid="4242888137592298523">"<xliff:g id="APP">%1$s</xliff:g> darf Teile aus jeder beliebigen App anzeigen"</string>
     <string name="slice_permission_allow" msgid="6340449521277951123">"Zulassen"</string>
     <string name="slice_permission_deny" msgid="6870256451658176895">"Ablehnen"</string>
-    <string name="auto_saver_title" msgid="6873691178754086596">"Tippen zum Planen des Stromsparmodus"</string>
+    <string name="auto_saver_title" msgid="6873691178754086596">"Tippen zum Planen des Energiesparmodus"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"Aktivieren, wenn der Akku wahrscheinlich nicht mehr lange hält"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"Nein danke"</string>
-    <string name="auto_saver_enabled_title" msgid="4294726198280286333">"Geplanter Stromsparmodus aktiviert"</string>
-    <string name="auto_saver_enabled_text" msgid="7889491183116752719">"Der Stromsparmodus wird bei einem Akkustand von <xliff:g id="PERCENTAGE">%d</xliff:g> %% automatisch aktiviert."</string>
+    <string name="auto_saver_enabled_title" msgid="4294726198280286333">"Geplanter Energiesparmodus aktiviert"</string>
+    <string name="auto_saver_enabled_text" msgid="7889491183116752719">"Der Energiesparmodus wird bei einem Akkustand von <xliff:g id="PERCENTAGE">%d</xliff:g> %% automatisch aktiviert."</string>
     <string name="open_saver_setting_action" msgid="2111461909782935190">"Einstellungen"</string>
     <string name="auto_saver_okay_action" msgid="7815925750741935386">"Ok"</string>
     <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Einstellungen"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Vergrößerungsfenster"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Einstellungen für Vergrößerungsfenster"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Heranzoomen"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Herauszoomen"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Nach oben bewegen"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Nach unten bewegen"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Nach links bewegen"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Nach rechts bewegen"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Vergrößerungsschalter"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Ganzen Bildschirm vergrößern"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Teil des Bildschirms vergrößern"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Schalter"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Gerätesteuerung"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Steuerelemente für verbundene Geräte hinzufügen"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Gerätesteuerung einrichten"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Empfehlungen werden geladen"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Medien"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Du kannst die aktuelle Sitzung ausblenden."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Aktuelle Sitzung kann nicht verborgen werden."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ablehnen"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Fortsetzen"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Einstellungen"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (nicht verbunden)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Verbindung nicht möglich. Versuch es noch einmal."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Neues Gerät koppeln"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Build-Nummer"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Build-Nummer in Zwischenablage kopiert."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 6d86e91..3bb15ff 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Δοκιμάστε να κάνετε ξανά λήψη του στιγμιότυπου οθόνης"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Αδύνατη η αποθήκευση του στιγμιότυπου οθόνης λόγω περιορισμένου αποθηκευτικού χώρου"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Η λήψη στιγμιότυπων οθόνης δεν επιτρέπεται από την εφαρμογή ή τον οργανισμό σας"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Επεξεργασία στιγμιότυπου οθόνης"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Παράβλεψη στιγμιότυπου οθόνης"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Προεπισκόπηση στιγμιότυπου οθόνης"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Εγγραφή οθόνης"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Ρυθμίσεις"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Παράθυρο μεγέθυνσης"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Στοιχεία ελέγχου παραθύρου μεγέθυνσης"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Μεγέθυνση"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Σμίκρυνση"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Μετακίνηση επάνω"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Μετακίνηση κάτω"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Μετακίνηση αριστερά"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Μετακίνηση δεξιά"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Εναλλαγή μεγιστοποίησης"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Μεγέθυνση ολόκληρης της οθόνης"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Μεγέθυνση μέρους της οθόνης"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Εναλλαγή"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Στοιχεία ελέγχου συσκευής"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Προσθήκη στοιχείων ελέγχου για τις συνδεδεμένες συσκευές σας."</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Ρύθμιση στοιχείων ελέγχου συσκευής"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Φόρτωση προτάσεων"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Μέσα"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Απόκρυψη της τρέχουσας περιόδου λειτουργίας."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Δεν είναι δυνατή η απόκρυψη της τρέχουσας περιόδου λειτουργίας."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Παράβλεψη"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Συνέχιση"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Ρυθμίσεις"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (αποσυνδέθηκε)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Δεν ήταν δυνατή η σύνδεση. Δοκιμάστε ξανά."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Σύζευξη νέας συσκευής"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Αριθμός έκδοσης"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Ο αριθμός έκδοσης αντιγράφηκε στο πρόχειρο."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index cb03d40..a743bbe 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Try taking screenshot again"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Can\'t save screenshot due to limited storage space"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Taking screenshots isn\'t allowed by the app or your organisation"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Edit screenshot"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Dismiss screenshot"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Screenshot preview"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Screen Recorder"</string>
@@ -1018,6 +1019,10 @@
     <string name="accessibility_control_move_down" msgid="5390922476900974512">"Move down"</string>
     <string name="accessibility_control_move_left" msgid="8156206978511401995">"Move left"</string>
     <string name="accessibility_control_move_right" msgid="8926821093629582888">"Move right"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Magnification switch"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Magnify entire screen"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Switch"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Device controls"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Add controls for your connected devices"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Set up device controls"</string>
@@ -1059,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Loading recommendations"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Hide the current session."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Current session cannot be hidden."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Dismiss"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Resume"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Settings"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 8e5849e..3fe7a6a 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Try taking screenshot again"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Can\'t save screenshot due to limited storage space"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Taking screenshots isn\'t allowed by the app or your organisation"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Edit screenshot"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Dismiss screenshot"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Screenshot preview"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Screen Recorder"</string>
@@ -1018,6 +1019,10 @@
     <string name="accessibility_control_move_down" msgid="5390922476900974512">"Move down"</string>
     <string name="accessibility_control_move_left" msgid="8156206978511401995">"Move left"</string>
     <string name="accessibility_control_move_right" msgid="8926821093629582888">"Move right"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Magnification switch"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Magnify entire screen"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Switch"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Device controls"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Add controls for your connected devices"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Set up device controls"</string>
@@ -1059,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Loading recommendations"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Hide the current session."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Current session cannot be hidden."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Dismiss"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Resume"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Settings"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index cb03d40..a743bbe 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Try taking screenshot again"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Can\'t save screenshot due to limited storage space"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Taking screenshots isn\'t allowed by the app or your organisation"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Edit screenshot"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Dismiss screenshot"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Screenshot preview"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Screen Recorder"</string>
@@ -1018,6 +1019,10 @@
     <string name="accessibility_control_move_down" msgid="5390922476900974512">"Move down"</string>
     <string name="accessibility_control_move_left" msgid="8156206978511401995">"Move left"</string>
     <string name="accessibility_control_move_right" msgid="8926821093629582888">"Move right"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Magnification switch"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Magnify entire screen"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Switch"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Device controls"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Add controls for your connected devices"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Set up device controls"</string>
@@ -1059,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Loading recommendations"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Hide the current session."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Current session cannot be hidden."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Dismiss"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Resume"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Settings"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index cb03d40..a743bbe 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Try taking screenshot again"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Can\'t save screenshot due to limited storage space"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Taking screenshots isn\'t allowed by the app or your organisation"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Edit screenshot"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Dismiss screenshot"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Screenshot preview"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Screen Recorder"</string>
@@ -1018,6 +1019,10 @@
     <string name="accessibility_control_move_down" msgid="5390922476900974512">"Move down"</string>
     <string name="accessibility_control_move_left" msgid="8156206978511401995">"Move left"</string>
     <string name="accessibility_control_move_right" msgid="8926821093629582888">"Move right"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Magnification switch"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Magnify entire screen"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Switch"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Device controls"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Add controls for your connected devices"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Set up device controls"</string>
@@ -1059,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Loading recommendations"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Hide the current session."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Current session cannot be hidden."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Dismiss"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Resume"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Settings"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index e107ed5..78006fd 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‏‏‏‎‏‎‎‎‏‎‎‏‏‎‎‎‎‏‎‏‏‏‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‏‎‎‎‎‎‎‏‎‏‏‎‎‎‎‏‎Try taking screenshot again‎‏‎‎‏‎"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‎‏‏‎‎‎‏‏‏‎‏‎‏‏‎‎‎‏‏‎‎‏‏‎‏‎‏‎‏‎‎‏‏‏‎‏‏‏‏‎‏‏‎‏‏‎‏‏‎‏‏‎‏‎Can\'t save screenshot due to limited storage space‎‏‎‎‏‎"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‎‎‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‎‎‎‏‎‎‏‏‎‏‎‎‎‎‏‎‏‏‎‎‏‎‏‏‏‎‎‎‎‎‎‎‏‏‎‏‏‎Taking screenshots isn\'t allowed by the app or your organization‎‏‎‎‏‎"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‏‎‏‏‎‏‏‏‏‏‎‎‏‎‎‏‏‏‏‎‏‎‎‏‏‏‎‎‏‎‎‎‏‎‎‏‏‏‏‏‎‎‎‎‎‏‏‏‎‎‏‏‎‏‏‏‎Edit screenshot‎‏‎‎‏‎"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‎‏‏‏‏‏‎‎‎‏‏‎‏‏‎‏‎‎‎‏‏‎‏‏‏‏‎‏‎‏‏‎‏‎‎‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‎‎‎‎‎‎‎‎Dismiss screenshot‎‏‎‎‏‎"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‎‎‏‏‏‏‏‏‎‎‎‎‎‎‏‎‏‎‏‏‎‎‎‎‎‎‎‏‎‏‏‎‏‎‏‎‏‏‎‏‎‏‏‎‎‎‏‏‎‏‏‎‏‎‎Screenshot preview‎‏‎‎‏‎"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‎‎‎‎‏‎‎‎‎‏‎‎‎‏‎‏‎‎‏‎‏‎‎‎‎‎‏‎‎‎‏‎‏‎‎‏‎‎‎‎‏‎‎‎‎‏‏‏‏‎‎‏‏‏‎‎‎Screen Recorder‎‏‎‎‏‎"</string>
@@ -1018,6 +1019,10 @@
     <string name="accessibility_control_move_down" msgid="5390922476900974512">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‏‎‏‎‎‎‎‎‏‏‎‎‏‏‏‎‏‏‎‏‏‏‎‎‎‏‎‏‏‏‏‎‏‎‏‏‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‎Move down‎‏‎‎‏‎"</string>
     <string name="accessibility_control_move_left" msgid="8156206978511401995">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‏‎‏‎‏‎‏‏‎‎‎‎‏‎‎‎‏‎‎‏‎‏‎‎‏‎‏‏‏‎‏‏‏‏‏‏‎‏‎‎‎‎‎‎‏‎‏‏‎Move left‎‏‎‎‏‎"</string>
     <string name="accessibility_control_move_right" msgid="8926821093629582888">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‎‎‏‎‎‏‏‏‎‎‎‎‎‏‏‏‏‏‎‏‎‎‎‎‎‎‏‏‎‏‎‎‎‎‎‏‎‏‎‏‏‏‏‎‎‎‏‎‏‎‎‎‎Move right‎‏‎‎‏‎"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‎‏‏‏‎‎‏‎‏‎‎‎‎‏‎‎‎‎‎‎‏‏‎‏‏‏‏‎‏‎‏‏‏‎‎‏‎‎‎‎‏‏‏‏‎‎‏‎‏‎‎‎‏‏‎‏‎Magnification switch‎‏‎‎‏‎"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‎‎‎‎‎‎‎‎‏‎‏‏‏‎‎‏‎‎‏‎‎‏‎‎‎‏‏‎‏‎‏‎‏‎‏‎‏‏‎‎‏‎‎‎‏‎‎‎‎‎‎‎‏‏‏‎‎Magnify entire screen‎‏‎‎‏‎"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‏‏‏‏‎‎‎‎‏‎‎‏‎‎‏‎‎‎‎‎‏‎‏‎‏‎‏‏‎‏‏‏‏‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‏‎‏‎Magnify part of screen‎‏‎‎‏‎"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‎‏‎‏‎‏‎‏‎‏‎‎‏‎‏‎‏‎‏‎‏‎‎‏‏‏‎‎‏‏‎‏‏‏‎‎‎‎‎‏‎‎‎‏‏‎‎‎‏‏‏‎‏‎‏‏‏‎Switch‎‏‎‎‏‎"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‏‎‏‎‎‏‎‏‏‎‎‏‎‎‎‏‏‏‏‏‏‏‎‎‎‏‏‎‎‎‎‏‎‏‏‏‏‎‏‏‎‏‏‎‎‏‏‎‎‎‎‎‎‏‎Device controls‎‏‎‎‏‎"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‏‎‎‏‎‎‎‏‏‏‏‎‏‎‎‏‎‏‏‎‏‎‎‏‏‏‎‎‏‏‎‏‎‎‏‎‏‎‎‏‏‏‎‎‏‏‎‎‏‏‏‏‎‎‏‎‏‎Add controls for your connected devices‎‏‎‎‏‎"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‏‎‎‎‎‏‎‎‎‎‎‏‎‏‏‏‏‎‏‎‎‎‎‎‏‏‏‏‎‏‏‎‎‏‎‎‏‎‎‏‎‎‎‎‏‏‎‏‏‏‏‎‎Set up device controls‎‏‎‎‏‎"</string>
@@ -1059,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎‎‎‎‏‏‎‏‎‎‏‏‎‏‎‏‏‎‏‎‏‏‎‏‏‏‏‏‎‎‏‎‎‏‎‏‎‏‏‏‎‎‎‏‎‎‏‎‎‎‏‎‏‎‏‎‎‎Loading recommendations‎‏‎‎‏‎"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‎‎‏‏‏‏‏‎‎‏‏‎‎‏‏‏‎‏‎‎‏‏‎‎‏‏‎‎‎‎‏‏‏‎‎‏‎‏‏‎‏‎‎‎‎‏‎‏‎‎‎‎‎‏‎‏‎Media‎‏‎‎‏‎"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‏‏‏‎‏‎‏‎‎‏‏‎‏‎‏‏‏‎‎‎‎‎‎‏‎‏‎‏‏‎‏‎‎‎‏‎‎‎‎‏‎‎‎‏‏‏‎‏‏‏‏‏‏‎‎‏‎Hide the current session.‎‏‎‎‏‎"</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‏‎‎‎‏‎‎‏‏‏‏‏‎‎‏‎‏‎‏‏‏‎‏‎‏‎‏‏‏‎‎‏‏‎‏‎‎‏‎‎‏‏‏‎‏‎‎‏‏‎‎‎‏‎‏‎‎Current session cannot be hidden.‎‏‎‎‏‎"</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‏‏‏‎‎‎‎‏‏‎‏‏‏‏‎‎‎‎‏‏‎‎‎‏‎‎‏‎‏‎‎‎‏‎‎‎‏‏‎‏‎‎‏‎‏‏‎‏‎‏‎Dismiss‎‏‎‎‏‎"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‎‏‏‎‏‎‏‎‏‎‎‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎‏‏‏‏‏‎‎‎‏‏‎‎‏‎‏‎‎‏‎‎‎‏‏‏‎‎‎‎‏‎‏‎Resume‎‏‎‎‏‎"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‏‏‎‏‎‏‏‏‎‏‎‏‏‎‏‎‎‎‎‎‎‏‎‏‏‎‏‎‎‎‏‏‎‏‏‎‏‏‎‎‎‎‎‎‏‏‎‎‎‏‏‎‎‎‎Settings‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index be37623..d30a395 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Vuelve a hacer una captura de pantalla"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"No se puede guardar la captura de pantalla debido a que no hay suficiente espacio de almacenamiento"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"La app o tu organización no permiten las capturas de pantalla"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Editar captura de pantalla"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Descartar captura de pantalla"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Vista previa de la captura de pantalla"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Grabadora de pantalla"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Configuración"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Ventana de ampliación"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Controles de ampliación de la ventana"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Acercar"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Alejar"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Mover hacia arriba"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Mover hacia abajo"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Mover hacia la izquierda"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Mover hacia la derecha"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Botón de ampliación"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Ampliar toda la pantalla"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte de la pantalla"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Botón"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Controles de dispositivos"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Agrega controles para los dispositivos conectados"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Configurar controles de dispositivos"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Cargando recomendaciones"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Contenido multimedia"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Oculta la sesión actual."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"No se puede ocultar la sesión actual."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Descartar"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Reanudar"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Configuración"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (desconectado)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"No se pudo establecer la conexión. Vuelve a intentarlo."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Vincular dispositivo nuevo"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número de compilación"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Se copió el número de compilación en el portapapeles."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 08b17cc..875c310 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Vuelve a intentar hacer la captura de pantalla"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"No se puede guardar la captura de pantalla porque no hay espacio de almacenamiento suficiente"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"La aplicación o tu organización no permiten realizar capturas de pantalla"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Editar captura de pantalla"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Cerrar captura de pantalla"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Vista previa de captura de pantalla"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Grabación de pantalla"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Ajustes"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Ventana de ampliación"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Ventana de controles de ampliación"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Ampliar"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Reducir"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Mover hacia arriba"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Mover hacia abajo"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Mover hacia la izquierda"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Mover hacia la derecha"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Botón para cambiar el modo de ampliación"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Ampliar toda la pantalla"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte de la pantalla"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Cambiar"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Control de dispositivos"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Añade controles para tus dispositivos conectados"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Configurar control de dispositivos"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Cargando recomendaciones"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Multimedia"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Ocultar la sesión."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"La sesión no se puede ocultar."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Cerrar"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Reanudar"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Ajustes"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (desconectado)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"No se ha podido conectar. Inténtalo de nuevo."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Vincular nuevo dispositivo"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número de compilación"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Número de compilación copiado en el portapapeles."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index b33ad01..f9940a3 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Proovige ekraanipilt uuesti jäädvustada"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Piiratud salvestusruumi tõttu ei saa ekraanipilti salvestada"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Rakendus või teie organisatsioon ei luba ekraanipilte jäädvustada"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Ekraanipildi muutmine"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Sule ekraanipilt"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Ekraanipildi eelvaade"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Ekraanisalvesti"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Seaded"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Suurendamisaken"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Suurendamisakna juhtelemendid"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Suumi sisse"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Suumi välja"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Teisalda üles"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Teisalda alla"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Teisalda vasakule"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Teisalda paremale"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Suurenduse lüliti"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Kogu ekraanikuva suurendamine"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekraanikuva osa suurendamine"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Vaheta"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Seadmete juhikud"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Lisage juhtelemendid ühendatud seadmete jaoks"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Seadmete juhtimisvidinate seadistamine"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Soovituste laadimine"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Meedia"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Peidetakse praegune seanss."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Praegust seanssi ei saa peita."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Loobu"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Jätka"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Seaded"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (pole ühendatud)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Ühenduse loomine ebaõnnestus. Proovige uuesti."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Uue seadme sidumine"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Järgunumber"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Järgunumber kopeeriti lõikelauale."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 3f1a53c..cb86a16 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Saiatu berriro pantaila-argazkia ateratzen"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Ezin da gorde pantaila-argazkia ez delako gelditzen tokirik"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Aplikazioak edo erakundeak ez du onartzen pantaila-argazkiak ateratzea"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Editatu pantaila-argazkia"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Baztertu pantaila-argazkia"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Pantaila-argazkiaren aurrebista"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Pantaila-grabagailua"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Ezarpenak"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Lupa-leihoa"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Lupa-leihoaren aukerak"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Handitu"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Txikitu"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Eraman gora"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Eraman behera"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Eraman ezkerrera"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Eraman eskuinera"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Lupa aplikatzeko botoia"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Handitu pantaila osoa"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Handitu pantailaren zati bat"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Botoia"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Gailuak kontrolatzeko widgetak"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Gehitu konektatutako gailuak kontrolatzeko widgetak"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Konfiguratu gailuak kontrolatzeko widgetak"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Gomendioak kargatzen"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Multimedia-edukia"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Ezkutatu uneko saioa."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Ezin da ezkutatu uneko saioa."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Baztertu"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Berrekin"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Ezarpenak"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (deskonektatuta)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Ezin izan da konektatu. Saiatu berriro."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Parekatu beste gailu batekin"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Konpilazio-zenbakia"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Kopiatu da konpilazio-zenbakia arbelean."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index e5606a2..fe38ea0 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"دوباره نماگرفت بگیرید"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"به دلیل محدود بودن فضای ذخیره‌سازی نمی‌توان نماگرفت را ذخیره کرد"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"برنامه یا سازمان شما اجازه نمی‌دهند نماگرفت بگیرید."</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"ویرایش نماگرفت"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"رد کردن نماگرفت"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"پیش‌نمایش نماگرفت"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"ضبط‌کننده صفحه‌نمایش"</string>
@@ -142,7 +143,7 @@
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"برای لغو راستی‌آزمایی ضربه بزنید"</string>
     <string name="biometric_dialog_face_icon_description_idle" msgid="4351777022315116816">"لطفاً دوباره امتحان کنید"</string>
     <string name="biometric_dialog_face_icon_description_authenticating" msgid="3401633342366146535">"درحال جستجوی چهره"</string>
-    <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"چهره احراز هویت شد"</string>
+    <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"چهره اصالت‌سنجی شد"</string>
     <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"تأیید شد"</string>
     <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"برای تکمیل، روی تأیید ضربه بزنید"</string>
     <string name="biometric_dialog_authenticated" msgid="7337147327545272484">"راستی‌آزمایی‌شده"</string>
@@ -307,8 +308,8 @@
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"حالت کار روشن شد."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"صرفه‌جویی داده خاموش شد."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"صرفه‌جویی داده روشن شد."</string>
-    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="7608378211873807353">"«حریم خصوصی حسگر» خاموش است."</string>
-    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="4267393685085328801">"«حریم خصوصی حسگر» روشن است."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="7608378211873807353">"«حریم‌خصوصی حسگر» خاموش است."</string>
+    <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="4267393685085328801">"«حریم‌خصوصی حسگر» روشن است."</string>
     <string name="accessibility_brightness" msgid="5391187016177823721">"روشنایی نمایشگر"</string>
     <string name="accessibility_ambient_display_charging" msgid="7725523068728128968">"درحال شارژ شدن"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5716594205739750015">"‏داده 2G-3G موقتاً متوقف شده است"</string>
@@ -431,7 +432,7 @@
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"شروع"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"توقف"</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"دستگاه"</string>
-    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"برای تغییر برنامه‌ها،‌ تند به بالا بکشید"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"برای تغییر برنامه‌ها،‌ تند به‌بالا بکشید"</string>
     <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"برای جابه‌جایی سریع میان برنامه‌ها، به چپ بکشید"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"تغییر وضعیت نمای کلی"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"شارژ کامل شد"</string>
@@ -450,8 +451,8 @@
     <string name="keyguard_more_overflow_text" msgid="5819512373606638727">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
     <string name="speed_bump_explanation" msgid="7248696377626341060">"اعلان‌های کمتر فوری در زیر"</string>
     <string name="notification_tap_again" msgid="4477318164947497249">"دوباره ضربه بزنید تا باز شود"</string>
-    <string name="keyguard_unlock" msgid="8031975796351361601">"برای باز کردن، انگشتتان را تند به بالا بکشید"</string>
-    <string name="keyguard_retry" msgid="886802522584053523">"برای امتحان مجدد، انگشتتان را تند به بالا بکشید"</string>
+    <string name="keyguard_unlock" msgid="8031975796351361601">"برای باز کردن، انگشتتان را تند به‌بالا بکشید"</string>
+    <string name="keyguard_retry" msgid="886802522584053523">"برای امتحان مجدد، انگشتتان را تند به‌بالا بکشید"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"این دستگاه به سازمان شما تعلق دارد"</string>
     <string name="do_disclosure_with_name" msgid="2091641464065004091">"این دستگاه به <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> تعلق دارد"</string>
     <string name="phone_hint" msgid="6682125338461375925">"انگشتتان را از نماد تلفن تند بکشید"</string>
@@ -740,7 +741,7 @@
     <string name="feedback_promoted" msgid="8075757485407091976">"سیستمْ این اعلان را ارتقا داده است."</string>
     <string name="feedback_demoted" msgid="5848066008939031913">"سیستمْ این اعلان را تنزل داده است."</string>
     <string name="feedback_prompt" msgid="2278631214125128281">"این مورد درست بود؟"</string>
-    <string name="feedback_response" msgid="4671729244976641339">"از بازخورد شما سپاس‌گذاریم!"</string>
+    <string name="feedback_response" msgid="4671729244976641339">"از بازخوردتان سپاس‌گزاریم!"</string>
     <string name="feedback_ok" msgid="6481426753298857144">"تأیید"</string>
     <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"کنترل‌های اعلان برای <xliff:g id="APP_NAME">%1$s</xliff:g> باز شد"</string>
     <string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"کنترل‌های اعلان برای <xliff:g id="APP_NAME">%1$s</xliff:g> بسته شد"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"تنظیمات"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"پنجره بزرگ‌نمایی"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"کنترل‌های پنجره بزرگ‌نمایی"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"بزرگ کردن"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"کوچک کردن"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"انتقال به بالا"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"انتقال به پایین"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"انتقال به راست"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"انتقال به چپ"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"کلید درشت‌نمایی"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"درشت‌نمایی تمام صفحه"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"درشت‌نمایی بخشی از صفحه"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"کلید"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"کنترل‌های دستگاه"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"افزودن کنترل‌ها برای دستگاه‌های متصل"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"تنظیم کنترل‌های دستگاه"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"درحال بار کردن توصیه‌ها"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"رسانه"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"جلسه فعلی پنهان شود."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"نمی‌توان جلسه فعلی را پنهان کرد."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"رد کردن"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"ازسرگیری"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"تنظیمات"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (اتصال قطع شد)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"متصل نشد. دوباره امتحان کنید."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"مرتبط کردن دستگاه جدید"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"شماره ساخت"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"شماره ساخت در بریده‌دان کپی شد."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index e00b50e..11c7e19 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Yritä ottaa kuvakaappaus uudelleen."</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Kuvakaappauksen tallennus epäonnistui, sillä tallennustilaa ei ole riittävästi"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Sovellus tai organisaatio ei salli kuvakaappauksien tallentamista."</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Muokkaa kuvakaappausta"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Hylkää kuvakaappaus"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Kuvakaappauksen esikatselu"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Näytön tallentaja"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Asetukset"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Suurennusikkuna"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Suurennusikkunan ohjaimet"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Lähennä"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Loitonna"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Siirrä ylös"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Siirrä alas"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Siirrä vasemmalle"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Siirrä oikealle"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Suurennusvalinta"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Suurenna koko näyttö"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Suurenna osa näytöstä"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Vaihda"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Laitteiden hallinta"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Lisää ohjaimia yhdistettyjä laitteita varten"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Laitteiden hallinnan käyttöönotto"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Ladataan suosituksia"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Piilota nykyinen käyttökerta."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Nykyistä käyttökertaa ei voi piilottaa."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ohita"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Jatka"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Asetukset"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (yhteys katkaistu)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Ei yhteyttä. Yritä uudelleen."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Muodosta uusi laitepari"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Koontiversion numero"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Koontiversion numero kopioitu leikepöydälle"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 03e205d..b8ea6ac 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Essayez de faire une autre capture d\'écran"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Impossible d\'enregistrer la capture d\'écran, car l\'espace de stockage est limité"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"L\'application ou votre organisation n\'autorise pas les saisies d\'écran"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Modifier la capture d\'écran"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Fermer la capture d\'écran"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Aperçu de la capture d\'écran"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Enregistreur d\'écran"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Paramètres"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Fenêtre d\'agrandissement"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Commandes pour la fenêtre d\'agrandissement"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Effectuer un zoom avant"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Effectuer un zoom arrière"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Déplacer vers le haut"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Déplacer vers le bas"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Déplacer vers la gauche"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Déplacer vers la droite"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Commutateur d\'agrandissement"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Agrandir l\'écran entier"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Agrandir une partie de l\'écran"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Commutateur"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Commandes des appareils"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Ajoutez des commandes pour vos appareils connectés"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Configurer les commandes des appareils"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Chargement des recommandations…"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Commandes multimédias"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Masquer la session en cours."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"La session actuelle ne peut pas être masquée."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Fermer"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Reprendre"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Paramètres"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (déconnecté)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Impossible de se connecter. Réessayez."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Associer un autre appareil"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numéro de version"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Le numéro de version a été copié dans le presse-papiers."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 82df63a..289ce40 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Essayez de nouveau de faire une capture d\'écran"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Impossible d\'enregistrer la capture d\'écran, car l\'espace de stockage est limité"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Les captures d\'écran ne sont pas autorisées par l\'application ni par votre organisation"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Modifier la capture d\'écran"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Fermer la capture d\'écran"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Aperçu de la capture d\'écran"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Enregistreur d\'écran"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Paramètres"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Fenêtre d\'agrandissement"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Fenêtre des commandes d\'agrandissement"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Faire un zoom avant"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Faire un zoom arrière"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Déplacer vers le haut"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Déplacer vers le bas"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Déplacer vers la gauche"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Déplacer vers la droite"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Changer de mode d\'agrandissement"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Agrandir tout l\'écran"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Agrandir une partie de l\'écran"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Changer"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Commandes des appareils"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Ajouter des commandes pour vos appareils connectés"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Configurer les commandes des appareils"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Chargement des recommandations"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Multimédia"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Masquer la session en cours."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Impossible de masquer la session actuelle."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Fermer"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Reprendre"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Paramètres"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (déconnecté)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Impossible de se connecter. Réessayez."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Associer un nouvel appareil"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numéro de build"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Numéro de build copié dans le presse-papiers."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 9be451d..e3d3755 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Volve tentar crear unha captura de pantalla"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Non se puido gardar a captura de pantalla porque o espazo de almacenamento é limitado"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"A aplicación ou a túa organización non permite realizar capturas de pantalla"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Edita a captura de pantalla"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Ignora a captura de pantalla"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Vista previa da captura de pantalla"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Gravadora da pantalla"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Configuración"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Ventá de superposición"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Controis de ampliación da ventá"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Achegar"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Afastar"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Mover cara arriba"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Mover cara abaixo"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Mover cara á esquerda"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Mover cara á dereita"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Interruptor do modo de ampliación"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Amplía toda a pantalla"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Amplía parte da pantalla"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Cambiar"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Control de dispositivos"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Engade controis para os dispositivos conectados"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Configurar o control de dispositivos"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Cargando recomendacións"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Contido multimedia"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Oculta a sesión actual."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Non se pode ocultar a sesión actual."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ignorar"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Retomar"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Configuración"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (dispositivo desconectado)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Non se puido establecer a conexión. Téntao de novo."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Vincular dispositivo novo"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número de compilación"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Copiouse o número de compilación no portapapeis."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 864d05f..02c72db 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ફરીથી સ્ક્રીનશૉટ લેવાનો પ્રયાસ કરો"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"મર્યાદિત સ્ટોરેજ સ્પેસને કારણે સ્ક્રીનશૉટ સાચવી શકાતો નથી"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ઍપ્લિકેશન કે તમારી સંસ્થા દ્વારા સ્ક્રીનશૉટ લેવાની મંજૂરી નથી"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"સ્ક્રીનશૉટમાં ફેરફાર કરો"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"સ્ક્રીનશૉટ છોડી દો"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"સ્ક્રીનશૉટનો પ્રીવ્યૂ"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"સ્ક્રીન રેકૉર્ડર"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"સેટિંગ"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"વિસ્તૃતીકરણ વિંડો"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"વિસ્તૃતીકરણ વિંડોના નિયંત્રણો"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"મોટું કરો"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"નાનું કરો"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"ઉપર ખસેડો"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"નીચે ખસેડો"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"ડાબી બાજુ ખસેડો"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"જમણી બાજુ ખસેડો"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"મોટું કરવાની સુવિધાવાળી સ્વિચ"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"સંપૂર્ણ સ્ક્રીન મોટી કરો"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"સ્ક્રીનનો કોઈ ભાગ મોટો કરો"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"સ્વિચ"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"ડિવાઇસનાં નિયંત્રણો"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"તમારા કનેક્ટ કરેલા ડિવાઇસ માટે નિયંત્રણો ઉમેરો"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"ડિવાઇસનાં નિયંત્રણો સેટઅપ કરો"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"સુઝાવ લોડ કરી રહ્યાં છીએ"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"મીડિયા"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"હાલનું સત્ર છુપાવો."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"વર્તમાન સત્ર છુપાવી શકાતું નથી."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"છોડી દો"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"ફરી શરૂ કરો"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"સેટિંગ"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ડિસ્કનેક્ટ થયેલું)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"કનેક્ટ કરી શકાયું નહીં. ફરી પ્રયાસ કરો."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"નવા ડિવાઇસ સાથે જોડાણ કરો"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"બિલ્ડ નંબર"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"બિલ્ડ નંબર ક્લિપબૉર્ડ પર કૉપિ કર્યો."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index e11055c..3f497e7 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"स्क्रीनशॉट दोबारा लेने की कोशिश करें"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"मेमोरी कम होने की वजह से स्क्रीनशॉट सेव नहीं किया जा सका"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ऐप्लिकेशन या आपका संगठन स्क्रीनशॉट लेने की अनुमति नहीं देता"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"स्क्रीनशॉट में बदलाव करें"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"स्क्रीनशॉट खारिज करें"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"स्क्रीनशॉट की झलक"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"स्क्रीन रिकॉर्डर"</string>
@@ -1014,18 +1015,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"सेटिंग"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"स्क्रीन को बड़ा करके दिखाने वाली विंडो"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"स्क्रीन को बड़ा करके दिखाने वाली विंडो के नियंत्रण"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"ज़ूम इन करें"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"ज़ूम आउट करें"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"ऊपर ले जाएं"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"नीचे ले जाएं"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"बाईं ओर ले जाएं"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"दाईं ओर ले जाएं"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"ज़ूम करने की सुविधा वाला स्विच"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"पूरी स्क्रीन को ज़ूम करें"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"स्क्रीन के किसी हिस्से को ज़ूम करें"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"स्विच"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"डिवाइस कंट्रोल"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"कनेक्ट किए गए डिवाइस के लिए कंट्रोल जोड़ें"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"डिवाइस कंट्रोल सेट अप करें"</string>
@@ -1067,6 +1066,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"सुझाव लोड हो रहे हैं"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"मीडिया"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"इस मीडिया सेशन को छिपाएं."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"मौजूदा मीडिया सत्र छिपाया नहीं जा सकता."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"खारिज करें"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"फिर से शुरू करें"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"सेटिंग"</string>
@@ -1089,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (डिसकनेक्ट किया गया)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"कनेक्ट नहीं किया जा सका. फिर से कोशिश करें."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"नया डिवाइस जोड़ें"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"बिल्ड नंबर"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"बिल्ड नंबर को क्लिपबोर्ड पर कॉपी किया गया."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index d5033fa..9958a2c 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Pokušajte ponovo napraviti snimku zaslona"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Zaslon nije snimljen zbog ograničenog prostora za pohranu"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Aplikacija ili vaša organizacija ne dopuštaju snimanje zaslona"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Uređivanje snimke zaslona"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Odbacivanje snimke zaslona"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Pregled snimke zaslona"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Snimač zaslona"</string>
@@ -1017,18 +1018,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Postavke"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Prozor za povećavanje"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Kontrole prozora za povećavanje"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Povećaj"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Smanji"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Premjesti gore"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Premjesti dolje"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Premjesti ulijevo"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Premjesti udesno"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Prebacivanje povećavanja"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Povećaj cijeli zaslon"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Povećaj dio zaslona"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Prebacivanje"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Kontrole uređaja"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Dodavanje kontrola za povezane uređaje"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Postavljanje kontrola uređaja"</string>
@@ -1071,6 +1070,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Učitavanje preporuka"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Mediji"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Sakrij trenutačnu sesiju."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Trenutačnu sesiju nije moguće sakriti."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Odbaci"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Nastavi"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Postavke"</string>
@@ -1093,8 +1093,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (nije povezano)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Povezivanje nije bilo moguće. Pokušajte ponovo."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Upari novi uređaj"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Broj međuverzije"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Broj međuverzije kopiran je u međuspremnik."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 6d960e7..0159ec6 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Próbálja meg újra elkészíteni a képernyőképet"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Nem menthet képernyőképet, mert kevés a tárhely"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Az alkalmazás vagy az Ön szervezete nem engedélyezi képernyőkép készítését"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Képernyőkép szerkesztése"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Képernyőkép elvetése"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Képernyőkép előnézete"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Képernyőrögzítő"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Beállítások"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Nagyítás ablaka"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Nagyítási vezérlők ablaka"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Nagyítás"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Kicsinyítés"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Mozgatás felfelé"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Mozgatás lefelé"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Mozgatás balra"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Mozgatás jobbra"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Nagyításváltó"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Teljes képernyő nagyítása"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Képernyő bizonyos részének nagyítása"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Váltás"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Eszközvezérlők"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Vezérlők hozzáadása a csatlakoztatott eszközökhöz"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Eszközvezérlők beállítása"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Javaslatok betöltése…"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Média"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Jelenlegi munkamenet elrejtése."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"A jelenlegi munkamenetet nem lehet elrejteni."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Elvetés"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Folytatás"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Beállítások"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (leválasztva)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Sikertelen csatlakozás. Próbálja újra."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Új eszköz párosítása"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Buildszám"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Buildszám a vágólapra másolva."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 9e746fa..1d80192 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Փորձեք նորից"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Չհաջողվեց պահել սքրինշոթը անբավարար հիշողության պատճառով"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Հավելվածը կամ ձեր կազմակերպությունը չի թույլատրում սքրինշոթի ստացումը"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Փոփոխել սքրինշոթը"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Փակել սքրինշոթը"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Սքրինշոթի նախադիտում"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Էկրանի տեսագրիչ"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Կարգավորումներ"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Խոշորացման պատուհան"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Խոշորացման պատուհանի կառավարման տարրեր"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Մեծացնել"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Փոքրացնել"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Տեղափոխել վերև"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Տեղափոխել ներքև"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Տեղափոխել ձախ"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Տեղափոխել աջ"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Խոշորացման փոփոխություն"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Խոշորացնել ամբողջ էկրանը"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Խոշորացնել էկրանի որոշակի հատվածը"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Փոխել"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Սարքերի կառավարման տարրեր"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Ավելացրեք կառավարման տարրեր ձեր միացված սարքերի համար"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Սարքերի կառավարման տարրերի կարգավորում"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Բեռնման խորհուրդներ"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Մեդիա"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Թաքցրեք ընթացիկ աշխատաշրջանը"</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Ընթացիկ աշխատաշրջանը չի կարող թաքցվել։"</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Փակել"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Շարունակել"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Կարգավորումներ"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (անջատված է)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Չհաջողվեց միանալ։ Նորից փորձեք։"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Նոր սարքի զուգակցում"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Կառուցման համարը"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Կառուցման համարը պատճենվեց սեղմատախտակին։"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 380e943..9aaf270 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Coba ambil screenshot lagi"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Tidak dapat menyimpan screenshot karena ruang penyimpanan terbatas"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Mengambil screenshot tidak diizinkan oleh aplikasi atau organisasi"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Edit screenshot"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Menutup screenshot"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Pratinjau screenshot"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Perekam Layar"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Setelan"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Jendela Pembesaran"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Kontrol Jendela Pembesaran"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Perbesar"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Perkecil"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Pindahkan ke atas"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Pindahkan ke bawah"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Pindahkan ke kiri"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Pindahkan ke kanan"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Tombol pembesaran"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Perbesar seluruh layar"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Perbesar sebagian layar"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Alihkan"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Kontrol perangkat"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Tambahkan kontrol untuk perangkat terhubung"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Siapkan kontrol perangkat"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Memuat rekomendasi"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Menyembunyikan sesi saat ini."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Sesi saat ini tidak dapat disembunyikan."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Tutup"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Lanjutkan"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Setelan"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (terputus)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Tidak dapat terhubung. Coba lagi."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Sambungkan perangkat baru"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Nomor versi"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Nomor versi disalin ke papan klip."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index dd12ed6..bf49e97 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Prófaðu að taka skjámynd aftur"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Ekki tókst að vista skjámynd vegna takmarkaðs geymslupláss"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Forritið eða fyrirtækið þitt leyfir ekki skjámyndatöku"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Breyta skjámynd"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Loka skjámynd"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Forskoðun skjámyndar"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Skjáupptaka"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Stillingar"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Stækkunargluggi"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Stækkunarstillingar glugga"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Auka aðdrátt"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Minnka aðdrátt"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Færa upp"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Færa niður"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Færa til vinstri"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Færa til hægri"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Stækkunarrofi"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Stækka allan skjáinn"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Stækka hluta skjásins"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Rofi"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Tækjastjórnun"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Bæta við stýringum fyrir tengd tæki"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Setja upp tækjastjórnun"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Hleður tillögum"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Margmiðlunarefni"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Fela núverandi lotu."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Ekki er hægt að fela núverandi lotu."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Hunsa"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Halda áfram"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Stillingar"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (aftengt)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Tenging mistókst. Reyndu aftur."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Para nýtt tæki"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Útgáfunúmer smíðar"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Útgáfunúmer smíðar afritað á klippiborð."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 851228f..8e665ec 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Riprova ad acquisire lo screenshot"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Impossibile salvare lo screenshot a causa dello spazio di archiviazione limitato"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"L\'acquisizione di screenshot non è consentita dall\'app o dall\'organizzazione"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Modifica screenshot"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Ignora screenshot"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Anteprima screenshot"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Registrazione dello schermo"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Impostazioni"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Finestra ingrandimento"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Finestra controlli di ingrandimento"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Aumenta lo zoom"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Diminuisci lo zoom"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Sposta su"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Sposta giù"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Sposta a sinistra"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Sposta a destra"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Opzione Ingrandimento"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Ingrandisci l\'intero schermo"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ingrandisci parte dello schermo"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Opzione"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Controllo dei dispositivi"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Aggiungi controlli per i dispositivi connessi"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Configura il controllo dei dispositivi"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Caricamento dei consigli"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Contenuti multimediali"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Nascondi la sessione attuale."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Impossibile nascondere la sessione corrente."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ignora"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Riprendi"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Impostazioni"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (disconnesso)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Impossibile connettersi. Riprova."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Accoppia nuovo dispositivo"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numero build"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Numero build copiato negli appunti."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index d4ad45e..052b721 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"יש לנסות שוב לבצע צילום מסך"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"לא היה מספיק מקום לשמור את צילום המסך"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"האפליקציה או הארגון שלך אינם מתירים ליצור צילומי מסך"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"עריכת צילום מסך"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"סגירת צילום מסך"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"תצוגה מקדימה של צילום מסך"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"מקליט המסך"</string>
@@ -941,7 +942,7 @@
     <string name="notification_channel_general" msgid="4384774889645929705">"הודעות כלליות"</string>
     <string name="notification_channel_storage" msgid="2720725707628094977">"אחסון"</string>
     <string name="notification_channel_hints" msgid="7703783206000346876">"טיפים"</string>
-    <string name="instant_apps" msgid="8337185853050247304">"אפליקציות אינסטנט"</string>
+    <string name="instant_apps" msgid="8337185853050247304">"אפליקציות ללא התקנה"</string>
     <string name="instant_apps_title" msgid="8942706782103036910">"<xliff:g id="APP">%1$s</xliff:g> פועלת"</string>
     <string name="instant_apps_message" msgid="6112428971833011754">"האפליקציה נפתחת בלי התקנה."</string>
     <string name="instant_apps_message_with_help" msgid="1816952263531203932">"האפליקציה נפתחת בלי התקנה. אפשר להקיש כדי לקבל מידע נוסף."</string>
@@ -1022,18 +1023,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"הגדרות"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"חלון הגדלה"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"בקרות של חלון ההגדלה"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"התקרבות"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"התרחקות"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"הזזה למעלה"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"הזזה למטה"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"הזזה שמאלה"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"הזזה ימינה"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"מעבר למצב הגדלה"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"הגדלת כל המסך"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"הגדלת חלק מהמסך"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"מעבר"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"פקדי מכשירים"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"יש להוסיף פקדים למכשירים המחוברים"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"הגדרה של פקדי מכשירים"</string>
@@ -1077,6 +1076,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"בטעינת המלצות"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"מדיה"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"הסתרת הסשן הנוכחי."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"לא ניתן להסתיר את הסשן הנוכחי."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"סגירה"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"המשך"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"הגדרות"</string>
@@ -1099,8 +1099,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (מנותק)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"לא ניתן היה להתחבר. יש לנסות שוב."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"התאמה של מכשיר חדש"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"‏מספר Build"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"‏מספר ה-Build הועתק ללוח."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 7547853..7e64155f 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"スクリーンショットを撮り直してください"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"空き容量が足りないため、スクリーンショットを保存できません"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"スクリーンショットの作成はアプリまたは組織で許可されていません"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"スクリーンショットの編集"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"スクリーンショットを閉じます"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"スクリーンショットのプレビュー"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"スクリーン レコーダー"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"設定"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"拡大ウィンドウ"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"拡大ウィンドウ コントロール"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"拡大"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"縮小"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"上に移動"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"下に移動"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"左に移動"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"右に移動"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"拡大スイッチ"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"画面全体を拡大します"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"画面の一部を拡大します"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"スイッチ"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"デバイス コントロール"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"接続済みデバイスのコントロールを追加します"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"デバイス コントロールの設定"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"候補を読み込んでいます"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"メディア"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"現在のセッションを非表示にします。"</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"現在のセッションは非表示にできません。"</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"閉じる"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"再開"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"設定"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>(未接続)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"接続できませんでした。もう一度お試しください。"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"新しいデバイスとのペア設定"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"ビルド番号"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"ビルド番号をクリップボードにコピーしました。"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 69b1d14..97aea29 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ხელახლა ცადეთ ეკრანის ანაბეჭდის გაკეთება"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"ეკრანის ანაბეჭდის შენახვა ვერ მოხერხდა შეზღუდული მეხსიერების გამო"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ეკრანის ანაბეჭდების შექმნა არ არის ნებადართული აპის ან თქვენი ორგანიზაციის მიერ"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"ეკრანის ანაბეჭდის რედაქტირება"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ეკრანის ანაბეჭდის დახურვა"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"ეკრანის ანაბეჭდის გადახედვა"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"ეკრანის ჩამწერი"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"პარამეტრები"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"გადიდების ფანჯარა"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"გადიდების კონტროლის ფანჯარა"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"მასშტაბის გადიდება"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"მასშტაბის შემცირება"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"ზემოთ გადატანა"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"ქვემოთ გადატანა"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"მარცხნივ გადატანა"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"მარჯვნივ გადატანა"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"გადიდების გადართვა"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"მთლიანი ეკრანის გადიდება"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ეკრანის ნაწილის გადიდება"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"გადართვა"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"მოწყობილ. მართვის საშუალებები"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"მართვის საშუალებების დამატება თქვენს დაკავშირებულ მოწყობილობებზე"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"მოწყობილობის მართვის საშუალებების დაყენება"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"მიმდინარეობს რეკომენდაციების ჩატვირთვა"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"მედია"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"დაიმალოს მიმდინარე სესია"</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"მიმდინარე სესიის დამალვა შეუძლებელია."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"დახურვა"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"გაგრძელება"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"პარამეტრები"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (კავშირი გაწყვეტილია)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"დაკავშირება ვერ მოხერხდა. ცადეთ ხელახლა."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ახალი მოწყობილობის დაწყვილება"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"ანაწყობის ნომერი"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"ანაწყობის ნომერი დაკოპირებულია გაცვლის ბუფერში."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index b86f32e..a6376e3 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Қайта скриншот жасап көріңіз"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Жадтағы шектеулі бос орынға байланысты скриншот сақталмайды"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Қолданба немесе ұйым скриншоттар түсіруге рұқсат етпейді"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Скриншотты өзгерту"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Скриншотты жабу"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Скриншотты алдын ала қарау"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Экран жазғыш"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Параметрлер"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Ұлғайту терезесі"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Ұлғайту терезесінің басқару элементтері"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Ұлғайту"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Кішірейту"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Жоғары қарай жылжыту"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Төмен қарай жылжыту"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Солға жылжыту"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Оңға жылжыту"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Ұлғайту режиміне ауыстырғыш"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Толық экранды ұлғайту"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Экранның бөлігін ұлғайту"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Ауысу"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Құрылғыны басқару элементтері"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Жалғанған құрылғылар үшін басқару виджеттерін қосу"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Құрылғыны басқару элементтерін реттеу"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Жүктеуге қатысты ұсыныстар"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Мультимедиа"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Ағымдағы сеансты жасыру"</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Қазіргі сеансты жасыру мүмкін емес."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Жабу"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Жалғастыру"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Параметрлер"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ажыратылған)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Қосылмады. Қайта қосылып көріңіз."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Жаңа құрылғыны жұптау"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Құрама нөмірі"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Құрама нөмірі буферге көшірілді."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index e895140..b8d2fa8 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"សាកល្បង​ថតរូបថត​អេក្រង់​ម្តងទៀត"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"មិនអាច​រក្សាទុក​រូបថតអេក្រង់​បានទេ ​ដោយសារ​ទំហំផ្ទុក​មានកម្រិតទាប"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ការថត​រូបអេក្រង់​មិនត្រូវ​បាន​អនុញ្ញាត​ដោយ​កម្មវិធី​នេះ ឬ​ស្ថាប័ន​របស់អ្នក"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"កែ​រូបថត​អេក្រង់"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ច្រានចោល​រូបថត​អេក្រង់"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"ការមើល​រូបថត​អេក្រង់​សាកល្បង"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"មុខងារថត​អេក្រង់"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"ការកំណត់"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"វិនដូ​ការពង្រីក"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"វិនដូគ្រប់គ្រង​​ការពង្រីក"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"ពង្រីក"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"បង្រួម"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"ផ្លាស់ទី​ឡើង​លើ"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"ផ្លាស់ទី​ចុះ​ក្រោម"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"ផ្លាស់ទី​ទៅ​ឆ្វេង"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"ផ្លាស់ទីទៅ​ស្តាំ"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"ប៊ូតុងបិទបើកការ​ពង្រីក"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"ពង្រីក​អេក្រង់​ទាំងមូល"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ពង្រីក​ផ្នែកនៃ​អេក្រង់"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ប៊ូតុងបិទបើក"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"ផ្ទាំងគ្រប់គ្រងឧបករណ៍"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"បញ្ចូល​ផ្ទាំងគ្រប់គ្រង​សម្រាប់​ឧបករណ៍​ដែលអ្នកបានភ្ជាប់"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"រៀបចំ​ផ្ទាំងគ្រប់គ្រងឧបករណ៍"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"កំពុងផ្ទុក​ការណែនាំ"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"មេឌៀ"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"លាក់វគ្គ​បច្ចុប្បន្ន។"</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"មិនអាចលាក់​វគ្គបច្ចុប្បន្នបានទេ។"</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"ច្រាន​ចោល"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"បន្ត"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ការកំណត់"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (បាន​ផ្ដាច់)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"មិន​អាច​ភ្ជាប់​បាន​ទេ។ សូមព្យាយាមម្ដងទៀត។"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ផ្គូផ្គង​ឧបករណ៍ថ្មី"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"លេខ​កំណែបង្កើត"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"បានចម្លងលេខ​កំណែបង្កើតទៅឃ្លីបបត។"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 2a9f649..5c502ac 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ಸ್ಕ್ರೀನ್‌ಶಾಟ್ ಅನ್ನು ಪುನಃ ತೆಗೆದುಕೊಳ್ಳಲು ಪ್ರಯತ್ನಿಸಿ"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"ಪರಿಮಿತ ಸಂಗ್ರಹಣೆ ಸ್ಥಳದ ಕಾರಣದಿಂದಾಗಿ ಸ್ಕ್ರೀನ್‌ಶಾಟ್ ಉಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ಅಪ್ಲಿಕೇಶನ್ ಅಥವಾ ಸಂಸ್ಥೆಯು ಸ್ಕ್ರೀನ್‌ಶಾಟ್‌ಗಳನ್ನು ತೆಗೆಯುವುದನ್ನು ಅನುಮತಿಸುವುದಿಲ್ಲ"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"ಸ್ಕ್ರೀನ್‌ಶಾಟ್‌ ಅನ್ನು ಎಡಿಟ್ ಮಾಡಿ"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ಸ್ಕ್ರೀನ್‌ಶಾಟ್ ಅನ್ನು ವಜಾಗೊಳಿಸಿ"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"ಸ್ಕ್ರೀನ್‍ಶಾಟ್‍ನ ಪೂರ್ವವೀಕ್ಷಣೆ"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡರ್"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"ವರ್ಧನೆಯ ವಿಂಡೋ"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"ವರ್ಧನೆಯ ವಿಂಡೋ ನಿಯಂತ್ರಣಗಳು"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"ಝೂಮ್ ಇನ್ ಮಾಡಿ"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"ಝೂಮ್ ಔಟ್ ಮಾಡಿ"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"ಮೇಲೆಕ್ಕೆ ಸರಿಸಿ"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"ಕೆಳಗೆ ಸರಿಸಿ"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"ಎಡಕ್ಕೆ ಸರಿಸಿ"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"ಬಲಕ್ಕೆ ಸರಿಸಿ"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"ಝೂಮ್ ಮಾಡುವ ಸ್ವಿಚ್"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"ಸ್ಕ್ರೀನ್‌ನ ಸಂಪೂರ್ಣ ಭಾಗವನ್ನು ಝೂಮ್ ಮಾಡಿ"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ಸ್ಕ್ರೀನ್‌ನ ಅರ್ಧಭಾಗವನ್ನು ಝೂಮ್ ಮಾಡಿ"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ಸ್ವಿಚ್"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"ಸಾಧನ ನಿಯಂತ್ರಣಗಳು"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"ನಿಮ್ಮ ಸಂಪರ್ಕಿತ ಸಾಧನಗಳಿಗೆ ನಿಯಂತ್ರಣಗಳನ್ನು ಸೇರಿಸಿ"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"ಸಾಧನ ನಿಯಂತ್ರಣಗಳನ್ನು ಸೆಟಪ್ ಮಾಡಿ"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"ಶಿಫಾರಸುಗಳು ಲೋಡ್ ಆಗುತ್ತಿವೆ"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"ಮಾಧ್ಯಮ"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"ಪ್ರಸ್ತುತ ಸೆಶನ್ ಅನ್ನು ಮರೆಮಾಡಿ."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"ಪ್ರಸ್ತುತ ಸೆಶನ್ ಅನ್ನು ಮರೆಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"ವಜಾಗೊಳಿಸಿ"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"ಪುನರಾರಂಭಿಸಿ"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ಸಂಪರ್ಕ ಕಡಿತಗೊಳಿಸಲಾಗಿದೆ)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"ಸಂಪರ್ಕಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ. ಪುನಃ ಪ್ರಯತ್ನಿಸಿ."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ಹೊಸ ಸಾಧನವನ್ನು ಜೋಡಿಸಿ"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"ಬಿಲ್ಡ್ ಸಂಖ್ಯೆ"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"ಬಿಲ್ಡ್ ಸಂಖ್ಯೆಯನ್ನು ಕ್ಲಿಪ್‌ಬೋರ್ಡ್‌ನಲ್ಲಿ ನಕಲಿಸಲಾಗಿದೆ."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 559205c..cef5158 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"스크린샷을 다시 찍어 보세요."</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"저장용량이 부족하여 스크린샷을 저장할 수 없습니다"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"앱이나 조직에서 스크린샷 촬영을 허용하지 않습니다."</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"스크린샷 수정"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"스크린샷 닫기"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"스크린샷 미리보기"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"화면 녹화"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"설정"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"확대 창"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"확대 창 컨트롤"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"확대"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"축소"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"위로 이동"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"아래로 이동"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"왼쪽으로 이동"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"오른쪽으로 이동"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"확대 전환"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"전체 화면 확대"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"화면 일부 확대"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"전환"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"기기 컨트롤"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"연결된 기기의 컨트롤을 추가하세요."</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"기기 컨트롤 설정"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"추천 제어 기능 로드 중"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"미디어"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"현재 세션을 숨깁니다."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"현재 세션은 숨길 수 없습니다."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"닫기"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"다시 시작"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"설정"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>(연결 끊김)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"연결할 수 없습니다. 다시 시도하세요."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"새 기기와 페어링"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"빌드 번호"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"빌드 번호가 클립보드에 복사되었습니다."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index a688150..572c3e7 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Скриншотту кайра тартып көрүңүз"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Сактагычта бош орун аз болгондуктан, скриншот сакталбай жатат"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Скриншот тартууга колдонмо же ишканаңыз тыюу салган."</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Скриншотту түзөтүү"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Скриншотту четке кагуу"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Скриншотту алдын ала көрүү"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"экрандан видео жаздырып алуу"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Жөндөөлөр"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Чоңойтуу терезеси"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Чоңойтуу терезесин башкаруу каражаттары"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Жакындатуу"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Алыстатуу"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Жогору жылдыруу"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Төмөн жылдыруу"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Солго жылдыруу"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Оңго жылдыруу"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Чоңойтуу режимине которулуу"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Толук экранды чоңойтуу"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Экрандын бир бөлүгүн чоңойтуу"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Которулуу"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Түзмөктү башкаруу элементтери"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Байланышкан түзмөктөрүңүздү башкаруу элементтерин кошосуз"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Түзмөктү башкаруу элементтерин жөндөө"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Сунуштар жүктөлүүдө"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Медиа"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Учурдагы сеансты жашыруу."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Учудагы сеансты жашырууга болбойт."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Жабуу"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Улантуу"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Жөндөөлөр"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ажыратылды)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Байланышпай койду. Кайталоо."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Жаңы түзмөктү жупташтыруу"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Курама номери"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Курама номери алмашуу буферине көчүрүлдү."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 1a6c338..1cc1b4e 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ກະລຸນາລອງຖ່າຍຮູບໜ້າຈໍອີກຄັ້ງ"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"ບໍ່ສາມາດຖ່າຍຮູບໜ້າຈໍໄດ້ເນື່ອງຈາກພື້ນທີ່ຈັດເກັບຂໍ້ມູນມີຈຳກັດ"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ແອັບ ຫຼື ອົງກອນຂອງທ່ານບໍ່ອະນຸຍາດໃຫ້ຖ່າຍຮູບໜ້າຈໍ"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"ແກ້ໄຂຮູບໜ້າຈໍ"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ປິດຮູບໜ້າຈໍ"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"ຕົວຢ່າງຮູບໜ້າຈໍ"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"ໂປຣແກຣມບັນທຶກໜ້າຈໍ"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"ການຕັ້ງຄ່າ"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"ໜ້າຈໍການຂະຫຍາຍ"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"ການຄວບຄຸມໜ້າຈໍການຂະຫຍາຍ"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"ຊູມເຂົ້າ"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"ຊູມອອກ"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"ຍ້າຍຂຶ້ນ"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"ຍ້າຍລົງ"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"ຍ້າຍໄປຊ້າຍ"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"ຍ້າຍໄປຂວາ"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"ສະຫຼັບການຂະຫຍາຍ"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"ຂະຫຍາຍທັງໜ້າຈໍ"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ຂະຫຍາຍບາງສ່ວນຂອງໜ້າຈໍ"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ສະຫຼັບ"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"ການຄວບຄຸມອຸປະກອນ"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"ເພີ່ມການຄວບຄຸມສຳລັບອຸປະກອນທີ່ເຊື່ອມຕໍ່ແລ້ວຂອງທ່ານ"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"ຕັ້ງຄ່າການຄວບຄຸມອຸປະກອນ"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"ກຳລັງໂຫຼດຄຳແນະນຳ"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"ມີເດຍ"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"ເຊື່ອງເຊດຊັນປັດຈຸບັນ."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"ບໍ່ສາມາດເຊື່ອງເຊດຊັນປັດຈຸບັນໄດ້."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"ປິດໄວ້"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"ສືບຕໍ່"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ການຕັ້ງຄ່າ"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ຕັດການເຊື່ອມຕໍ່ແລ້ວ)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"ບໍ່ສາມາດເຊື່ອມຕໍ່ໄດ້. ລອງໃໝ່."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ຈັບຄູ່ອຸປະກອນໃໝ່"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"ໝາຍເລກສ້າງ"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"ສຳເນົາໝາຍເລກສ້າງໄປໃສ່ຄລິບບອດແລ້ວ."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 6fb84dd8..152a532 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Pabandykite padaryti ekrano kopiją dar kartą"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Negalima išsaugoti ekrano kopijos dėl ribotos saugyklos vietos"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Jūsų organizacijoje arba naudojant šią programą neleidžiama daryti ekrano kopijų"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Redaguoti ekrano kopiją"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Praleisti ekrano kopiją"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Ekrano kopijos peržiūra"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Ekrano vaizdo įrašytuvas"</string>
@@ -1028,6 +1029,10 @@
     <string name="accessibility_control_move_down" msgid="5390922476900974512">"Perkelti žemyn"</string>
     <string name="accessibility_control_move_left" msgid="8156206978511401995">"Perkelti kairėn"</string>
     <string name="accessibility_control_move_right" msgid="8926821093629582888">"Perkelti dešinėn"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Didinimo jungiklis"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Didinti visą ekraną"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Didinti ekrano dalį"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Perjungti"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Įrenginio valdikliai"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Pridėkite prijungtų įrenginių valdiklių"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Įrenginio valdiklių nustatymas"</string>
@@ -1071,6 +1076,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Įkeliamos rekomendacijos"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Medija"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Slėpti dabartinį seansą."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Dabartinio seanso paslėpti negalima."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Atsisakyti"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Tęsti"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Nustatymai"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 9864a61..ae67d0f 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Mēģiniet izveidot jaunu ekrānuzņēmumu."</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Nevar saglabāt ekrānuzņēmumu, jo krātuvē nepietiek vietas."</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Lietotne vai jūsu organizācija neatļauj veikt ekrānuzņēmumus."</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Rediģēt ekrānuzņēmumu"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Nerādīt ekrānuzņēmumu"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Ekrānuzņēmuma priekšskatījums"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Ekrāna ierakstītājs"</string>
@@ -1017,18 +1018,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Iestatījumi"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Palielināšanas logs"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Palielināšanas loga vadīklas"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Tuvināt"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Tālināt"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Pārvietot uz augšu"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Pārvietot uz leju"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Pārvietot pa kreisi"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Pārvietot pa labi"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Palielinājuma slēdzis"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Palielināt visu ekrānu"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Palielināt ekrāna daļu"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Pārslēgt"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Ierīču vadīklas"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Pievienojiet vadīklas pievienotajām ierīcēm"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Ierīču vadīklu iestatīšana"</string>
@@ -1071,6 +1070,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Notiek ieteikumu ielāde"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Multivide"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Paslēpiet pašreizējo sesiju."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Pašreizējo sesiju nevar paslēpt"</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Nerādīt"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Atsākt"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Iestatījumi"</string>
@@ -1093,8 +1093,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (savienojums pārtraukts)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Nevarēja izveidot savienojumu. Mēģiniet vēlreiz."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Savienošana pārī ar jaunu ierīci"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Versijas numurs"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Versijas numurs ir kopēts starpliktuvē."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 362cbdc..fca688d 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Повторно обидете се да направите слика од екранот"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Сликата од екранот не може да се зачува поради ограничена меморија"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Апликацијата или вашата организација не дозволува снимање слики од екранот"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Изменете ја сликата од екранот"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Отфрлете ја сликата од екранот"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Преглед на слика од екранот"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Снимач на екран"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Поставки"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Прозорец за зголемување"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Контроли на прозорец за зголемување"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Зумирај"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Одзумирај"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Премести нагоре"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Премести надолу"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Премести налево"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Премести надесно"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Прекинувач за зголемување"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Зголеми цел екран"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Зголеми дел од екранот"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Префрли"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Контроли за уредите"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Додајте контроли за поврзаните уреди"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Поставете ги контролите за уредите"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Се вчитуваат препораки"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Аудиовизуелни содржини"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Сокриј ја тековнава сесија."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Тековната сесија не може да се сокрие."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Отфрли"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Продолжи"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Поставки"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (исклучен)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Не можеше да се поврзе. Обидете се повторно."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Спарете нов уред"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Број на верзија"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Бројот на верзијата е копиран во привремената меморија."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 95634b8..2a6ed6e 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"സ്‌ക്രീൻഷോട്ട് എടുക്കാൻ വീണ്ടും ശ്രമിക്കുക"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"സ്‌റ്റോറേജ് ഇടം പരിമിതമായതിനാൽ സ്‌ക്രീൻഷോട്ട് സംരക്ഷിക്കാനാകുന്നില്ല"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"സ്ക്രീൻഷോട്ടുകൾ എടുക്കുന്നത് ആപ്പോ നിങ്ങളുടെ സ്ഥാപനമോ അനുവദിക്കുന്നില്ല"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"സ്ക്രീൻഷോട്ട് എഡിറ്റ് ചെയ്യുക"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"സ്ക്രീൻഷോട്ട് ഡിസ്‌മിസ് ചെയ്യുക"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"സ്‌ക്രീൻഷോട്ട് പ്രിവ്യു"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"സ്ക്രീൻ റെക്കോർഡർ"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"ക്രമീകരണം"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"മാഗ്നിഫിക്കേഷൻ വിൻഡോ"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"മാഗ്നിഫിക്കേഷൻ വിൻഡോ നിയന്ത്രണങ്ങൾ"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"സൂം ഇൻ ചെയ്യുക"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"സൂം ഔട്ട് ചെയ്യുക"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"മുകളിലേക്ക് നീക്കുക"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"താഴേക്ക് നീക്കുക"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"ഇടത്തേക്ക് നീക്കുക"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"വലത്തേക്ക് നീക്കുക"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"മാഗ്നിഫിക്കേഷൻ മോഡ് മാറുക"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"മുഴുവൻ സ്‌ക്രീനും മാഗ്നിഫൈ ചെയ്യുക"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"സ്‌ക്രീനിന്റെ ഭാഗം മാഗ്നിഫൈ ചെയ്യുക"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"മാറുക"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"ഉപകരണ നിയന്ത്രണങ്ങൾ"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"കണക്റ്റ് ചെയ്ത ഉപകരണങ്ങൾക്ക് നിയന്ത്രണങ്ങൾ ചേർക്കുക"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"ഉപകരണ നിയന്ത്രണങ്ങൾ സജ്ജീകരിക്കുക"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"നിർദ്ദേശങ്ങൾ ലോഡ് ചെയ്യുന്നു"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"മീഡിയ"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"നിലവിലെ സെഷൻ മറയ്‌ക്കുക."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"നിലവിലെ സെഷൻ മറയ്ക്കാനാകില്ല."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"ഡിസ്‌മിസ് ചെയ്യുക"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"പുനരാരംഭിക്കുക"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ക്രമീകരണം"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (വിച്ഛേദിച്ചു)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"കണക്റ്റ് ചെയ്യാനായില്ല. വീണ്ടും ശ്രമിക്കുക."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"പുതിയ ഉപകരണവുമായി ജോടിയാക്കുക"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"ബിൽഡ് നമ്പർ"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"ക്ലിപ്പ്ബോർഡിലേക്ക് ബിൽഡ് നമ്പർ പകർത്തി."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index a2e1745..4eb2fb1 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Дэлгэцийн зургийг дахин дарж үзнэ үү"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Сангийн багтаамж бага байгаа тул дэлгэцээс дарсан зургийг хадгалах боломжгүй байна"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Таны апп, байгууллагад дэлгэцийн зураг авахыг зөвшөөрдөггүй"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Дэлгэцийн агшныг засах"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Дэлгэцийн агшныг хаах"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Дэлгэцийн агшныг урьдчилан үзэх"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Дэлгэцийн үйлдэл бичигч"</string>
@@ -549,29 +550,29 @@
     <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Таны байгууллага таны ажлын профайлд сертификатын зөвшөөрөл суулгасан байна. Таны аюулгүй сүлжээний ачааллыг өөрчлөх эсвэл хянах боломжтой."</string>
     <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Сертификатын зөвшөөрлийг энэ төхөөрөмжид суулгасан байна. Таны аюулгүй сүлжээний ачааллыг өөрчлөх эсвэл хянах боломжтой."</string>
     <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Таны админ төхөөрөмжийн ачааллыг хянадаг сүлжээний логийг асаасан байна."</string>
-    <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Та имэйл, апп, вэб хуудас зэрэг сүлжээний үйл ажиллагааг хянах боломжтой <xliff:g id="VPN_APP">%1$s</xliff:g>-д холбогдсон байна."</string>
-    <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Та имэйл, апп, вэб хуудас зэрэг сүлжээний үйл ажиллагааг хянах боломжтой <xliff:g id="VPN_APP_0">%1$s</xliff:g>, <xliff:g id="VPN_APP_1">%2$s</xliff:g>-д холбогдсон байна."</string>
-    <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Таны ажлын профайл <xliff:g id="VPN_APP">%1$s</xliff:g>-д холбогдсон байна. Энэ нь таны имэйл, апп, вэб хуудас зэрэг сүлжээний үйл ажиллагааг хянах боломжтой."</string>
-    <string name="monitoring_description_personal_profile_named_vpn" msgid="8179722332380953673">"Таны хувийн профайлыг имэйл, апп, вэб хуудас зэрэг сүлжээний үйл ажиллагааг хянах боломжтой <xliff:g id="VPN_APP">%1$s</xliff:g>-д холбосон байна."</string>
+    <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Та имэйл, апп, веб хуудас зэрэг сүлжээний үйл ажиллагааг хянах боломжтой <xliff:g id="VPN_APP">%1$s</xliff:g>-д холбогдсон байна."</string>
+    <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Та имэйл, апп, веб хуудас зэрэг сүлжээний үйл ажиллагааг хянах боломжтой <xliff:g id="VPN_APP_0">%1$s</xliff:g>, <xliff:g id="VPN_APP_1">%2$s</xliff:g>-д холбогдсон байна."</string>
+    <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Таны ажлын профайл <xliff:g id="VPN_APP">%1$s</xliff:g>-д холбогдсон байна. Энэ нь таны имэйл, апп, веб хуудас зэрэг сүлжээний үйл ажиллагааг хянах боломжтой."</string>
+    <string name="monitoring_description_personal_profile_named_vpn" msgid="8179722332380953673">"Таны хувийн профайлыг имэйл, апп, веб хуудас зэрэг сүлжээний үйл ажиллагааг хянах боломжтой <xliff:g id="VPN_APP">%1$s</xliff:g>-д холбосон байна."</string>
     <string name="monitoring_description_do_header_generic" msgid="6130190408164834986">"Таны төхөөрөмжийг <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> удирддаг."</string>
     <string name="monitoring_description_do_header_with_name" msgid="2696255132542779511">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> таны төхөөрөмжийг удирдахын тулд <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g>-г ашигладаг."</string>
     <string name="monitoring_description_do_body" msgid="7700878065625769970">"Таны админ тохиргоо, байгууллагын хандалт, апп, төхөөрөмжтэй холбоотой өгөгдөл болон таны төхөөрөмжийн байршлын мэдээллийг хянах, удирдах боломжтой."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="1467280496376492558">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="645149183455573790">"Дэлгэрэнгүй үзэх"</string>
-    <string name="monitoring_description_do_body_vpn" msgid="7699280130070502303">"Таны имэйл, апп, вэб хуудас зэрэг сүлжээний үйл ажиллагааг хянах боломжтой <xliff:g id="VPN_APP">%1$s</xliff:g>-д холбогдсон байна."</string>
+    <string name="monitoring_description_do_body_vpn" msgid="7699280130070502303">"Таны имэйл, апп, веб хуудас зэрэг сүлжээний үйл ажиллагааг хянах боломжтой <xliff:g id="VPN_APP">%1$s</xliff:g>-д холбогдсон байна."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="8292589617720435430">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="5264167033247632071">"VPN тохиргоог нээх"</string>
     <string name="monitoring_description_ca_cert_settings_separator" msgid="7107390013344435439">" "</string>
     <string name="monitoring_description_ca_cert_settings" msgid="8329781950135541003">"Итгэмжлэгдсэн мандат үнэмлэхийг нээх"</string>
     <string name="monitoring_description_network_logging" msgid="577305979174002252">"Таны админ төхөөрөмжийн ачааллыг хянадаг сүлжээний логийг асаасан байна.\n\nДэлгэрэнгүй мэдээлэл авах бол админтайгаа холбогдоно уу."</string>
-    <string name="monitoring_description_vpn" msgid="1685428000684586870">"Та апп-д VPN холболт хийхийг зөвшөөрсөн байна.\n\nЭнэхүү апп нь таны имэйл, апп, вэбсайт зэрэг төхөөрөмж болон сүлжээний үйл ажиллагааг хянах боломжтой."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="4964237035412372751">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> таны ажлын профайлыг удирддаг.\n\nТаны админ имэйл, апп болон вэб хуудас зэрэг сүлжээний үйл ажиллагааг хянах боломжтой.\n\nДэлгэрэнгүй мэдээлэл авах бол админтайгаа холбогдоно уу.\n\nТа сүлжээний үйл ажиллагааг хянах боломжтой VPN-д холбогдсон байна."</string>
+    <string name="monitoring_description_vpn" msgid="1685428000684586870">"Та апп-д VPN холболт хийхийг зөвшөөрсөн байна.\n\nЭнэхүү апп нь таны имэйл, апп, вебсайт зэрэг төхөөрөмж болон сүлжээний үйл ажиллагааг хянах боломжтой."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="4964237035412372751">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> таны ажлын профайлыг удирддаг.\n\nТаны админ имэйл, апп болон веб хуудас зэрэг сүлжээний үйл ажиллагааг хянах боломжтой.\n\nДэлгэрэнгүй мэдээлэл авах бол админтайгаа холбогдоно уу.\n\nТа сүлжээний үйл ажиллагааг хянах боломжтой VPN-д холбогдсон байна."</string>
     <string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
-    <string name="monitoring_description_app" msgid="376868879287922929">"Та <xliff:g id="APPLICATION">%1$s</xliff:g>-д холбогдсон бөгөөд энэ нь таны имэйл, апп, вэбсайт зэрэг сүлжээний үйл ажиллагааг хянах боломжтой."</string>
-    <string name="monitoring_description_app_personal" msgid="1970094872688265987">"Та <xliff:g id="APPLICATION">%1$s</xliff:g>-д холбогдсон бөгөөд энэ нь таны имэйл, апп, вэбсайт зэрэг сүлжээний хувийн үйл ажиллагааг хянах боломжтой."</string>
-    <string name="branded_monitoring_description_app_personal" msgid="1703511985892688885">"Та имэйл, апп, вэб хуудас зэрэг хувийн сүлжээнийхээ үйл ажиллагааг хянах боломжтой <xliff:g id="APPLICATION">%1$s</xliff:g>-д холбогдсон байна."</string>
-    <string name="monitoring_description_app_work" msgid="3713084153786663662">"Таны ажлын профайлыг <xliff:g id="ORGANIZATION">%1$s</xliff:g> удирддаг. Энэ нь таны имэйл, апп, вэб хуудас зэрэг ажлын сүлжээний үйл ажиллагааг хянах боломжтой <xliff:g id="APPLICATION">%2$s</xliff:g>-д холбогдсон. \n\nДэлгэрэнгүй мэдээллийг авахын тулд админтай холбогдоно уу."</string>
-    <string name="monitoring_description_app_personal_work" msgid="6175816356939166101">"Таны ажлын профайлыг <xliff:g id="ORGANIZATION">%1$s</xliff:g> удирддаг. Энэ нь таны имэйл, апп, вэб хуудас зэрэг сүлжээний үйл ажиллагааг хянах боломжтой <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>-тай холбогдсон. \n\nМөн таны сүлжээний хувийн үйл ажиллагааг хянах боломжтой <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>-д холбогдсон байна."</string>
+    <string name="monitoring_description_app" msgid="376868879287922929">"Та <xliff:g id="APPLICATION">%1$s</xliff:g>-д холбогдсон бөгөөд энэ нь таны имэйл, апп, вебсайт зэрэг сүлжээний үйл ажиллагааг хянах боломжтой."</string>
+    <string name="monitoring_description_app_personal" msgid="1970094872688265987">"Та <xliff:g id="APPLICATION">%1$s</xliff:g>-д холбогдсон бөгөөд энэ нь таны имэйл, апп, вебсайт зэрэг сүлжээний хувийн үйл ажиллагааг хянах боломжтой."</string>
+    <string name="branded_monitoring_description_app_personal" msgid="1703511985892688885">"Та имэйл, апп, веб хуудас зэрэг хувийн сүлжээнийхээ үйл ажиллагааг хянах боломжтой <xliff:g id="APPLICATION">%1$s</xliff:g>-д холбогдсон байна."</string>
+    <string name="monitoring_description_app_work" msgid="3713084153786663662">"Таны ажлын профайлыг <xliff:g id="ORGANIZATION">%1$s</xliff:g> удирддаг. Энэ нь таны имэйл, апп, веб хуудас зэрэг ажлын сүлжээний үйл ажиллагааг хянах боломжтой <xliff:g id="APPLICATION">%2$s</xliff:g>-д холбогдсон. \n\nДэлгэрэнгүй мэдээллийг авахын тулд админтай холбогдоно уу."</string>
+    <string name="monitoring_description_app_personal_work" msgid="6175816356939166101">"Таны ажлын профайлыг <xliff:g id="ORGANIZATION">%1$s</xliff:g> удирддаг. Энэ нь таны имэйл, апп, веб хуудас зэрэг сүлжээний үйл ажиллагааг хянах боломжтой <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>-тай холбогдсон. \n\nМөн таны сүлжээний хувийн үйл ажиллагааг хянах боломжтой <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>-д холбогдсон байна."</string>
     <string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent-р түгжээгүй байлгасан"</string>
     <string name="keyguard_indication_trust_disabled" msgid="6820793704816727918">"Таныг гараар нээх хүртэл төхөөрөмж түгжээтэй байх болно"</string>
     <string name="keyguard_indication_trust_unlocked_plugged_in" msgid="2323452175329362855">"<xliff:g id="KEYGUARD_INDICATION">%1$s</xliff:g>\n<xliff:g id="POWER_INDICATION">%2$s</xliff:g>"</string>
@@ -653,8 +654,8 @@
     <string name="status_bar_alarm" msgid="87160847643623352">"Сэрүүлэг"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Ажлын профайл"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Нислэгийн горим"</string>
-    <string name="add_tile" msgid="6239678623873086686">"Вэбсайтын цонх нэмэх"</string>
-    <string name="broadcast_tile" msgid="5224010633596487481">"Вэбсайтын цонх дамжуулах"</string>
+    <string name="add_tile" msgid="6239678623873086686">"Вебсайтын цонх нэмэх"</string>
+    <string name="broadcast_tile" msgid="5224010633596487481">"Вебсайтын цонх дамжуулах"</string>
     <string name="zen_alarm_warning_indef" msgid="5252866591716504287">"Та өмнө нь унтраагаагүй тохиолдолд <xliff:g id="WHEN">%1$s</xliff:g>-т сэрүүлгээ сонсохгүй"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g>-т та дараагийн сэрүүлгээ сонсохгүй"</string>
     <string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> цагт"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Тохиргоо"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Томруулалтын цонх"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Томруулалтын цонхны хяналт"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Томруулах"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Жижигрүүлэх"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Дээш зөөх"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Доош зөөх"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Зүүн тийш зөөх"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Баруун тийш зөөх"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Томруулах сэлгэлт"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Дэлгэцийг бүхэлд нь томруулах"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Дэлгэцийн нэг хэсгийг томруулах"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Сэлгэх"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Төхөөрөмжийн хяналт"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Холбогдсон төхөөрөмжүүд дээрээ хяналт нэмэх"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Төхөөрөмжийн хяналтыг тохируулах"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Зөвлөмжүүдийг ачаалж байна"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Медиа"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Одоогийн харилцан үйлдлийг нуугаарай."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Одоогийн харилцан үйлдлийг нуух боломжгүй."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Хаах"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Үргэлжлүүлэх"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Тохиргоо"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (салсан)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Холбогдож чадсангүй. Дахин оролдоно уу."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Шинэ төхөөрөмж хослуулах"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Хийгдсэн дугаар"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Хийгдсэн дугаарыг түр санах ойд хуулсан."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 7a62c17..57c1e07 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"स्क्रीनशॉट पुन्हा घेण्याचा प्रयत्न करा"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"मर्यादित स्टोरेज जागेमुळे स्क्रीनशॉट सेव्ह करू शकत नाही"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"अ‍ॅप किंवा आपल्या संस्थेद्वारे स्क्रीनशॉट घेण्याची अनुमती नाही"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"स्क्रीनशॉट संपादित करा"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"स्क्रीनशॉट डिसमिस करा"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"स्क्रीनशॉटचे पूर्वावलोकन"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"स्क्रीन रेकॉर्डर"</string>
@@ -113,10 +114,10 @@
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"स्क्रीन रेकॉर्डिंग हटवताना एरर आली"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"परवानग्या मिळवता आल्या नाहीत"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"स्क्रीन रेकॉर्डिंग सुरू करताना एरर आली"</string>
-    <string name="usb_preference_title" msgid="1439924437558480718">"USB फाईल स्थानांतरण पर्याय"</string>
+    <string name="usb_preference_title" msgid="1439924437558480718">"USB फाइल स्थानांतरण पर्याय"</string>
     <string name="use_mtp_button_title" msgid="5036082897886518086">"मीडिया प्लेअर म्हणून माउंट करा (MTP)"</string>
     <string name="use_ptp_button_title" msgid="7676427598943446826">"कॅमेरा म्हणून माउंट करा (PTP)"</string>
-    <string name="installer_cd_button_title" msgid="5499998592841984743">"Mac साठी Android फाईल स्थानांतर अ‍ॅप इंस्टॉल करा"</string>
+    <string name="installer_cd_button_title" msgid="5499998592841984743">"Mac साठी Android फाइल स्थानांतर अ‍ॅप इंस्टॉल करा"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"मागे"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"होम"</string>
     <string name="accessibility_menu" msgid="2701163794470513040">"मेनू"</string>
@@ -265,7 +266,7 @@
     <string name="accessibility_quick_settings_wifi" msgid="167707325133803052">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wifi बंद झाले."</string>
     <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wifi सुरू झाले."</string>
-    <string name="accessibility_quick_settings_mobile" msgid="1817825313718492906">"मोबाईल <xliff:g id="SIGNAL">%1$s</xliff:g>. <xliff:g id="TYPE">%2$s</xliff:g>. <xliff:g id="NETWORK">%3$s</xliff:g>."</string>
+    <string name="accessibility_quick_settings_mobile" msgid="1817825313718492906">"मोबाइल <xliff:g id="SIGNAL">%1$s</xliff:g>. <xliff:g id="TYPE">%2$s</xliff:g>. <xliff:g id="NETWORK">%3$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_battery" msgid="533594896310663853">"बॅटरी <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_airplane_off" msgid="1275658769368793228">"विमान मोड बंद."</string>
     <string name="accessibility_quick_settings_airplane_on" msgid="8106176561295294255">"विमान मोड सुरू."</string>
@@ -288,7 +289,7 @@
     <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"स्थान अहवाल बंद केला."</string>
     <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"स्थान अहवाल सुरू केला."</string>
     <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"<xliff:g id="TIME">%s</xliff:g> साठी अलार्म सेट केला."</string>
-    <string name="accessibility_quick_settings_close" msgid="2974895537860082341">"पॅनेल बंद करा."</string>
+    <string name="accessibility_quick_settings_close" msgid="2974895537860082341">"पॅनल बंद करा."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"अधिक वेळ."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"कमी वेळ."</string>
     <string name="accessibility_quick_settings_flashlight_off" msgid="7606563260714825190">"फ्लॅशलाइट बंद."</string>
@@ -298,8 +299,8 @@
     <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"फ्लॅशलाइट सुरू केला."</string>
     <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"रंग उत्क्रमण बंद केले."</string>
     <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"रंग उत्क्रमण सुरू केले."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"मोबाईल हॉटस्पॉट बंद केला."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"मोबाईल हॉटस्पॉट सुरू केला."</string>
+    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"मोबाइल हॉटस्पॉट बंद केला."</string>
+    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"मोबाइल हॉटस्पॉट सुरू केला."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"स्क्रीन कास्ट करणे थांबले."</string>
     <string name="accessibility_quick_settings_work_mode_off" msgid="562749867895549696">"कार्य मोड बंद."</string>
     <string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"कार्य मोड सुरू."</string>
@@ -624,10 +625,10 @@
     <string name="qs_status_phone_vibrate" msgid="7055409506885541979">"फोन व्हायब्रेटवर आहे"</string>
     <string name="qs_status_phone_muted" msgid="3763664791309544103">"फोन म्यूट केला"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. सशब्द करण्यासाठी टॅप करा."</string>
-    <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. व्हायब्रेट सेट करण्यासाठी टॅप करा. प्रवेशयोग्यता सेवा नि:शब्द केल्या जाऊ शकतात."</string>
-    <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. नि:शब्द करण्यासाठी टॅप करा. प्रवेशक्षमता सेवा नि:शब्द केल्या जाऊ शकतात."</string>
+    <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. व्हायब्रेट सेट करण्यासाठी टॅप करा. प्रवेशयोग्यता सेवा म्यूट केल्या जाऊ शकतात."</string>
+    <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. म्यूट करण्यासाठी टॅप करा. प्रवेशक्षमता सेवा म्यूट केल्या जाऊ शकतात."</string>
     <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. व्हायब्रेट सेट करण्यासाठी टॅप करा."</string>
-    <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. नि:शब्द करण्यासाठी टॅप करा."</string>
+    <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. म्यूट करण्यासाठी टॅप करा."</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"म्यूट करा"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"म्यूट काढून टाका"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"व्हायब्रेट करा"</string>
@@ -723,7 +724,7 @@
     <string name="bubble_overflow_empty_title" msgid="3120029421991510842">"अलीकडील कोणतेही बबल नाहीत"</string>
     <string name="bubble_overflow_empty_subtitle" msgid="2030874469510497397">"अलीकडील बबल आणि डिसमिस केलेले बबल येथे दिसतील"</string>
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"या सूचनांमध्ये सुधारणा केली जाऊ शकत नाही."</string>
-    <string name="notification_multichannel_desc" msgid="7414593090056236179">"या सूचनांचा संच येथे कॉन्फिगर केला जाऊ शकत नाही"</string>
+    <string name="notification_multichannel_desc" msgid="7414593090056236179">"या सूचनांचा संच येथे कॉंफिगर केला जाऊ शकत नाही"</string>
     <string name="notification_delegate_header" msgid="1264510071031479920">"प्रॉक्सी केलेल्या सूचना"</string>
     <string name="notification_channel_dialog_title" msgid="6856514143093200019">"सर्व <xliff:g id="APP_NAME">%1$s</xliff:g> वरील सूचना"</string>
     <string name="see_more_title" msgid="7409317011708185729">"आणखी पाहा"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"सेटिंग्ज"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"मॅग्निफिकेशन विंडो"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"मॅग्निफिकेशन विंडो नियंत्रणे"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"झूम इन करा"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"झूम आउट करा"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"वर हलवा"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"खाली हलवा"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"डावीकडे हलवा"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"उजवीकडे हलवा"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"मॅग्निफिकेशन स्विच"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"संपूर्ण स्क्रीन मॅग्निफाय करा"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"स्क्रीनचा काही भाग मॅग्निफाय करा"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"स्विच करा"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"डिव्हाइस नियंत्रणे"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"तुमच्या कनेक्ट केलेल्या डिव्हाइससाठी नियंत्रणे जोडा"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"डिव्हाइस नियंत्रणे सेट करा"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"शिफारशी लोड करत आहे"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"मीडिया"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"सध्याचे सेशन लपवा."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"सध्याचे सेशन लपवता येणार नाही."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"डिसमिस करा"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"पुन्हा सुरू करा"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"सेटिंग्ज"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (डिस्कनेक्ट केले)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"कनेक्ट करू शकलो नाही. पुन्हा प्रयत्न करा."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"नवीन डिव्हाइससोबत पेअर करा"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"बिल्ड नंबर"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"बिल्ड नंबर क्लिपबोर्डवर कॉपी केला."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 2b501bb..058055f 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Cuba ambil tangkapan skrin sekali lagi"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Tidak dapat menyimpan tangkapan skrin kerana ruang storan terhad"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Pengambilan tangkapan skrin tidak dibenarkan oleh apl atau organisasi anda"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Edit tangkapan skrin"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Ketepikan tangkapan skrin"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Pratonton tangkapan skrin"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Perakam Skrin"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Tetapan"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Tetingkap Pembesaran"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Kawalan Tetingkap Pembesaran"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Zum masuk"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Zum keluar"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Alih ke atas"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Alih ke bawah"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Alih ke kiri"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Alih ke kanan"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Suis pembesaran"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Besarkan seluruh skrin"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Besarkan sebahagian skrin"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Tukar"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Kawalan peranti"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Tambah kawalan untuk peranti yang disambungkan"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Sediakan kawalan peranti"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Memuatkan cadangan"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Sembunyikan sesi semasa."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Sesi semasa tidak boleh disembunyikan."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Tolak"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Sambung semula"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Tetapan"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (diputuskan sambungan)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Tidak boleh menyambung. Cuba lagi."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Gandingkan peranti baharu"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Nombor binaan"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Nombor binaan disalin ke papan keratan."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 1cbf17fe..5249efa 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"မျက်နှာပြင်ပုံကို ထပ်ရိုက်ကြည့်ပါ"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"သိုလှောင်ခန်းနေရာ အကန့်အသတ်ရှိသောကြောင့် ဖန်သားပြင်ဓာတ်ပုံကို သိမ်းဆည်း၍မရပါ"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ဖန်သားပြင်ဓာတ်ပုံရိုက်ကူးခြင်းကို ဤအက်ပ် သို့မဟုတ် သင်၏အဖွဲ့အစည်းက ခွင့်မပြုပါ"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"ဖန်သားပြင်ဓာတ်ပုံကို တည်းဖြတ်သည်"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ဖန်သားပြင်ဓာတ်ပုံ ပယ်ရန်"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"ဖန်သားပြင်ဓာတ်ပုံ အစမ်းကြည့်ရှုခြင်း"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"ဖန်သားပြင် ရိုက်ကူးမှု"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"ဆက်တင်များ"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"ဝင်းဒိုး ချဲ့ခြင်း"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"ဝင်းဒိုး ထိန်းချုပ်မှုများ ချဲ့ခြင်း"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"ဇူးမ်ဆွဲရန်"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"ဇူးမ်ဖြုတ်ရန်"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"အပေါ်သို့ရွှေ့ရန်"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"အောက်သို့ရွှေ့ရန်"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"ဘယ်ဘက်သို့ရွှေ့ရန်"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"ညာဘက်သို့ရွှေ့ရန်"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"ချဲ့ရန် ခလုတ်"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"ဖန်သားပြင် တစ်ခုလုံးကို ချဲ့ပါ"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ဖန်သားပြင် တစ်စိတ်တစ်ပိုင်းကို ချဲ့ပါ"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ခလုတ်"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"စက်ထိန်းစနစ်"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"ချိတ်ဆက်စက်များအတွက် ထိန်းချုပ်မှုများထည့်ပါ"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"စက်ထိန်းစနစ် ထည့်သွင်းခြင်း"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"အကြံပြုချက်များ ဖွင့်နေသည်"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"မီဒီယာ"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"လက်ရှိ စက်ရှင်ကို ဖျောက်ထားမည်။"</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"လက်ရှိစက်ရှင်ကို ဝှက်၍မရနိုင်ပါ။"</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"ပယ်ရန်"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"ဆက်လုပ်ရန်"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ဆက်တင်များ"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ချိတ်ဆက်မထားပါ)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"ချိတ်ဆက်၍ မရပါ။ ထပ်စမ်းကြည့်ပါ။"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"စက်အသစ် တွဲချိတ်ရန်"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"တည်ဆောက်မှုနံပါတ်"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"တည်ဆောက်မှုနံပါတ်ကို ကလစ်ဘုတ်သို့ မိတ္တူကူးပြီးပါပြီ။"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 6d6d66f..b676e91 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Prøv å ta skjermdump på nytt"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Kan ikke lagre skjermdumpen på grunn av begrenset lagringsplass"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Appen eller organisasjonen din tillater ikke at du tar skjermdumper"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Rediger skjermdumpen"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Avvis skjermdumpen"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Forhåndsvisning av skjermdump"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Skjermopptaker"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Innstillinger"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Forstørringsvindu"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Kontroller for forstørringsvindu"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Zoom inn"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Zoom ut"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Flytt opp"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Flytt ned"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Flytt til venstre"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Flytt til høyre"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Forstørringsbryter"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Forstørr hele skjermen"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Forstørr en del av skjermen"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Bytt"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Enhetsstyring"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Legg til kontroller for de tilkoblede enhetene dine"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Konfigurer enhetsstyring"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Laster inn anbefalinger"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Medier"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Skjul den nåværende økten."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Den nåværende økten kan ikke skjules."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Lukk"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Gjenoppta"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Innstillinger"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (frakoblet)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Kunne ikke koble til. Prøv på nytt."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Koble til en ny enhet"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Delversjonsnummer"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Delversjonsnummeret er kopiert til utklippstavlen."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index e1dd8c0..c3c8903 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"स्क्रिनसट फेरि लिएर हेर्नुहोस्"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"भण्डारण ठाउँ सीमित भएका कारण स्क्रिनसट सुरक्षित गर्न सकिएन"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"उक्त एप वा तपाईंको संगठनले स्क्रिनसटहरू लिन दिँदैन"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"स्क्रिनसट सम्पादन गर्नुहोस्"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"स्क्रिनसट हटाउनुहोस्"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"स्क्रिनसटको पूर्वावलोकन"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"स्क्रिन रेकर्डर"</string>
@@ -339,7 +340,7 @@
     <string name="dessert_case" msgid="9104973640704357717">"Dessert Case"</string>
     <string name="start_dreams" msgid="9131802557946276718">"स्क्रिन सेभर"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
-    <string name="quick_settings_header_onboarding_text" msgid="1918085351115504765">"थप विकल्पहरूका लागि आइकनहरूमा छोइराख्नुहोस्"</string>
+    <string name="quick_settings_header_onboarding_text" msgid="1918085351115504765">"थप विकल्पहरूका लागि आइकनहरूमा टच एण्ड होल्ड गर्नुहोस्"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"बाधा नपुऱ्याउनुहोस्"</string>
     <string name="quick_settings_dnd_priority_label" msgid="6251076422352664571">"प्राथमिकता मात्र"</string>
     <string name="quick_settings_dnd_alarms_label" msgid="1241780970469630835">"अलार्महरू मात्र"</string>
@@ -366,7 +367,7 @@
     <string name="quick_settings_location_off_label" msgid="7923929131443915919">"स्थान बन्द छ"</string>
     <string name="quick_settings_media_device_label" msgid="8034019242363789941">"मिडिया उपकरण"</string>
     <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
-    <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"आपतकालीन कल मात्र"</string>
+    <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"आपत्‌कालीन कल मात्र"</string>
     <string name="quick_settings_settings_label" msgid="2214639529565474534">"सेटिङहरू"</string>
     <string name="quick_settings_time_label" msgid="3352680970557509303">"समय"</string>
     <string name="quick_settings_user_label" msgid="1253515509432672496">"मलाई"</string>
@@ -591,16 +592,16 @@
     <string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"असक्षम पार्नुहोस्"</string>
     <string name="accessibility_output_chooser" msgid="7807898688967194183">"आउटपुट यन्त्र बदल्नुहोस्"</string>
     <string name="screen_pinning_title" msgid="9058007390337841305">"एप पिन गरिएको छ"</string>
-    <string name="screen_pinning_description" msgid="8699395373875667743">"तपाईंले अनपिन नगरेसम्म यसले त्यसलाई दृश्यमा कायम राख्छ। अनपिन गर्न पछाडि र परिदृश्य बटनलाई छोइराख्नुहोस्।"</string>
-    <string name="screen_pinning_description_recents_invisible" msgid="4564466648700390037">"तपाईंले अनपिन नगरेसम्म यसले त्यसलाई दृश्यमा कायम राख्छ। अनपिन गर्न पछाडि र गृह नामक बटनहरूलाई छोइराख्नुहोस्।"</string>
+    <string name="screen_pinning_description" msgid="8699395373875667743">"तपाईंले अनपिन नगरेसम्म यसले त्यसलाई दृश्यमा कायम राख्छ। अनपिन गर्न पछाडि र परिदृश्य बटनलाई टच एण्ड होल्ड गर्नुहोस्।"</string>
+    <string name="screen_pinning_description_recents_invisible" msgid="4564466648700390037">"तपाईंले अनपिन नगरेसम्म यसले त्यसलाई दृश्यमा कायम राख्छ। अनपिन गर्न पछाडि र गृह नामक बटनहरूलाई टच एण्ड होल्ड गर्नुहोस्।"</string>
     <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"तपाईंले यो एप अनपिन नगरेसम्म यो एप यहाँ देखिइरहने छ। अनपिन गर्न माथितिर स्वाइप गरी होल्ड गर्नुहोस्।"</string>
-    <string name="screen_pinning_description_accessible" msgid="7386449191953535332">"तपाईंले अनपिन नगरेसम्म यसले त्यसलाई दृश्यमा कायम राख्छ। अनपिन गर्न परिदृश्य बटनलाई छोइराख्नुहोस्।"</string>
-    <string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"तपाईंले अनपिन नगरेसम्म यसले त्यसलाई दृश्यमा कायम राख्छ। अनपिन गर्न गृह नामक बटनलाई छोइराख्नुहोस्।"</string>
+    <string name="screen_pinning_description_accessible" msgid="7386449191953535332">"तपाईंले अनपिन नगरेसम्म यसले त्यसलाई दृश्यमा कायम राख्छ। अनपिन गर्न परिदृश्य बटनलाई टच एण्ड होल्ड गर्नुहोस्।"</string>
+    <string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"तपाईंले अनपिन नगरेसम्म यसले त्यसलाई दृश्यमा कायम राख्छ। अनपिन गर्न गृह नामक बटनलाई टच एण्ड होल्ड गर्नुहोस्।"</string>
     <string name="screen_pinning_exposes_personal_data" msgid="8189852022981524789">"स्क्रिनमा व्यक्तिगत डेटा (जस्तै सम्पर्क ठेगाना र इमेलको सामग्री) देखिन सक्छ।"</string>
     <string name="screen_pinning_can_open_other_apps" msgid="7529756813231421455">"पिन गरिएको एपले अन्य एप खोल्न सक्छ।"</string>
-    <string name="screen_pinning_toast" msgid="8177286912533744328">"यो एप अनपनि गर्न पछाडि र विवरण नामक बटनहरूलाई छोइराख्नुहोस्"</string>
-    <string name="screen_pinning_toast_recents_invisible" msgid="6850978077443052594">"यो एप अनपनि गर्न पछाडि र होम बटनलाई छोइराख्नुहोस्"</string>
-    <string name="screen_pinning_toast_gesture_nav" msgid="170699893395336705">"यो एप अनपिन गर्न माथितिर स्वाइप गरी स्क्रिनमा छोइराख्नुहोस्"</string>
+    <string name="screen_pinning_toast" msgid="8177286912533744328">"यो एप अनपनि गर्न पछाडि र विवरण नामक बटनहरूलाई टच एण्ड होल्ड गर्नुहोस्"</string>
+    <string name="screen_pinning_toast_recents_invisible" msgid="6850978077443052594">"यो एप अनपनि गर्न पछाडि र होम बटनलाई टच एण्ड होल्ड गर्नुहोस्"</string>
+    <string name="screen_pinning_toast_gesture_nav" msgid="170699893395336705">"यो एप अनपिन गर्न माथितिर स्वाइप गरी स्क्रिनमा टच एण्ड होल्ड गर्नुहोस्"</string>
     <string name="screen_pinning_positive" msgid="3285785989665266984">"बुझेँ"</string>
     <string name="screen_pinning_negative" msgid="6882816864569211666">"धन्यवाद पर्दैन"</string>
     <string name="screen_pinning_start" msgid="7483998671383371313">"एप पिन गरियो"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"सेटिङ"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"म्याग्निफिकेसन विन्डो"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"म्याग्निफिकेसन विन्डोका नियन्त्रणहरू"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"जुम इन गर्नुहोस्"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"जुम आउट गर्नुहोस्"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"माथि सार्नुहोस्"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"तल सार्नुहोस्"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"बायाँ सार्नुहोस्"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"दायाँ सार्नुहोस्"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"म्याग्निफिकेसन स्विच"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"पूरै स्क्रिन म्याग्निफाइ गर्नुहोस्"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"स्क्रिनको केही भाग म्याग्निफाइ गर्नुहोस्"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"बदल्नुहोस्"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"यन्त्र नियन्त्रण गर्ने विजेटहरू"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"आफ्ना जोडिएका यन्त्रहरूका लागि नियन्त्रण सुविधाहरू थप्नुहोस्"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"यन्त्र नियन्त्रण गर्ने विजेटहरू सेटअप गर्नुहोस्"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"सिफारिसहरू लोड गर्दै"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"मिडिया"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"हालको सत्र लुकाउनुहोस्।"</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"हाल चलिरहेको सत्र लुकाउन सकिँदैन।"</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"हटाउनुहोस्"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"सुचारु गर्नुहोस्"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"सेटिङ"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (डिस्कनेक्ट गरिएको)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"कनेक्ट गर्न सकिएन। फेरि प्रयास गर्नुहोस्।"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"नयाँ यन्त्रको जोडा बनाउनुहोस्"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"बिल्ड नम्बर"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"बिल्ड नम्बर कपी गरी क्लिपबोर्डमा सारियो।"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index e2db22c..691bfcb 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Probeer opnieuw een screenshot te maken"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Kan screenshot niet opslaan vanwege beperkte opslagruimte"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Het maken van screenshots wordt niet toegestaan door de app of je organisatie"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Screenshot bewerken"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Screenshot sluiten"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Voorbeeld van screenshot"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Schermopname"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Instellingen"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Vergrotingsvenster"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Bediening van vergrotingsvenster"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Inzoomen"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Uitzoomen"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Omhoog verplaatsen"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Omlaag verplaatsen"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Naar links verplaatsen"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Naar rechts verplaatsen"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Vergrotingsschakelaar"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Hele scherm vergroten"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Deel van het scherm vergroten"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Schakelen"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Apparaatbediening"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Bedieningselementen voor je gekoppelde apparaten toevoegen"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Apparaatbediening instellen"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Aanbevelingen laden"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"De huidige sessie verbergen."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"De huidige sessie kan niet worden verborgen."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Sluiten"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Hervatten"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Instellingen"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (verbinding verbroken)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Kan geen verbinding maken. Probeer het nog eens."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Nieuw apparaat koppelen"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Build-nummer"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Build-nummer naar klembord gekopieerd."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 970e61c..de48527 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ପୁଣିଥରେ ସ୍କ୍ରୀନ୍‌ଶଟ୍ ନେବାକୁ ଚେଷ୍ଟା କରନ୍ତୁ"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"ସୀମିତ ଷ୍ଟୋରେଜ୍‍ ସ୍ପେସ୍‍ ହେତୁ ସ୍କ୍ରୀନଶଟ୍‍ ସେଭ୍‍ ହୋଇପାରିବ ନାହିଁ"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ଆପ୍‍ କିମ୍ବା ସଂସ୍ଥା ଦ୍ୱାରା ସ୍କ୍ରୀନଶଟ୍‍ ନେବାକୁ ଅନୁମତି ଦିଆଯାଇ ନାହିଁ"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"ସ୍କ୍ରିନସଟକୁ ଏଡିଟ୍ କରନ୍ତୁ"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ସ୍କ୍ରିନସଟ୍ ଖାରଜ କରନ୍ତୁ"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"ସ୍କ୍ରିନସଟର ପ୍ରିଭ୍ୟୁ"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"ସ୍କ୍ରିନ୍ ରେକର୍ଡର୍"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"ସେଟିଂସ୍"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"ମ୍ୟାଗ୍ନିଫିକେସନ୍ ୱିଣ୍ଡୋ"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"ମ୍ୟାଗ୍ନିଫିକେସନ୍ ୱିଣ୍ଡୋ ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକ"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"ଜୁମ୍ ଇନ୍ କରନ୍ତୁ"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"ଜୁମ୍ ଆଉଟ୍ କରନ୍ତୁ"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"ଉପରକୁ ମୁଭ୍ କରନ୍ତୁ"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"ତଳକୁ ମୁଭ୍ କରନ୍ତୁ"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"ବାମକୁ ମୁଭ୍ କରନ୍ତୁ"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"ଡାହାଣକୁ ମୁଭ୍ କରନ୍ତୁ"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"ମ୍ୟାଗ୍ନିଫିକେସନ୍ ସ୍ୱିଚ୍"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"ସମ୍ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନ୍ ମାଗ୍ନିଫାଏ କରନ୍ତୁ"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ସ୍କ୍ରିନର ଅଂଶ ମାଗ୍ନିଫାଏ କରନ୍ତୁ"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ସ୍ୱିଚ୍ କରନ୍ତୁ"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"ଡିଭାଇସ୍ ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକ"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"ଆପଣଙ୍କ ସଂଯୁକ୍ତ ଡିଭାଇସଗୁଡ଼ିକ ପାଇଁ ନିୟନ୍ତ୍ରଣ ଯୋଗ କରନ୍ତୁ"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"ଡିଭାଇସ୍ ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକୁ ସେଟ୍ ଅପ୍ କରନ୍ତୁ"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"ସୁପାରିଶଗୁଡ଼ିକ ଲୋଡ୍ କରାଯାଉଛି"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"ମିଡିଆ"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"ବର୍ତ୍ତମାନର ସେସନ୍ ଲୁଚାନ୍ତୁ।"</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"ବର୍ତ୍ତମାନର ସେସନକୁ ଲୁଚାଯାଇପାରିବ ନାହିଁ।"</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"ଖାରଜ କରନ୍ତୁ"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"ପୁଣି ଆରମ୍ଭ କରନ୍ତୁ"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ସେଟିଂସ୍"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ବିଚ୍ଛିନ୍ନ କରାଯାଇଛି)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"ସଂଯୋଗ କରାଯାଇପାରିଲା ନାହିଁ। ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ନୂଆ ଡିଭାଇସକୁ ପେୟାର୍ କରନ୍ତୁ"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"ବିଲ୍ଡ ନମ୍ୱର"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"କ୍ଲିପବୋର୍ଡକୁ କପି କରାଯାଇଥିବା ବିଲ୍ଡ ନମ୍ୱର।"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 690d6e5b..13f4506 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਦੁਬਾਰਾ ਲੈ ਕੇ ਦੇਖੋ"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"ਸੀਮਿਤ ਸਟੋਰੇਜ ਹੋਣ ਕਾਰਨ ਸਕ੍ਰੀਨਸ਼ਾਟ ਰੱਖਿਅਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ਐਪ ਜਾਂ ਤੁਹਾਡੀ ਸੰਸਥਾ ਵੱਲੋਂ ਸਕ੍ਰੀਨਸ਼ਾਟ ਲੈਣ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਦਿੱਤੀ ਗਈ ਹੈ"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਦਾ ਸੰਪਾਦਨ ਕਰੋ"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਖਾਰਜ ਕਰੋ"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਪੂਰਵ-ਝਲਕ"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡਰ"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"ਸੈਟਿੰਗਾਂ"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"ਵੱਡਦਰਸ਼ੀਕਰਨ Window"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"ਵੱਡਦਰਸ਼ੀਕਰਨ Window ਦੇ ਕੰਟਰੋਲ"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"ਜ਼ੂਮ ਵਧਾਓ"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"ਜ਼ੂਮ ਘਟਾਓ"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"ਉੱਪਰ ਲਿਜਾਓ"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"ਹੇਠਾਂ ਲਿਜਾਓ"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"ਖੱਬੇ ਲਿਜਾਓ"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"ਸੱਜੇ ਲਿਜਾਓ"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"ਵੱਡਦਰਸ਼ੀਕਰਨ ਸਵਿੱਚ"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"ਸਾਰੀ ਸਕ੍ਰੀਨ ਨੂੰ ਵੱਡਾ ਕਰੋ"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ਸਕ੍ਰੀਨ ਦੇ ਹਿੱਸੇ ਨੂੰ ਵੱਡਾ ਕਰੋ"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ਸਵਿੱਚ"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"ਡੀਵਾਈਸ ਕੰਟਰੋਲ"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"ਆਪਣੇ ਕਨੈਕਟ ਕੀਤੇ ਡੀਵਾਈਸਾਂ ਲਈ ਕੰਟਰੋਲ ਸ਼ਾਮਲ ਕਰੋ"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"ਡੀਵਾਈਸ ਕੰਟਰੋਲਾਂ ਦਾ ਸੈੱਟਅੱਪ ਕਰੋ"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"ਸਿਫ਼ਾਰਸ਼ਾਂ ਲੋਡ ਹੋ ਰਹੀਆਂ ਹਨ"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"ਮੀਡੀਆ"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"ਮੌਜੂਦਾ ਸੈਸ਼ਨ ਨੂੰ ਲੁਕਾਓ।"</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"ਮੌਜੂਦਾ ਸੈਸ਼ਨ ਨੂੰ ਲੁਕਾਇਆ ਨਹੀਂ ਜਾ ਸਕਦਾ।"</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"ਖਾਰਜ ਕਰੋ"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"ਮੁੜ-ਚਾਲੂ ਕਰੋ"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ਸੈਟਿੰਗਾਂ"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ਡਿਸਕਨੈਕਟ ਹੋਇਆ)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"ਕਨੈਕਟ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ਨਵਾਂ ਡੀਵਾਈਸ ਜੋੜਾਬੱਧ ਕਰੋ"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"ਬਿਲਡ ਨੰਬਰ"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"ਬਿਲਡ ਨੰਬਰ ਨੂੰ ਕਲਿੱਪਬੋਰਡ \'ਤੇ ਕਾਪੀ ਕੀਤਾ ਗਿਆ।"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 9b31c52..6da167d 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Spróbuj jeszcze raz wykonać zrzut ekranu"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Nie można zapisać zrzutu ekranu, bo brakuje miejsca w pamięci"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Nie możesz wykonać zrzutu ekranu, bo nie zezwala na to aplikacja lub Twoja organizacja."</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Edytuj zrzut ekranu"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Zamknij zrzut ekranu"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Podgląd zrzutu ekranu"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Nagrywanie ekranu"</string>
@@ -1022,18 +1023,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Ustawienia"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Okno powiększenia"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Elementy sterujące okna powiększenia"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Powiększ"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Pomniejsz"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Przesuń w górę"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Przesuń w dół"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Przesuń w lewo"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Przesuń w prawo"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Przełączanie powiększenia"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Powiększ cały ekran"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Powiększ część ekranu"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Przełącz"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Sterowanie urządzeniami"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Dodaj elementy sterujące połączonymi urządzeniami"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Konfigurowanie sterowania urządzeniami"</string>
@@ -1077,6 +1076,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Wczytuję rekomendacje"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Multimedia"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Ukryj bieżącą sesję."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Nie można ukryć bieżącej sesji."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Odrzuć"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Wznów"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Ustawienia"</string>
@@ -1099,8 +1099,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (rozłączono)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Nie udało się połączyć. Spróbuj ponownie."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Sparuj nowe urządzenie"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numer kompilacji"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Numer kompilacji został skopiowany do schowka."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index b76ebda..0db05d8 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Tente fazer a captura de tela novamente"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Não é possível salvar a captura de tela, porque não há espaço suficiente"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"O app ou a organização não permitem capturas de tela"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Editar captura de tela"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Dispensar captura de tela"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Visualização de captura de tela"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Gravador de tela"</string>
@@ -1018,6 +1019,10 @@
     <string name="accessibility_control_move_down" msgid="5390922476900974512">"Mover para baixo"</string>
     <string name="accessibility_control_move_left" msgid="8156206978511401995">"Mover para a esquerda"</string>
     <string name="accessibility_control_move_right" msgid="8926821093629582888">"Mover para a direita"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Chave de ampliação"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Ampliar toda a tela"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte da tela"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Trocar"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Controles do dispositivo"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Adiciona controles aos dispositivos conectados"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Configurar controles do dispositivo"</string>
@@ -1059,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Carregando recomendações"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Mídia"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Ocultar a sessão atual."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Não é possível ocultar a sessão atual."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Dispensar"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Retomar"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Configurações"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 089bbd4..147ed1d 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Experimente voltar a efetuar a captura de ecrã."</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Não é possível guardar a captura de ecrã devido a espaço de armazenamento limitado."</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"A app ou a sua entidade não permitem tirar capturas de ecrã"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Editar captura de ecrã"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Ignorar captura de ecrã"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Pré-visualização da captura de ecrã"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Gravador de ecrã"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Definições"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Janela de ampliação"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Controlos da janela de ampliação"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Aumentar zoom"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Diminuir zoom"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Mover para cima"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Mover para baixo"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Mover para a esquerda"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Mover para a direita"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Interruptor de ampliação"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Ampliar o ecrã inteiro"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte do ecrã"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Mudar"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Controlos de dispositivos"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Adicione controlos para os dispositivos associados."</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Configure os controlos de dispositivos"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"A carregar recomendações…"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Multimédia"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Oculte a sessão atual."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Não é possível ocultar a sessão atual."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ignorar"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Retomar"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Definições"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (desligado)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Não foi possível ligar. Tente novamente."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Sincronize o novo dispositivo"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número da compilação"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Número da compilação copiado para a área de transferência."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index b76ebda..0db05d8 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Tente fazer a captura de tela novamente"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Não é possível salvar a captura de tela, porque não há espaço suficiente"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"O app ou a organização não permitem capturas de tela"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Editar captura de tela"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Dispensar captura de tela"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Visualização de captura de tela"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Gravador de tela"</string>
@@ -1018,6 +1019,10 @@
     <string name="accessibility_control_move_down" msgid="5390922476900974512">"Mover para baixo"</string>
     <string name="accessibility_control_move_left" msgid="8156206978511401995">"Mover para a esquerda"</string>
     <string name="accessibility_control_move_right" msgid="8926821093629582888">"Mover para a direita"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Chave de ampliação"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Ampliar toda a tela"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte da tela"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Trocar"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Controles do dispositivo"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Adiciona controles aos dispositivos conectados"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Configurar controles do dispositivo"</string>
@@ -1059,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Carregando recomendações"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Mídia"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Ocultar a sessão atual."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Não é possível ocultar a sessão atual."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Dispensar"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Retomar"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Configurações"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 3194b9f..24e71fb 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Încercați să faceți din nou o captură de ecran"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Captura de ecran nu poate fi salvată din cauza spațiului de stocare limitat"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Crearea capturilor de ecran nu este permisă de aplicație sau de organizația dvs."</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Editați captura de ecran"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Închideți captura de ecran"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Previzualizare a capturii de ecran"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Recorder pentru ecran"</string>
@@ -1017,18 +1018,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Setări"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Fereastra de mărire"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Comenzi pentru fereastra de mărire"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Măriți"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Micșorați"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Deplasați în sus"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Deplasați în jos"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Deplasați spre stânga"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Deplasați spre dreapta"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Comutator de mărire"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Măriți întregul ecran"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Măriți o parte a ecranului"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Comutator"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Comenzile dispozitivelor"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Adăugați comenzi pentru dispozitivele conectate"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Configurați comenzile dispozitivelor"</string>
@@ -1071,6 +1070,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Se încarcă recomandările"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Ascunde sesiunea actuală."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Sesiunea actuală nu se poate ascunde."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Închideți"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Reia"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Setări"</string>
@@ -1093,8 +1093,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (s-a deconectat)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Nu s-a putut conecta. Reîncercați."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Asociați un nou dispozitiv"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numărul versiunii"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Numărul versiunii s-a copiat în clipboard."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 26e3433..1ca1c80 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Попробуйте сделать скриншот снова."</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Не удалось сохранить скриншот: недостаточно места."</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Не удалось сделать скриншот: нет разрешения от приложения или организации."</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Изменить скриншот"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Закрыть скриншот"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Предварительный просмотр скриншота"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Запись видео с экрана"</string>
@@ -1022,18 +1023,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Настройки"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Окно увеличения"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Настройки окна увеличения"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Увеличить"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Уменьшить"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Переместить вверх"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Переместить вниз"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Переместить влево"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Переместить вправо"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Переключатель режима увеличения"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Увеличить весь экран"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Увеличить часть экрана"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Переключить"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Управление устройствами"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Добавьте виджеты для управления устройствами."</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Настройте виджеты управления устройствами"</string>
@@ -1077,6 +1076,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Загрузка рекомендаций…"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Медиа"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Скрыть текущий сеанс?"</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Скрыть текущий сеанс нельзя."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Скрыть"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Возобновить"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Настройки"</string>
@@ -1099,8 +1099,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (отключено)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Не удалось подключиться. Повторите попытку."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Подключить новое устройство"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Номер сборки"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Номер сборки скопирован в буфер обмена."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 8eea1ed..eb789c1 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"තිර රුව නැවත ගැනීමට උත්සාහ කරන්න"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"සීමිත ගබඩා ඉඩ නිසා තිර රුව සුරැකිය නොහැකිය"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"තිර රූ ගැනීමට යෙදුම හෝ ඔබගේ සංවිධානය ඉඩ නොදේ"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"තිර රුව සංස්කරණය කරන්න"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"තිර රුව ඉවත ලන්න"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"තිර රූ පෙර දසුන"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"තිර රෙකෝඩරය"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"සැකසීම්"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"විශාලන කවුළුව"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"විශාලනය කිරීමේ කවුළු පාලන"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"විශාලනය වැඩි කරන්න"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"විශාලනය අඩු කරන්න"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"ඉහළට ගෙන යන්න"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"පහළට ගෙන යන්න"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"වමට ගෙන යන්න"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"දකුණට ගෙන යන්න"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"විශාලන ස්විචය"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"සම්පූර්ණ තිරය විශාලනය කරන්න"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"තිරයේ කොටසක් විශාලනය කරන්න"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ස්විචය"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"උපාංග පාලන"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"ඔබේ සම්බන්ධිත උපාංග සඳහා පාලන එක් කරන්න"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"උපාංග පාලන පිහිටුවන්න"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"නිර්දේශ පූරණය කරමින්"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"මාධ්‍ය"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"වත්මන් සැසිය සඟවන්න."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"වත්මන් සැසිය සැඟවිය නොහැකිය."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"ඉවත ලන්න"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"නැවත පටන් ගන්න"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"සැකසීම්"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (විසන්ධි විය)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"සම්බන්ධ වීමට නොහැකි විය. නැවත උත්සාහ කරන්න."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"නව උපාංගය යුගල කරන්න"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"නිමැවුම් අංකය"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"නිමැවුම් අංකය පසුරු පුවරුවට පිටපත් කරන ලදි."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 47045d8..9ec0771 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Skúste snímku urobiť znova"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Snímka obrazovky sa nedá uložiť z dôvodu nedostatku miesta v úložisku"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Vytváranie snímok obrazovky je zakázané aplikáciou alebo vašou organizáciou"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Upraviť snímku obrazovky"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Zavrieť snímku obrazovky"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Ukážka snímky obrazovky"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Rekordér obrazovky"</string>
@@ -1022,18 +1023,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Nastavenia"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Okno priblíženia"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Ovládacie prvky okna priblíženia"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Priblížiť"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Oddialiť"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Posunúť nahor"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Posunúť nadol"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Posunúť doľava"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Posunúť doprava"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Prepínač zväčenia"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Zväčšiť celú obrazovku"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Zväčšiť časť obrazovky"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Prepnúť"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Ovládanie zariadení"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Pridajte si ovládače pripojených zariadení"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Nastavenie ovládania zariadení"</string>
@@ -1077,6 +1076,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Načítavajú sa odporúčania"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Médiá"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Skryť aktuálnu reláciu."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Aktuálnu reláciu nie je možné skryť."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Zavrieť"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Pokračovať"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Nastavenia"</string>
@@ -1099,8 +1099,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (odpojené)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Nepodarilo sa pripojiť. Skúste to znova."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Spárovať nové zariadenie"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Číslo zostavy"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Číslo zostavy bolo skopírované do schránky."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 7b68a3a..14f6553 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Poskusite znova ustvariti posnetek zaslona"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Shranjevanje posnetka zaslona ni mogoče zaradi omejenega prostora za shranjevanje"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Aplikacija ali vaša organizacija ne dovoljuje posnetkov zaslona"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Urejanje posnetka zaslona"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Opusti posnetek zaslona"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Predogled posnetka zaslona"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Snemalnik zaslona"</string>
@@ -1022,18 +1023,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Nastavitve"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Povečevalno okno"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Kontrolniki povečevalnega okna"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Povečaj"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Pomanjšaj"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Premakni navzgor"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Premakni navzdol"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Premakni levo"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Premakni desno"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Stikalo za povečavo"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Povečava celotnega zaslona"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Povečava dela zaslona"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Stikalo"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Kontrolniki naprave"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Dodajte kontrolnike za povezane naprave"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Nastavitev kontrolnikov naprave"</string>
@@ -1077,6 +1076,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Nalaganje priporočil"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Predstavnost"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Skrije trenutno sejo."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Trenutne seje ni mogoče skriti."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Opusti"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Nadaljuj"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Nastavitve"</string>
@@ -1099,8 +1099,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (povezava prekinjena)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Povezave ni bilo mogoče vzpostaviti. Poskusite znova."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Seznanitev nove naprave"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Delovna različica"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Delovna različica je bila kopirana v odložišče."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index b7ebb2a..87ca508 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Provo ta nxjerrësh përsëri pamjen e ekranit"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Pamja e ekranit nuk mund të ruhet për shkak të hapësirës ruajtëse të kufizuar"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Nxjerrja e pamjeve të ekranit nuk lejohet nga aplikacioni ose organizata jote."</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Modifiko pamjen e ekranit"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Hiq pamjen e ekranit"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Pamja paraprake e imazhit"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Regjistruesi i ekranit"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Cilësimet"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Dritarja e zmadhimit"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Kontrollet e dritares së zmadhimit"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Zmadho"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Zvogëlo"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Lëvize lart"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Lëvize poshtë"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Lëvize majtas"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Lëvize djathtas"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Ndërrimi i zmadhimit"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Zmadho të gjithë ekranin"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Zmadho një pjesë të ekranit"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Ndërro"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Kontrollet e pajisjes"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Shto kontrolle për pajisjet e tua të lidhura"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Konfiguro kontrollet e pajisjes"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Po ngarkon rekomandimet"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Fshih sesionin aktual."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Sesioni aktual nuk mund të fshihet."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Hiq"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Vazhdo"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Cilësimet"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (e shkëputur)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Nuk mund të lidhej. Provo sërish."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Çifto pajisjen e re"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numri i ndërtimit"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Numri i ndërtimit u kopjua te kujtesa e fragmenteve"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index b6a90cd..cd95885 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Пробајте да поново направите снимак екрана"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Чување снимка екрана није успело због ограниченог меморијског простора"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Апликација или организација не дозвољавају прављење снимака екрана"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Измените снимак екрана"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Одбаците снимак екрана"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Преглед снимка екрана"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Снимач екрана"</string>
@@ -1017,18 +1018,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Подешавања"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Прозор за увећање"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Контроле прозора за увећање"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Увећајте"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Умањите"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Померите нагоре"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Померите надоле"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Померите налево"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Померите надесно"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Прелазак на други режим увећања"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Увећајте цео екран"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Увећајте део екрана"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Пређи"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Контроле уређаја"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Додајте контроле за повезане уређаје"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Подесите контроле уређаја"</string>
@@ -1071,6 +1070,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Учитавају се препоруке"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Медији"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Сакријте актуелну сесију."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Актуелна сесија не може да се сакрије."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Одбаци"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Настави"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Подешавања"</string>
@@ -1093,8 +1093,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (веза је прекинута)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Повезивање није успело. Пробајте поново."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Упари нови уређај"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Број верзије"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Број верзије је копиран у привремену меморију."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 890f504..d34c814 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Testa att ta en skärmdump igen"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Det går inte att spara skärmdumpen eftersom lagringsutrymmet inte räcker"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Appen eller organisationen tillåter inte att du tar skärmdumpar"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Redigera skärmdump"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Stäng skärmdump"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Förhandsgranskning av skärmdump"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Skärminspelare"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Inställningar"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Förstoringsfönster"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Inställningar för förstoringsfönster"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Zooma in"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Zooma ut"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Flytta uppåt"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Flytta nedåt"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Flytta åt vänster"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Flytta åt höger"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Förstoringsreglage"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Förstora hela skärmen"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Förstora en del av skärmen"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Reglage"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Enhetsstyrning"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Lägg till snabbkontroller för anslutna enheter"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Konfigurera enhetsstyrning"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Rekommendationer läses in"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Dölj den aktuella sessionen."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Det går inte att dölja den aktuella sessionen."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Stäng"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Återuppta"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Inställningar"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (frånkopplad)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Det gick inte att ansluta. Försök igen."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Parkoppla en ny enhet"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Versionsnummer"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Versionsnumret har kopierats till urklipp."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 34042ad..eab7f69 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Jaribu kupiga picha ya skrini tena"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Imeshindwa kuhifadhi picha ya skrini kwa sababu nafasi haitoshi"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Programu au shirika lako halikuruhusu kupiga picha za skrini"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Badilisha picha ya skrini"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Ondoa picha ya skrini"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Onyesho la kukagua picha ya skrini"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Kinasa Skrini"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Mipangilio"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Dirisha la Ukuzaji"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Vidhibiti vya Dirisha la Ukuzaji"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Vuta karibu"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Sogeza mbali"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Sogeza juu"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Sogeza chini"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Sogeza kushoto"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Sogeza kulia"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Swichi ya ukuzaji"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Kuza skrini yote"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Kuza sehemu ya skrini"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Swichi"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Vidhibiti vya vifaa"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Weka vidhibiti vya vifaa ulivyounganisha"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Weka mipangilio ya vidhibiti vya vifaa"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Inapakia mapendekezo"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Maudhui"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Ficha kipindi cha sasa."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Huwezi kuficha kipindi cha sasa."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ondoa"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Endelea"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Mipangilio"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (hakijaunganishwa)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Imeshindwa kuunganisha. Jaribu tena."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Oanisha kifaa kipya"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Nambari ya muundo"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Nambari ya muundo imewekwa kwenye ubao wa kunakili."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index b30859f..a72ebef 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ஸ்கிரீன் ஷாட்டை மீண்டும் எடுக்க முயலவும்"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"போதுமான சேமிப்பிடம் இல்லாததால் ஸ்கிரீன்ஷாட்டைச் சேமிக்க முடியவில்லை"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ஸ்கிரீன் ஷாட்டுகளை எடுப்பதை, ஆப்ஸ் அல்லது உங்கள் நிறுவனம் அனுமதிக்கவில்லை"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"ஸ்கிரீன்ஷாட்டைத் திருத்தும்"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ஸ்கிரீன்ஷாட்டை நிராகரி"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"ஸ்கிரீன்ஷாட்டின் மாதிரிக்காட்சி"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"ஸ்கிரீன் ரெக்கார்டர்"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"அமைப்புகள்"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"பெரிதாக்கல் சாளரம்"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"பெரிதாக்கல் சாளரக் கட்டுப்பாடுகள்"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"பெரிதாக்கு"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"சிறிதாக்கு"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"மேலே நகர்த்து"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"கீழே நகர்த்து"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"இடப்புறம் நகர்த்து"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"வலப்புறம் நகர்த்து"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"பெரிதாக்கல் ஸ்விட்ச்"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"முழுத்திரையைப் பெரிதாக்கும்"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"திரையின் ஒரு பகுதியைப் பெரிதாக்கும்"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ஸ்விட்ச்"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"சாதனக் கட்டுப்பாடுகள்"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"இணைக்கப்பட்ட சாதனங்களில் கட்டுப்பாடுகளைச் சேர்க்கலாம்"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"சாதனக் கட்டுப்பாடுகளை அமைத்தல்"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"பரிந்துரைகளை ஏற்றுகிறது"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"மீடியா"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"இந்த அமர்வை மறையுங்கள்."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"தற்போதைய அமர்வை மறைக்க முடியாது."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"மூடுக"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"தொடர்க"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"அமைப்புகள்"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (இணைப்பு துண்டிக்கப்பட்டது)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"இணைக்க முடியவில்லை. மீண்டும் முயலவும்."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"புதிய சாதனத்தை இணைத்தல்"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"பதிப்பு எண்"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"பதிப்பு எண் கிளிப்போர்டுக்கு நகலெடுக்கப்பட்டது."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index cb134a1..0d23f25 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"స్క్రీన్‌షాట్ తీయడానికి మళ్లీ ప్రయత్నించండి"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"నిల్వ స్థలం పరిమితంగా ఉన్న కారణంగా స్క్రీన్‌షాట్‌ను సేవ్ చేయడం సాధ్యపడదు"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"స్క్రీన్‌షాట్‌లు తీయడానికి యాప్ లేదా మీ సంస్థ అనుమతించలేదు"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"స్క్రీన్‌షాట్‌ను ఎడిట్ చేస్తుంది"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"స్క్రీన్‌షాట్‌ను మూసివేస్తుంది"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"స్క్రీన్‌షాట్ ప్రివ్యూ"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"స్క్రీన్ రికార్డర్"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"సెట్టింగ్‌లు"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"మాగ్నిఫికేషన్ విండో"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"మాగ్నిఫికేషన్ నియంత్రణల విండో"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"దగ్గరగా జూమ్ చేయండి"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"దూరంగా జూమ్ చేయండి"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"పైకి పంపండి"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"కిందకి పంపండి"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"ఎడమవైపుగా జరపండి"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"కుడివైపుగా జరపండి"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"మాగ్నిఫికేషన్ స్విచ్"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"స్క్రీన్ మొత్తాన్ని మాగ్నిఫై చేయండి"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"స్క్రీన్‌లో భాగాన్ని మాగ్నిఫై చేయండి"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"స్విచ్ చేయి"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"పరికరం నియంత్రణలు"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"మీ కనెక్ట్ అయిన పరికరాలకు నియంత్రణలను జోడించండి"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"పరికరం నియంత్రణలను సెటప్ చేయడం"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"సిఫార్సులు లోడ్ అవుతున్నాయి"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"మీడియా"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"ప్రస్తుత సెషన్‌ను దాచు."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"ప్రస్తుత సెషన్‌ను దాచడం సాధ్యం కాదు."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"విస్మరించు"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"కొనసాగించండి"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"సెట్టింగ్‌లు"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (డిస్‌కనెక్ట్ అయ్యింది)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"కనెక్ట్ చేయడం సాధ్యపడలేదు. మళ్లీ ట్రై చేయండి."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"కొత్త పరికరాన్ని పెయిర్ చేయండి"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"బిల్డ్ నంబర్"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"బిల్డ్ నంబర్, క్లిప్‌బోర్డ్‌కు కాపీ చేయబడింది."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index d678353..094cb76 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ลองบันทึกภาพหน้าจออีกครั้ง"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"บันทึกภาพหน้าจอไม่ได้เนื่องจากพื้นที่เก็บข้อมูลมีจำกัด"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"แอปหรือองค์กรของคุณไม่อนุญาตให้จับภาพหน้าจอ"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"แก้ไขภาพหน้าจอ"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ปิดภาพหน้าจอ"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"ตัวอย่างภาพหน้าจอ"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"โปรแกรมอัดหน้าจอ"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"การตั้งค่า"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"หน้าต่างการขยาย"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"การควบคุมหน้าต่างการขยาย"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"ซูมเข้า"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"ซูมออก"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"ย้ายขึ้น"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"ย้ายลง"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"ย้ายไปทางซ้าย"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"ย้ายไปทางขวา"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"เปลี่ยนโหมดการขยาย"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"ขยายทั้งหน้าจอ"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ขยายบางส่วนของหน้าจอ"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"เปลี่ยน"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"ระบบควบคุมอุปกรณ์"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"เพิ่มตัวควบคุมของอุปกรณ์ที่เชื่อมต่อ"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"ตั้งค่าระบบควบคุมอุปกรณ์"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"กำลังโหลดคำแนะนำ"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"สื่อ"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"ซ่อนเซสชันปัจจุบัน"</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"ซ่อนเซสชันปัจจุบันไม่ได้"</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"ปิด"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"เล่นต่อ"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"การตั้งค่า"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ยกเลิกการเชื่อมต่อแล้ว)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"เชื่อมต่อไม่ได้ ลองใหม่"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"จับคู่อุปกรณ์ใหม่"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"หมายเลขบิวด์"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"คัดลอกหมายเลขบิวด์ไปยังคลิปบอร์ดแล้ว"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 21fdd31..8ef1869 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Subukang kumuhang muli ng screenshot"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Hindi ma-save ang screenshot dahil sa limitadong espasyo ng storage"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Hindi pinahihintulutan ng app o ng iyong organisasyon ang pagkuha ng mga screenshot"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"I-edit ang screenshot"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"I-dismiss ang screenshot"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Preview ng screenshot"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Recorder ng Screen"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Mga Setting"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Window ng Pag-magnify"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Mga Kontrol sa Pag-magnify ng Window"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Mag-zoom in"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Mag-zoom out"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Itaas"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Ibaba"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Ilipat pakaliwa"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Ilipat pakanan"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Switch ng pag-magnify"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"I-magnify ang buong screen"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"I-magnify ang isang bahagi ng screen"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Switch"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Mga kontrol ng device"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Magdagdag ng kontrol para sa mga nakakonektang device"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"I-set up ang mga kontrol ng device"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Nilo-load ang rekomendasyon"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Itago ang kasalukuyang session."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Hindi maitatago ang kasalukuyang session."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"I-dismiss"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Ituloy"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Mga Setting"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (nakadiskonekta)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Hindi makakonekta. Subukan ulit."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Magpares ng bagong device"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numero ng build"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Nakopya sa clipboard ang numero ng build."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 3e04d19..c17c8f7 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Tekrar ekran görüntüsü almayı deneyin"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Depolama alanı sınırlı olduğundan ekran görüntüsü kaydedilemiyor"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Uygulama veya kuruluşunuz, ekran görüntüsü alınmasına izin vermiyor."</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Ekran görüntüsünü düzenleyin"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Ekran görüntüsünü kapat"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Ekran görüntüsü önizlemesi"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Ekran Kaydedicisi"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Ayarlar"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Büyütme Penceresi"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Büyütme Penceresi Kontrolleri"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Yakınlaştır"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Uzaklaştır"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Yukarı taşı"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Aşağı taşı"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Sola taşı"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Sağa taşı"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Büyütme moduna geçin"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Ekranın tamamını büyütün"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekranın bir parçasını büyütün"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Geç"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Cihaz denetimleri"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Bağlı cihazlarınız için denetimler ekleyin"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Cihaz denetimlerini kur"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Öneriler yükleniyor"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Medya"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Mevcut oturumu gizle."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Mevcut oturum gizlenemez."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Kapat"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Devam ettir"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Ayarlar"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (bağlı değil)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Bağlanılamadı. Tekrar deneyin."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Yeni cihaz eşle"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Derleme numarası"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Derleme numarası panoya kopyalandı."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index a7c6bef..7dcfc3b 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Спробуйте зробити знімок екрана ще раз"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Не вдалося зберегти знімок екрана через обмежений обсяг пам’яті"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Додаток або адміністратор вашої організації не дозволяють робити знімки екрана"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Редагувати знімок екрана"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Закрити знімок екрана"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Перегляд знімка екрана"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Відеозапис екрана"</string>
@@ -1022,18 +1023,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Налаштування"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Вікно збільшення"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Елементи керування вікна збільшення"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Наблизити"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Віддалити"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Перемістити вгору"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Перемістити вниз"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Перемістити ліворуч"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Перемістити праворуч"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Перемикач режиму збільшення"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Збільшити весь екран"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Збільшити частину екрана"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Перемкнути"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Керування пристроями"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Додайте елементи керування для підключених пристроїв"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Налаштувати елементи керування пристроями"</string>
@@ -1077,6 +1076,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Завантаження рекомендацій"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Медіа"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Приховати поточний сеанс."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Поточний сеанс не можна приховати."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Закрити"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Відновити"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Налаштування"</string>
@@ -1099,8 +1099,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (відключено)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Не вдалося підключитися. Повторіть спробу."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Підключити новий пристрій"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Номер складання"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Номер складання скопійовано в буфер обміну."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index f24492c..4c0ebaa 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"دوبارہ اسکرین شاٹ لینے کی کوشش کریں"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"اسٹوریج کی محدود جگہ کی وجہ سے اسکرین شاٹ کو محفوظ نہیں کیا جا سکتا"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ایپ یا آپ کی تنظیم کی جانب سے اسکرین شاٹس لینے کی اجازت نہیں ہے"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"اسکرین شاٹ میں ترمیم کریں"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"اسکرین شاٹ برخاست کریں"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"اسکرین شاٹ کا پیش منظر"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"سکرین ریکارڈر"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"ترتیبات"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"میگنیفکیشن ونڈو"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"میگنیفکیشن ونڈو کنٹرولز"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"زوم ان کریں"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"زوم آؤٹ کریں"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"اوپر منتقل کریں"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"نیچے منتقل کریں"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"بائیں منتقل کریں"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"دائیں منتقل کریں"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"میگنیفکیشن پر سوئچ کریں"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"پوری اسکرین کو بڑا کریں"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"اسکرین کا حصہ بڑا کریں"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"سوئچ کریں"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"آلہ کے کنٹرولز"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"اپنے منسلک آلات کے لیے کنٹرولز شامل کریں"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"آلہ کے کنٹرولز سیٹ اپ کریں"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"تجاویز لوڈ ہو رہی ہیں"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"میڈیا"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"موجودہ سیشن چھپائیں۔"</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"موجودہ سیشن کو چھپایا نہیں جا سکتا۔"</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"برخاست کریں"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"دوبارہ شروع کریں"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ترتیبات"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (غیر منسلک ہو گیا)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"منسلک نہیں ہو سکا۔ پھر کوشش کریں۔"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"نئے آلہ کا جوڑا بنائیں"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"بلڈ نمبر"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"بلڈ نمبر کلپ بورڈ میں کاپی ہو گیا۔"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 32c2406..33d4f4c 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Qayta skrinshot olib ko‘ring"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Xotirada joy kamligi uchun skrinshot saqlanmadi"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Ilova yoki tashkilotingiz skrinshot olishni taqiqlagan"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Skrinshotni tahrirlash"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Skrinshotni yopish"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Skrinshotga razm solish"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Ekrandan yozib olish"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Sozlamalar"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Kattalashtirish oynasi"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Kattalashtirish oynasi sozlamalari"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Yaqinlashtirish"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Uzoqlashtirish"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Tepaga siljitish"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Pastga siljitish"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Chapga siljitish"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Oʻngga siljitish"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Kattalashtirish rejimini almashtirish"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Butun ekranni kattalashtirish"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekran qismini kattalashtirish"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Almashtirish"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Qurilmalarni boshqarish"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Ulangan qurilmalar uchun boshqaruv elementlari"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Qurilma boshqaruv elementlarini sozlash"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Tavsiyalar yuklanmoqda"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Joriy seans berkitilsin."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Joriy seansni berkitish imkonsiz."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Yopish"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Davom etish"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Sozlamalar"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (uzilgan)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Ulanmadi. Qayta urining."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Yangi qurilmani ulash"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Nashr raqami"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Nashr raqami vaqtinchalik xotiraga nusxalandi."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index d9c80df..179f7c2 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Hãy thử chụp lại màn hình"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Không thể lưu ảnh chụp màn hình do giới hạn dung lượng bộ nhớ"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Ứng dụng hoặc tổ chức của bạn không cho phép chụp ảnh màn hình"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Chỉnh sửa ảnh chụp màn hình"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Đóng ảnh chụp màn hình"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Xem trước ảnh chụp màn hình"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Trình ghi màn hình"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Cài đặt"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Cửa sổ phóng to"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Các tùy chọn điều khiển cửa sổ phóng to"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Phóng to"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Thu nhỏ"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Di chuyển lên"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Di chuyển xuống"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Di chuyển sang trái"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Di chuyển sang phải"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Nút chuyển phóng to"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Phóng to toàn bộ màn hình"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Phóng to một phần màn hình"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Chuyển"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Điều khiển thiết bị"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Thêm các tùy chọn điều khiển cho các thiết bị đã kết nối của bạn"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Thiết lập các tùy chọn điều khiển thiết bị"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Đang tải các đề xuất"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Nội dung nghe nhìn"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Ẩn phiên hiện tại."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Bạn không thể ẩn phiên hiện tại."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Đóng"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Tiếp tục"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Cài đặt"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (đã ngắt kết nối)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Không thể kết nối. Hãy thử lại."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Ghép nối thiết bị mới"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Số bản dựng"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Đã sao chép số bản dựng vào khay nhớ tạm."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 9f9424d..e1f483c 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"请再次尝试截屏"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"由于存储空间有限,无法保存屏幕截图"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"此应用或您所在的单位不允许进行屏幕截图"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"编辑屏幕截图"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"关闭屏幕截图"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"屏幕截图预览"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"屏幕录制器"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"设置"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"放大窗口"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"放大窗口控件"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"放大"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"缩小"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"上移"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"下移"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"左移"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"右移"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"切换放大模式"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"放大整个屏幕"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"放大部分屏幕"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"切换"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"设备控制器"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"为您所连接的设备添加控件"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"设置设备控件"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"正在加载推荐内容"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"媒体"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"隐藏当前会话。"</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"无法隐藏当前会话。"</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"关闭"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"继续播放"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"设置"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>(已断开连接)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"无法连接。请重试。"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"与新设备配对"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"版本号"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"已将版本号复制到剪贴板。"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 2825a9e..f986ef33 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"請再嘗試拍攝螢幕擷取畫面"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"由於儲存空間有限,因此無法儲存螢幕擷取畫面"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"應用程式或您的機構不允許擷取螢幕畫面"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"編輯螢幕截圖"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"關閉螢幕截圖"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"螢幕截圖預覽"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"螢幕畫面錄影工具"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"設定"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"放大視窗"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"放大視窗控制項"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"放大"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"縮細"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"向上移"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"向下移"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"向左移"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"向右移"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"放大開關"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"放大整個螢幕畫面"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"放大部分螢幕畫面"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"切換"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"裝置控制"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"為連接的裝置新增控制選項"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"設定裝置控制"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"正在載入建議"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"媒體"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"隱藏目前的工作階段。"</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"無法隱藏目前的工作階段。"</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"關閉"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"繼續播放"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"設定"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (已中斷連線)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"無法連線,請再試一次。"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"配對新裝置"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"版本號碼"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"版本號碼已複製到剪貼簿。"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 8acf2c6..b442b79 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"請再次嘗試拍攝螢幕截圖"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"由於儲存空間有限,因此無法儲存螢幕截圖"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"這個應用程式或貴機構不允許擷取螢幕畫面"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"編輯螢幕截圖"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"關閉螢幕截圖"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"螢幕截圖預覽"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"螢幕錄影器"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"設定"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"放大視窗"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"放大視窗控制項"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"放大"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"縮小"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"向上移"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"向下移"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"向左移"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"向右移"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"切換放大模式"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"放大整個螢幕畫面"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"放大局部螢幕畫面"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"切換"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"裝置控制"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"新增已連結裝置的控制項"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"設定裝置控制"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"正在載入建議控制項"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"媒體"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"隱藏目前的工作階段。"</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"無法隱藏目前的工作階段。"</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"關閉"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"繼續播放"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"設定"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (已中斷連線)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"無法連線,請再試一次。"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"配對新裝置"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"版本號碼"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"已將版本號碼複製到剪貼簿。"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 4935206..764f0b7 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -86,6 +86,7 @@
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Zama ukuthatha isithombe-skrini futhi"</string>
     <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Ayikwazi ukulondoloza isithombe-skrini ngenxa yesikhala sesitoreji esikhawulelwe"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Ukuthatha izithombe-skrini akuvunyelwe uhlelo lokusebenza noma inhlangano yakho"</string>
+    <string name="screenshot_edit" msgid="3510496440489019191">"Hlela isithombe-skrini"</string>
     <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Cashisa isithombe-skrini"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"Ukubuka kuqala isithombe-skrini"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Irekhoda yesikrini"</string>
@@ -1012,18 +1013,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Amasethingi"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Iwindi Lesikhulisi"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Izilawuli Zewindi Lesikhulisi"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Sondeza"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Hlehlisa"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Khuphula"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Yehlisa"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Yisa kwesokunxele"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Yisa kwesokudla"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Iswishi yokukhulisa"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Khulisa sonke isikrini"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Khulisa ingxenye eyesikrini"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Iswishi"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Izilawuli zezinsiza"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Engeza izilawuli zedivayisi yakho exhunyiwe"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Setha izilawuli zezinsiza"</string>
@@ -1065,6 +1064,7 @@
     <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Ilayisha izincomo"</string>
     <string name="controls_media_title" msgid="1746947284862928133">"Imidiya"</string>
     <string name="controls_media_close_session" msgid="3957093425905475065">"Fihla iseshini yamanje."</string>
+    <string name="controls_media_active_session" msgid="1984383994625845642">"Iseshini yamanje ayikwazi ukufihlwa."</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Cashisa"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Qalisa kabusha"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Izilungiselelo"</string>
@@ -1087,8 +1087,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (inqamukile)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Ayikwazanga ukuxhumeka. Zama futhi."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Bhanqa idivayisi entsha"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Yakha inombolo"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Yakha inombolo ekopishelwe kubhodi yokunamathisela."</string>
 </resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 3d7b779..6df8b4e 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -54,6 +54,8 @@
     <color name="global_actions_emergency_background">@color/GM2_red_400</color>
     <color name="global_actions_emergency_text">@color/GM2_grey_100</color>
 
+    <color name="global_actions_shutdown_ui_text">@color/control_primary_text</color>
+
     <!-- Tint color for the content on the notification overflow card. -->
     <color name="keyguard_overflow_content_color">#ff686868</color>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 7cbbaf9..4b1ed0a 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -323,7 +323,7 @@
     <dimen name="screenshot_action_container_padding_right">8dp</dimen>
     <!-- Radius of the chip background on global screenshot actions -->
     <dimen name="screenshot_button_corner_radius">20dp</dimen>
-    <dimen name="screenshot_action_chip_margin_right">8dp</dimen>
+    <dimen name="screenshot_action_chip_margin_start">8dp</dimen>
     <dimen name="screenshot_action_chip_margin_vertical">10dp</dimen>
     <dimen name="screenshot_action_chip_padding_vertical">7dp</dimen>
     <dimen name="screenshot_action_chip_icon_size">18dp</dimen>
@@ -1217,7 +1217,7 @@
     <!-- Interior padding of the message bubble -->
     <dimen name="bubble_message_padding">4dp</dimen>
     <!-- Offset between bubbles in their stacked position. -->
-    <dimen name="bubble_stack_offset">5dp</dimen>
+    <dimen name="bubble_stack_offset">10dp</dimen>
     <!-- How far offscreen the bubble stack rests. Cuts off padding and part of icon bitmap. -->
     <dimen name="bubble_stack_offscreen">9dp</dimen>
     <!-- How far down the screen the stack starts. -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 2427d360..e2ba615 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -218,7 +218,7 @@
 
     <!-- Notification ticker displayed when a screenshot is being saved to the Gallery. [CHAR LIMIT=30] -->
     <string name="screenshot_saving_ticker">Saving screenshot\u2026</string>
-    <!-- Notification title displayed when a screenshot is being saved to the Gallery. [CHAR LIMIT=50] -->
+    <!-- Informs the user that a screenshot is being saved. [CHAR LIMIT=50] -->
     <string name="screenshot_saving_title">Saving screenshot\u2026</string>
     <!-- Notification title displayed when a screenshot is saved to the Gallery. [CHAR LIMIT=50] -->
     <string name="screenshot_saved_title">Screenshot saved</string>
@@ -233,6 +233,8 @@
     <!-- Notification text displayed when we fail to take a screenshot. [CHAR LIMIT=100] -->
     <string name="screenshot_failed_to_capture_text">Taking screenshots isn\'t allowed by the app or
         your organization</string>
+    <!-- Content description indicating that tapping the element will allow editing the screenshot [CHAR LIMIT=NONE] -->
+    <string name="screenshot_edit">Edit screenshot</string>
     <!-- Content description indicating that tapping a button will dismiss the screenshots UI [CHAR LIMIT=NONE] -->
     <string name="screenshot_dismiss_ui_description">Dismiss screenshot</string>
     <!-- Content description indicating that the view is a preview of the screenshot that was just taken [CHAR LIMIT=NONE] -->
@@ -2678,6 +2680,14 @@
     <string name="accessibility_control_move_left">Move left</string>
     <!-- Action in accessibility menu to move the magnification window right. [CHAR LIMIT=30] -->
     <string name="accessibility_control_move_right">Move right</string>
+    <!-- Content description for magnification mode switch. [CHAR LIMIT=NONE] -->
+    <string name="magnification_mode_switch_description">Magnification switch</string>
+    <!-- A11y state description for magnification mode switch that device is in full-screen mode. [CHAR LIMIT=NONE] -->
+    <string name="magnification_mode_switch_state_full_screen">Magnify entire screen</string>
+    <!-- A11y state description for magnification mode switch that device is in window mode. [CHAR LIMIT=NONE] -->
+    <string name="magnification_mode_switch_state_window">Magnify part of screen</string>
+    <!-- Click action label for magnification switch. [CHAR LIMIT=NONE] -->
+    <string name="magnification_mode_switch_click_label">Switch</string>
 
     <!-- Device Controls strings -->
     <!-- Device Controls empty state, title [CHAR LIMIT=30] -->
@@ -2774,6 +2784,8 @@
     <string name="controls_media_title">Media</string>
     <!-- Explanation for closing controls associated with a specific media session [CHAR_LIMIT=NONE] -->
     <string name="controls_media_close_session">Hide the current session.</string>
+    <!-- Explanation that controls associated with a specific media session are active [CHAR_LIMIT=NONE] -->
+    <string name="controls_media_active_session">Current session cannot be hidden.</string>
     <!-- Label for a button that will hide media controls [CHAR_LIMIT=30] -->
     <string name="controls_media_dismiss_button">Dismiss</string>
     <!-- Label for button to resume media playback [CHAR_LIMIT=NONE] -->
@@ -2834,4 +2846,10 @@
     <string name="build_number_clip_data_label">Build number</string>
     <!-- Text to display when copying the build number off QS [CHAR LIMIT=NONE]-->
     <string name="build_number_copy_toast">Build number copied to clipboard.</string>
+
+    <!-- Status for last interaction [CHAR LIMIT=120] -->
+    <string name="last_interaction_status" translatable="false">You last chatted <xliff:g id="duration" example="5 hours">%1$s</xliff:g> ago</string>
+    <!-- Status for conversation without interaction data [CHAR LIMIT=120] -->
+    <string name="basic_status" translatable="false">Open conversation</string>
+
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 2b0a963..2839189 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -637,6 +637,13 @@
         <item name="wallpaperTextColor">@*android:color/primary_text_material_dark</item>
     </style>
 
+    <style name="Theme.CreateUser" parent="@style/Theme.SystemUI">
+        <item name="android:windowIsTranslucent">true</item>
+        <item name="android:windowBackground">#33000000</item>
+        <item name="android:windowActionBar">false</item>
+        <item name="android:windowNoTitle">true</item>
+    </style>
+
     <style name="TextAppearance.Control">
         <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
     </style>
diff --git a/packages/SystemUI/res/xml/fileprovider.xml b/packages/SystemUI/res/xml/fileprovider.xml
index fa6468f..b67378e 100644
--- a/packages/SystemUI/res/xml/fileprovider.xml
+++ b/packages/SystemUI/res/xml/fileprovider.xml
@@ -18,4 +18,5 @@
 <paths xmlns:android="http://schemas.android.com/apk/res/android">
     <cache-path name="leak" path="leak/"/>
     <external-path name="screenrecord" path="."/>
+    <cache-path name="multi_user" path="multi_user/" />
 </paths>
\ No newline at end of file
diff --git a/packages/SystemUI/res/xml/people_space_widget_info.xml b/packages/SystemUI/res/xml/people_space_widget_info.xml
new file mode 100644
index 0000000..f08c8c8
--- /dev/null
+++ b/packages/SystemUI/res/xml/people_space_widget_info.xml
@@ -0,0 +1,24 @@
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
+    android:minWidth="72dp"
+    android:minHeight="150dp"
+    android:updatePeriodMillis="60000"
+    android:previewImage="@drawable/cloud"
+    android:resizeMode="horizontal|vertical"
+    android:initialLayout="@layout/people_space_widget">
+</appwidget-provider>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index c94bcaa..e4427f4 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -16,6 +16,9 @@
 
 package com.android.systemui.shared.recents;
 
+import android.app.PictureInPictureParams;
+import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
 import android.graphics.Bitmap;
 import android.graphics.Insets;
 import android.graphics.Rect;
@@ -166,4 +169,27 @@
      * Notifies to expand notification panel.
      */
     void expandNotificationPanel() = 29;
+
+    /**
+     * Notifies that Activity is about to be swiped to home with entering PiP transition and
+     * queries the destination bounds for PiP depends on Launcher's rotation and shelf height.
+     *
+     * @param componentName ComponentName represents the Activity
+     * @param activityInfo ActivityInfo tied to the Activity
+     * @param pictureInPictureParams PictureInPictureParams tied to the Activity
+     * @param launcherRotation Launcher rotation to calculate the PiP destination bounds
+     * @param shelfHeight Shelf height of launcher to calculate the PiP destination bounds
+     * @return destination bounds the PiP window should land into
+     */
+    Rect startSwipePipToHome(in ComponentName componentName, in ActivityInfo activityInfo,
+                in PictureInPictureParams pictureInPictureParams,
+                int launcherRotation, int shelfHeight) = 30;
+
+    /**
+     * Notifies the swiping Activity to PiP onto home transition is finished
+     *
+     * @param componentName ComponentName represents the Activity
+     * @param destinationBounds the destination bounds the PiP window lands into
+     */
+    void stopSwipePipToHome(in ComponentName componentName, in Rect destinationBounds) = 31;
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
index 9d3620f..3de0b4b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
@@ -61,18 +61,28 @@
         snapshotId = 0;
     }
 
-    public ThumbnailData(TaskSnapshot snapshot) {
+    private static Bitmap makeThumbnail(TaskSnapshot snapshot) {
         final HardwareBuffer buffer = snapshot.getHardwareBuffer();
-        if (buffer == null || (buffer.getUsage() & HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE) == 0) {
+        Bitmap thumbnail = null;
+        try {
+            if (buffer != null) {
+                thumbnail = Bitmap.wrapHardwareBuffer(buffer, snapshot.getColorSpace());
+            }
+        } catch (IllegalArgumentException ex) {
             // TODO(b/157562905): Workaround for a crash when we get a snapshot without this state
             Log.e("ThumbnailData", "Unexpected snapshot without USAGE_GPU_SAMPLED_IMAGE: "
-                    + buffer);
+                    + buffer, ex);
+        }
+        if (thumbnail == null) {
             Point taskSize = snapshot.getTaskSize();
             thumbnail = Bitmap.createBitmap(taskSize.x, taskSize.y, ARGB_8888);
             thumbnail.eraseColor(Color.BLACK);
-        } else {
-            thumbnail = Bitmap.wrapHardwareBuffer(buffer, snapshot.getColorSpace());
         }
+        return thumbnail;
+    }
+
+    public ThumbnailData(TaskSnapshot snapshot) {
+        thumbnail = makeThumbnail(snapshot);
         insets = new Rect(snapshot.getContentInsets());
         orientation = snapshot.getOrientation();
         rotation = snapshot.getRotation();
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index a98f666..4238019 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -20,10 +20,6 @@
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
 import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
 import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 
 import android.annotation.NonNull;
 import android.app.Activity;
@@ -46,7 +42,6 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
@@ -59,7 +54,6 @@
 
 import com.android.internal.app.IVoiceInteractionManagerService;
 import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.Task.TaskKey;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 
 import java.util.ArrayList;
@@ -82,13 +76,11 @@
 
     private final PackageManager mPackageManager;
     private final BackgroundExecutor mBackgroundExecutor;
-    private final TaskStackChangeListeners mTaskStackChangeListeners;
 
     private ActivityManagerWrapper() {
         final Context context = AppGlobals.getInitialApplication();
         mPackageManager = context.getPackageManager();
         mBackgroundExecutor = BackgroundExecutor.get();
-        mTaskStackChangeListeners = new TaskStackChangeListeners(Looper.getMainLooper());
     }
 
     public static ActivityManagerWrapper getInstance() {
@@ -229,6 +221,22 @@
     public void startRecentsActivity(Intent intent, long eventTime,
             final RecentsAnimationListener animationHandler, final Consumer<Boolean> resultCallback,
             Handler resultCallbackHandler) {
+        boolean result = startRecentsActivity(intent, eventTime, animationHandler);
+        if (resultCallback != null) {
+            resultCallbackHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    resultCallback.accept(result);
+                }
+            });
+        }
+    }
+
+    /**
+     * Starts the recents activity. The caller should manage the thread on which this is called.
+     */
+    public boolean startRecentsActivity(
+            Intent intent, long eventTime, RecentsAnimationListener animationHandler) {
         try {
             IRecentsAnimationRunner runner = null;
             if (animationHandler != null) {
@@ -260,32 +268,18 @@
                 };
             }
             ActivityTaskManager.getService().startRecentsActivity(intent, eventTime, runner);
-            if (resultCallback != null) {
-                resultCallbackHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        resultCallback.accept(true);
-                    }
-                });
-            }
+            return true;
         } catch (Exception e) {
-            if (resultCallback != null) {
-                resultCallbackHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        resultCallback.accept(false);
-                    }
-                });
-            }
+            return false;
         }
     }
 
     /**
      * Cancels the remote recents animation started from {@link #startRecentsActivity}.
      */
-    public void cancelRecentsAnimation(boolean restoreHomeStackPosition) {
+    public void cancelRecentsAnimation(boolean restoreHomeRootTaskPosition) {
         try {
-            ActivityTaskManager.getService().cancelRecentsAnimation(restoreHomeStackPosition);
+            ActivityTaskManager.getService().cancelRecentsAnimation(restoreHomeRootTaskPosition);
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to cancel recents animation", e);
         }
@@ -294,53 +288,17 @@
     /**
      * Starts a task from Recents.
      *
-     * @see {@link #startActivityFromRecentsAsync(TaskKey, ActivityOptions, int, int, Consumer, Handler)}
-     */
-    public void startActivityFromRecentsAsync(Task.TaskKey taskKey, ActivityOptions options,
-            Consumer<Boolean> resultCallback, Handler resultCallbackHandler) {
-        startActivityFromRecentsAsync(taskKey, options, WINDOWING_MODE_UNDEFINED,
-                ACTIVITY_TYPE_UNDEFINED, resultCallback, resultCallbackHandler);
-    }
-
-    /**
-     * Starts a task from Recents.
-     *
      * @param resultCallback The result success callback
      * @param resultCallbackHandler The handler to receive the result callback
      */
-    public void startActivityFromRecentsAsync(final Task.TaskKey taskKey, ActivityOptions options,
-            int windowingMode, int activityType, final Consumer<Boolean> resultCallback,
-            final Handler resultCallbackHandler) {
-        if (taskKey.windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-            // We show non-visible docked tasks in Recents, but we always want to launch
-            // them in the fullscreen stack.
-            if (options == null) {
-                options = ActivityOptions.makeBasic();
-            }
-            options.setLaunchWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
-        } else if (windowingMode != WINDOWING_MODE_UNDEFINED
-                || activityType != ACTIVITY_TYPE_UNDEFINED) {
-            if (options == null) {
-                options = ActivityOptions.makeBasic();
-            }
-            options.setLaunchWindowingMode(windowingMode);
-            options.setLaunchActivityType(activityType);
-        }
-        final ActivityOptions finalOptions = options;
-
-
-        boolean result = false;
-        try {
-            result = startActivityFromRecents(taskKey.id, finalOptions);
-        } catch (Exception e) {
-            // Fall through
-        }
-        final boolean finalResult = result;
+    public void startActivityFromRecentsAsync(Task.TaskKey taskKey, ActivityOptions options,
+            Consumer<Boolean> resultCallback, Handler resultCallbackHandler) {
+        final boolean result = startActivityFromRecents(taskKey, options);
         if (resultCallback != null) {
             resultCallbackHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    resultCallback.accept(finalResult);
+                    resultCallback.accept(result);
                 }
             });
         }
@@ -349,6 +307,14 @@
     /**
      * Starts a task from Recents synchronously.
      */
+    public boolean startActivityFromRecents(Task.TaskKey taskKey, ActivityOptions options) {
+        ActivityOptionsCompat.addTaskInfo(options, taskKey);
+        return startActivityFromRecents(taskKey.id, options);
+    }
+
+    /**
+     * Starts a task from Recents synchronously.
+     */
     public boolean startActivityFromRecents(int taskId, ActivityOptions options) {
         try {
             Bundle optsBundle = options == null ? null : options.toBundle();
@@ -360,23 +326,17 @@
     }
 
     /**
-     * Registers a task stack listener with the system.
-     * This should be called on the main thread.
+     * @deprecated use {@link TaskStackChangeListeners#registerTaskStackListener}
      */
     public void registerTaskStackListener(TaskStackChangeListener listener) {
-        synchronized (mTaskStackChangeListeners) {
-            mTaskStackChangeListeners.addListener(ActivityManager.getService(), listener);
-        }
+        TaskStackChangeListeners.getInstance().registerTaskStackListener(listener);
     }
 
     /**
-     * Unregisters a task stack listener with the system.
-     * This should be called on the main thread.
+     * @deprecated use {@link TaskStackChangeListeners#unregisterTaskStackListener}
      */
     public void unregisterTaskStackListener(TaskStackChangeListener listener) {
-        synchronized (mTaskStackChangeListeners) {
-            mTaskStackChangeListeners.removeListener(listener);
-        }
+        TaskStackChangeListeners.getInstance().unregisterTaskStackListener(listener);
     }
 
     /**
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
index 0db4faf..1a71f11 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
@@ -26,6 +26,8 @@
 import android.content.Context;
 import android.os.Handler;
 
+import com.android.systemui.shared.recents.model.Task;
+
 /**
  * Wrapper around internal ActivityOptions creation.
  */
@@ -94,4 +96,15 @@
         opts.setSourceInfo(ActivityOptions.SourceInfo.TYPE_LAUNCHER, uptimeMillis);
         return opts;
     }
+
+    /**
+     * Sets Task specific information to the activity options
+     */
+    public static void addTaskInfo(ActivityOptions opts, Task.TaskKey taskKey) {
+        if (taskKey.windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+            // We show non-visible docked tasks in Recents, but we always want to launch
+            // them in the fullscreen stack.
+            opts.setLaunchWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+        }
+    }
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiver.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiver.java
deleted file mode 100644
index 7cd6c51..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiver.java
+++ /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
- */
-
-package com.android.systemui.shared.system;
-
-import android.graphics.Bitmap;
-import android.os.Bundle;
-
-/**
- * Abstract class for assist data receivers.
- */
-public abstract class AssistDataReceiver {
-    public void onHandleAssistData(Bundle resultData) {}
-    public void onHandleAssistScreenshot(Bitmap screenshot) {}
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ContextCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ContextCompat.java
deleted file mode 100644
index 51fcb0a..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ContextCompat.java
+++ /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.
- */
-
-package com.android.systemui.shared.system;
-
-import android.content.Context;
-
-/**
- * Wraps a context to expose some methods for launcher to call.
- */
-public class ContextCompat {
-    private final Context mWrapped;
-
-    public ContextCompat(Context context) {
-        mWrapped = context;
-    }
-
-    public int getUserId() {
-        return mWrapped.getUserId();
-    }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/LatencyTrackerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/LatencyTrackerCompat.java
index 0d5933e..bf4fb0b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/LatencyTrackerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/LatencyTrackerCompat.java
@@ -28,7 +28,18 @@
         return LatencyTracker.isEnabled(context);
     }
 
+    /**
+     * @see LatencyTracker
+     * @deprecated Please use {@link LatencyTrackerCompat#logToggleRecents(Context, int)} instead.
+     */
+    @Deprecated
     public static void logToggleRecents(int duration) {
-        LatencyTracker.logAction(LatencyTracker.ACTION_TOGGLE_RECENTS, duration);
+        LatencyTracker.logActionDeprecated(LatencyTracker.ACTION_TOGGLE_RECENTS, duration, false);
+    }
+
+    /** @see LatencyTracker */
+    public static void logToggleRecents(Context context, int duration) {
+        LatencyTracker.getInstance(context).logAction(LatencyTracker.ACTION_TOGGLE_RECENTS,
+                duration);
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
index 86129e0..70021b6 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
@@ -65,7 +65,7 @@
     public SyncRtSurfaceTransactionApplierCompat(View targetView) {
         mTargetViewRootImpl = targetView != null ? targetView.getViewRootImpl() : null;
         mBarrierSurfaceControl = mTargetViewRootImpl != null
-            ? mTargetViewRootImpl.getRenderSurfaceControl() : null;
+            ? mTargetViewRootImpl.getSurfaceControl() : null;
 
         mApplyHandler = new Handler(new Callback() {
             @Override
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
index 44372d7..8d010c7 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
@@ -62,21 +62,6 @@
         onActivityLaunchOnSecondaryDisplayRerouted();
     }
 
-    /**
-     * Called when contents are drawn for the first time on a display which can only contain one
-     * task.
-     *
-     * @param displayId the id of the display on which contents are drawn.
-     */
-    public void onSingleTaskDisplayDrawn(int displayId) { }
-
-    /**
-     * Called when the last task is removed from a display which can only contain one task.
-     *
-     * @param displayId the id of the display from which the window is removed.
-     */
-    public void onSingleTaskDisplayEmpty(int displayId) {}
-
     public void onTaskProfileLocked(int taskId, int userId) { }
     public void onTaskCreated(int taskId, ComponentName componentName) { }
     public void onTaskRemoved(int taskId) { }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
index f214648..adaee55 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
@@ -19,14 +19,12 @@
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityManager.TaskSnapshot;
 import android.app.ActivityTaskManager;
-import android.app.IActivityManager;
 import android.app.TaskStackListener;
 import android.content.ComponentName;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
-import android.os.RemoteException;
 import android.os.Trace;
 import android.util.Log;
 
@@ -42,208 +40,40 @@
 public class TaskStackChangeListeners extends TaskStackListener {
 
     private static final String TAG = TaskStackChangeListeners.class.getSimpleName();
+    private static final TaskStackChangeListeners INSTANCE = new TaskStackChangeListeners();
+
+    private final Impl mImpl;
+
+    private TaskStackChangeListeners() {
+        mImpl = new Impl(Looper.getMainLooper());
+    }
+
+    public static TaskStackChangeListeners getInstance() {
+        return INSTANCE;
+    }
 
     /**
-     * List of {@link TaskStackChangeListener} registered from {@link #addListener}.
+     * Registers a task stack listener with the system.
+     * This should be called on the main thread.
      */
-    private final List<TaskStackChangeListener> mTaskStackListeners = new ArrayList<>();
-    private final List<TaskStackChangeListener> mTmpListeners = new ArrayList<>();
-
-    private final Handler mHandler;
-    private boolean mRegistered;
-
-    public TaskStackChangeListeners(Looper looper) {
-        mHandler = new H(looper);
-    }
-
-    public void addListener(IActivityManager am, TaskStackChangeListener listener) {
-        synchronized (mTaskStackListeners) {
-            mTaskStackListeners.add(listener);
-        }
-        if (!mRegistered) {
-            // Register mTaskStackListener to IActivityManager only once if needed.
-            try {
-                ActivityTaskManager.getService().registerTaskStackListener(this);
-                mRegistered = true;
-            } catch (Exception e) {
-                Log.w(TAG, "Failed to call registerTaskStackListener", e);
-            }
+    public void registerTaskStackListener(TaskStackChangeListener listener) {
+        synchronized (mImpl) {
+            mImpl.addListener(listener);
         }
     }
 
-    public void removeListener(TaskStackChangeListener listener) {
-        boolean isEmpty;
-        synchronized (mTaskStackListeners) {
-            mTaskStackListeners.remove(listener);
-            isEmpty = mTaskStackListeners.isEmpty();
-        }
-        if (isEmpty && mRegistered) {
-            // Unregister mTaskStackListener once we have no more listeners
-            try {
-                ActivityTaskManager.getService().unregisterTaskStackListener(this);
-                mRegistered = false;
-            } catch (Exception e) {
-                Log.w(TAG, "Failed to call unregisterTaskStackListener", e);
-            }
+    /**
+     * Unregisters a task stack listener with the system.
+     * This should be called on the main thread.
+     */
+    public void unregisterTaskStackListener(TaskStackChangeListener listener) {
+        synchronized (mImpl) {
+            mImpl.removeListener(listener);
         }
     }
 
-    @Override
-    public void onTaskStackChanged() throws RemoteException {
-        // Call the task changed callback for the non-ui thread listeners first. Copy to a set of
-        // temp listeners so that we don't lock on mTaskStackListeners while calling all the
-        // callbacks. This call is always on the same binder thread, so we can just synchronize
-        // on the copying of the listener list.
-        synchronized (mTaskStackListeners) {
-            mTmpListeners.addAll(mTaskStackListeners);
-        }
-        for (int i = mTmpListeners.size() - 1; i >= 0; i--) {
-            mTmpListeners.get(i).onTaskStackChangedBackground();
-        }
-        mTmpListeners.clear();
+    private static class Impl extends TaskStackListener implements Handler.Callback {
 
-        mHandler.removeMessages(H.ON_TASK_STACK_CHANGED);
-        mHandler.sendEmptyMessage(H.ON_TASK_STACK_CHANGED);
-    }
-
-    @Override
-    public void onActivityPinned(String packageName, int userId, int taskId, int stackId)
-            throws RemoteException {
-        mHandler.removeMessages(H.ON_ACTIVITY_PINNED);
-        mHandler.obtainMessage(H.ON_ACTIVITY_PINNED,
-                new PinnedActivityInfo(packageName, userId, taskId, stackId)).sendToTarget();
-    }
-
-    @Override
-    public void onActivityUnpinned() throws RemoteException {
-        mHandler.removeMessages(H.ON_ACTIVITY_UNPINNED);
-        mHandler.sendEmptyMessage(H.ON_ACTIVITY_UNPINNED);
-    }
-
-    @Override
-    public void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible,
-            boolean clearedTask, boolean wasVisible) throws RemoteException {
-        final SomeArgs args = SomeArgs.obtain();
-        args.arg1 = task;
-        args.argi1 = homeTaskVisible ? 1 : 0;
-        args.argi2 = clearedTask ? 1 : 0;
-        args.argi3 = wasVisible ? 1 : 0;
-        mHandler.removeMessages(H.ON_ACTIVITY_RESTART_ATTEMPT);
-        mHandler.obtainMessage(H.ON_ACTIVITY_RESTART_ATTEMPT, args).sendToTarget();
-    }
-
-    @Override
-    public void onActivityForcedResizable(String packageName, int taskId, int reason)
-            throws RemoteException {
-        mHandler.obtainMessage(H.ON_ACTIVITY_FORCED_RESIZABLE, taskId, reason, packageName)
-                .sendToTarget();
-    }
-
-    @Override
-    public void onActivityDismissingDockedStack() throws RemoteException {
-        mHandler.sendEmptyMessage(H.ON_ACTIVITY_DISMISSING_DOCKED_STACK);
-    }
-
-    @Override
-    public void onActivityLaunchOnSecondaryDisplayFailed(RunningTaskInfo taskInfo,
-            int requestedDisplayId) throws RemoteException {
-        mHandler.obtainMessage(H.ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED, requestedDisplayId,
-                0 /* unused */,
-                taskInfo).sendToTarget();
-    }
-
-    @Override
-    public void onActivityLaunchOnSecondaryDisplayRerouted(RunningTaskInfo taskInfo,
-            int requestedDisplayId) throws RemoteException {
-        mHandler.obtainMessage(H.ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED,
-                requestedDisplayId, 0 /* unused */, taskInfo).sendToTarget();
-    }
-
-    @Override
-    public void onTaskProfileLocked(int taskId, int userId) throws RemoteException {
-        mHandler.obtainMessage(H.ON_TASK_PROFILE_LOCKED, taskId, userId).sendToTarget();
-    }
-
-    @Override
-    public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) throws RemoteException {
-        mHandler.obtainMessage(H.ON_TASK_SNAPSHOT_CHANGED, taskId, 0, snapshot).sendToTarget();
-    }
-
-    @Override
-    public void onTaskCreated(int taskId, ComponentName componentName) throws RemoteException {
-        mHandler.obtainMessage(H.ON_TASK_CREATED, taskId, 0, componentName).sendToTarget();
-    }
-
-    @Override
-    public void onTaskRemoved(int taskId) throws RemoteException {
-        mHandler.obtainMessage(H.ON_TASK_REMOVED, taskId, 0).sendToTarget();
-    }
-
-    @Override
-    public void onTaskMovedToFront(RunningTaskInfo taskInfo)
-            throws RemoteException {
-        mHandler.obtainMessage(H.ON_TASK_MOVED_TO_FRONT, taskInfo).sendToTarget();
-    }
-
-    @Override
-    public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) throws RemoteException {
-        mHandler.obtainMessage(H.ON_BACK_PRESSED_ON_TASK_ROOT, taskInfo).sendToTarget();
-    }
-
-    @Override
-    public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation)
-            throws RemoteException {
-        mHandler.obtainMessage(H.ON_ACTIVITY_REQUESTED_ORIENTATION_CHANGE, taskId,
-                requestedOrientation).sendToTarget();
-    }
-
-    @Override
-    public void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken)
-            throws RemoteException {
-        mHandler.obtainMessage(H.ON_SIZE_COMPAT_MODE_ACTIVITY_CHANGED, displayId, 0 /* unused */,
-                activityToken).sendToTarget();
-    }
-
-    @Override
-    public void onSingleTaskDisplayDrawn(int displayId) throws RemoteException {
-        mHandler.obtainMessage(H.ON_SINGLE_TASK_DISPLAY_DRAWN, displayId,
-                0 /* unused */).sendToTarget();
-    }
-
-    @Override
-    public void onSingleTaskDisplayEmpty(int displayId) throws RemoteException {
-        mHandler.obtainMessage(H.ON_SINGLE_TASK_DISPLAY_EMPTY, displayId,
-                0 /* unused */).sendToTarget();
-    }
-
-    @Override
-    public void onTaskDisplayChanged(int taskId, int newDisplayId) throws RemoteException {
-        mHandler.obtainMessage(H.ON_TASK_DISPLAY_CHANGED, taskId, newDisplayId).sendToTarget();
-    }
-
-    @Override
-    public void onRecentTaskListUpdated() throws RemoteException {
-        mHandler.obtainMessage(H.ON_TASK_LIST_UPDATED).sendToTarget();
-    }
-
-    @Override
-    public void onRecentTaskListFrozenChanged(boolean frozen) {
-        mHandler.obtainMessage(H.ON_TASK_LIST_FROZEN_UNFROZEN, frozen ? 1 : 0, 0 /* unused */)
-                .sendToTarget();
-    }
-
-    @Override
-    public void onTaskDescriptionChanged(RunningTaskInfo taskInfo) {
-        mHandler.obtainMessage(H.ON_TASK_DESCRIPTION_CHANGED, taskInfo).sendToTarget();
-    }
-
-    @Override
-    public void onActivityRotation(int displayId) {
-        mHandler.obtainMessage(H.ON_ACTIVITY_ROTATION, displayId, 0 /* unused */)
-                .sendToTarget();
-    }
-
-    private final class H extends Handler {
         private static final int ON_TASK_STACK_CHANGED = 1;
         private static final int ON_TASK_SNAPSHOT_CHANGED = 2;
         private static final int ON_ACTIVITY_PINNED = 3;
@@ -260,21 +90,199 @@
         private static final int ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED = 16;
         private static final int ON_SIZE_COMPAT_MODE_ACTIVITY_CHANGED = 17;
         private static final int ON_BACK_PRESSED_ON_TASK_ROOT = 18;
-        private static final int ON_SINGLE_TASK_DISPLAY_DRAWN = 19;
-        private static final int ON_TASK_DISPLAY_CHANGED = 20;
-        private static final int ON_TASK_LIST_UPDATED = 21;
-        private static final int ON_SINGLE_TASK_DISPLAY_EMPTY = 22;
-        private static final int ON_TASK_LIST_FROZEN_UNFROZEN = 23;
-        private static final int ON_TASK_DESCRIPTION_CHANGED = 24;
-        private static final int ON_ACTIVITY_ROTATION = 25;
+        private static final int ON_TASK_DISPLAY_CHANGED = 19;
+        private static final int ON_TASK_LIST_UPDATED = 20;
+        private static final int ON_TASK_LIST_FROZEN_UNFROZEN = 21;
+        private static final int ON_TASK_DESCRIPTION_CHANGED = 22;
+        private static final int ON_ACTIVITY_ROTATION = 23;
 
+        /**
+         * List of {@link TaskStackChangeListener} registered from {@link #addListener}.
+         */
+        private final List<TaskStackChangeListener> mTaskStackListeners = new ArrayList<>();
+        private final List<TaskStackChangeListener> mTmpListeners = new ArrayList<>();
 
-        public H(Looper looper) {
-            super(looper);
+        private final Handler mHandler;
+        private boolean mRegistered;
+
+        Impl(Looper looper) {
+            mHandler = new Handler(looper, this);
+        }
+
+        public void addListener(TaskStackChangeListener listener) {
+            synchronized (mTaskStackListeners) {
+                mTaskStackListeners.add(listener);
+            }
+            if (!mRegistered) {
+                // Register mTaskStackListener to IActivityManager only once if needed.
+                try {
+                    ActivityTaskManager.getService().registerTaskStackListener(this);
+                    mRegistered = true;
+                } catch (Exception e) {
+                    Log.w(TAG, "Failed to call registerTaskStackListener", e);
+                }
+            }
+        }
+
+        public void removeListener(TaskStackChangeListener listener) {
+            boolean isEmpty;
+            synchronized (mTaskStackListeners) {
+                mTaskStackListeners.remove(listener);
+                isEmpty = mTaskStackListeners.isEmpty();
+            }
+            if (isEmpty && mRegistered) {
+                // Unregister mTaskStackListener once we have no more listeners
+                try {
+                    ActivityTaskManager.getService().unregisterTaskStackListener(this);
+                    mRegistered = false;
+                } catch (Exception e) {
+                    Log.w(TAG, "Failed to call unregisterTaskStackListener", e);
+                }
+            }
         }
 
         @Override
-        public void handleMessage(Message msg) {
+        public void onTaskStackChanged() {
+            // Call the task changed callback for the non-ui thread listeners first. Copy to a set
+            // of temp listeners so that we don't lock on mTaskStackListeners while calling all the
+            // callbacks. This call is always on the same binder thread, so we can just synchronize
+            // on the copying of the listener list.
+            synchronized (mTaskStackListeners) {
+                mTmpListeners.addAll(mTaskStackListeners);
+            }
+            for (int i = mTmpListeners.size() - 1; i >= 0; i--) {
+                mTmpListeners.get(i).onTaskStackChangedBackground();
+            }
+            mTmpListeners.clear();
+
+            mHandler.removeMessages(ON_TASK_STACK_CHANGED);
+            mHandler.sendEmptyMessage(ON_TASK_STACK_CHANGED);
+        }
+
+        @Override
+        public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
+            mHandler.removeMessages(ON_ACTIVITY_PINNED);
+            mHandler.obtainMessage(ON_ACTIVITY_PINNED,
+                    new PinnedActivityInfo(packageName, userId, taskId, stackId)).sendToTarget();
+        }
+
+        @Override
+        public void onActivityUnpinned() {
+            mHandler.removeMessages(ON_ACTIVITY_UNPINNED);
+            mHandler.sendEmptyMessage(ON_ACTIVITY_UNPINNED);
+        }
+
+        @Override
+        public void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible,
+                boolean clearedTask, boolean wasVisible) {
+            final SomeArgs args = SomeArgs.obtain();
+            args.arg1 = task;
+            args.argi1 = homeTaskVisible ? 1 : 0;
+            args.argi2 = clearedTask ? 1 : 0;
+            args.argi3 = wasVisible ? 1 : 0;
+            mHandler.removeMessages(ON_ACTIVITY_RESTART_ATTEMPT);
+            mHandler.obtainMessage(ON_ACTIVITY_RESTART_ATTEMPT, args).sendToTarget();
+        }
+
+        @Override
+        public void onActivityForcedResizable(String packageName, int taskId, int reason) {
+            mHandler.obtainMessage(ON_ACTIVITY_FORCED_RESIZABLE, taskId, reason, packageName)
+                    .sendToTarget();
+        }
+
+        @Override
+        public void onActivityDismissingDockedStack() {
+            mHandler.sendEmptyMessage(ON_ACTIVITY_DISMISSING_DOCKED_STACK);
+        }
+
+        @Override
+        public void onActivityLaunchOnSecondaryDisplayFailed(RunningTaskInfo taskInfo,
+                int requestedDisplayId) {
+            mHandler.obtainMessage(ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED,
+                    requestedDisplayId,
+                    0 /* unused */,
+                    taskInfo).sendToTarget();
+        }
+
+        @Override
+        public void onActivityLaunchOnSecondaryDisplayRerouted(RunningTaskInfo taskInfo,
+                int requestedDisplayId) {
+            mHandler.obtainMessage(ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED,
+                    requestedDisplayId, 0 /* unused */, taskInfo).sendToTarget();
+        }
+
+        @Override
+        public void onTaskProfileLocked(int taskId, int userId) {
+            mHandler.obtainMessage(ON_TASK_PROFILE_LOCKED, taskId, userId).sendToTarget();
+        }
+
+        @Override
+        public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) {
+            mHandler.obtainMessage(ON_TASK_SNAPSHOT_CHANGED, taskId, 0, snapshot).sendToTarget();
+        }
+
+        @Override
+        public void onTaskCreated(int taskId, ComponentName componentName) {
+            mHandler.obtainMessage(ON_TASK_CREATED, taskId, 0, componentName).sendToTarget();
+        }
+
+        @Override
+        public void onTaskRemoved(int taskId) {
+            mHandler.obtainMessage(ON_TASK_REMOVED, taskId, 0).sendToTarget();
+        }
+
+        @Override
+        public void onTaskMovedToFront(RunningTaskInfo taskInfo) {
+            mHandler.obtainMessage(ON_TASK_MOVED_TO_FRONT, taskInfo).sendToTarget();
+        }
+
+        @Override
+        public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
+            mHandler.obtainMessage(ON_BACK_PRESSED_ON_TASK_ROOT, taskInfo).sendToTarget();
+        }
+
+        @Override
+        public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) {
+            mHandler.obtainMessage(ON_ACTIVITY_REQUESTED_ORIENTATION_CHANGE, taskId,
+                    requestedOrientation).sendToTarget();
+        }
+
+        @Override
+        public void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken) {
+            mHandler.obtainMessage(ON_SIZE_COMPAT_MODE_ACTIVITY_CHANGED, displayId,
+                    0 /* unused */,
+                    activityToken).sendToTarget();
+        }
+
+        @Override
+        public void onTaskDisplayChanged(int taskId, int newDisplayId) {
+            mHandler.obtainMessage(ON_TASK_DISPLAY_CHANGED, taskId, newDisplayId).sendToTarget();
+        }
+
+        @Override
+        public void onRecentTaskListUpdated() {
+            mHandler.obtainMessage(ON_TASK_LIST_UPDATED).sendToTarget();
+        }
+
+        @Override
+        public void onRecentTaskListFrozenChanged(boolean frozen) {
+            mHandler.obtainMessage(ON_TASK_LIST_FROZEN_UNFROZEN, frozen ? 1 : 0, 0 /* unused */)
+                    .sendToTarget();
+        }
+
+        @Override
+        public void onTaskDescriptionChanged(RunningTaskInfo taskInfo) {
+            mHandler.obtainMessage(ON_TASK_DESCRIPTION_CHANGED, taskInfo).sendToTarget();
+        }
+
+        @Override
+        public void onActivityRotation(int displayId) {
+            mHandler.obtainMessage(ON_ACTIVITY_ROTATION, displayId, 0 /* unused */)
+                    .sendToTarget();
+        }
+
+        @Override
+        public boolean handleMessage(Message msg) {
             synchronized (mTaskStackListeners) {
                 switch (msg.what) {
                     case ON_TASK_STACK_CHANGED: {
@@ -298,7 +306,8 @@
                         final PinnedActivityInfo info = (PinnedActivityInfo) msg.obj;
                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
                             mTaskStackListeners.get(i).onActivityPinned(
-                                    info.mPackageName, info.mUserId, info.mTaskId, info.mStackId);
+                                    info.mPackageName, info.mUserId, info.mTaskId,
+                                    info.mStackId);
                         }
                         break;
                     }
@@ -396,19 +405,6 @@
                         }
                         break;
                     }
-                    case ON_SINGLE_TASK_DISPLAY_DRAWN: {
-                        for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
-                            mTaskStackListeners.get(i).onSingleTaskDisplayDrawn(msg.arg1);
-                        }
-                        break;
-                    }
-                    case ON_SINGLE_TASK_DISPLAY_EMPTY: {
-                        for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
-                            mTaskStackListeners.get(i).onSingleTaskDisplayEmpty(
-                                    msg.arg1);
-                        }
-                        break;
-                    }
                     case ON_TASK_DISPLAY_CHANGED: {
                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
                             mTaskStackListeners.get(i).onTaskDisplayChanged(msg.arg1, msg.arg2);
@@ -423,7 +419,8 @@
                     }
                     case ON_TASK_LIST_FROZEN_UNFROZEN: {
                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
-                            mTaskStackListeners.get(i).onRecentTaskListFrozenChanged(msg.arg1 != 0);
+                            mTaskStackListeners.get(i).onRecentTaskListFrozenChanged(
+                                    msg.arg1 != 0);
                         }
                         break;
                     }
@@ -445,6 +442,7 @@
             if (msg.obj instanceof SomeArgs) {
                 ((SomeArgs) msg.obj).recycle();
             }
+            return true;
         }
     }
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java
index 73783ae..4a28d56 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java
@@ -34,10 +34,6 @@
     }
 
     public SurfaceControl getRenderSurfaceControl() {
-        return mViewRoot == null ? null : mViewRoot.getRenderSurfaceControl();
-    }
-
-    public SurfaceControl getSurfaceControl() {
         return mViewRoot == null ? null : mViewRoot.getSurfaceControl();
     }
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
index e5f2ad5..5b41d5f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
@@ -27,6 +27,7 @@
 import android.os.Handler;
 import android.os.RemoteException;
 import android.util.Log;
+import android.view.InsetsState;
 import android.view.SurfaceControl;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
@@ -54,8 +55,6 @@
             WindowManager.TRANSIT_WALLPAPER_INTRA_CLOSE;
     public static final int TRANSIT_TASK_OPEN_BEHIND = WindowManager.TRANSIT_TASK_OPEN_BEHIND;
     public static final int TRANSIT_ACTIVITY_RELAUNCH = WindowManager.TRANSIT_ACTIVITY_RELAUNCH;
-    public static final int TRANSIT_DOCK_TASK_FROM_RECENTS =
-            WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
     public static final int TRANSIT_KEYGUARD_GOING_AWAY = WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
     public static final int TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER =
             WindowManager.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
@@ -81,12 +80,37 @@
             WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
     public static final int WINDOWING_MODE_FREEFORM = WindowConfiguration.WINDOWING_MODE_FREEFORM;
 
+    public static final int ITYPE_EXTRA_NAVIGATION_BAR = InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
+
     private static final WindowManagerWrapper sInstance = new WindowManagerWrapper();
 
     public static WindowManagerWrapper getInstance() {
         return sInstance;
     }
 
+
+    /**
+     * Sets {@param providesInsetsTypes} as the inset types provided by {@param params}.
+     * @param params The window layout params.
+     * @param providesInsetsTypes The inset types we would like this layout params to provide.
+     */
+    public void setProvidesInsetsTypes(WindowManager.LayoutParams params,
+            int[] providesInsetsTypes) {
+        params.providesInsetsTypes = providesInsetsTypes;
+    }
+
+    /**
+     *  Sets if app requested fixed orientation should be ignored for given displayId.
+     */
+    public void setIgnoreOrientationRequest(int displayId, boolean ignoreOrientationRequest) {
+        try {
+            WindowManagerGlobal.getWindowManagerService().setIgnoreOrientationRequest(
+                    displayId, ignoreOrientationRequest);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to setIgnoreOrientationRequest()", e);
+        }
+    }
+
     /**
      * @return the stable insets for the primary display.
      */
diff --git a/packages/SystemUI/src/com/android/keyguard/GradientTextClock.java b/packages/SystemUI/src/com/android/keyguard/GradientTextClock.java
new file mode 100644
index 0000000..7cf1bd0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/GradientTextClock.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
+
+import android.content.Context;
+import android.graphics.LinearGradient;
+import android.graphics.Shader;
+import android.util.AttributeSet;
+import android.widget.TextClock;
+
+/**
+ * Displays the time with the hour positioned above the minutes. (ie: 09 above 30 is 9:30)
+ * The time's text color is a gradient that changes its colors based on its controller.
+ */
+public class GradientTextClock extends TextClock {
+    private int[] mGradientColors;
+    private float[] mPositions;
+
+    public GradientTextClock(Context context) {
+        this(context, null, 0, 0);
+    }
+
+    public GradientTextClock(Context context, AttributeSet attrs) {
+        this(context, attrs, 0, 0);
+    }
+
+    public GradientTextClock(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public GradientTextClock(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    public void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        addOnLayoutChangeListener(mOnLayoutChangeListener);
+    }
+
+    @Override
+    public void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        removeOnLayoutChangeListener(mOnLayoutChangeListener);
+    }
+
+    @Override
+    public void refreshTime() {
+        super.refreshTime();
+    }
+
+    @Override
+    public void setFormat12Hour(CharSequence format) {
+        super.setFormat12Hour(FORMAT_12);
+    }
+
+    @Override
+    public void setFormat24Hour(CharSequence format) {
+        super.setFormat24Hour(FORMAT_24);
+    }
+
+    public void setGradientColors(int[] colors) {
+        mGradientColors = colors;
+        updatePaint();
+    }
+
+    public void setColorPositions(float[] positions) {
+        mPositions = positions;
+    }
+
+    private void updatePaint() {
+        getPaint().setShader(
+                new LinearGradient(
+                        getX(), getY(), getX(), getMeasuredHeight() + getY(),
+                        mGradientColors, mPositions, Shader.TileMode.REPEAT));
+    }
+
+    private final OnLayoutChangeListener mOnLayoutChangeListener =
+            (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
+                if (bottom != oldBottom || top != oldTop) {
+                    updatePaint();
+                }
+            };
+
+    public static final CharSequence FORMAT_12 = "hh\nmm";
+    public static final CharSequence FORMAT_24 = "HH\nmm";
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
index 53f8474..89911e0 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -81,8 +81,7 @@
     abstract void resetState();
 
     @Override
-    public void init() {
-        super.init();
+    public void initInternal() {
         mMessageAreaController.init();
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 5ad8cad..c6ee15f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -15,6 +15,7 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.MathUtils;
+import android.util.TypedValue;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
@@ -72,6 +73,16 @@
     private TextClock mClockViewBold;
 
     /**
+     * Gradient clock for usage when mode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL.
+     */
+    private TimeBasedColorsClockController mNewLockscreenClockViewController;
+
+    /**
+     * Frame for clock when mode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL.
+     */
+    private FrameLayout mNewLockscreenClockFrame;
+
+    /**
      * Frame for default and custom clock.
      */
     private FrameLayout mSmallClockFrame;
@@ -104,6 +115,8 @@
     private boolean mSupportsDarkText;
     private int[] mColorPalette;
 
+    private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
+
     public KeyguardClockSwitch(Context context, AttributeSet attrs) {
         super(context, attrs);
 
@@ -128,11 +141,48 @@
         return mClockPlugin != null;
     }
 
+    /**
+      * Update lock screen mode for testing different layouts
+      */
+    public void updateLockScreenMode(int mode) {
+        mLockScreenMode = mode;
+        RelativeLayout.LayoutParams statusAreaLP = (RelativeLayout.LayoutParams)
+                mKeyguardStatusArea.getLayoutParams();
+
+        if (mode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) {
+            final int startEndPadding = (int) TypedValue.applyDimension(
+                    TypedValue.COMPLEX_UNIT_DIP,
+                    12,
+                    getResources().getDisplayMetrics());
+            setPaddingRelative(startEndPadding, 0, startEndPadding, 0);
+            mSmallClockFrame.setVisibility(GONE);
+            mNewLockscreenClockFrame.setVisibility(VISIBLE);
+            mNewLockscreenClockViewController.init();
+
+            statusAreaLP.removeRule(RelativeLayout.BELOW);
+            statusAreaLP.addRule(RelativeLayout.LEFT_OF, R.id.new_lockscreen_clock_view);
+            statusAreaLP.addRule(RelativeLayout.ALIGN_PARENT_START);
+        } else {
+            setPaddingRelative(0, 0, 0, 0);
+            mSmallClockFrame.setVisibility(VISIBLE);
+            mNewLockscreenClockFrame.setVisibility(GONE);
+
+            statusAreaLP.removeRule(RelativeLayout.LEFT_OF);
+            statusAreaLP.removeRule(RelativeLayout.ALIGN_PARENT_START);
+            statusAreaLP.addRule(RelativeLayout.BELOW, R.id.clock_view);
+        }
+
+        requestLayout();
+    }
+
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
         mClockView = findViewById(R.id.default_clock_view);
         mClockViewBold = findViewById(R.id.default_clock_view_bold);
+        mNewLockscreenClockFrame = findViewById(R.id.new_lockscreen_clock_view);
+        mNewLockscreenClockViewController =
+                new TimeBasedColorsClockController(findViewById(R.id.gradient_clock_view));
         mSmallClockFrame = findViewById(R.id.clock_view);
         mKeyguardStatusArea = findViewById(R.id.keyguard_status_area);
     }
@@ -255,6 +305,7 @@
         if (mClockPlugin != null) {
             mClockPlugin.setDarkAmount(darkAmount);
         }
+        mNewLockscreenClockViewController.setDarkAmount(darkAmount);
         updateBigClockAlpha();
     }
 
@@ -305,6 +356,7 @@
      * Refresh the time of the clock, due to either time tick broadcast or doze time tick alarm.
      */
     public void refresh() {
+        mNewLockscreenClockViewController.refreshTime(System.currentTimeMillis());
         mClockView.refreshTime();
         mClockViewBold.refreshTime();
         if (mClockPlugin != null) {
@@ -363,6 +415,10 @@
      * these cases.
      */
     void setKeyguardShowingHeader(boolean hasHeader) {
+        if (mLockScreenMode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL) {
+            hasHeader = false;
+        }
+
         if (mShowingHeader == hasHeader) {
             return;
         }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index fe5fcc6..ea98830 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -17,24 +17,32 @@
 package com.android.keyguard;
 
 import android.app.WallpaperManager;
-import android.view.View;
+import android.content.res.Resources;
+import android.text.format.DateFormat;
+import android.util.TypedValue;
 import android.view.ViewGroup;
 
 import com.android.internal.colorextraction.ColorExtractor;
 import com.android.keyguard.clock.ClockManager;
+import com.android.systemui.R;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ClockPlugin;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.util.ViewController;
+
+import java.util.Locale;
+import java.util.TimeZone;
 
 import javax.inject.Inject;
 
 /**
  * Injectable controller for {@link KeyguardClockSwitch}.
  */
-public class KeyguardClockSwitchController {
+public class KeyguardClockSwitchController extends ViewController<KeyguardClockSwitch> {
     private static final boolean CUSTOM_CLOCKS_ENABLED = true;
 
-    private final KeyguardClockSwitch mView;
+    private final Resources mResources;
     private final StatusBarStateController mStatusBarStateController;
     private final SysuiColorExtractor mColorExtractor;
     private final ClockManager mClockManager;
@@ -65,35 +73,15 @@
 
     private ClockManager.ClockChangedListener mClockChangedListener = this::setClockPlugin;
 
-    private final View.OnAttachStateChangeListener mOnAttachStateChangeListener =
-            new View.OnAttachStateChangeListener() {
-        @Override
-        public void onViewAttachedToWindow(View v) {
-            if (CUSTOM_CLOCKS_ENABLED) {
-                mClockManager.addOnClockChangedListener(mClockChangedListener);
-            }
-            mStatusBarStateController.addCallback(mStateListener);
-            mColorExtractor.addOnColorsChangedListener(mColorsListener);
-            mView.updateColors(getGradientColors());
-        }
-
-        @Override
-        public void onViewDetachedFromWindow(View v) {
-            if (CUSTOM_CLOCKS_ENABLED) {
-                mClockManager.removeOnClockChangedListener(mClockChangedListener);
-            }
-            mStatusBarStateController.removeCallback(mStateListener);
-            mColorExtractor.removeOnColorsChangedListener(mColorsListener);
-            mView.setClockPlugin(null, mStatusBarStateController.getState());
-        }
-    };
-
     @Inject
-    public KeyguardClockSwitchController(KeyguardClockSwitch keyguardClockSwitch,
+    public KeyguardClockSwitchController(
+            KeyguardClockSwitch keyguardClockSwitch,
+            @Main Resources resources,
             StatusBarStateController statusBarStateController,
             SysuiColorExtractor colorExtractor, ClockManager clockManager,
             KeyguardSliceViewController keyguardSliceViewController) {
-        mView = keyguardClockSwitch;
+        super(keyguardClockSwitch);
+        mResources = resources;
         mStatusBarStateController = statusBarStateController;
         mColorExtractor = colorExtractor;
         mClockManager = clockManager;
@@ -103,15 +91,40 @@
     /**
      * Attach the controller to the view it relates to.
      */
-    public void init() {
-        if (mView.isAttachedToWindow()) {
-            mOnAttachStateChangeListener.onViewAttachedToWindow(mView);
-        }
-        mView.addOnAttachStateChangeListener(mOnAttachStateChangeListener);
-
+    @Override
+    public void initInternal() {
         mKeyguardSliceViewController.init();
     }
 
+    @Override
+    protected void onViewAttached() {
+        if (CUSTOM_CLOCKS_ENABLED) {
+            mClockManager.addOnClockChangedListener(mClockChangedListener);
+        }
+        refreshFormat();
+        mStatusBarStateController.addCallback(mStateListener);
+        mColorExtractor.addOnColorsChangedListener(mColorsListener);
+        mView.updateColors(getGradientColors());
+    }
+
+    @Override
+    protected void onViewDetached() {
+        if (CUSTOM_CLOCKS_ENABLED) {
+            mClockManager.removeOnClockChangedListener(mClockChangedListener);
+        }
+        mStatusBarStateController.removeCallback(mStateListener);
+        mColorExtractor.removeOnColorsChangedListener(mColorsListener);
+        mView.setClockPlugin(null, mStatusBarStateController.getState());
+    }
+
+    /**
+     * Updates clock's text
+     */
+    public void onDensityOrFontScaleChanged() {
+        mView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
+                mResources.getDimensionPixelSize(R.dimen.widget_big_font_size));
+    }
+
     /**
      * Set container for big clock face appearing behind NSSL and KeyguardStatusView.
      */
@@ -119,6 +132,61 @@
         mView.setBigClockContainer(bigClockContainer, mStatusBarStateController.getState());
     }
 
+    /**
+     * Set whether or not the lock screen is showing notifications.
+     */
+    public void setHasVisibleNotifications(boolean hasVisibleNotifications) {
+        mView.setHasVisibleNotifications(hasVisibleNotifications);
+    }
+
+    /**
+     * If we're presenting a custom clock of just the default one.
+     */
+    public boolean hasCustomClock() {
+        return mView.hasCustomClock();
+    }
+
+    /**
+     * Get the clock text size.
+     */
+    public float getClockTextSize() {
+        return mView.getTextSize();
+    }
+
+    /**
+     * Returns the preferred Y position of the clock.
+     *
+     * @param totalHeight The height available to position the clock.
+     * @return Y position of clock.
+     */
+    public int getClockPreferredY(int totalHeight) {
+        return mView.getPreferredY(totalHeight);
+    }
+
+    /**
+     * Refresh clock. Called in response to TIME_TICK broadcasts.
+     */
+    void refresh() {
+        mView.refresh();
+    }
+
+    /**
+     * Update lockscreen mode that may change clock display.
+     */
+    void updateLockScreenMode(int mode) {
+        mView.updateLockScreenMode(mode);
+    }
+
+    void updateTimeZone(TimeZone timeZone) {
+        mView.onTimeZoneChanged(timeZone);
+    }
+
+    void refreshFormat() {
+        Patterns.update(mResources);
+        mView.setFormat12Hour(Patterns.sClockView12);
+        mView.setFormat24Hour(Patterns.sClockView24);
+    }
+
     private void setClockPlugin(ClockPlugin plugin) {
         mView.setClockPlugin(plugin, mStatusBarStateController.getState());
     }
@@ -126,4 +194,35 @@
     private ColorExtractor.GradientColors getGradientColors() {
         return mColorExtractor.getColors(WallpaperManager.FLAG_LOCK);
     }
+
+    // DateFormat.getBestDateTimePattern is extremely expensive, and refresh is called often.
+    // This is an optimization to ensure we only recompute the patterns when the inputs change.
+    private static final class Patterns {
+        static String sClockView12;
+        static String sClockView24;
+        static String sCacheKey;
+
+        static void update(Resources res) {
+            final Locale locale = Locale.getDefault();
+            final String clockView12Skel = res.getString(R.string.clock_12hr_format);
+            final String clockView24Skel = res.getString(R.string.clock_24hr_format);
+            final String key = locale.toString() + clockView12Skel + clockView24Skel;
+            if (key.equals(sCacheKey)) return;
+
+            sClockView12 = DateFormat.getBestDateTimePattern(locale, clockView12Skel);
+            // CLDR insists on adding an AM/PM indicator even though it wasn't in the skeleton
+            // format.  The following code removes the AM/PM indicator if we didn't want it.
+            if (!clockView12Skel.contains("a")) {
+                sClockView12 = sClockView12.replaceAll("a", "").trim();
+            }
+
+            sClockView24 = DateFormat.getBestDateTimePattern(locale, clockView24Skel);
+
+            // Use fancy colon.
+            sClockView24 = sClockView24.replace(':', '\uee01');
+            sClockView12 = sClockView12.replace(':', '\uee01');
+
+            sCacheKey = key;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 36d5543..901a736 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -20,7 +20,7 @@
 import android.app.Presentation;
 import android.content.Context;
 import android.graphics.Color;
-import android.graphics.Point;
+import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
 import android.media.MediaRouter;
 import android.media.MediaRouter.RouteInfo;
@@ -127,7 +127,7 @@
         Presentation presentation = mPresentations.get(displayId);
         if (presentation == null) {
             final Presentation newPresentation = new KeyguardPresentation(mContext, display,
-                    mKeyguardStatusViewComponentFactory, LayoutInflater.from(mContext));
+                    mKeyguardStatusViewComponentFactory);
             newPresentation.setOnDismissListener(dialog -> {
                 if (newPresentation.equals(mPresentations.get(displayId))) {
                     mPresentations.remove(displayId);
@@ -245,7 +245,6 @@
         private static final int VIDEO_SAFE_REGION = 80; // Percentage of display width & height
         private static final int MOVE_CLOCK_TIMEOUT = 10000; // 10s
         private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
-        private final LayoutInflater mLayoutInflater;
         private KeyguardClockSwitchController mKeyguardClockSwitchController;
         private View mClock;
         private int mUsableWidth;
@@ -264,18 +263,16 @@
         };
 
         KeyguardPresentation(Context context, Display display,
-                KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory,
-                LayoutInflater layoutInflater) {
-            super(context, display, R.style.Theme_SystemUI_KeyguardPresentation);
+                KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory) {
+            super(context, display, R.style.Theme_SystemUI_KeyguardPresentation,
+                    WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
             mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
-            mLayoutInflater = layoutInflater;
-            getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
             setCancelable(false);
         }
 
         @Override
         public void cancel() {
-            // Do not allow anything to cancel KeyguardPresetation except KeyguardDisplayManager.
+            // Do not allow anything to cancel KeyguardPresentation except KeyguardDisplayManager.
         }
 
         @Override
@@ -287,14 +284,15 @@
         protected void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
 
-            Point p = new Point();
-            getDisplay().getSize(p);
-            mUsableWidth = VIDEO_SAFE_REGION * p.x/100;
-            mUsableHeight = VIDEO_SAFE_REGION * p.y/100;
-            mMarginLeft = (100 - VIDEO_SAFE_REGION) * p.x / 200;
-            mMarginTop = (100 - VIDEO_SAFE_REGION) * p.y / 200;
+            final Rect bounds = getWindow().getWindowManager().getMaximumWindowMetrics()
+                    .getBounds();
+            mUsableWidth = VIDEO_SAFE_REGION * bounds.width() / 100;
+            mUsableHeight = VIDEO_SAFE_REGION * bounds.height() / 100;
+            mMarginLeft = (100 - VIDEO_SAFE_REGION) * bounds.width() / 200;
+            mMarginTop = (100 - VIDEO_SAFE_REGION) * bounds.height() / 200;
 
-            setContentView(mLayoutInflater.inflate(R.layout.keyguard_presentation, null));
+            setContentView(LayoutInflater.from(getContext())
+                    .inflate(R.layout.keyguard_presentation, null));
 
             // Logic to make the lock screen fullscreen
             getWindow().getDecorView().setSystemUiVisibility(
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
index 351369c..1d12c1a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
@@ -178,8 +178,7 @@
     }
 
     /** Initialize the Controller. */
-    public void init() {
-        super.init();
+    public void initInternal() {
         mKeyguardSecurityContainerController.init();
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index 3db9db7..94913c8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -190,8 +190,8 @@
     }
 
     @Override
-    public void init() {
-        super.init();
+    public void initInternal() {
+        super.initInternal();
         mMessageAreaController.init();
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardRootViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardRootViewController.java
index 5c125fc..4e375c2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardRootViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardRootViewController.java
@@ -19,7 +19,7 @@
 import android.view.ViewGroup;
 
 import com.android.keyguard.dagger.KeyguardBouncerScope;
-import com.android.keyguard.dagger.RootView;
+import com.android.systemui.dagger.qualifiers.RootView;
 import com.android.systemui.statusbar.phone.KeyguardBouncer;
 import com.android.systemui.util.ViewController;
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 1c23605..e9173a3 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -169,8 +169,7 @@
     }
 
     @Override
-    public void init() {
-        super.init();
+    public void initInternal() {
         mSecurityViewFlipperController.init();
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index a479bca..a9c06ed 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -33,9 +33,11 @@
 import android.text.TextUtils.TruncateAt;
 import android.util.AttributeSet;
 import android.util.TypedValue;
+import android.view.Gravity;
 import android.view.View;
 import android.view.animation.Animation;
 import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
 import android.widget.TextView;
 
 import androidx.slice.SliceItem;
@@ -55,8 +57,10 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * View visible under the clock on the lock screen and AoD.
@@ -86,6 +90,8 @@
     private float mRowWithHeaderTextSize;
     private View.OnClickListener mOnClickListener;
 
+    private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
+
     public KeyguardSliceView(Context context, AttributeSet attrs) {
         super(context, attrs);
 
@@ -142,6 +148,40 @@
         }
     }
 
+    /**
+     * Updates the lockscreen mode which may change the layout of the keyguard slice view.
+     */
+    public void updateLockScreenMode(int mode) {
+        mLockScreenMode = mode;
+        if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) {
+            // add top padding to better align with top of clock
+            final int topPadding = (int) TypedValue.applyDimension(
+                    TypedValue.COMPLEX_UNIT_DIP,
+                    20,
+                    getResources().getDisplayMetrics());
+            mTitle.setPaddingRelative(0, topPadding, 0, 0);
+            mTitle.setGravity(Gravity.START);
+            setGravity(Gravity.START);
+            RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) getLayoutParams();
+            lp.removeRule(RelativeLayout.CENTER_HORIZONTAL);
+            setLayoutParams(lp);
+        } else {
+            final int horizontalPaddingDpValue = (int) TypedValue.applyDimension(
+                    TypedValue.COMPLEX_UNIT_DIP,
+                    44,
+                    getResources().getDisplayMetrics()
+            );
+            mTitle.setPaddingRelative(horizontalPaddingDpValue, 0, horizontalPaddingDpValue, 0);
+            mTitle.setGravity(Gravity.CENTER_HORIZONTAL);
+            setGravity(Gravity.CENTER_HORIZONTAL);
+            RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) getLayoutParams();
+            lp.addRule(RelativeLayout.CENTER_HORIZONTAL);
+            setLayoutParams(lp);
+        }
+        mRow.setLockscreenMode(mode);
+        requestLayout();
+    }
+
     Map<View, PendingIntent> showSlice(RowContent header, List<SliceContent> subItems) {
         Trace.beginSection("KeyguardSliceView#showSlice");
         mHasHeader = header != null;
@@ -166,6 +206,8 @@
         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.gravity = mLockScreenMode !=  KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL
+                ? Gravity.START :  Gravity.CENTER;
         layoutParams.topMargin = mHasHeader ? mRowWithHeaderPadding : mRowPadding;
         mRow.setLayoutParams(layoutParams);
 
@@ -282,6 +324,7 @@
         pw.println("  mTextColor: " + Integer.toHexString(mTextColor));
         pw.println("  mDarkAmount: " + mDarkAmount);
         pw.println("  mHasHeader: " + mHasHeader);
+        pw.println("  mLockScreenMode: " + mLockScreenMode);
     }
 
     @Override
@@ -291,6 +334,8 @@
     }
 
     public static class Row extends LinearLayout {
+        private Set<KeyguardSliceTextView> mKeyguardSliceTextViewSet = new HashSet();
+        private int mLockScreenModeRow = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
 
         /**
          * This view is visible in AOD, which means that the device will sleep if we
@@ -361,12 +406,18 @@
         protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
             int width = MeasureSpec.getSize(widthMeasureSpec);
             int childCount = getChildCount();
+
             for (int i = 0; i < childCount; i++) {
                 View child = getChildAt(i);
                 if (child instanceof KeyguardSliceTextView) {
-                    ((KeyguardSliceTextView) child).setMaxWidth(width / 3);
+                    if (mLockScreenModeRow == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) {
+                        ((KeyguardSliceTextView) child).setMaxWidth(Integer.MAX_VALUE);
+                    } else {
+                        ((KeyguardSliceTextView) child).setMaxWidth(width / 3);
+                    }
                 }
             }
+
             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
         }
 
@@ -384,6 +435,42 @@
         public boolean hasOverlappingRendering() {
             return false;
         }
+
+        @Override
+        public void addView(View view, int index) {
+            super.addView(view, index);
+
+            if (view instanceof KeyguardSliceTextView) {
+                ((KeyguardSliceTextView) view).setLockScreenMode(mLockScreenModeRow);
+                mKeyguardSliceTextViewSet.add((KeyguardSliceTextView) view);
+            }
+        }
+
+        @Override
+        public void removeView(View view) {
+            super.removeView(view);
+            if (view instanceof KeyguardSliceTextView) {
+                mKeyguardSliceTextViewSet.remove((KeyguardSliceTextView) view);
+            }
+        }
+
+        /**
+         * Updates the lockscreen mode which may change the layout of this view.
+         */
+        public void setLockscreenMode(int mode) {
+            mLockScreenModeRow = mode;
+            if (mLockScreenModeRow == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) {
+                setOrientation(LinearLayout.VERTICAL);
+                setGravity(Gravity.START);
+            } else {
+                setOrientation(LinearLayout.HORIZONTAL);
+                setGravity(Gravity.CENTER);
+            }
+
+            for (KeyguardSliceTextView textView : mKeyguardSliceTextViewSet) {
+                textView.setLockScreenMode(mLockScreenModeRow);
+            }
+        }
     }
 
     /**
@@ -392,6 +479,7 @@
     @VisibleForTesting
     static class KeyguardSliceTextView extends TextView implements
             ConfigurationController.ConfigurationListener {
+        private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
 
         @StyleRes
         private static int sStyleId = R.style.TextAppearance_Keyguard_Secondary;
@@ -432,9 +520,16 @@
 
         private void updatePadding() {
             boolean hasText = !TextUtils.isEmpty(getText());
-            int horizontalPadding = (int) getContext().getResources()
+            int padding = (int) getContext().getResources()
                     .getDimension(R.dimen.widget_horizontal_padding) / 2;
-            setPadding(horizontalPadding, 0, horizontalPadding * (hasText ? 1 : -1), 0);
+            if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) {
+                // orientation is vertical, so add padding to top & bottom
+                setPadding(0, padding, 0, padding * (hasText ? 1 : -1));
+            } else {
+                // oreintation is horizontal, so add padding to left & right
+                setPadding(padding, 0, padding * (hasText ? 1 : -1), 0);
+            }
+
             setCompoundDrawablePadding((int) mContext.getResources()
                     .getDimension(R.dimen.widget_icon_padding));
         }
@@ -461,5 +556,18 @@
                 }
             }
         }
+
+        /**
+         * Updates the lockscreen mode which may change the layout of this view.
+         */
+        public void setLockScreenMode(int mode) {
+            mLockScreenMode = mode;
+            if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) {
+                setGravity(Gravity.START);
+            } else {
+                setGravity(Gravity.CENTER);
+            }
+            updatePadding();
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
index 2470b95..8b55b06 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
@@ -44,6 +44,7 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.tuner.TunerService;
+import com.android.systemui.util.ViewController;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -55,11 +56,10 @@
 
 /** Controller for a {@link KeyguardSliceView}. */
 @KeyguardStatusViewScope
-public class KeyguardSliceViewController implements Dumpable {
+public class KeyguardSliceViewController extends ViewController<KeyguardSliceView> implements
+        Dumpable {
     private static final String TAG = "KeyguardSliceViewCtrl";
 
-    private final KeyguardSliceView mView;
-    private final KeyguardStatusView mKeyguardStatusView;
     private final ActivityStarter mActivityStarter;
     private final ConfigurationController mConfigurationController;
     private final TunerService mTunerService;
@@ -69,41 +69,7 @@
     private Uri mKeyguardSliceUri;
     private Slice mSlice;
     private Map<View, PendingIntent> mClickActions;
-
-    private final View.OnAttachStateChangeListener mOnAttachStateChangeListener =
-            new View.OnAttachStateChangeListener() {
-
-                @Override
-                public void onViewAttachedToWindow(View v) {
-
-                    Display display = mView.getDisplay();
-                    if (display != null) {
-                        mDisplayId = display.getDisplayId();
-                    }
-                    mTunerService.addTunable(mTunable, Settings.Secure.KEYGUARD_SLICE_URI);
-                    // Make sure we always have the most current slice
-                    if (mDisplayId == DEFAULT_DISPLAY && mLiveData != null) {
-                        mLiveData.observeForever(mObserver);
-                    }
-                    mConfigurationController.addCallback(mConfigurationListener);
-                    mDumpManager.registerDumpable(
-                            TAG + "@" + Integer.toHexString(
-                                    KeyguardSliceViewController.this.hashCode()),
-                            KeyguardSliceViewController.this);
-                }
-
-                @Override
-                public void onViewDetachedFromWindow(View v) {
-
-                    // TODO(b/117344873) Remove below work around after this issue be fixed.
-                    if (mDisplayId == DEFAULT_DISPLAY) {
-                        mLiveData.removeObserver(mObserver);
-                    }
-                    mTunerService.removeTunable(mTunable);
-                    mConfigurationController.removeCallback(mConfigurationListener);
-                    mDumpManager.unregisterDumpable(TAG);
-                }
-            };
+    private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
 
     TunerService.Tunable mTunable = (key, newValue) -> setupUri(newValue);
 
@@ -134,27 +100,57 @@
     };
 
     @Inject
-    public KeyguardSliceViewController(KeyguardSliceView keyguardSliceView,
-            KeyguardStatusView keyguardStatusView, ActivityStarter activityStarter,
-            ConfigurationController configurationController, TunerService tunerService,
+    public KeyguardSliceViewController(
+            KeyguardSliceView keyguardSliceView,
+            ActivityStarter activityStarter,
+            ConfigurationController configurationController,
+            TunerService tunerService,
             DumpManager dumpManager) {
-        mView = keyguardSliceView;
-        mKeyguardStatusView = keyguardStatusView;
+        super(keyguardSliceView);
         mActivityStarter = activityStarter;
         mConfigurationController = configurationController;
         mTunerService = tunerService;
         mDumpManager = dumpManager;
     }
 
-    /** Initialize the controller. */
-    public void init() {
-        if (mView.isAttachedToWindow()) {
-            mOnAttachStateChangeListener.onViewAttachedToWindow(mView);
+    @Override
+    protected void onViewAttached() {
+        Display display = mView.getDisplay();
+        if (display != null) {
+            mDisplayId = display.getDisplayId();
         }
-        mView.addOnAttachStateChangeListener(mOnAttachStateChangeListener);
-        mView.setOnClickListener(mOnClickListener);
-        // TODO: remove the line below.
-        mKeyguardStatusView.setKeyguardSliceViewController(this);
+        mTunerService.addTunable(mTunable, Settings.Secure.KEYGUARD_SLICE_URI);
+        // Make sure we always have the most current slice
+        if (mDisplayId == DEFAULT_DISPLAY && mLiveData != null) {
+            mLiveData.observeForever(mObserver);
+        }
+        mConfigurationController.addCallback(mConfigurationListener);
+        mDumpManager.registerDumpable(
+                TAG + "@" + Integer.toHexString(
+                        KeyguardSliceViewController.this.hashCode()),
+                KeyguardSliceViewController.this);
+        mView.updateLockScreenMode(mLockScreenMode);
+    }
+
+    @Override
+    protected void onViewDetached() {
+        // TODO(b/117344873) Remove below work around after this issue be fixed.
+        if (mDisplayId == DEFAULT_DISPLAY) {
+            mLiveData.removeObserver(mObserver);
+        }
+        mTunerService.removeTunable(mTunable);
+        mConfigurationController.removeCallback(mConfigurationListener);
+        mDumpManager.unregisterDumpable(
+                TAG + "@" + Integer.toHexString(
+                        KeyguardSliceViewController.this.hashCode()));
+    }
+
+    /**
+     * Updates the lockscreen mode which may change the layout of the keyguard slice view.
+     */
+    public void updateLockScreenMode(int mode) {
+        mLockScreenMode = mode;
+        mView.updateLockScreenMode(mLockScreenMode);
     }
 
     /**
@@ -228,12 +224,10 @@
         Trace.endSection();
     }
 
-
     @Override
     public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
         pw.println("  mSlice: " + mSlice);
         pw.println("  mClickActions: " + mClickActions);
-
-        mKeyguardStatusView.dump(fd, pw, args);
+        pw.println("  mLockScreenMode: " + mLockScreenMode);
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index 6e11174..2036b33 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -19,16 +19,13 @@
 import android.app.ActivityManager;
 import android.app.IActivityManager;
 import android.content.Context;
-import android.content.res.Resources;
 import android.graphics.Color;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.text.TextUtils;
-import android.text.format.DateFormat;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.util.Slog;
 import android.util.TypedValue;
 import android.view.View;
 import android.widget.GridLayout;
@@ -39,15 +36,18 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.policy.ConfigurationController;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.util.Locale;
-import java.util.TimeZone;
 
-public class KeyguardStatusView extends GridLayout implements
-        ConfigurationController.ConfigurationListener {
+/**
+ * View consisting of:
+ * - keyguard clock
+ * - logout button (on certain managed devices)
+ * - owner information (if set)
+ * - notification icons (shown on AOD)
+ */
+public class KeyguardStatusView extends GridLayout {
     private static final boolean DEBUG = KeyguardConstants.DEBUG;
     private static final String TAG = "KeyguardStatusView";
     private static final int MARQUEE_DELAY_MS = 2000;
@@ -62,9 +62,7 @@
     private View mNotificationIcons;
     private Runnable mPendingMarqueeStart;
     private Handler mHandler;
-    private KeyguardSliceViewController mKeyguardSliceViewController;
 
-    private boolean mPulsing;
     private float mDarkAmount = 0;
     private int mTextColor;
 
@@ -76,51 +74,6 @@
     private int mIconTopMarginWithHeader;
     private boolean mShowingHeader;
 
-    private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
-
-        @Override
-        public void onTimeChanged() {
-            refreshTime();
-        }
-
-        @Override
-        public void onTimeZoneChanged(TimeZone timeZone) {
-            updateTimeZone(timeZone);
-        }
-
-        @Override
-        public void onKeyguardVisibilityChanged(boolean showing) {
-            if (showing) {
-                if (DEBUG) Slog.v(TAG, "refresh statusview showing:" + showing);
-                refreshTime();
-                updateOwnerInfo();
-                updateLogoutView();
-            }
-        }
-
-        @Override
-        public void onStartedWakingUp() {
-            setEnableMarquee(true);
-        }
-
-        @Override
-        public void onFinishedGoingToSleep(int why) {
-            setEnableMarquee(false);
-        }
-
-        @Override
-        public void onUserSwitchComplete(int userId) {
-            refreshFormat();
-            updateOwnerInfo();
-            updateLogoutView();
-        }
-
-        @Override
-        public void onLogoutEnabledChanged() {
-            updateLogoutView();
-        }
-    };
-
     public KeyguardStatusView(Context context) {
         this(context, null, 0);
     }
@@ -137,21 +90,7 @@
         onDensityOrFontScaleChanged();
     }
 
-    /**
-     * If we're presenting a custom clock of just the default one.
-     */
-    public boolean hasCustomClock() {
-        return mClockView.hasCustomClock();
-    }
-
-    /**
-     * Set whether or not the lock screen is showing notifications.
-     */
-    public void setHasVisibleNotifications(boolean hasVisibleNotifications) {
-        mClockView.setHasVisibleNotifications(hasVisibleNotifications);
-    }
-
-    private void setEnableMarquee(boolean enabled) {
+    void setEnableMarquee(boolean enabled) {
         if (DEBUG) Log.v(TAG, "Schedule setEnableMarquee: " + (enabled ? "Enable" : "Disable"));
         if (enabled) {
             if (mPendingMarqueeStart == null) {
@@ -198,7 +137,6 @@
 
         boolean shouldMarquee = Dependency.get(KeyguardUpdateMonitor.class).isDeviceInteractive();
         setEnableMarquee(shouldMarquee);
-        refreshFormat();
         updateOwnerInfo();
         updateLogoutView();
         updateDark();
@@ -233,60 +171,14 @@
         layoutOwnerInfo();
     }
 
-    @Override
-    public void onDensityOrFontScaleChanged() {
-        if (mClockView != null) {
-            mClockView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
-                    getResources().getDimensionPixelSize(R.dimen.widget_big_font_size));
-        }
-        if (mOwnerInfo != null) {
-            mOwnerInfo.setTextSize(TypedValue.COMPLEX_UNIT_PX,
-                    getResources().getDimensionPixelSize(R.dimen.widget_label_font_size));
-        }
-        loadBottomMargin();
-    }
-
-    public void dozeTimeTick() {
-        refreshTime();
-        mKeyguardSliceViewController.refresh();
-    }
-
-    private void refreshTime() {
-        mClockView.refresh();
-    }
-
-    private void updateTimeZone(TimeZone timeZone) {
-        mClockView.onTimeZoneChanged(timeZone);
-    }
-
-    private void refreshFormat() {
-        Patterns.update(mContext);
-        mClockView.setFormat12Hour(Patterns.clockView12);
-        mClockView.setFormat24Hour(Patterns.clockView24);
-    }
-
-    public int getLogoutButtonHeight() {
+    int getLogoutButtonHeight() {
         if (mLogoutView == null) {
             return 0;
         }
         return mLogoutView.getVisibility() == VISIBLE ? mLogoutView.getHeight() : 0;
     }
 
-    public float getClockTextSize() {
-        return mClockView.getTextSize();
-    }
-
-    /**
-     * Returns the preferred Y position of the clock.
-     *
-     * @param totalHeight The height available to position the clock.
-     * @return Y position of clock.
-     */
-    public int getClockPreferredY(int totalHeight) {
-        return mClockView.getPreferredY(totalHeight);
-    }
-
-    private void updateLogoutView() {
+    void updateLogoutView() {
         if (mLogoutView == null) {
             return;
         }
@@ -296,7 +188,16 @@
                 com.android.internal.R.string.global_action_logout));
     }
 
-    private void updateOwnerInfo() {
+    void onDensityOrFontScaleChanged() {
+        if (mOwnerInfo != null) {
+            mOwnerInfo.setTextSize(TypedValue.COMPLEX_UNIT_PX,
+                    getResources().getDimensionPixelSize(
+                            com.android.systemui.R.dimen.widget_label_font_size));
+            loadBottomMargin();
+        }
+    }
+
+    void updateOwnerInfo() {
         if (mOwnerInfo == null) return;
         String info = mLockPatternUtils.getDeviceOwnerInfo();
         if (info == null) {
@@ -311,30 +212,36 @@
         updateDark();
     }
 
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        Dependency.get(KeyguardUpdateMonitor.class).registerCallback(mInfoCallback);
-        Dependency.get(ConfigurationController.class).addCallback(this);
+    void setDarkAmount(float darkAmount) {
+        if (mDarkAmount == darkAmount) {
+            return;
+        }
+        mDarkAmount = darkAmount;
+        mClockView.setDarkAmount(darkAmount);
+        updateDark();
     }
 
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        Dependency.get(KeyguardUpdateMonitor.class).removeCallback(mInfoCallback);
-        Dependency.get(ConfigurationController.class).removeCallback(this);
-    }
+    void updateDark() {
+        boolean dark = mDarkAmount == 1;
+        if (mLogoutView != null) {
+            mLogoutView.setAlpha(dark ? 0 : 1);
+        }
 
-    @Override
-    public void onLocaleListChanged() {
-        refreshFormat();
+        if (mOwnerInfo != null) {
+            boolean hasText = !TextUtils.isEmpty(mOwnerInfo.getText());
+            mOwnerInfo.setVisibility(hasText ? VISIBLE : GONE);
+            layoutOwnerInfo();
+        }
+
+        final int blendedTextColor = ColorUtils.blendARGB(mTextColor, Color.WHITE, mDarkAmount);
+        mKeyguardSlice.setDarkAmount(mDarkAmount);
+        mClockView.setTextColor(blendedTextColor);
     }
 
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("KeyguardStatusView:");
         pw.println("  mOwnerInfo: " + (mOwnerInfo == null
                 ? "null" : mOwnerInfo.getVisibility() == VISIBLE));
-        pw.println("  mPulsing: " + mPulsing);
         pw.println("  mDarkAmount: " + mDarkAmount);
         pw.println("  mTextColor: " + Integer.toHexString(mTextColor));
         if (mLogoutView != null) {
@@ -354,64 +261,6 @@
                 R.dimen.widget_vertical_padding_with_header);
     }
 
-    // DateFormat.getBestDateTimePattern is extremely expensive, and refresh is called often.
-    // This is an optimization to ensure we only recompute the patterns when the inputs change.
-    private static final class Patterns {
-        static String clockView12;
-        static String clockView24;
-        static String cacheKey;
-
-        static void update(Context context) {
-            final Locale locale = Locale.getDefault();
-            final Resources res = context.getResources();
-            final String clockView12Skel = res.getString(R.string.clock_12hr_format);
-            final String clockView24Skel = res.getString(R.string.clock_24hr_format);
-            final String key = locale.toString() + clockView12Skel + clockView24Skel;
-            if (key.equals(cacheKey)) return;
-
-            clockView12 = DateFormat.getBestDateTimePattern(locale, clockView12Skel);
-            // CLDR insists on adding an AM/PM indicator even though it wasn't in the skeleton
-            // format.  The following code removes the AM/PM indicator if we didn't want it.
-            if (!clockView12Skel.contains("a")) {
-                clockView12 = clockView12.replaceAll("a", "").trim();
-            }
-
-            clockView24 = DateFormat.getBestDateTimePattern(locale, clockView24Skel);
-
-            // Use fancy colon.
-            clockView24 = clockView24.replace(':', '\uee01');
-            clockView12 = clockView12.replace(':', '\uee01');
-
-            cacheKey = key;
-        }
-    }
-
-    public void setDarkAmount(float darkAmount) {
-        if (mDarkAmount == darkAmount) {
-            return;
-        }
-        mDarkAmount = darkAmount;
-        mClockView.setDarkAmount(darkAmount);
-        updateDark();
-    }
-
-    private void updateDark() {
-        boolean dark = mDarkAmount == 1;
-        if (mLogoutView != null) {
-            mLogoutView.setAlpha(dark ? 0 : 1);
-        }
-
-        if (mOwnerInfo != null) {
-            boolean hasText = !TextUtils.isEmpty(mOwnerInfo.getText());
-            mOwnerInfo.setVisibility(hasText ? VISIBLE : GONE);
-            layoutOwnerInfo();
-        }
-
-        final int blendedTextColor = ColorUtils.blendARGB(mTextColor, Color.WHITE, mDarkAmount);
-        mKeyguardSlice.setDarkAmount(mDarkAmount);
-        mClockView.setTextColor(blendedTextColor);
-    }
-
     private void layoutOwnerInfo() {
         if (mOwnerInfo != null && mOwnerInfo.getVisibility() != GONE) {
             // Animate owner info during wake-up transition
@@ -433,13 +282,6 @@
         }
     }
 
-    public void setPulsing(boolean pulsing) {
-        if (mPulsing == pulsing) {
-            return;
-        }
-        mPulsing = pulsing;
-    }
-
     private boolean shouldShowLogout() {
         return Dependency.get(KeyguardUpdateMonitor.class).isLogoutEnabled()
                 && KeyguardUpdateMonitor.getCurrentUser() != UserHandle.USER_SYSTEM;
@@ -454,9 +296,4 @@
             Log.e(TAG, "Failed to logout user", re);
         }
     }
-
-    // TODO: remove this method when a controller is available.
-    void setKeyguardSliceViewController(KeyguardSliceViewController keyguardSliceViewController) {
-        mKeyguardSliceViewController = keyguardSliceViewController;
-    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
new file mode 100644
index 0000000..0a57b09
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
+
+import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
+
+import android.util.Slog;
+import android.view.View;
+
+import com.android.systemui.Interpolators;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.AnimatableProperty;
+import com.android.systemui.statusbar.notification.PropertyAnimator;
+import com.android.systemui.statusbar.notification.stack.AnimationProperties;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.ViewController;
+
+import java.util.TimeZone;
+
+import javax.inject.Inject;
+
+/**
+ * Injectable controller for {@link KeyguardStatusView}.
+ */
+public class KeyguardStatusViewController extends ViewController<KeyguardStatusView> {
+    private static final boolean DEBUG = KeyguardConstants.DEBUG;
+    private static final String TAG = "KeyguardStatusViewController";
+
+    private static final AnimationProperties CLOCK_ANIMATION_PROPERTIES =
+            new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+
+    private final KeyguardSliceViewController mKeyguardSliceViewController;
+    private final KeyguardClockSwitchController mKeyguardClockSwitchController;
+    private final KeyguardStateController mKeyguardStateController;
+    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    private final ConfigurationController mConfigurationController;
+
+    private boolean mKeyguardStatusViewAnimating;
+
+    @Inject
+    public KeyguardStatusViewController(
+            KeyguardStatusView keyguardStatusView,
+            KeyguardSliceViewController keyguardSliceViewController,
+            KeyguardClockSwitchController keyguardClockSwitchController,
+            KeyguardStateController keyguardStateController,
+            KeyguardUpdateMonitor keyguardUpdateMonitor,
+            ConfigurationController configurationController) {
+        super(keyguardStatusView);
+        mKeyguardSliceViewController = keyguardSliceViewController;
+        mKeyguardClockSwitchController = keyguardClockSwitchController;
+        mKeyguardStateController = keyguardStateController;
+        mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+        mConfigurationController = configurationController;
+    }
+
+    @Override
+    public void initInternal() {
+        mKeyguardClockSwitchController.init();
+    }
+
+    @Override
+    protected void onViewAttached() {
+        mKeyguardUpdateMonitor.registerCallback(mInfoCallback);
+        mConfigurationController.addCallback(mConfigurationListener);
+    }
+
+    @Override
+    protected void onViewDetached() {
+        mKeyguardUpdateMonitor.removeCallback(mInfoCallback);
+        mConfigurationController.removeCallback(mConfigurationListener);
+    }
+
+    /**
+     * Updates views on doze time tick.
+     */
+    public void dozeTimeTick() {
+        refreshTime();
+        mKeyguardSliceViewController.refresh();
+    }
+
+    /**
+     * The amount we're in doze.
+     */
+    public void setDarkAmount(float darkAmount) {
+        mView.setDarkAmount(darkAmount);
+    }
+
+    /**
+     * Set whether or not the lock screen is showing notifications.
+     */
+    public void setHasVisibleNotifications(boolean hasVisibleNotifications) {
+        mKeyguardClockSwitchController.setHasVisibleNotifications(hasVisibleNotifications);
+    }
+
+    /**
+     * If we're presenting a custom clock of just the default one.
+     */
+    public boolean hasCustomClock() {
+        return mKeyguardClockSwitchController.hasCustomClock();
+    }
+
+    /**
+     * Get the height of the logout button.
+     */
+    public int getLogoutButtonHeight() {
+        return mView.getLogoutButtonHeight();
+    }
+
+    /**
+     * Set keyguard status view alpha.
+     */
+    public void setAlpha(float alpha) {
+        if (!mKeyguardStatusViewAnimating) {
+            mView.setAlpha(alpha);
+        }
+    }
+
+    /**
+     * Set pivot x.
+     */
+    public void setPivotX(float pivot) {
+        mView.setPivotX(pivot);
+    }
+
+    /**
+     * Set pivot y.
+     */
+    public void setPivotY(float pivot) {
+        mView.setPivotY(pivot);
+    }
+
+    /**
+     * Get the clock text size.
+     */
+    public float getClockTextSize() {
+        return mKeyguardClockSwitchController.getClockTextSize();
+    }
+
+    /**
+     * Returns the preferred Y position of the clock.
+     *
+     * @param totalHeight The height available to position the clock.
+     * @return Y position of clock.
+     */
+    public int getClockPreferredY(int totalHeight) {
+        return mKeyguardClockSwitchController.getClockPreferredY(totalHeight);
+    }
+
+    /**
+     * Get the height of the keyguard status view.
+     */
+    public int getHeight() {
+        return mView.getHeight();
+    }
+
+    /**
+     * Set whether the view accessibility importance mode.
+     */
+    public void setStatusAccessibilityImportance(int mode) {
+        mView.setImportantForAccessibility(mode);
+    }
+
+    /**
+     * Update position of the view with an optional animation
+     */
+    public void updatePosition(int clockTranslationX, int clockTranslationY,
+            boolean animateClock) {
+        PropertyAnimator.setProperty(mView, AnimatableProperty.X,
+                clockTranslationX, CLOCK_ANIMATION_PROPERTIES, animateClock);
+        PropertyAnimator.setProperty(mView, AnimatableProperty.Y,
+                clockTranslationY, CLOCK_ANIMATION_PROPERTIES, animateClock);
+    }
+
+    /**
+     * Set the visibility of the keyguard status view based on some new state.
+     */
+    public void setKeyguardStatusViewVisibility(
+            int statusBarState,
+            boolean keyguardFadingAway,
+            boolean goingToFullShade,
+            int oldStatusBarState) {
+        mView.animate().cancel();
+        mKeyguardStatusViewAnimating = false;
+        if ((!keyguardFadingAway && oldStatusBarState == KEYGUARD
+                && statusBarState != KEYGUARD) || goingToFullShade) {
+            mKeyguardStatusViewAnimating = true;
+            mView.animate()
+                    .alpha(0f)
+                    .setStartDelay(0)
+                    .setDuration(160)
+                    .setInterpolator(Interpolators.ALPHA_OUT)
+                    .withEndAction(
+                    mAnimateKeyguardStatusViewGoneEndRunnable);
+            if (keyguardFadingAway) {
+                mView.animate()
+                        .setStartDelay(mKeyguardStateController.getKeyguardFadingAwayDelay())
+                        .setDuration(mKeyguardStateController.getShortenedFadingAwayDuration())
+                        .start();
+            }
+        } else if (oldStatusBarState == StatusBarState.SHADE_LOCKED && statusBarState == KEYGUARD) {
+            mView.setVisibility(View.VISIBLE);
+            mKeyguardStatusViewAnimating = true;
+            mView.setAlpha(0f);
+            mView.animate()
+                    .alpha(1f)
+                    .setStartDelay(0)
+                    .setDuration(320)
+                    .setInterpolator(Interpolators.ALPHA_IN)
+                    .withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable);
+        } else if (statusBarState == KEYGUARD) {
+            if (keyguardFadingAway) {
+                mKeyguardStatusViewAnimating = true;
+                mView.animate()
+                        .alpha(0)
+                        .translationYBy(-getHeight() * 0.05f)
+                        .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN)
+                        .setDuration(125)
+                        .setStartDelay(0)
+                        .withEndAction(mAnimateKeyguardStatusViewInvisibleEndRunnable)
+                        .start();
+            } else {
+                mView.setVisibility(View.VISIBLE);
+                mView.setAlpha(1f);
+            }
+        } else {
+            mView.setVisibility(View.GONE);
+            mView.setAlpha(1f);
+        }
+    }
+
+    private void refreshTime() {
+        mKeyguardClockSwitchController.refresh();
+    }
+
+    private final ConfigurationController.ConfigurationListener mConfigurationListener =
+            new ConfigurationController.ConfigurationListener() {
+        @Override
+        public void onLocaleListChanged() {
+            refreshTime();
+        }
+
+        @Override
+        public void onDensityOrFontScaleChanged() {
+            mKeyguardClockSwitchController.onDensityOrFontScaleChanged();
+            mView.onDensityOrFontScaleChanged();
+        }
+    };
+
+    private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
+        @Override
+        public void onLockScreenModeChanged(int mode) {
+            mKeyguardClockSwitchController.updateLockScreenMode(mode);
+            mKeyguardSliceViewController.updateLockScreenMode(mode);
+        }
+
+        @Override
+        public void onTimeChanged() {
+            refreshTime();
+        }
+
+        @Override
+        public void onTimeZoneChanged(TimeZone timeZone) {
+            mKeyguardClockSwitchController.updateTimeZone(timeZone);
+        }
+
+        @Override
+        public void onKeyguardVisibilityChanged(boolean showing) {
+            if (showing) {
+                if (DEBUG) Slog.v(TAG, "refresh statusview showing:" + showing);
+                refreshTime();
+                mView.updateOwnerInfo();
+                mView.updateLogoutView();
+            }
+        }
+
+        @Override
+        public void onStartedWakingUp() {
+            mView.setEnableMarquee(true);
+        }
+
+        @Override
+        public void onFinishedGoingToSleep(int why) {
+            mView.setEnableMarquee(false);
+        }
+
+        @Override
+        public void onUserSwitchComplete(int userId) {
+            mKeyguardClockSwitchController.refreshFormat();
+            mView.updateOwnerInfo();
+            mView.updateLogoutView();
+        }
+
+        @Override
+        public void onLogoutEnabledChanged() {
+            mView.updateLogoutView();
+        }
+    };
+
+    private final Runnable mAnimateKeyguardStatusViewInvisibleEndRunnable = () -> {
+        mKeyguardStatusViewAnimating = false;
+        mView.setVisibility(View.INVISIBLE);
+    };
+
+
+    private final Runnable mAnimateKeyguardStatusViewGoneEndRunnable = () -> {
+        mKeyguardStatusViewAnimating = false;
+        mView.setVisibility(View.GONE);
+    };
+
+    private final Runnable mAnimateKeyguardStatusViewVisibleEndRunnable = () -> {
+        mKeyguardStatusViewAnimating = false;
+    };
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index bdb34bb..1a98c20 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -100,8 +100,8 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.util.Assert;
@@ -180,6 +180,10 @@
     private static final int MSG_USER_STOPPED = 340;
     private static final int MSG_USER_REMOVED = 341;
     private static final int MSG_KEYGUARD_GOING_AWAY = 342;
+    private static final int MSG_LOCK_SCREEN_MODE = 343;
+
+    public static final int LOCK_SCREEN_MODE_NORMAL = 0;
+    public static final int LOCK_SCREEN_MODE_LAYOUT_1 = 1;
 
     /** Biometric authentication state: Not listening. */
     private static final int BIOMETRIC_STATE_STOPPED = 0;
@@ -263,6 +267,7 @@
     private final ArrayList<WeakReference<KeyguardUpdateMonitorCallback>>
             mCallbacks = Lists.newArrayList();
     private ContentObserver mDeviceProvisionedObserver;
+    private ContentObserver mLockScreenModeObserver;
 
     private boolean mSwitchingUser;
 
@@ -286,6 +291,7 @@
     private boolean mLockIconPressed;
     private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
     private final Executor mBackgroundExecutor;
+    private int mLockScreenMode;
 
     /**
      * Short delay before restarting fingerprint authentication after a successful try. This should
@@ -1694,6 +1700,9 @@
                     case MSG_KEYGUARD_GOING_AWAY:
                         handleKeyguardGoingAway((boolean) msg.obj);
                         break;
+                    case MSG_LOCK_SCREEN_MODE:
+                        handleLockScreenMode();
+                        break;
                     default:
                         super.handleMessage(msg);
                         break;
@@ -1796,7 +1805,7 @@
 
         mIsAutomotive = isAutomotive();
 
-        ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
+        TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
         mUserManager = context.getSystemService(UserManager.class);
         mIsPrimaryUser = mUserManager.isPrimaryUser();
         int user = ActivityManager.getCurrentUser();
@@ -1828,6 +1837,23 @@
                 }
             }
         }
+
+        updateLockScreenMode();
+        mLockScreenModeObserver = new ContentObserver(mHandler) {
+            @Override
+            public void onChange(boolean selfChange) {
+                updateLockScreenMode();
+                mHandler.sendEmptyMessage(MSG_LOCK_SCREEN_MODE);
+            }
+        };
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.SHOW_NEW_LOCKSCREEN),
+                false, mLockScreenModeObserver);
+    }
+
+    private void updateLockScreenMode() {
+        mLockScreenMode = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.SHOW_NEW_LOCKSCREEN, 0);
     }
 
     private final UserSwitchObserver mUserSwitchObserver = new UserSwitchObserver() {
@@ -2350,6 +2376,20 @@
     }
 
     /**
+     * Handle {@link #MSG_LOCK_SCREEN_MODE}
+     */
+    private void handleLockScreenMode() {
+        Assert.isMainThread();
+        if (DEBUG) Log.d(TAG, "handleLockScreenMode(" + mLockScreenMode + ")");
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+            if (cb != null) {
+                cb.onLockScreenModeChanged(mLockScreenMode);
+            }
+        }
+    }
+
+    /**
      * Handle (@line #MSG_TIMEZONE_UPDATE}
      */
     private void handleTimeZoneUpdate(String timeZone) {
@@ -2668,6 +2708,8 @@
         callback.onClockVisibilityChanged();
         callback.onKeyguardVisibilityChangedRaw(mKeyguardIsVisible);
         callback.onTelephonyCapable(mTelephonyCapable);
+        callback.onLockScreenModeChanged(mLockScreenMode);
+
         for (Entry<Integer, SimData> data : mSimDatas.entrySet()) {
             final SimData state = data.getValue();
             callback.onSimStateChanged(state.subId, state.slotId, state.simState);
@@ -2959,13 +3001,17 @@
             mContext.getContentResolver().unregisterContentObserver(mDeviceProvisionedObserver);
         }
 
+        if (mLockScreenModeObserver != null) {
+            mContext.getContentResolver().unregisterContentObserver(mLockScreenModeObserver);
+        }
+
         try {
             ActivityManager.getService().unregisterUserSwitchObserver(mUserSwitchObserver);
         } catch (RemoteException e) {
             Log.d(TAG, "RemoteException onDestroy. cannot unregister userSwitchObserver");
         }
 
-        ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskStackListener);
+        TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
 
         mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver);
         mBroadcastDispatcher.unregisterReceiver(mBroadcastAllReceiver);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 12e0ecd..3c5eceb 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -317,4 +317,9 @@
      */
     public void onSecondaryLockscreenRequirementChanged(int userId) { }
 
+    /**
+     * Called to switch lock screen layout/clock layouts
+     */
+    public void onLockScreenModeChanged(int mode) { }
+
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/TimeBasedColorsClockController.java b/packages/SystemUI/src/com/android/keyguard/TimeBasedColorsClockController.java
new file mode 100644
index 0000000..3cbae0a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/TimeBasedColorsClockController.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
+
+import android.util.MathUtils;
+
+import com.android.internal.graphics.ColorUtils;
+import com.android.settingslib.Utils;
+import com.android.systemui.R;
+import com.android.systemui.util.ViewController;
+
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+
+/**
+ * Changes the color of the text clock based on the time of day.
+ */
+public class TimeBasedColorsClockController extends ViewController<GradientTextClock> {
+    private final int[] mGradientColors = new int[3];
+    private final float[] mPositions = new float[3];
+
+    /**
+     * 0 = fully awake
+     * between 0 and 1 = transitioning between awake and doze
+     * 1 = fully in doze
+     */
+    private float mDarkAmount = 0f;
+
+    public TimeBasedColorsClockController(GradientTextClock view) {
+        super(view);
+    }
+
+    @Override
+    protected void onViewAttached() {
+        refreshTime(System.currentTimeMillis());
+    }
+
+    @Override
+    protected void onViewDetached() {
+
+    }
+
+    /**
+     * Updates the time for this view. Also updates any color changes.
+     */
+    public void refreshTime(long timeInMillis) {
+        updateColors(timeInMillis);
+        updatePositions(timeInMillis);
+        mView.refreshTime();
+    }
+
+    /**
+     * Set the amount (ratio) that the device has transitioned to doze.
+     *
+     * @param darkAmount Amount of transition to doze: 1f for doze and 0f for awake.
+     */
+    public void setDarkAmount(float darkAmount) {
+        mDarkAmount = darkAmount;
+
+        // TODO: (b/170228350) currently this relayouts throughout the animation;
+        //  eventually this should use new Text APIs to animate the variable font weight
+        refreshTime(System.currentTimeMillis());
+
+        int weight = (int) MathUtils.lerp(200, 400, 1f - darkAmount);
+        mView.setFontVariationSettings("'wght' " + weight);
+    }
+
+    private int getTimeIndex(long timeInMillis) {
+        Calendar now = getCalendar(timeInMillis);
+        int hour = now.get(Calendar.HOUR_OF_DAY); // 0 - 23
+        if (hour < mTimes[0]) {
+            return mTimes.length - 1;
+        }
+
+        for (int i = 1; i < mTimes.length; i++) {
+            if (hour < mTimes[i]) {
+                return i - 1;
+            }
+        }
+
+        return mTimes.length - 1;
+    }
+
+    private void updateColors(long timeInMillis) {
+        final int index = getTimeIndex(timeInMillis);
+        final int wallpaperTextColor =
+                Utils.getColorAttrDefaultColor(mView.getContext(), R.attr.wallpaperTextColor);
+        for (int i = 0; i < mGradientColors.length; i++) {
+            // wallpaperTextColor on LS when mDarkAmount = 0f
+            // full color on AOD when mDarkAmount = 1f
+            mGradientColors[i] =
+                    ColorUtils.blendARGB(wallpaperTextColor, COLORS[index][i], mDarkAmount);
+        }
+        mView.setGradientColors(mGradientColors);
+    }
+
+    private void updatePositions(long timeInMillis) {
+        Calendar now = getCalendar(timeInMillis);
+        final int index = getTimeIndex(timeInMillis);
+
+        final Calendar startTime = new GregorianCalendar();
+        startTime.setTimeInMillis(now.getTimeInMillis());
+        startTime.set(Calendar.HOUR_OF_DAY, mTimes[index]);
+        if (startTime.getTimeInMillis() > now.getTimeInMillis()) {
+            // start should be earlier than 'now'
+            startTime.add(Calendar.DATE, -1);
+        }
+
+        final Calendar endTime = new GregorianCalendar();
+        endTime.setTimeInMillis(now.getTimeInMillis());
+        if (index == mTimes.length - 1) {
+            endTime.set(Calendar.HOUR_OF_DAY, mTimes[0]);
+            endTime.add(Calendar.DATE, 1); // end time is tomorrow
+        } else {
+            endTime.set(Calendar.HOUR_OF_DAY, mTimes[index + 1]);
+        }
+
+        long totalTimeInThisColorGradient = endTime.getTimeInMillis() - startTime.getTimeInMillis();
+        long timeIntoThisColorGradient = now.getTimeInMillis() - startTime.getTimeInMillis();
+        float percentageWithinGradient =
+                (float) timeIntoThisColorGradient / (float) totalTimeInThisColorGradient;
+
+        for (int i = 0; i < mPositions.length; i++) {
+            // currently hard-coded .3 movement of gradient
+            mPositions[i] = POSITIONS[index][i] - (.3f * percentageWithinGradient);
+        }
+        mView.setColorPositions(mPositions);
+    }
+
+    private Calendar getCalendar(long timeInMillis) {
+        Calendar now = new GregorianCalendar();
+        now.setTimeInMillis(timeInMillis);
+        return now;
+    }
+
+    private static final int[] SUNRISE = new int[] {0xFF6F75AA, 0xFFAFF0FF, 0xFFFFDEBF};
+    private static final int[] DAY = new int[] {0xFF9BD8FB, 0xFFD7F5FF, 0xFFFFF278};
+    private static final int[] NIGHT = new int[] {0xFF333D5E, 0xFFC5A1D6, 0xFF907359};
+
+    private static final float[] SUNRISE_START_POSITIONS = new float[] {.3f, .5f, .8f};
+    private static final float[] DAY_START_POSITIONS = new float[] {.4f, .8f, 1f};
+    private static final float[] NIGHT_START_POSITIONS = new float[] {.25f, .5f, .8f};
+
+    // TODO (b/170228350): use TwilightManager to set sunrise/sunset times
+    private final int mSunriseTime = 6; // 6am
+    private final int mDaytime = 9; // 9 am
+    private final int mNightTime = 19; // 7pm
+
+    private int[] mTimes = new int[] {
+            mSunriseTime,
+            mDaytime,
+            mNightTime
+    };
+    private static final int[][] COLORS = new int[][] {
+            SUNRISE,
+            DAY,
+            NIGHT
+    };
+    private static final float[][] POSITIONS = new float[][] {
+            SUNRISE_START_POSITIONS,
+            DAY_START_POSITIONS,
+            NIGHT_START_POSITIONS
+    };
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
index 8811088..4fad9a9 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
@@ -24,6 +24,7 @@
 import com.android.keyguard.KeyguardSecurityContainer;
 import com.android.keyguard.KeyguardSecurityViewFlipper;
 import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.RootView;
 import com.android.systemui.statusbar.phone.KeyguardBouncer;
 
 import dagger.Module;
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewComponent.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewComponent.java
index 21ccff7..1b6476c 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewComponent.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewComponent.java
@@ -18,6 +18,7 @@
 
 import com.android.keyguard.KeyguardClockSwitchController;
 import com.android.keyguard.KeyguardStatusView;
+import com.android.keyguard.KeyguardStatusViewController;
 
 import dagger.BindsInstance;
 import dagger.Subcomponent;
@@ -36,4 +37,7 @@
 
     /** Builds a {@link com.android.keyguard.KeyguardClockSwitchController}. */
     KeyguardClockSwitchController getKeyguardClockSwitchController();
+
+    /** Builds a {@link com.android.keyguard.KeyguardStatusViewController}. */
+    KeyguardStatusViewController getKeyguardStatusViewController();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index f24644b..8ac6edb 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -47,6 +47,7 @@
 import com.android.systemui.fragments.FragmentService;
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.media.dialog.MediaOutputDialogFactory;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.navigationbar.NavigationBarController;
 import com.android.systemui.navigationbar.NavigationModeController;
@@ -118,13 +119,11 @@
 import com.android.systemui.tracing.ProtoTracer;
 import com.android.systemui.tuner.TunablePadding.TunablePaddingService;
 import com.android.systemui.tuner.TunerService;
+import com.android.systemui.util.DeviceConfigProxy;
 import com.android.systemui.util.leak.GarbageMonitor;
 import com.android.systemui.util.leak.LeakDetector;
 import com.android.systemui.util.leak.LeakReporter;
 import com.android.systemui.util.sensors.AsyncSensorManager;
-import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.common.DisplayImeController;
-import com.android.wm.shell.common.SystemWindows;
 
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
@@ -339,11 +338,10 @@
     @Inject Lazy<CommandQueue> mCommandQueue;
     @Inject Lazy<Recents> mRecents;
     @Inject Lazy<StatusBar> mStatusBar;
-    @Inject Lazy<DisplayController> mDisplayController;
-    @Inject Lazy<SystemWindows> mSystemWindows;
-    @Inject Lazy<DisplayImeController> mDisplayImeController;
     @Inject Lazy<RecordingController> mRecordingController;
     @Inject Lazy<ProtoTracer> mProtoTracer;
+    @Inject Lazy<MediaOutputDialogFactory> mMediaOutputDialogFactory;
+    @Inject Lazy<DeviceConfigProxy> mDeviceConfigProxy;
 
     @Inject
     public Dependency() {
@@ -528,10 +526,8 @@
         mProviders.put(CommandQueue.class, mCommandQueue::get);
         mProviders.put(Recents.class, mRecents::get);
         mProviders.put(StatusBar.class, mStatusBar::get);
-        mProviders.put(DisplayController.class, mDisplayController::get);
-        mProviders.put(SystemWindows.class, mSystemWindows::get);
-        mProviders.put(DisplayImeController.class, mDisplayImeController::get);
         mProviders.put(ProtoTracer.class, mProtoTracer::get);
+        mProviders.put(DeviceConfigProxy.class, mDeviceConfigProxy::get);
 
         // TODO(b/118592525): to support multi-display , we start to add something which is
         //                    per-display, while others may be global. I think it's time to add
@@ -541,6 +537,8 @@
 
         mProviders.put(RecordingController.class, mRecordingController::get);
 
+        mProviders.put(MediaOutputDialogFactory.class, mMediaOutputDialogFactory::get);
+
         Dependency.setInstance(this);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index 4657b06..f210d50 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -72,8 +72,6 @@
             Key.QS_HAS_TURNED_OFF_MOBILE_DATA,
             Key.TOUCHED_RINGER_TOGGLE,
             Key.HAS_SEEN_ODI_CAPTIONS_TOOLTIP,
-            Key.HAS_SEEN_BUBBLES_EDUCATION,
-            Key.HAS_SEEN_BUBBLES_MANAGE_EDUCATION,
             Key.HAS_SEEN_REVERSE_BOTTOM_SHEET,
             Key.CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT,
             Key.HAS_SEEN_PRIORITY_ONBOARDING
@@ -123,8 +121,6 @@
         String QS_HAS_TURNED_OFF_MOBILE_DATA = "QsHasTurnedOffMobileData";
         String TOUCHED_RINGER_TOGGLE = "TouchedRingerToggle";
         String HAS_SEEN_ODI_CAPTIONS_TOOLTIP = "HasSeenODICaptionsTooltip";
-        String HAS_SEEN_BUBBLES_EDUCATION = "HasSeenBubblesOnboarding";
-        String HAS_SEEN_BUBBLES_MANAGE_EDUCATION = "HasSeenBubblesManageOnboarding";
         String HAS_SEEN_REVERSE_BOTTOM_SHEET = "HasSeenReverseBottomSheet";
         String CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT = "ControlsStructureSwipeTooltipCount";
         /** Tracks whether the user has seen the onboarding screen for priority conversations */
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index c4a305e..8147f66 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -317,10 +317,9 @@
                         updateColorInversion(value);
                     }
                 };
-
-                mColorInversionSetting.setListening(true);
-                mColorInversionSetting.onChange(false);
             }
+            mColorInversionSetting.setListening(true);
+            mColorInversionSetting.onChange(false);
 
             IntentFilter filter = new IntentFilter();
             filter.addAction(Intent.ACTION_USER_SWITCHED);
@@ -582,6 +581,10 @@
 
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
+        if (DEBUG_DISABLE_SCREEN_DECORATIONS) {
+            Log.i(TAG, "ScreenDecorations is disabled");
+            return;
+        }
         mHandler.post(() -> {
             int oldRotation = mRotation;
             mPendingRotationChange = false;
@@ -636,8 +639,8 @@
                 com.android.internal.R.dimen.rounded_corner_radius_bottom);
 
         final boolean changed = mRoundedDefault.x != newRoundedDefault
-                        || mRoundedDefaultTop.x != newRoundedDefault
-                        || mRoundedDefaultBottom.x != newRoundedDefault;
+                        || mRoundedDefaultTop.x != newRoundedDefaultTop
+                        || mRoundedDefaultBottom.x != newRoundedDefaultBottom;
 
         if (changed) {
             // If config_roundedCornerMultipleRadius set as true, ScreenDecorations respect the
@@ -766,6 +769,10 @@
 
     @Override
     public void onTuningChanged(String key, String newValue) {
+        if (DEBUG_DISABLE_SCREEN_DECORATIONS) {
+            Log.i(TAG, "ScreenDecorations is disabled");
+            return;
+        }
         mHandler.post(() -> {
             if (mOverlays == null) return;
             if (SIZE.equals(key)) {
diff --git a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
index 34efa35..02f34ac 100644
--- a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
@@ -42,8 +42,8 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.systemui.statusbar.CommandQueue;
 
 import java.lang.ref.WeakReference;
@@ -66,11 +66,11 @@
 
     @VisibleForTesting
     @Inject
-    SizeCompatModeActivityController(Context context, ActivityManagerWrapper am,
+    SizeCompatModeActivityController(Context context, TaskStackChangeListeners listeners,
             CommandQueue commandQueue) {
         super(context);
         mCommandQueue = commandQueue;
-        am.registerTaskStackListener(new TaskStackChangeListener() {
+        listeners.registerTaskStackListener(new TaskStackChangeListener() {
             @Override
             public void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken) {
                 // Note the callback already runs on main thread.
diff --git a/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java b/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java
index 2365f12..47adffc 100644
--- a/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java
@@ -111,9 +111,7 @@
         final String providerPkg = getIntent().getStringExtra("provider_pkg");
         if (providerPkg == null || mProviderPkg.equals(providerPkg)) return;
         final String callingPkg = getCallingPkg();
-        EventLog.writeEvent(0x534e4554, "159145361", getUid(callingPkg), String.format(
-                "pkg %s (disguised as %s) attempted to request permission to show %s slices in %s",
-                callingPkg, providerPkg, mProviderPkg, mCallingPkg));
+        EventLog.writeEvent(0x534e4554, "159145361", getUid(callingPkg));
     }
 
     @Nullable
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 7dcec3d..3348bd1 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -19,15 +19,18 @@
 import android.app.ActivityThread;
 import android.app.Application;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 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.os.Process;
 import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.util.Log;
 import android.util.TimingsTraceLog;
 
@@ -35,6 +38,8 @@
 import com.android.systemui.dagger.GlobalRootComponent;
 import com.android.systemui.dagger.SysUIComponent;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.people.PeopleSpaceActivity;
+import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
 import com.android.systemui.util.NotificationChannels;
 
 import java.lang.reflect.Constructor;
@@ -104,6 +109,35 @@
                             mServices[i].onBootCompleted();
                         }
                     }
+                    // If flag SHOW_PEOPLE_SPACE is true, enable People Space launcher icon.
+                    // TODO(b/170396074): Remove this when we don't need an icon anymore.
+                    try {
+                        int showPeopleSpace = Settings.Global.getInt(context.getContentResolver(),
+                                Settings.Global.SHOW_PEOPLE_SPACE);
+                        context.getPackageManager().setComponentEnabledSetting(
+                                new ComponentName(context, PeopleSpaceActivity.class),
+                                showPeopleSpace == 1
+                                        ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
+                                        : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+                                PackageManager.DONT_KILL_APP);
+                    } catch (Exception e) {
+                        Log.w(TAG, "Error enabling People Space launch icon:", e);
+                    }
+
+                    // If SHOW_PEOPLE_SPACE is true, enable People Space widget provider.
+                    // TODO(b/170396074): Remove this when we don't need a widget anymore.
+                    try {
+                        int showPeopleSpace = Settings.Global.getInt(context.getContentResolver(),
+                                Settings.Global.SHOW_PEOPLE_SPACE);
+                        context.getPackageManager().setComponentEnabledSetting(
+                                new ComponentName(context, PeopleSpaceWidgetProvider.class),
+                                showPeopleSpace == 1
+                                        ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
+                                        : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+                                PackageManager.DONT_KILL_APP);
+                    } catch (Exception e) {
+                        Log.w(TAG, "Error enabling People Space widget:", e);
+                    }
                 }
             }, bootCompletedFilter);
 
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 80253b4..bdd0b55 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -30,6 +30,7 @@
 import com.android.systemui.navigationbar.gestural.BackGestureTfClassifierProvider;
 import com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider;
 
+import java.util.Optional;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
 
@@ -49,6 +50,11 @@
     }
 
     public static void createFromConfig(Context context) {
+        createFromConfig(context, false);
+    }
+
+    @VisibleForTesting
+    public static void createFromConfig(Context context, boolean fromTest) {
         if (mFactory != null) {
             return;
         }
@@ -62,7 +68,7 @@
             Class<?> cls = null;
             cls = context.getClassLoader().loadClass(clsName);
             mFactory = (SystemUIFactory) cls.newInstance();
-            mFactory.init(context);
+            mFactory.init(context, fromTest);
         } catch (Throwable t) {
             Log.w(TAG, "Error creating SystemUIFactory component: " + clsName, t);
             throw new RuntimeException(t);
@@ -76,16 +82,40 @@
 
     public SystemUIFactory() {}
 
-    private void init(Context context) throws ExecutionException, InterruptedException {
+    @VisibleForTesting
+    public void init(Context context, boolean fromTest)
+            throws ExecutionException, InterruptedException {
+        final boolean initializeComponents = !fromTest
+                && android.os.Process.myUserHandle().isSystem();
         mRootComponent = buildGlobalRootComponent(context);
         // Stand up WMComponent
         mWMComponent = mRootComponent.getWMComponentBuilder().build();
+        if (initializeComponents) {
+            // Only initialize when not starting from tests since this currently initializes some
+            // components that shouldn't be run in the test environment
+            mWMComponent.init();
+        }
 
         // And finally, retrieve whatever SysUI needs from WMShell and build SysUI.
-        // TODO: StubAPIClass is just a placeholder.
-        mSysUIComponent = mRootComponent.getSysUIComponent()
-                .setStubAPIClass(mWMComponent.createStubAPIClass())
+        SysUIComponent.Builder builder = mRootComponent.getSysUIComponent();
+        if (initializeComponents) {
+            // Only initialize when not starting from tests since this currently initializes some
+            // components that shouldn't be run in the test environment
+            builder = builder.setPip(mWMComponent.getPip())
+                    .setSplitScreen(mWMComponent.getSplitScreen())
+                    .setOneHanded(mWMComponent.getOneHanded());
+        } else {
+            builder = builder.setPip(Optional.ofNullable(null))
+                    .setSplitScreen(Optional.ofNullable(null))
+                    .setOneHanded(Optional.ofNullable(null));
+        }
+        mSysUIComponent = builder
+                .setInputConsumerController(mWMComponent.getInputConsumerController())
+                .setShellTaskOrganizer(mWMComponent.getShellTaskOrganizer())
                 .build();
+        if (initializeComponents) {
+            mSysUIComponent.init();
+        }
 
         // Every other part of our codebase currently relies on Dependency, so we
         // really need to ensure the Dependency gets initialized early on.
@@ -93,6 +123,16 @@
         dependency.start();
     }
 
+    /**
+     * Prepares the SysUIComponent builder before it is built.
+     * @param sysUIBuilder the builder provided by the root component's getSysUIComponent() method
+     * @param wm the built WMComponent from the root component's getWMComponent() method
+     */
+    protected SysUIComponent.Builder prepareSysUIComponentBuilder(
+            SysUIComponent.Builder sysUIBuilder, WMComponent wm) {
+        return sysUIBuilder;
+    }
+
     protected GlobalRootComponent buildGlobalRootComponent(Context context) {
         return DaggerGlobalRootComponent.builder()
                 .context(context)
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
index 68e404e..c6e5f09 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
@@ -16,11 +16,14 @@
 
 package com.android.systemui.accessibility;
 
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
+
 import android.annotation.NonNull;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.graphics.PixelFormat;
 import android.graphics.PointF;
+import android.os.Bundle;
 import android.provider.Settings;
 import android.util.MathUtils;
 import android.view.Gravity;
@@ -28,6 +31,9 @@
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 import android.widget.ImageView;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -41,11 +47,18 @@
  */
 class MagnificationModeSwitch {
 
-    private static final int DURATION_MS = 5000;
-    private static final int START_DELAY_MS = 3000;
-    private final Runnable mAnimationTask;
+    @VisibleForTesting
+    static final long FADING_ANIMATION_DURATION_MS = 300;
+    @VisibleForTesting
+    static final int DEFAULT_FADE_OUT_ANIMATION_DELAY_MS = 3000;
+    private int mUiTimeout;
+    private final Runnable mFadeInAnimationTask;
+    private final Runnable mFadeOutAnimationTask;
+    @VisibleForTesting
+    boolean mIsFadeOutAnimating = false;
 
     private final Context mContext;
+    private final AccessibilityManager mAccessibilityManager;
     private final WindowManager mWindowManager;
     private final ImageView mImageView;
     private final PointF mLastDown = new PointF();
@@ -63,6 +76,7 @@
     @VisibleForTesting
     MagnificationModeSwitch(Context context, @NonNull ImageView imageView) {
         mContext = context;
+        mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
         mWindowManager = (WindowManager) mContext.getSystemService(
                 Context.WINDOW_SERVICE);
         mParams = createLayoutParams();
@@ -71,14 +85,51 @@
         applyResourcesValues();
         mImageView.setImageResource(getIconResId(mMagnificationMode));
         mImageView.setOnTouchListener(this::onTouch);
+        mImageView.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+            @Override
+            public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+                super.onInitializeAccessibilityNodeInfo(host, info);
+                info.setStateDescription(formatStateDescription());
+                info.setContentDescription(mContext.getResources().getString(
+                        R.string.magnification_mode_switch_description));
+                final AccessibilityAction clickAction = new AccessibilityAction(
+                        AccessibilityAction.ACTION_CLICK.getId(), mContext.getResources().getString(
+                        R.string.magnification_mode_switch_click_label));
+                info.addAction(clickAction);
+                info.setClickable(true);
+            }
 
-        mAnimationTask = () -> {
+            @Override
+            public boolean performAccessibilityAction(View host, int action, Bundle args) {
+                if (action == AccessibilityAction.ACTION_CLICK.getId()) {
+                    handleSingleTap();
+                    return true;
+                }
+                return super.performAccessibilityAction(host, action, args);
+            }
+        });
+
+        mFadeInAnimationTask = () -> {
             mImageView.animate()
-                    .alpha(0f)
-                    .setDuration(DURATION_MS)
-                    .withEndAction(() -> removeButton())
+                    .alpha(1f)
+                    .setDuration(FADING_ANIMATION_DURATION_MS)
                     .start();
         };
+        mFadeOutAnimationTask = () -> {
+            mImageView.animate()
+                    .alpha(0f)
+                    .setDuration(FADING_ANIMATION_DURATION_MS)
+                    .withEndAction(() -> removeButton())
+                    .start();
+            mIsFadeOutAnimating = true;
+        };
+    }
+
+    private CharSequence formatStateDescription() {
+        final int stringId = mMagnificationMode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW
+                ? R.string.magnification_mode_switch_state_window
+                : R.string.magnification_mode_switch_state_full_screen;
+        return mContext.getResources().getString(stringId);
     }
 
     private void applyResourcesValues() {
@@ -93,7 +144,6 @@
         }
         switch (event.getAction()) {
             case MotionEvent.ACTION_DOWN:
-                mImageView.setAlpha(1.0f);
                 mImageView.animate().cancel();
                 mLastDown.set(event.getRawX(), event.getRawY());
                 mLastDrag.set(event.getRawX(), event.getRawY());
@@ -134,9 +184,13 @@
         if (!mIsVisible) {
             return;
         }
-        mImageView.animate().cancel();
-        mWindowManager.removeView(mImageView);
         // Reset button status.
+        mImageView.removeCallbacks(mFadeInAnimationTask);
+        mImageView.removeCallbacks(mFadeOutAnimationTask);
+        mImageView.animate().cancel();
+        mIsFadeOutAnimating = false;
+        mImageView.setAlpha(0f);
+        mWindowManager.removeView(mImageView);
         mIsVisible = false;
         mParams.x = 0;
         mParams.y = 0;
@@ -150,14 +204,19 @@
         if (!mIsVisible) {
             mWindowManager.addView(mImageView, mParams);
             mIsVisible = true;
+            mImageView.postOnAnimation(mFadeInAnimationTask);
+            mUiTimeout = mAccessibilityManager.getRecommendedTimeoutMillis(
+                    DEFAULT_FADE_OUT_ANIMATION_DELAY_MS,
+                    AccessibilityManager.FLAG_CONTENT_ICONS
+                            | AccessibilityManager.FLAG_CONTENT_CONTROLS);
         }
-        mImageView.setAlpha(1.0f);
-        // TODO(b/143852371): use accessibility timeout as a delay.
-        // Dismiss the magnification switch button after the button is displayed for a period of
-        // time.
-        mImageView.animate().cancel();
-        mImageView.removeCallbacks(mAnimationTask);
-        mImageView.postDelayed(mAnimationTask, START_DELAY_MS);
+        if (mIsFadeOutAnimating) {
+            mImageView.animate().cancel();
+            mImageView.setAlpha(1f);
+        }
+        // Refresh the time slot of the fade-out task whenever this method is called.
+        mImageView.removeCallbacks(mFadeOutAnimationTask);
+        mImageView.postOnAnimationDelayed(mFadeOutAnimationTask, mUiTimeout);
     }
 
     void onConfigurationChanged(int configDiff) {
@@ -187,6 +246,7 @@
         imageView.setClickable(true);
         imageView.setFocusable(true);
         imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
+        imageView.setAlpha(0f);
         return imageView;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index a705ec7..e9e453b 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -72,8 +72,7 @@
         super(context);
         mHandler = mainHandler;
         mLastConfiguration = new Configuration(context.getResources().getConfiguration());
-        mAccessibilityManager = (AccessibilityManager) mContext.getSystemService(
-                Context.ACCESSIBILITY_SERVICE);
+        mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
         mCommandQueue = commandQueue;
         mModeSwitchesController = modeSwitchesController;
         final WindowMagnificationController controller = new WindowMagnificationController(mContext,
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index c3474bb..340ca04 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -249,8 +249,8 @@
         if ((configDiff & ActivityInfo.CONFIG_DENSITY) != 0) {
             updateDimensions();
             if (isWindowVisible()) {
-                mWm.removeView(mMirrorView);
-                createMirrorWindow();
+                deleteWindowMagnification();
+                enableWindowMagnification(Float.NaN, Float.NaN, Float.NaN);
             }
         } else if ((configDiff & ActivityInfo.CONFIG_ORIENTATION) != 0) {
             onRotate();
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
index 02a672b..c1c2de1 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
@@ -47,6 +47,7 @@
 import com.android.systemui.shared.system.PackageManagerWrapper;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.systemui.statusbar.StatusBarState;
 
 import java.io.PrintWriter;
@@ -171,6 +172,7 @@
     private final DeviceConfigHelper mDeviceConfigHelper;
     private final Lazy<StatusBarStateController> mStatusBarStateController;
     private final Lazy<ActivityManagerWrapper> mActivityManagerWrapper;
+    private final Lazy<TaskStackChangeListeners> mTaskStackChangeListeners;
     private final Lazy<OverviewProxyService> mOverviewProxyService;
     private final Lazy<SysUiState> mSysUiFlagContainer;
     private final Lazy<WakefulnessLifecycle> mWakefulnessLifecycle;
@@ -207,6 +209,7 @@
             DeviceConfigHelper deviceConfigHelper,
             Lazy<StatusBarStateController> statusBarStateController,
             Lazy<ActivityManagerWrapper> activityManagerWrapper,
+            Lazy<TaskStackChangeListeners> taskStackChangeListeners,
             Lazy<OverviewProxyService> overviewProxyService,
             Lazy<SysUiState> sysUiFlagContainer,
             Lazy<WakefulnessLifecycle> wakefulnessLifecycle,
@@ -218,6 +221,7 @@
         mDeviceConfigHelper = deviceConfigHelper;
         mStatusBarStateController = statusBarStateController;
         mActivityManagerWrapper = activityManagerWrapper;
+        mTaskStackChangeListeners = taskStackChangeListeners;
         mOverviewProxyService = overviewProxyService;
         mSysUiFlagContainer = sysUiFlagContainer;
         mWakefulnessLifecycle = wakefulnessLifecycle;
@@ -245,7 +249,7 @@
         ActivityManager.RunningTaskInfo runningTaskInfo =
                 mActivityManagerWrapper.get().getRunningTask();
         mRunningTaskId = runningTaskInfo == null ? 0 : runningTaskInfo.taskId;
-        mActivityManagerWrapper.get().registerTaskStackListener(mTaskStackChangeListener);
+        mTaskStackChangeListeners.get().registerTaskStackListener(mTaskStackChangeListener);
         mOverviewProxyService.get().addCallback(mOverviewProxyListener);
         mSysUiFlagContainer.get().addCallback(mSysUiStateCallback);
         mIsAwake = mWakefulnessLifecycle.get().getWakefulness()
@@ -300,7 +304,7 @@
             mContext = null;
         }
         mStatusBarStateController.get().removeCallback(mStatusBarStateListener);
-        mActivityManagerWrapper.get().unregisterTaskStackListener(mTaskStackChangeListener);
+        mTaskStackChangeListeners.get().unregisterTaskStackListener(mTaskStackChangeListener);
         mOverviewProxyService.get().removeCallback(mOverviewProxyListener);
         mSysUiFlagContainer.get().removeCallback(mSysUiStateCallback);
         mWakefulnessLifecycle.get().removeObserver(mWakefulnessLifecycleObserver);
diff --git a/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java b/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
index 50d559b..61951cc 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
@@ -35,6 +35,7 @@
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.PackageManagerWrapper;
 import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.StatusBar;
 
@@ -82,7 +83,6 @@
         mStatusBarOptionalLazy = statusBarOptionalLazy;
         mStatusBarStateController = Dependency.get(StatusBarStateController.class);
 
-        ActivityManagerWrapper activityManagerWrapper = ActivityManagerWrapper.getInstance();
         mDefaultHome = getCurrentDefaultHome();
         bootCompleteCache.addListener(() -> mDefaultHome = getCurrentDefaultHome());
         IntentFilter intentFilter = new IntentFilter();
@@ -95,12 +95,13 @@
                 mDefaultHome = getCurrentDefaultHome();
             }
         }, intentFilter);
-        mLauncherShowing = isLauncherShowing(activityManagerWrapper.getRunningTask());
-        activityManagerWrapper.registerTaskStackListener(new TaskStackChangeListener() {
-            @Override
-            public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
-                mLauncherShowing = isLauncherShowing(taskInfo);
-            }
+        mLauncherShowing = isLauncherShowing(ActivityManagerWrapper.getInstance().getRunningTask());
+        TaskStackChangeListeners.getInstance().registerTaskStackListener(
+                new TaskStackChangeListener() {
+                    @Override
+                    public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
+                        mLauncherShowing = isLauncherShowing(taskInfo);
+                    }
         });
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index c409d87..e3b0049 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -63,7 +63,7 @@
  * Note that the current architecture is designed so that a single {@link UdfpsController}
  * controls/manages all UDFPS sensors. In other words, a single controller is registered with
  * {@link com.android.server.biometrics.sensors.fingerprint.FingerprintService}, and interfaces such
- * as {@link FingerprintManager#onFingerDown(int, int, int, float, float)} or
+ * as {@link FingerprintManager#onPointerDown(int, int, int, float, float)} or
  * {@link IUdfpsOverlayController#showUdfpsOverlay(int)}should all have
  * {@code sensorId} parameters.
  */
@@ -374,7 +374,7 @@
                 fw.write(mHbmEnableCommand);
                 fw.close();
             }
-            mFingerprintManager.onFingerDown(mUdfpsSensorId, x, y, minor, major);
+            mFingerprintManager.onPointerDown(mUdfpsSensorId, x, y, minor, major);
         } catch (IOException e) {
             mView.hideScrimAndDot();
             Log.e(TAG, "onFingerDown | failed to enable HBM: " + e.getMessage());
@@ -382,7 +382,7 @@
     }
 
     private void onFingerUp() {
-        mFingerprintManager.onFingerUp(mUdfpsSensorId);
+        mFingerprintManager.onPointerUp(mUdfpsSensorId);
         // Hiding the scrim before disabling HBM results in less noticeable flicker.
         mView.hideScrimAndDot();
         if (mHbmSupported) {
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
index 83de324..eea168a 100644
--- a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
@@ -112,7 +112,7 @@
      * @param executor An executor to dispatch [BroadcastReceiver.onReceive]. Pass null to use an
      *                 executor in the main thread (default).
      * @param user A user handle to determine which broadcast should be dispatched to this receiver.
-     *             By default, it is the user of the context (system user in SystemUI).
+     *             Pass `null` to use the user of the context (system user in SystemUI).
      * @throws IllegalArgumentException if the filter has other constraints that are not actions or
      *                                  categories or the filter has no actions.
      */
@@ -120,13 +120,17 @@
     open fun registerReceiver(
         receiver: BroadcastReceiver,
         filter: IntentFilter,
-        executor: Executor? = context.mainExecutor,
-        user: UserHandle = context.user
+        executor: Executor? = null,
+        user: UserHandle? = null
     ) {
         checkFilter(filter)
         this.handler
-                .obtainMessage(MSG_ADD_RECEIVER,
-                        ReceiverData(receiver, filter, executor ?: context.mainExecutor, user))
+                .obtainMessage(MSG_ADD_RECEIVER, ReceiverData(
+                        receiver,
+                        filter,
+                        executor ?: context.mainExecutor,
+                        user ?: context.user
+                ))
                 .sendToTarget()
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index 5b85208..1891daf 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -15,8 +15,8 @@
  */
 package com.android.systemui.bubbles;
 
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.os.AsyncTask.Status.FINISHED;
-import static android.view.Display.INVALID_DISPLAY;
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
 
@@ -25,6 +25,7 @@
 import android.annotation.Nullable;
 import android.app.Notification;
 import android.app.PendingIntent;
+import android.app.Person;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
@@ -35,16 +36,18 @@
 import android.graphics.Path;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
+import android.os.Parcelable;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.InstanceId;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -169,11 +172,10 @@
     }
 
     @VisibleForTesting(visibility = PRIVATE)
-    Bubble(@NonNull final NotificationEntry e,
+    Bubble(@NonNull final BubbleEntry entry,
             @Nullable final BubbleController.NotificationSuppressionChangedListener listener,
             final BubbleController.PendingIntentCanceledListener intentCancelListener) {
-        Objects.requireNonNull(e);
-        mKey = e.getKey();
+        mKey = entry.getKey();
         mSuppressionListener = listener;
         mIntentCancelListener = intent -> {
             if (mIntent != null) {
@@ -181,7 +183,7 @@
             }
             intentCancelListener.onPendingIntentCanceled(this);
         };
-        setEntry(e);
+        setEntry(entry);
     }
 
     @Override
@@ -254,7 +256,8 @@
     }
 
     /**
-     * Cleanup expanded view for bubbles going into overflow.
+     * Call this to clean up the task for the bubble. Ensure this is always called when done with
+     * the bubble.
      */
     void cleanupExpandedView() {
         if (mExpandedView != null) {
@@ -268,8 +271,7 @@
     }
 
     /**
-     * Call when the views should be removed, ensure this is called to clean up ActivityView
-     * content.
+     * Call when all the views should be removed/cleaned up.
      */
     void cleanupViews() {
         cleanupExpandedView();
@@ -294,8 +296,8 @@
     }
 
     /**
-     * Sets whether this bubble is considered visually interruptive. Normally pulled from the
-     * {@link NotificationEntry}, this method is purely for testing.
+     * Sets whether this bubble is considered visually interruptive. This method is purely for
+     * testing.
      */
     @VisibleForTesting
     void setVisuallyInterruptiveForTest(boolean visuallyInterruptive) {
@@ -388,30 +390,28 @@
     /**
      * Sets the entry associated with this bubble.
      */
-    void setEntry(@NonNull final NotificationEntry entry) {
+    void setEntry(@NonNull final BubbleEntry entry) {
         Objects.requireNonNull(entry);
-        Objects.requireNonNull(entry.getSbn());
-        mLastUpdated = entry.getSbn().getPostTime();
-        mIsBubble = entry.getSbn().getNotification().isBubbleNotification();
-        mPackageName = entry.getSbn().getPackageName();
-        mUser = entry.getSbn().getUser();
+        mLastUpdated = entry.getStatusBarNotification().getPostTime();
+        mIsBubble = entry.getStatusBarNotification().getNotification().isBubbleNotification();
+        mPackageName = entry.getStatusBarNotification().getPackageName();
+        mUser = entry.getStatusBarNotification().getUser();
         mTitle = getTitle(entry);
-        mIsClearable = entry.isClearable();
-        mShouldSuppressNotificationDot = entry.shouldSuppressNotificationDot();
-        mShouldSuppressNotificationList = entry.shouldSuppressNotificationList();
-        mShouldSuppressPeek = entry.shouldSuppressPeek();
-        mChannelId = entry.getSbn().getNotification().getChannelId();
-        mNotificationId = entry.getSbn().getId();
-        mAppUid = entry.getSbn().getUid();
-        mInstanceId = entry.getSbn().getInstanceId();
-        mFlyoutMessage = BubbleViewInfoTask.extractFlyoutMessage(entry);
-        mShortcutInfo = (entry.getRanking() != null ? entry.getRanking().getShortcutInfo() : null);
-        mMetadataShortcutId = (entry.getBubbleMetadata() != null
-                ? entry.getBubbleMetadata().getShortcutId() : null);
+        mChannelId = entry.getStatusBarNotification().getNotification().getChannelId();
+        mNotificationId = entry.getStatusBarNotification().getId();
+        mAppUid = entry.getStatusBarNotification().getUid();
+        mInstanceId = entry.getStatusBarNotification().getInstanceId();
+        mFlyoutMessage = extractFlyoutMessage(entry);
         if (entry.getRanking() != null) {
+            mShortcutInfo = entry.getRanking().getShortcutInfo();
             mIsVisuallyInterruptive = entry.getRanking().visuallyInterruptive();
+            if (entry.getRanking().getChannel() != null) {
+                mIsImportantConversation =
+                        entry.getRanking().getChannel().isImportantConversation();
+            }
         }
         if (entry.getBubbleMetadata() != null) {
+            mMetadataShortcutId = entry.getBubbleMetadata().getShortcutId();
             mFlags = entry.getBubbleMetadata().getFlags();
             mDesiredHeight = entry.getBubbleMetadata().getDesiredHeight();
             mDesiredHeightResId = entry.getBubbleMetadata().getDesiredHeightResId();
@@ -433,8 +433,11 @@
             }
             mDeleteIntent = entry.getBubbleMetadata().getDeleteIntent();
         }
-        mIsImportantConversation =
-                entry.getChannel() != null && entry.getChannel().isImportantConversation();
+
+        mIsClearable = entry.isClearable();
+        mShouldSuppressNotificationDot = entry.shouldSuppressNotificationDot();
+        mShouldSuppressNotificationList = entry.shouldSuppressNotificationList();
+        mShouldSuppressPeek = entry.shouldSuppressPeek();
     }
 
     @Nullable
@@ -465,14 +468,6 @@
         return mIntentActive;
     }
 
-    /**
-     * @return the display id of the virtual display on which bubble contents is drawn.
-     */
-    @Override
-    public int getDisplayId() {
-        return mExpandedView != null ? mExpandedView.getVirtualDisplayId() : INVALID_DISPLAY;
-    }
-
     public InstanceId getInstanceId() {
         return mInstanceId;
     }
@@ -487,6 +482,14 @@
     }
 
     /**
+     * @return the task id of the task in which bubble contents is drawn.
+     */
+    @Override
+    public int getTaskId() {
+        return mExpandedView != null ? mExpandedView.getTaskId() : INVALID_TASK_ID;
+    }
+
+    /**
      * Should be invoked whenever a Bubble is accessed (selected while expanded).
      */
     void markAsAccessedAt(long lastAccessedMillis) {
@@ -643,14 +646,14 @@
     }
 
     private int getDimenForPackageUser(Context context, int resId, String pkg, int userId) {
-        PackageManager pm = context.getPackageManager();
         Resources r;
         if (pkg != null) {
             try {
                 if (userId == UserHandle.USER_ALL) {
                     userId = UserHandle.USER_SYSTEM;
                 }
-                r = pm.getResourcesForApplicationAsUser(pkg, userId);
+                r = context.createContextAsUser(UserHandle.of(userId), /* flags */ 0)
+                        .getPackageManager().getResourcesForApplication(pkg);
                 return r.getDimensionPixelSize(resId);
             } catch (PackageManager.NameNotFoundException ex) {
                 // Uninstalled, don't care
@@ -735,9 +738,75 @@
     }
 
     @Nullable
-    private static String getTitle(@NonNull final NotificationEntry e) {
-        final CharSequence titleCharSeq = e.getSbn().getNotification().extras.getCharSequence(
-                Notification.EXTRA_TITLE);
+    private static String getTitle(@NonNull final BubbleEntry e) {
+        final CharSequence titleCharSeq = e.getStatusBarNotification()
+                .getNotification().extras.getCharSequence(Notification.EXTRA_TITLE);
         return titleCharSeq == null ? null : titleCharSeq.toString();
     }
+
+    /**
+     * Returns our best guess for the most relevant text summary of the latest update to this
+     * notification, based on its type. Returns null if there should not be an update message.
+     */
+    @NonNull
+    static Bubble.FlyoutMessage extractFlyoutMessage(BubbleEntry entry) {
+        Objects.requireNonNull(entry);
+        final Notification underlyingNotif = entry.getStatusBarNotification().getNotification();
+        final Class<? extends Notification.Style> style = underlyingNotif.getNotificationStyle();
+
+        Bubble.FlyoutMessage bubbleMessage = new Bubble.FlyoutMessage();
+        bubbleMessage.isGroupChat = underlyingNotif.extras.getBoolean(
+                Notification.EXTRA_IS_GROUP_CONVERSATION);
+        try {
+            if (Notification.BigTextStyle.class.equals(style)) {
+                // Return the big text, it is big so probably important. If it's not there use the
+                // normal text.
+                CharSequence bigText =
+                        underlyingNotif.extras.getCharSequence(Notification.EXTRA_BIG_TEXT);
+                bubbleMessage.message = !TextUtils.isEmpty(bigText)
+                        ? bigText
+                        : underlyingNotif.extras.getCharSequence(Notification.EXTRA_TEXT);
+                return bubbleMessage;
+            } else if (Notification.MessagingStyle.class.equals(style)) {
+                final List<Notification.MessagingStyle.Message> messages =
+                        Notification.MessagingStyle.Message.getMessagesFromBundleArray(
+                                (Parcelable[]) underlyingNotif.extras.get(
+                                        Notification.EXTRA_MESSAGES));
+
+                final Notification.MessagingStyle.Message latestMessage =
+                        Notification.MessagingStyle.findLatestIncomingMessage(messages);
+                if (latestMessage != null) {
+                    bubbleMessage.message = latestMessage.getText();
+                    Person sender = latestMessage.getSenderPerson();
+                    bubbleMessage.senderName = sender != null ? sender.getName() : null;
+                    bubbleMessage.senderAvatar = null;
+                    bubbleMessage.senderIcon = sender != null ? sender.getIcon() : null;
+                    return bubbleMessage;
+                }
+            } else if (Notification.InboxStyle.class.equals(style)) {
+                CharSequence[] lines =
+                        underlyingNotif.extras.getCharSequenceArray(Notification.EXTRA_TEXT_LINES);
+
+                // Return the last line since it should be the most recent.
+                if (lines != null && lines.length > 0) {
+                    bubbleMessage.message = lines[lines.length - 1];
+                    return bubbleMessage;
+                }
+            } else if (Notification.MediaStyle.class.equals(style)) {
+                // Return nothing, media updates aren't typically useful as a text update.
+                return bubbleMessage;
+            } else {
+                // Default to text extra.
+                bubbleMessage.message =
+                        underlyingNotif.extras.getCharSequence(Notification.EXTRA_TEXT);
+                return bubbleMessage;
+            }
+        } catch (ClassCastException | NullPointerException | ArrayIndexOutOfBoundsException e) {
+            // No use crashing, we'll just return null and the caller will assume there's no update
+            // message.
+            e.printStackTrace();
+        }
+
+        return bubbleMessage;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 90b64ea..0c3dc82 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.bubbles;
 
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.Notification.FLAG_BUBBLE;
 import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
 import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED;
@@ -27,8 +28,6 @@
 import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
 import static android.service.notification.NotificationStats.DISMISSAL_BUBBLE;
 import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Display.INVALID_DISPLAY;
 import static android.view.View.INVISIBLE;
 import static android.view.View.VISIBLE;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
@@ -46,6 +45,7 @@
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityTaskManager;
 import android.app.INotificationManager;
 import android.app.Notification;
 import android.app.NotificationChannel;
@@ -70,7 +70,6 @@
 import android.util.Log;
 import android.util.Pair;
 import android.util.SparseSetArray;
-import android.view.Display;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManager;
@@ -80,15 +79,18 @@
 import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.UiEventLogger;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.systemui.Dumpable;
 import com.android.systemui.bubbles.dagger.BubbleModule;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationRemoveInterceptor;
@@ -111,6 +113,7 @@
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.WindowManagerShellWrapper;
 import com.android.wm.shell.common.FloatingContentCoordinator;
 import com.android.wm.shell.pip.PinnedStackListenerForwarder;
@@ -168,12 +171,13 @@
     private final ShadeController mShadeController;
     private final FloatingContentCoordinator mFloatingContentCoordinator;
     private final BubbleDataRepository mDataRepository;
-    private BubbleLogger mLogger = new BubbleLoggerImpl();
-
+    private BubbleLogger mLogger;
+    private final Handler mMainHandler;
     private BubbleData mBubbleData;
     private ScrimView mBubbleScrim;
     @Nullable private BubbleStackView mStackView;
     private BubbleIconFactory mBubbleIconFactory;
+    private BubblePositioner mBubblePositioner;
 
     /**
      * The relative position of the stack when we removed it and nulled it out. If the stack is
@@ -231,11 +235,18 @@
      */
     private int mDensityDpi = Configuration.DENSITY_DPI_UNDEFINED;
 
+    /**
+     * Last known font scale, used to detect font size changes in {@link #onConfigChanged}.
+     */
+    private float mFontScale = 0;
+
     /** Last known direction, used to detect layout direction changes @link #onConfigChanged}. */
     private int mLayoutDirection = View.LAYOUT_DIRECTION_UNDEFINED;
 
     private boolean mInflateSynchronously;
 
+    private ShellTaskOrganizer mTaskOrganizer;
+
     // TODO (b/145659174): allow for multiple callbacks to support the "shadow" new notif pipeline
     private final List<NotifCallback> mCallbacks = new ArrayList<>();
 
@@ -345,7 +356,46 @@
     /**
      * Injected constructor. See {@link BubbleModule}.
      */
-    public BubbleController(Context context,
+    public static BubbleController create(Context context,
+            NotificationShadeWindowController notificationShadeWindowController,
+            StatusBarStateController statusBarStateController,
+            ShadeController shadeController,
+            @Nullable BubbleStackView.SurfaceSynchronizer synchronizer,
+            ConfigurationController configurationController,
+            NotificationInterruptStateProvider interruptionStateProvider,
+            ZenModeController zenModeController,
+            NotificationLockscreenUserManager notifUserManager,
+            NotificationGroupManagerLegacy groupManager,
+            NotificationEntryManager entryManager,
+            NotifPipeline notifPipeline,
+            FeatureFlags featureFlags,
+            DumpManager dumpManager,
+            FloatingContentCoordinator floatingContentCoordinator,
+            SysUiState sysUiState,
+            INotificationManager notificationManager,
+            @Nullable IStatusBarService statusBarService,
+            WindowManager windowManager,
+            WindowManagerShellWrapper windowManagerShellWrapper,
+            LauncherApps launcherApps,
+            UiEventLogger uiEventLogger,
+            @Main Handler mainHandler,
+            ShellTaskOrganizer organizer) {
+        BubbleLogger logger = new BubbleLogger(uiEventLogger);
+        return new BubbleController(context, notificationShadeWindowController,
+                statusBarStateController, shadeController, new BubbleData(context, logger),
+                synchronizer, configurationController, interruptionStateProvider, zenModeController,
+                notifUserManager, groupManager, entryManager, notifPipeline, featureFlags,
+                dumpManager, floatingContentCoordinator,
+                new BubbleDataRepository(context, launcherApps), sysUiState, notificationManager,
+                statusBarService, windowManager, windowManagerShellWrapper, launcherApps, logger,
+                mainHandler, organizer, new BubblePositioner(context, windowManager));
+    }
+
+    /**
+     * Testing constructor.
+     */
+    @VisibleForTesting
+    BubbleController(Context context,
             NotificationShadeWindowController notificationShadeWindowController,
             StatusBarStateController statusBarStateController,
             ShadeController shadeController,
@@ -367,7 +417,11 @@
             @Nullable IStatusBarService statusBarService,
             WindowManager windowManager,
             WindowManagerShellWrapper windowManagerShellWrapper,
-            LauncherApps launcherApps) {
+            LauncherApps launcherApps,
+            BubbleLogger bubbleLogger,
+            Handler mainHandler,
+            ShellTaskOrganizer organizer,
+            BubblePositioner positioner) {
         dumpManager.registerDumpable(TAG, this);
         mContext = context;
         mShadeController = shadeController;
@@ -377,6 +431,8 @@
         mFloatingContentCoordinator = floatingContentCoordinator;
         mDataRepository = dataRepository;
         mINotificationManager = notificationManager;
+        mLogger = bubbleLogger;
+        mMainHandler = mainHandler;
         mZenModeController.addCallback(new ZenModeController.Callback() {
             @Override
             public void onZenChanged(int zen) {
@@ -440,7 +496,7 @@
         statusBarStateController.addCallback(mStatusBarStateListener);
 
         mTaskStackListener = new BubbleTaskStackListener();
-        ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
+        TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
 
         try {
             windowManagerShellWrapper.addPinnedStackListener(new BubblesImeListener());
@@ -475,6 +531,8 @@
                 });
 
         mBubbleIconFactory = new BubbleIconFactory(context);
+        mTaskOrganizer = organizer;
+        mBubblePositioner = positioner;
 
         launcherApps.registerCallback(new LauncherApps.Callback() {
             @Override
@@ -537,6 +595,12 @@
         }
     }
 
+    private void onBubbleExpandChanged(boolean shouldExpand) {
+        mSysUiState
+                .setFlag(QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED, shouldExpand)
+                .commitUpdate(mContext.getDisplayId());
+    }
+
     private void setupNEM() {
         mNotificationEntryManager.addNotificationEntryListener(
                 new NotificationEntryListener() {
@@ -743,6 +807,16 @@
         return mBubbleData.getOverflowBubbles();
     }
 
+    @Override
+    public ShellTaskOrganizer getTaskOrganizer() {
+        return mTaskOrganizer;
+    }
+
+    @Override
+    public BubblePositioner getPositioner() {
+        return mBubblePositioner;
+    }
+
     /**
      * BubbleStackView is lazily created by this method the first time a Bubble is added. This
      * method initializes the stack view and adds it to the StatusBar just above the scrim.
@@ -751,10 +825,11 @@
         if (mStackView == null) {
             mStackView = new BubbleStackView(
                     mContext, mBubbleData, mSurfaceSynchronizer, mFloatingContentCoordinator,
-                    mSysUiState, this::onAllBubblesAnimatedOut, this::onImeVisibilityChanged,
-                    this::hideCurrentInputMethod);
+                    this::onAllBubblesAnimatedOut, this::onImeVisibilityChanged,
+                    this::hideCurrentInputMethod, this::onBubbleExpandChanged, mBubblePositioner);
             mStackView.setStackStartPosition(mPositionFromRemovedStack);
             mStackView.addView(mBubbleScrim);
+            mStackView.onOrientationChanged();
             if (mExpandListener != null) {
                 mStackView.setExpandListener(mExpandListener);
             }
@@ -785,9 +860,8 @@
                 ViewGroup.LayoutParams.MATCH_PARENT,
                 ViewGroup.LayoutParams.MATCH_PARENT,
                 WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
-                // Start not focusable - we'll become focusable when expanded so the ActivityView
-                // can use the IME.
                 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                     | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
                 PixelFormat.TRANSLUCENT);
 
@@ -803,16 +877,13 @@
             mAddedToWindowManager = true;
             mWindowManager.addView(mStackView, mWmLayoutParams);
         } catch (IllegalStateException e) {
-            // This means the stack has already been added. This shouldn't happen, since we keep
-            // track of that, but just in case, update the previously added view's layout params.
+            // This means the stack has already been added. This shouldn't happen...
             e.printStackTrace();
-            updateWmFlags();
         }
     }
 
     private void onImeVisibilityChanged(boolean imeVisible) {
         mImeVisible = imeVisible;
-        updateWmFlags();
     }
 
     /** Removes the BubbleStackView from the WindowManager if it's there. */
@@ -839,35 +910,6 @@
     }
 
     /**
-     * Updates the BubbleStackView's WindowManager.LayoutParams, and updates the WindowManager with
-     * the new params if the stack has been added.
-     */
-    private void updateWmFlags() {
-        if (mStackView == null) {
-            return;
-        }
-        if (isStackExpanded() && !mImeVisible) {
-            // If we're expanded, and the IME isn't visible, we want to be focusable. This ensures
-            // that any taps within Bubbles (including on the ActivityView) results in Bubbles
-            // receiving focus and clearing it from any other windows that might have it.
-            mWmLayoutParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-        } else {
-            // If we're collapsed, we don't want to be focusable since tapping on the stack would
-            // steal focus from apps. We also don't want to be focusable if the IME is visible,
-            mWmLayoutParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-        }
-
-        if (mAddedToWindowManager) {
-            try {
-                mWindowManager.updateViewLayout(mStackView, mWmLayoutParams);
-            } catch (IllegalArgumentException e) {
-                // If the stack is somehow not there, ignore the attempt to update it.
-                e.printStackTrace();
-            }
-        }
-    }
-
-    /**
      * Called by the BubbleStackView and whenever all bubbles have animated out, and none have been
      * added in the meantime.
      */
@@ -945,16 +987,24 @@
 
     @Override
     public void onConfigChanged(Configuration newConfig) {
+        if (mBubblePositioner != null) {
+            // This doesn't trigger any changes, always update it
+            mBubblePositioner.update(newConfig.orientation);
+        }
         if (mStackView != null && newConfig != null) {
             if (newConfig.orientation != mOrientation) {
                 mOrientation = newConfig.orientation;
-                mStackView.onOrientationChanged(newConfig.orientation);
+                mStackView.onOrientationChanged();
             }
             if (newConfig.densityDpi != mDensityDpi) {
                 mDensityDpi = newConfig.densityDpi;
                 mBubbleIconFactory = new BubbleIconFactory(mContext);
                 mStackView.onDisplaySizeChanged();
             }
+            if (newConfig.fontScale != mFontScale) {
+                mFontScale = newConfig.fontScale;
+                mStackView.updateFlyout(mFontScale);
+            }
             if (newConfig.getLayoutDirection() != mLayoutDirection) {
                 mLayoutDirection = newConfig.getLayoutDirection();
                 mStackView.onLayoutDirectionChanged(mLayoutDirection);
@@ -971,8 +1021,6 @@
             if (listener != null) {
                 listener.onBubbleExpandChanged(isExpanding, key);
             }
-
-            updateWmFlags();
         });
         if (mStackView != null) {
             mStackView.setExpandListener(mExpandListener);
@@ -1016,7 +1064,7 @@
     @Override
     public boolean isBubbleExpanded(NotificationEntry entry) {
         return isStackExpanded() && mBubbleData != null && mBubbleData.getSelectedBubble() != null
-                && mBubbleData.getSelectedBubble().getKey().equals(entry.getKey()) ? true : false;
+                && mBubbleData.getSelectedBubble().getKey().equals(entry.getKey());
     }
 
     @Override
@@ -1071,13 +1119,6 @@
         }
     }
 
-    @Override
-    public void performBackPressIfNeeded() {
-        if (mStackView != null) {
-            mStackView.performBackPressIfNeeded();
-        }
-    }
-
     /**
      * Adds or updates a bubble associated with the provided notification entry.
      *
@@ -1120,9 +1161,10 @@
                 && mBubbleData.hasOverflowBubbleWithKey(notif.getKey())) {
             // Update the bubble but don't promote it out of overflow
             Bubble b = mBubbleData.getOverflowBubbleWithKey(notif.getKey());
-            b.setEntry(notif);
+            b.setEntry(notifToBubbleEntry(notif));
         } else {
-            Bubble bubble = mBubbleData.getOrCreateBubble(notif, null /* persistedBubble */);
+            Bubble bubble = mBubbleData.getOrCreateBubble(
+                    notifToBubbleEntry(notif), null /* persistedBubble */);
             inflateAndAdd(bubble, suppressFlyout, showInShade);
         }
     }
@@ -1208,8 +1250,7 @@
             mBubbleData.removeSuppressedSummary(groupKey);
 
             // Remove any associated bubble children with the summary
-            final List<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(
-                    groupKey, mNotificationEntryManager);
+            final List<Bubble> bubbleChildren = getBubblesInGroup(groupKey);
             for (int i = 0; i < bubbleChildren.size(); i++) {
                 removeBubble(bubbleChildren.get(i).getKey(), DISMISS_GROUP_CANCELLED);
             }
@@ -1255,6 +1296,25 @@
         }
     }
 
+    /**
+     * Retrieves any bubbles that are part of the notification group represented by the provided
+     * group key.
+     */
+    private ArrayList<Bubble> getBubblesInGroup(@Nullable String groupKey) {
+        ArrayList<Bubble> bubbleChildren = new ArrayList<>();
+        if (groupKey == null) {
+            return bubbleChildren;
+        }
+        for (Bubble bubble : mBubbleData.getActiveBubbles()) {
+            final NotificationEntry entry =
+                    mNotificationEntryManager.getPendingOrActiveNotif(bubble.getKey());
+            if (entry != null && groupKey.equals(entry.getSbn().getGroupKey())) {
+                bubbleChildren.add(bubble);
+            }
+        }
+        return bubbleChildren;
+    }
+
     private void setIsBubble(@NonNull final NotificationEntry entry, final boolean isBubble,
             final boolean autoExpand) {
         Objects.requireNonNull(entry);
@@ -1365,8 +1425,7 @@
                 }
                 if (entry != null) {
                     final String groupKey = entry.getSbn().getGroupKey();
-                    if (mBubbleData.getBubblesInGroup(
-                            groupKey, mNotificationEntryManager).isEmpty()) {
+                    if (getBubblesInGroup(groupKey).isEmpty()) {
                         // Time to potentially remove the summary
                         for (NotifCallback cb : mCallbacks) {
                             cb.maybeCancelSummary(entry);
@@ -1449,8 +1508,7 @@
         }
 
         String groupKey = entry.getSbn().getGroupKey();
-        ArrayList<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(
-                groupKey, mNotificationEntryManager);
+        ArrayList<Bubble> bubbleChildren = getBubblesInGroup(groupKey);
         boolean isSuppressedSummary = (mBubbleData.isSummarySuppressed(groupKey)
                 && mBubbleData.getSummaryKey(groupKey).equals(entry.getKey()));
         boolean isSummary = entry.getSbn().getNotification().isGroupSummary();
@@ -1541,19 +1599,21 @@
         mStackView.updateContentDescription();
     }
 
-    @Override
-    public int getExpandedDisplayId(Context context) {
+    /**
+     * The task id of the expanded view, if the stack is expanded and not occluded by the
+     * status bar, otherwise returns {@link ActivityTaskManager#INVALID_TASK_ID}.
+     */
+    private int getExpandedTaskId() {
         if (mStackView == null) {
-            return INVALID_DISPLAY;
+            return INVALID_TASK_ID;
         }
-        final boolean defaultDisplay = context.getDisplay() != null
-                && context.getDisplay().getDisplayId() == DEFAULT_DISPLAY;
         final BubbleViewProvider expandedViewProvider = mStackView.getExpandedBubble();
-        if (defaultDisplay && expandedViewProvider != null && isStackExpanded()
+        if (expandedViewProvider != null && isStackExpanded()
+                && !mStackView.isExpansionAnimating()
                 && !mNotificationShadeWindowController.getPanelExpanded()) {
-            return expandedViewProvider.getDisplayId();
+            return expandedViewProvider.getTaskId();
         }
-        return INVALID_DISPLAY;
+        return INVALID_TASK_ID;
     }
 
     @VisibleForTesting
@@ -1587,18 +1647,17 @@
 
         @Override
         public void onTaskMovedToFront(RunningTaskInfo taskInfo) {
-            if (mStackView != null && taskInfo.displayId == Display.DEFAULT_DISPLAY) {
-                if (!mStackView.isExpansionAnimating()) {
-                    mBubbleData.setExpanded(false);
-                }
+            int expandedId = getExpandedTaskId();
+            if (expandedId != INVALID_TASK_ID && expandedId != taskInfo.taskId) {
+                mBubbleData.setExpanded(false);
             }
         }
 
         @Override
-        public void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible,
+        public void onActivityRestartAttempt(RunningTaskInfo taskInfo, boolean homeTaskVisible,
                 boolean clearedTask, boolean wasVisible) {
             for (Bubble b : mBubbleData.getBubbles()) {
-                if (b.getDisplayId() == task.displayId) {
+                if (taskInfo.taskId == b.getTaskId()) {
                     mBubbleData.setSelectedBubble(b);
                     mBubbleData.setExpanded(true);
                     return;
@@ -1606,43 +1665,6 @@
             }
         }
 
-        @Override
-        public void onActivityLaunchOnSecondaryDisplayRerouted() {
-            if (mStackView != null) {
-                mBubbleData.setExpanded(false);
-            }
-        }
-
-        @Override
-        public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
-            if (mStackView != null && taskInfo.displayId == getExpandedDisplayId(mContext)) {
-                if (mImeVisible) {
-                    hideCurrentInputMethod();
-                } else {
-                    mBubbleData.setExpanded(false);
-                }
-            }
-        }
-
-        @Override
-        public void onSingleTaskDisplayDrawn(int displayId) {
-            if (mStackView == null) {
-                return;
-            }
-            mStackView.showExpandedViewContents(displayId);
-        }
-
-        @Override
-        public void onSingleTaskDisplayEmpty(int displayId) {
-            final BubbleViewProvider expandedBubble = mStackView != null
-                    ? mStackView.getExpandedBubble()
-                    : null;
-            int expandedId = expandedBubble != null ? expandedBubble.getDisplayId() : -1;
-            if (mStackView != null && mStackView.isExpanded() && expandedId == displayId) {
-                mBubbleData.setExpanded(false);
-            }
-            mBubbleData.notifyDisplayEmpty(displayId);
-        }
     }
 
     /**
@@ -1686,6 +1708,7 @@
     }
 
     /** PinnedStackListener that dispatches IME visibility updates to the stack. */
+    //TODO(b/170442945): Better way to do this / insets listener?
     private class BubblesImeListener extends PinnedStackListenerForwarder.PinnedStackListener {
         @Override
         public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
@@ -1694,4 +1717,10 @@
             }
         }
     }
+
+    static BubbleEntry notifToBubbleEntry(NotificationEntry e) {
+        return new BubbleEntry(e.getSbn(), e.getRanking(), e.isClearable(),
+                e.shouldSuppressNotificationDot(), e.shouldSuppressNotificationList(),
+                e.shouldSuppressPeek());
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index 2c3cb5f..b4626f2 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -32,12 +32,9 @@
 import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.systemui.R;
 import com.android.systemui.bubbles.BubbleController.DismissReason;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.shared.system.SysUiStatsLog;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -52,15 +49,12 @@
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 
-import javax.inject.Inject;
-
 /**
  * Keeps track of active bubbles.
  */
-@SysUISingleton
 public class BubbleData {
 
-    private BubbleLoggerImpl mLogger = new BubbleLoggerImpl();
+    private BubbleLogger mLogger;
 
     private int mCurrentUserId;
 
@@ -154,14 +148,14 @@
      * associated with it). This list is used to check if the summary should be hidden from the
      * shade.
      *
-     * Key: group key of the NotificationEntry
-     * Value: key of the NotificationEntry
+     * Key: group key of the notification
+     * Value: key of the notification
      */
     private HashMap<String, String> mSuppressedGroupKeys = new HashMap<>();
 
-    @Inject
-    public BubbleData(Context context) {
+    public BubbleData(Context context, BubbleLogger bubbleLogger) {
         mContext = context;
+        mLogger = bubbleLogger;
         mBubbles = new ArrayList<>();
         mOverflowBubbles = new ArrayList<>();
         mPendingBubbles = new HashMap<>();
@@ -205,6 +199,11 @@
         return mSelectedBubble;
     }
 
+    /** Return a read-only current active bubble lists. */
+    public List<Bubble> getActiveBubbles() {
+        return Collections.unmodifiableList(mBubbles);
+    }
+
     public void setExpanded(boolean expanded) {
         if (DEBUG_BUBBLE_DATA) {
             Log.d(TAG, "setExpanded: " + expanded);
@@ -235,8 +234,8 @@
      * @param persistedBubble The bubble to use, only non-null if it's a bubble being promoted from
      *              the overflow that was persisted over reboot.
      */
-    Bubble getOrCreateBubble(NotificationEntry entry, Bubble persistedBubble) {
-        String key = entry != null ? entry.getKey() : persistedBubble.getKey();
+    public Bubble getOrCreateBubble(BubbleEntry entry, Bubble persistedBubble) {
+        String key = persistedBubble != null ? persistedBubble.getKey() : entry.getKey();
         Bubble bubbleToReturn = getBubbleInStackWithKey(key);
 
         if (bubbleToReturn == null) {
@@ -266,7 +265,7 @@
     /**
      * When this method is called it is expected that all info in the bubble has completed loading.
      * @see Bubble#inflate(BubbleViewInfoTask.Callback, Context,
-     * BubbleStackView, BubbleIconFactory).
+     * BubbleStackView, BubbleIconFactory, boolean).
      */
     void notificationEntryUpdated(Bubble bubble, boolean suppressFlyout, boolean showInShade) {
         if (DEBUG_BUBBLE_DATA) {
@@ -329,7 +328,7 @@
      * Retrieves the notif entry key of the summary associated with the provided group key.
      *
      * @param groupKey the group to look up
-     * @return the key for the {@link NotificationEntry} that is the summary of this group.
+     * @return the key for the notification that is the summary of this group.
      */
     String getSummaryKey(String groupKey) {
         return mSuppressedGroupKeys.get(groupKey);
@@ -350,25 +349,6 @@
     }
 
     /**
-     * Retrieves any bubbles that are part of the notification group represented by the provided
-     * group key.
-     */
-    ArrayList<Bubble> getBubblesInGroup(@Nullable String groupKey, @NonNull
-            NotificationEntryManager nem) {
-        ArrayList<Bubble> bubbleChildren = new ArrayList<>();
-        if (groupKey == null) {
-            return bubbleChildren;
-        }
-        for (Bubble b : mBubbles) {
-            final NotificationEntry entry = nem.getPendingOrActiveNotif(b.getKey());
-            if (entry != null && groupKey.equals(entry.getSbn().getGroupKey())) {
-                bubbleChildren.add(b);
-            }
-        }
-        return bubbleChildren;
-    }
-
-    /**
      * Removes bubbles from the given package whose shortcut are not in the provided list of valid
      * shortcuts.
      */
@@ -571,22 +551,6 @@
         dispatchPendingChanges();
     }
 
-    /**
-     * Indicates that the provided display is no longer in use and should be cleaned up.
-     *
-     * @param displayId the id of the display to clean up.
-     */
-    void notifyDisplayEmpty(int displayId) {
-        for (Bubble b : mBubbles) {
-            if (b.getDisplayId() == displayId) {
-                if (b.getExpandedView() != null) {
-                    b.getExpandedView().notifyDisplayEmpty();
-                }
-                return;
-            }
-        }
-    }
-
     private void dispatchPendingChanges() {
         if (mListener != null && mStateChange.anythingChanged()) {
             mListener.applyUpdate(mStateChange);
@@ -637,12 +601,12 @@
      * @param normalX Normalized x position of the stack
      * @param normalY Normalized y position of the stack
      */
-     void logBubbleEvent(@Nullable BubbleViewProvider provider, int action, String packageName,
+    void logBubbleEvent(@Nullable BubbleViewProvider provider, int action, String packageName,
             int bubbleCount, int bubbleIndex, float normalX, float normalY) {
         if (provider == null) {
             mLogger.logStackUiChanged(packageName, action, bubbleCount, normalX, normalY);
         } else if (provider.getKey().equals(BubbleOverflow.KEY)) {
-            if (action == SysUiStatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED) {
+            if (action == FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED) {
                 mLogger.logShowOverflow(packageName, mCurrentUserId);
             }
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt
index f129d31..2ab9e87 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt
@@ -17,6 +17,7 @@
 
 import android.annotation.SuppressLint
 import android.annotation.UserIdInt
+import android.content.Context
 import android.content.pm.LauncherApps
 import android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_CACHED
 import android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC
@@ -26,21 +27,16 @@
 import com.android.systemui.bubbles.storage.BubbleEntity
 import com.android.systemui.bubbles.storage.BubblePersistentRepository
 import com.android.systemui.bubbles.storage.BubbleVolatileRepository
-import com.android.systemui.dagger.SysUISingleton
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.cancelAndJoin
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.yield
-import javax.inject.Inject
 
-@SysUISingleton
-internal class BubbleDataRepository @Inject constructor(
-    private val volatileRepository: BubbleVolatileRepository,
-    private val persistentRepository: BubblePersistentRepository,
-    private val launcherApps: LauncherApps
-) {
+internal class BubbleDataRepository(context: Context, private val launcherApps: LauncherApps) {
+    private val volatileRepository = BubbleVolatileRepository(launcherApps)
+    private val persistentRepository = BubblePersistentRepository(context)
 
     private val ioScope = CoroutineScope(Dispatchers.IO)
     private val uiScope = CoroutineScope(Dispatchers.Main)
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleEntry.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleEntry.java
new file mode 100644
index 0000000..6a13025
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleEntry.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.Notification.BubbleMetadata;
+import android.app.NotificationManager.Policy;
+import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.StatusBarNotification;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * Represents a notification with needed data and flag for bubbles.
+ *
+ * @see Bubble
+ */
+public class BubbleEntry {
+
+    private StatusBarNotification mSbn;
+    private Ranking mRanking;
+
+    private boolean mIsClearable;
+    private boolean mShouldSuppressNotificationDot;
+    private boolean mShouldSuppressNotificationList;
+    private boolean mShouldSuppressPeek;
+
+    public BubbleEntry(@NonNull StatusBarNotification sbn,
+            Ranking ranking, boolean isClearable, boolean shouldSuppressNotificationDot,
+            boolean shouldSuppressNotificationList, boolean shouldSuppressPeek) {
+        mSbn = sbn;
+        mRanking = ranking;
+
+        mIsClearable = isClearable;
+        mShouldSuppressNotificationDot = shouldSuppressNotificationDot;
+        mShouldSuppressNotificationList = shouldSuppressNotificationList;
+        mShouldSuppressPeek = shouldSuppressPeek;
+    }
+
+    /** @return the {@link StatusBarNotification} for this entry. */
+    @NonNull
+    public StatusBarNotification getStatusBarNotification() {
+        return mSbn;
+    }
+
+    /** @return the {@link Ranking} for this entry. */
+    public Ranking getRanking() {
+        return mRanking;
+    }
+
+    /** @return the key in the {@link StatusBarNotification}. */
+    public String getKey() {
+        return mSbn.getKey();
+    }
+
+    /** @return the {@link BubbleMetadata} in the {@link StatusBarNotification}. */
+    @Nullable
+    public BubbleMetadata getBubbleMetadata() {
+        return getStatusBarNotification().getNotification().getBubbleMetadata();
+    }
+
+    /** @return true if this notification is clearable. */
+    public boolean isClearable() {
+        return mIsClearable;
+    }
+
+    /** @return true if {@link Policy#SUPPRESSED_EFFECT_BADGE} set for this notification. */
+    public boolean shouldSuppressNotificationDot() {
+        return mShouldSuppressNotificationDot;
+    }
+
+    /**
+     * @return true if {@link Policy#SUPPRESSED_EFFECT_NOTIFICATION_LIST}
+     * set for this notification.
+     */
+    public boolean shouldSuppressNotificationList() {
+        return mShouldSuppressNotificationList;
+    }
+
+    /** @return true if {@link Policy#SUPPRESSED_EFFECT_PEEK} set for this notification. */
+    public boolean shouldSuppressPeek() {
+        return mShouldSuppressPeek;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 83a816b..bc06020 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -16,27 +16,19 @@
 
 package com.android.systemui.bubbles;
 
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
-import static android.graphics.PixelFormat.TRANSPARENT;
-import static android.view.Display.INVALID_DISPLAY;
-import static android.view.InsetsState.ITYPE_IME;
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
-import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
 
 import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_EXPANDED_VIEW;
 import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
 import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.systemui.bubbles.BubbleOverflowActivity.EXTRA_BUBBLE_CONTROLLER;
 
 import android.annotation.NonNull;
 import android.annotation.SuppressLint;
-import android.app.ActivityManager;
 import android.app.ActivityOptions;
-import android.app.ActivityTaskManager;
-import android.app.ActivityView;
 import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
@@ -46,22 +38,15 @@
 import android.content.res.TypedArray;
 import android.graphics.Color;
 import android.graphics.Outline;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.drawable.ShapeDrawable;
-import android.hardware.display.VirtualDisplay;
-import android.os.Binder;
-import android.os.RemoteException;
+import android.os.Bundle;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.view.Gravity;
 import android.view.SurfaceControl;
-import android.view.SurfaceView;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewOutlineProvider;
-import android.view.WindowInsets;
-import android.view.WindowManager;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.FrameLayout;
 import android.widget.LinearLayout;
@@ -73,6 +58,7 @@
 import com.android.systemui.R;
 import com.android.systemui.recents.TriangleShape;
 import com.android.systemui.statusbar.AlphaOptimizedButton;
+import com.android.wm.shell.common.HandlerExecutor;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -82,18 +68,6 @@
  */
 public class BubbleExpandedView extends LinearLayout {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleExpandedView" : TAG_BUBBLES;
-    private static final String WINDOW_TITLE = "ImeInsetsWindowWithoutContent";
-
-    private enum ActivityViewStatus {
-        // ActivityView is being initialized, cannot start an activity yet.
-        INITIALIZING,
-        // ActivityView is initialized, and ready to start an activity.
-        INITIALIZED,
-        // Activity runs in the ActivityView.
-        ACTIVITY_STARTED,
-        // ActivityView is released, so activity launching will no longer be permitted.
-        RELEASED,
-    }
 
     // The triangle pointing to the expanded view
     private View mPointerView;
@@ -101,143 +75,122 @@
     @Nullable private int[] mExpandedViewContainerLocation;
 
     private AlphaOptimizedButton mSettingsIcon;
+    private TaskView mTaskView;
 
-    // Views for expanded state
-    private ActivityView mActivityView;
+    private int mTaskId = INVALID_TASK_ID;
 
-    private ActivityViewStatus mActivityViewStatus = ActivityViewStatus.INITIALIZING;
-    private int mTaskId = -1;
-
-    private PendingIntent mPendingIntent;
-
-    private boolean mKeyboardVisible;
+    private boolean mImeVisible;
     private boolean mNeedsNewHeight;
 
-    private Point mDisplaySize;
     private int mMinHeight;
     private int mOverflowHeight;
     private int mSettingsIconHeight;
     private int mPointerWidth;
     private int mPointerHeight;
-    private ShapeDrawable mPointerDrawable;
+    private ShapeDrawable mCurrentPointer;
+    private ShapeDrawable mTopPointer;
+    private ShapeDrawable mLeftPointer;
+    private ShapeDrawable mRightPointer;
     private int mExpandedViewPadding;
-
+    private float mCornerRadius = 0f;
 
     @Nullable private Bubble mBubble;
-
+    private PendingIntent mPendingIntent;
+    // TODO(b/170891664): Don't use a flag, set the BubbleOverflow object instead
     private boolean mIsOverflow;
 
     private Bubbles mBubbles = Dependency.get(Bubbles.class);
-    private WindowManager mWindowManager;
-    private ActivityManager mActivityManager;
-
     private BubbleStackView mStackView;
-    private View mVirtualImeView;
-    private WindowManager mVirtualDisplayWindowManager;
-    private boolean mImeShowing = false;
-    private float mCornerRadius = 0f;
+    private BubblePositioner mPositioner;
 
     /**
      * Container for the ActivityView that has a solid, round-rect background that shows if the
      * ActivityView hasn't loaded.
      */
-    private FrameLayout mActivityViewContainer = new FrameLayout(getContext());
+    private final FrameLayout mExpandedViewContainer = new FrameLayout(getContext());
 
-    /** The SurfaceView that the ActivityView draws to. */
-    @Nullable private SurfaceView mActivitySurface;
+    private final TaskView.Listener mTaskViewListener = new TaskView.Listener() {
+        private boolean mInitialized = false;
+        private boolean mDestroyed = false;
 
-    private ActivityView.StateCallback mStateCallback = new ActivityView.StateCallback() {
         @Override
-        public void onActivityViewReady(ActivityView view) {
+        public void onInitialized() {
             if (DEBUG_BUBBLE_EXPANDED_VIEW) {
-                Log.d(TAG, "onActivityViewReady: mActivityViewStatus=" + mActivityViewStatus
+                Log.d(TAG, "onActivityViewReady: destroyed=" + mDestroyed
+                        + " initialized=" + mInitialized
                         + " bubble=" + getBubbleKey());
             }
-            switch (mActivityViewStatus) {
-                case INITIALIZING:
-                case INITIALIZED:
-                    // Custom options so there is no activity transition animation
-                    ActivityOptions options = ActivityOptions.makeCustomAnimation(getContext(),
-                            0 /* enterResId */, 0 /* exitResId */);
-                    options.setTaskAlwaysOnTop(true);
-                    // Soptions.setLaunchWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
-                    // Post to keep the lifecycle normal
-                    post(() -> {
-                        if (DEBUG_BUBBLE_EXPANDED_VIEW) {
-                            Log.d(TAG, "onActivityViewReady: calling startActivity, "
-                                    + "bubble=" + getBubbleKey());
-                        }
-                        if (mActivityView == null) {
-                            mBubbles.removeBubble(getBubbleKey(),
-                                    BubbleController.DISMISS_INVALID_INTENT);
-                            return;
-                        }
-                        try {
-                            if (!mIsOverflow && mBubble.hasMetadataShortcutId()
-                                    && mBubble.getShortcutInfo() != null) {
-                                options.setApplyActivityFlagsForBubbles(true);
-                                mActivityView.startShortcutActivity(mBubble.getShortcutInfo(),
-                                        options, null /* sourceBounds */);
-                            } else {
-                                Intent fillInIntent = new Intent();
-                                // Apply flags to make behaviour match documentLaunchMode=always.
-                                fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
-                                fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
-                                if (mBubble != null) {
-                                    mBubble.setIntentActive();
-                                }
-                                mActivityView.startActivity(mPendingIntent, fillInIntent, options);
-                            }
-                        } catch (RuntimeException e) {
-                            // If there's a runtime exception here then there's something
-                            // wrong with the intent, we can't really recover / try to populate
-                            // the bubble again so we'll just remove it.
-                            Log.w(TAG, "Exception while displaying bubble: " + getBubbleKey()
-                                    + ", " + e.getMessage() + "; removing bubble");
-                            mBubbles.removeBubble(getBubbleKey(),
-                                    BubbleController.DISMISS_INVALID_INTENT);
-                        }
-                    });
-                    mActivityViewStatus = ActivityViewStatus.ACTIVITY_STARTED;
-                    break;
-                case ACTIVITY_STARTED:
-                    post(() -> mActivityManager.moveTaskToFront(mTaskId, 0));
-                    break;
+
+            if (mDestroyed || mInitialized) {
+                return;
             }
+            // Custom options so there is no activity transition animation
+            ActivityOptions options = ActivityOptions.makeCustomAnimation(getContext(),
+                    0 /* enterResId */, 0 /* exitResId */);
+
+            // TODO: I notice inconsistencies in lifecycle
+            // Post to keep the lifecycle normal
+            post(() -> {
+                if (DEBUG_BUBBLE_EXPANDED_VIEW) {
+                    Log.d(TAG, "onActivityViewReady: calling startActivity, bubble="
+                            + getBubbleKey());
+                }
+                try {
+                    if (!mIsOverflow && mBubble.hasMetadataShortcutId()) {
+                        options.setApplyActivityFlagsForBubbles(true);
+                        mTaskView.startShortcutActivity(mBubble.getShortcutInfo(),
+                                options, null /* sourceBounds */);
+                    } else {
+                        Intent fillInIntent = new Intent();
+                        // Apply flags to make behaviour match documentLaunchMode=always.
+                        fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
+                        fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
+                        if (mBubble != null) {
+                            mBubble.setIntentActive();
+                        }
+                        mTaskView.startActivity(mPendingIntent, fillInIntent, options);
+                    }
+                } catch (RuntimeException e) {
+                    // If there's a runtime exception here then there's something
+                    // wrong with the intent, we can't really recover / try to populate
+                    // the bubble again so we'll just remove it.
+                    Log.w(TAG, "Exception while displaying bubble: " + getBubbleKey()
+                            + ", " + e.getMessage() + "; removing bubble");
+                    mBubbles.removeBubble(getBubbleKey(),
+                            BubbleController.DISMISS_INVALID_INTENT);
+                }
+            });
+            mInitialized = true;
         }
 
         @Override
-        public void onActivityViewDestroyed(ActivityView view) {
-            if (DEBUG_BUBBLE_EXPANDED_VIEW) {
-                Log.d(TAG, "onActivityViewDestroyed: mActivityViewStatus=" + mActivityViewStatus
-                        + " bubble=" + getBubbleKey());
-            }
-            mActivityViewStatus = ActivityViewStatus.RELEASED;
+        public void onReleased() {
+            mDestroyed = true;
         }
 
         @Override
-        public void onTaskCreated(int taskId, ComponentName componentName) {
+        public void onTaskCreated(int taskId, ComponentName name) {
             if (DEBUG_BUBBLE_EXPANDED_VIEW) {
                 Log.d(TAG, "onTaskCreated: taskId=" + taskId
                         + " bubble=" + getBubbleKey());
             }
-            // Since Bubble ActivityView applies singleTaskDisplay this is
-            // guaranteed to only be called once per ActivityView. The taskId is
-            // saved to use for removeTask, preventing appearance in recent tasks.
+            // The taskId is saved to use for removeTask, preventing appearance in recent tasks.
             mTaskId = taskId;
+
+            // With the task org, the taskAppeared callback will only happen once the task has
+            // already drawn
+            setContentVisibility(true);
         }
 
-        /**
-         * 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 onTaskVisibilityChanged(int taskId, boolean visible) {
+            setContentVisibility(visible);
+        }
+
         @Override
         public void onTaskRemovalStarted(int taskId) {
             if (DEBUG_BUBBLE_EXPANDED_VIEW) {
                 Log.d(TAG, "onTaskRemovalStarted: taskId=" + taskId
-                        + " mActivityViewStatus=" + mActivityViewStatus
                         + " bubble=" + getBubbleKey());
             }
             if (mBubble != null) {
@@ -246,6 +199,13 @@
                         BubbleController.DISMISS_TASK_FINISHED));
             }
         }
+
+        @Override
+        public void onBackPressedOnTaskRoot(int taskId) {
+            if (mTaskId == taskId && mStackView.isExpanded()) {
+                mBubbles.collapseStack();
+            }
+        }
     };
 
     public BubbleExpandedView(Context context) {
@@ -264,70 +224,50 @@
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
         updateDimensions();
-        mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
-    }
-
-    void updateDimensions() {
-        mDisplaySize = new Point();
-        mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
-        // Get the real size -- this includes screen decorations (notches, statusbar, navbar).
-        mWindowManager.getDefaultDisplay().getRealSize(mDisplaySize);
-        Resources res = getResources();
-        mMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height);
-        mOverflowHeight = res.getDimensionPixelSize(R.dimen.bubble_overflow_height);
-        mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin);
     }
 
     @SuppressLint("ClickableViewAccessibility")
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        if (DEBUG_BUBBLE_EXPANDED_VIEW) {
-            Log.d(TAG, "onFinishInflate: bubble=" + getBubbleKey());
-        }
 
         Resources res = getResources();
         mPointerView = findViewById(R.id.pointer_view);
         mPointerWidth = res.getDimensionPixelSize(R.dimen.bubble_pointer_width);
         mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height);
 
-        mPointerDrawable = new ShapeDrawable(TriangleShape.create(
+        mTopPointer = new ShapeDrawable(TriangleShape.create(
                 mPointerWidth, mPointerHeight, true /* pointUp */));
+        mLeftPointer = new ShapeDrawable(TriangleShape.createHorizontal(
+                mPointerWidth, mPointerHeight, true /* pointLeft */));
+        mRightPointer = new ShapeDrawable(TriangleShape.createHorizontal(
+                mPointerWidth, mPointerHeight, false /* pointLeft */));
+
+        mCurrentPointer = mTopPointer;
         mPointerView.setVisibility(INVISIBLE);
 
         mSettingsIconHeight = getContext().getResources().getDimensionPixelSize(
                 R.dimen.bubble_manage_button_height);
         mSettingsIcon = findViewById(R.id.settings_button);
 
-        mActivityView = new ActivityView.Builder(mContext)
-                .setSingleInstance(true)
-                .setDisableSurfaceViewBackgroundLayer(true)
-                .setUseTrustedDisplay(true)
-                .build();
+        mPositioner = mBubbles.getPositioner();
 
+        mTaskView = new TaskView(mContext, mBubbles.getTaskOrganizer(),
+                new HandlerExecutor(getHandler()));
         // Set ActivityView's alpha value as zero, since there is no view content to be shown.
         setContentVisibility(false);
 
-        mActivityViewContainer.setOutlineProvider(new ViewOutlineProvider() {
+        mExpandedViewContainer.setOutlineProvider(new ViewOutlineProvider() {
             @Override
             public void getOutline(View view, Outline outline) {
                 outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mCornerRadius);
             }
         });
-        mActivityViewContainer.setClipToOutline(true);
-        mActivityViewContainer.addView(mActivityView);
-        mActivityViewContainer.setLayoutParams(
+        mExpandedViewContainer.setClipToOutline(true);
+        mExpandedViewContainer.addView(mTaskView);
+        mExpandedViewContainer.setLayoutParams(
                 new ViewGroup.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
-        addView(mActivityViewContainer);
-
-        if (mActivityView != null
-                && mActivityView.getChildCount() > 0
-                && mActivityView.getChildAt(0) instanceof SurfaceView) {
-            // Retrieve the surface from the ActivityView so we can screenshot it and change its
-            // z-ordering. This should always be possible, since ActivityView's constructor adds the
-            // SurfaceView as its first child.
-            mActivitySurface = (SurfaceView) mActivityView.getChildAt(0);
-        }
+        addView(mExpandedViewContainer);
 
         // Expanded stack layout, top to bottom:
         // Expanded view container
@@ -335,33 +275,21 @@
         // ==> expanded view
         //   ==> activity view
         //   ==> manage button
-        bringChildToFront(mActivityView);
+        bringChildToFront(mTaskView);
         bringChildToFront(mSettingsIcon);
+        mTaskView.setListener(mTaskViewListener);
 
         applyThemeAttrs();
 
-        setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> {
-            // Keep track of IME displaying because we should not make any adjustments that might
-            // cause a config change while the IME is displayed otherwise it'll loose focus.
-            final int keyboardHeight = insets.getSystemWindowInsetBottom()
-                    - insets.getStableInsetBottom();
-            mKeyboardVisible = keyboardHeight != 0;
-            if (!mKeyboardVisible && mNeedsNewHeight) {
-                updateHeight();
-            }
-            return view.onApplyWindowInsets(insets);
-        });
-
         mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding);
-        setPadding(mExpandedViewPadding, mExpandedViewPadding, mExpandedViewPadding,
-                mExpandedViewPadding);
+        setClipToPadding(false);
         setOnTouchListener((view, motionEvent) -> {
-            if (!usingActivityView()) {
+            if (mTaskView == null) {
                 return false;
             }
 
             final Rect avBounds = new Rect();
-            mActivityView.getBoundsOnScreen(avBounds);
+            mTaskView.getBoundsOnScreen(avBounds);
 
             // Consume and ignore events on the expanded view padding that are within the
             // ActivityView's vertical bounds. These events are part of a back gesture, and so they
@@ -382,57 +310,11 @@
         setLayoutDirection(LAYOUT_DIRECTION_LOCALE);
     }
 
-    private String getBubbleKey() {
-        return mBubble != null ? mBubble.getKey() : "null";
-    }
-
-    /**
-     * Asks the ActivityView's surface to draw on top of all other views in the window. This is
-     * useful for ordering surfaces during animations, but should otherwise be set to false so that
-     * bubbles and menus can draw over the ActivityView.
-     */
-    void setSurfaceZOrderedOnTop(boolean onTop) {
-        if (mActivitySurface == null) {
-            return;
-        }
-
-        mActivitySurface.setZOrderedOnTop(onTop, true);
-    }
-
-    /** Return a GraphicBuffer with the contents of the ActivityView's underlying surface. */
-    @Nullable
-    SurfaceControl.ScreenshotHardwareBuffer snapshotActivitySurface() {
-        if (mActivitySurface == null) {
-            return null;
-        }
-
-        return SurfaceControl.captureLayers(
-                mActivitySurface.getSurfaceControl(),
-                new Rect(0, 0, mActivityView.getWidth(), mActivityView.getHeight()),
-                1 /* scale */);
-    }
-
-    int[] getActivityViewLocationOnScreen() {
-        if (mActivityView != null) {
-            return mActivityView.getLocationOnScreen();
-        } else {
-            return new int[]{0, 0};
-        }
-    }
-
-    void setManageClickListener(OnClickListener manageClickListener) {
-        findViewById(R.id.settings_button).setOnClickListener(manageClickListener);
-    }
-
-    /**
-     * Updates the ActivityView's obscured touchable region. This calls onLocationChanged, which
-     * results in a call to {@link BubbleStackView#subtractObscuredTouchableRegion}. This is useful
-     * if a view has been added or removed from on top of the ActivityView, such as the manage menu.
-     */
-    void updateObscuredTouchableRegion() {
-        if (mActivityView != null) {
-            mActivityView.onLocationChanged();
-        }
+    void updateDimensions() {
+        Resources res = getResources();
+        mMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height);
+        mOverflowHeight = res.getDimensionPixelSize(R.dimen.bubble_overflow_height);
+        mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin);
     }
 
     void applyThemeAttrs() {
@@ -440,35 +322,105 @@
                 android.R.attr.dialogCornerRadius,
                 android.R.attr.colorBackgroundFloating});
         mCornerRadius = ta.getDimensionPixelSize(0, 0);
-        mActivityViewContainer.setBackgroundColor(ta.getColor(1, Color.WHITE));
+        mExpandedViewContainer.setBackgroundColor(ta.getColor(1, Color.WHITE));
         ta.recycle();
 
-        if (mActivityView != null && ScreenDecorationsUtils.supportsRoundedCornersOnWindows(
+        if (mTaskView != null && ScreenDecorationsUtils.supportsRoundedCornersOnWindows(
                 mContext.getResources())) {
-            mActivityView.setCornerRadius(mCornerRadius);
+            mTaskView.setCornerRadius(mCornerRadius);
         }
+        updatePointerView();
+    }
 
+    private void updatePointerView() {
         final int mode =
                 getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
         switch (mode) {
             case Configuration.UI_MODE_NIGHT_NO:
-                mPointerDrawable.setTint(getResources().getColor(R.color.bubbles_light));
+                mCurrentPointer.setTint(getResources().getColor(R.color.bubbles_light));
                 break;
             case Configuration.UI_MODE_NIGHT_YES:
-                mPointerDrawable.setTint(getResources().getColor(R.color.bubbles_dark));
+                mCurrentPointer.setTint(getResources().getColor(R.color.bubbles_dark));
                 break;
         }
-        mPointerView.setBackground(mPointerDrawable);
+        LayoutParams lp = (LayoutParams) mPointerView.getLayoutParams();
+        if (mCurrentPointer == mLeftPointer || mCurrentPointer == mRightPointer) {
+            lp.width = mPointerHeight;
+            lp.height = mPointerWidth;
+        } else {
+            lp.width = mPointerWidth;
+            lp.height = mPointerHeight;
+        }
+        mPointerView.setLayoutParams(lp);
+        mPointerView.setBackground(mCurrentPointer);
+    }
+
+
+    private String getBubbleKey() {
+        return mBubble != null ? mBubble.getKey() : "null";
+    }
+
+    /**
+     * Sets whether the surface displaying app content should sit on top. This is useful for
+     * ordering surfaces during animations. When content is drawn on top of the app (e.g. bubble
+     * being dragged out, the manage menu) this is set to false, otherwise it should be true.
+     */
+    void setSurfaceZOrderedOnTop(boolean onTop) {
+        if (mTaskView == null) {
+            return;
+        }
+        mTaskView.setZOrderedOnTop(onTop, true /* allowDynamicChange */);
+    }
+
+    void setImeVisible(boolean visible) {
+        mImeVisible = visible;
+        if (!mImeVisible && mNeedsNewHeight) {
+            updateHeight();
+        }
+    }
+
+    /** Return a GraphicBuffer with the contents of the task view surface. */
+    @Nullable
+    SurfaceControl.ScreenshotHardwareBuffer snapshotActivitySurface() {
+        if (mTaskView == null) {
+            return null;
+        }
+        return SurfaceControl.captureLayers(
+                mTaskView.getSurfaceControl(),
+                new Rect(0, 0, mTaskView.getWidth(), mTaskView.getHeight()),
+                1 /* scale */);
+    }
+
+    int[] getTaskViewLocationOnScreen() {
+        if (mTaskView != null) {
+            return mTaskView.getLocationOnScreen();
+        } else {
+            return new int[]{0, 0};
+        }
+    }
+
+    // TODO: Could listener be passed when we pass StackView / can we avoid setting this like this
+    void setManageClickListener(OnClickListener manageClickListener) {
+        mSettingsIcon.setOnClickListener(manageClickListener);
+    }
+
+    /**
+     * Updates the obscured touchable region for the task surface. This calls onLocationChanged,
+     * which results in a call to {@link BubbleStackView#subtractObscuredTouchableRegion}. This is
+     * useful if a view has been added or removed from on top of the ActivityView, such as the
+     * manage menu.
+     */
+    void updateObscuredTouchableRegion() {
+        if (mTaskView != null) {
+            mTaskView.onLocationChanged();
+        }
     }
 
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
-        mKeyboardVisible = false;
+        mImeVisible = false;
         mNeedsNewHeight = false;
-        if (mActivityView != null) {
-            setImeWindowToDisplay(0, 0);
-        }
         if (DEBUG_BUBBLE_EXPANDED_VIEW) {
             Log.d(TAG, "onDetachedFromWindow: bubble=" + getBubbleKey());
         }
@@ -490,84 +442,23 @@
         final float alpha = visibility ? 1f : 0f;
 
         mPointerView.setAlpha(alpha);
-
-        if (mActivityView != null && alpha != mActivityView.getAlpha()) {
-            mActivityView.setAlpha(alpha);
-            mActivityView.bringToFront();
+        if (mTaskView == null) {
+            return;
+        }
+        if (alpha != mTaskView.getAlpha()) {
+            mTaskView.setAlpha(alpha);
         }
     }
 
-    @Nullable ActivityView getActivityView() {
-        return mActivityView;
+    @Nullable
+    View getTaskView() {
+        return mTaskView;
     }
 
     int getTaskId() {
         return mTaskId;
     }
 
-    /**
-     * Called by {@link BubbleStackView} when the insets for the expanded state should be updated.
-     * This should be done post-move and post-animation.
-     */
-    void updateInsets(WindowInsets insets) {
-        if (usingActivityView()) {
-            int[] screenLoc = mActivityView.getLocationOnScreen();
-            final int activityViewBottom = screenLoc[1] + mActivityView.getHeight();
-            final int keyboardTop = mDisplaySize.y - Math.max(insets.getSystemWindowInsetBottom(),
-                    insets.getDisplayCutout() != null
-                            ? insets.getDisplayCutout().getSafeInsetBottom()
-                            : 0);
-            setImeWindowToDisplay(getWidth(), Math.max(activityViewBottom - keyboardTop, 0));
-        }
-    }
-
-    private void setImeWindowToDisplay(int w, int h) {
-        if (getVirtualDisplayId() == INVALID_DISPLAY) {
-            return;
-        }
-        if (h == 0 || w == 0) {
-            if (mImeShowing) {
-                mVirtualImeView.setVisibility(GONE);
-                mImeShowing = false;
-            }
-            return;
-        }
-        final Context virtualDisplayContext = mContext.createDisplayContext(
-                getVirtualDisplay().getDisplay());
-
-        if (mVirtualDisplayWindowManager == null) {
-            mVirtualDisplayWindowManager =
-                    (WindowManager) virtualDisplayContext.getSystemService(Context.WINDOW_SERVICE);
-        }
-        if (mVirtualImeView == null) {
-            mVirtualImeView = new View(virtualDisplayContext);
-            mVirtualImeView.setVisibility(VISIBLE);
-            mVirtualDisplayWindowManager.addView(mVirtualImeView,
-                    getVirtualImeViewAttrs(w, h));
-        } else {
-            mVirtualDisplayWindowManager.updateViewLayout(mVirtualImeView,
-                    getVirtualImeViewAttrs(w, h));
-            mVirtualImeView.setVisibility(VISIBLE);
-        }
-
-        mImeShowing = true;
-    }
-
-    private WindowManager.LayoutParams getVirtualImeViewAttrs(int w, int h) {
-        // To use TYPE_NAVIGATION_BAR_PANEL instead of TYPE_IME_BAR to bypass the IME window type
-        // token check when adding the window.
-        final WindowManager.LayoutParams attrs =
-                new WindowManager.LayoutParams(w, h, TYPE_NAVIGATION_BAR_PANEL,
-                        FLAG_LAYOUT_NO_LIMITS | FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE,
-                        TRANSPARENT);
-        attrs.gravity = Gravity.BOTTOM;
-        attrs.setTitle(WINDOW_TITLE);
-        attrs.token = new Binder();
-        attrs.providesInsetsTypes = new int[]{ITYPE_IME};
-        attrs.alpha = 0.0f;
-        return attrs;
-    }
-
     void setStackView(BubbleStackView stackView) {
         mStackView = stackView;
     }
@@ -576,7 +467,10 @@
         mIsOverflow = overflow;
 
         Intent target = new Intent(mContext, BubbleOverflowActivity.class);
-        mPendingIntent = PendingIntent.getActivity(mContext, /* requestCode */ 0,
+        Bundle extras = new Bundle();
+        extras.putBinder(EXTRA_BUBBLE_CONTROLLER, ObjectWrapper.wrap(mBubbles));
+        target.putExtras(extras);
+        mPendingIntent = PendingIntent.getActivity(mContext, 0 /* requestCode */,
                 target, PendingIntent.FLAG_UPDATE_CURRENT);
         mSettingsIcon.setVisibility(GONE);
     }
@@ -614,7 +508,7 @@
                 mPendingIntent = mBubble.getBubbleIntent();
                 if (mPendingIntent != null || mBubble.hasMetadataShortcutId()) {
                     setContentVisibility(false);
-                    mActivityView.setVisibility(VISIBLE);
+                    mTaskView.setVisibility(VISIBLE);
                 }
             }
             applyThemeAttrs();
@@ -624,59 +518,42 @@
         }
     }
 
+    /**
+     * Bubbles are backed by a pending intent or a shortcut, once the activity is
+     * started we never change it / restart it on notification updates -- unless the bubbles'
+     * backing data switches.
+     *
+     * This indicates if the new bubble is backed by a different data source than what was
+     * previously shown here (e.g. previously a pending intent & now a shortcut).
+     *
+     * @param newBubble the bubble this view is being updated with.
+     * @return true if the backing content has changed.
+     */
     private boolean didBackingContentChange(Bubble newBubble) {
         boolean prevWasIntentBased = mBubble != null && mPendingIntent != null;
         boolean newIsIntentBased = newBubble.getBubbleIntent() != null;
         return prevWasIntentBased != newIsIntentBased;
     }
 
-    /**
-     * Lets activity view know it should be shown / populated with activity content.
-     */
-    void populateExpandedView() {
-        if (DEBUG_BUBBLE_EXPANDED_VIEW) {
-            Log.d(TAG, "populateExpandedView: "
-                    + "bubble=" + getBubbleKey());
-        }
-
-        if (usingActivityView()) {
-            mActivityView.setCallback(mStateCallback);
-        } else {
-            Log.e(TAG, "Cannot populate expanded view.");
-        }
-    }
-
-    boolean performBackPressIfNeeded() {
-        if (!usingActivityView()) {
-            return false;
-        }
-        mActivityView.performBackPress();
-        return true;
-    }
-
     void updateHeight() {
-        if (DEBUG_BUBBLE_EXPANDED_VIEW) {
-            Log.d(TAG, "updateHeight: bubble=" + getBubbleKey());
-        }
-
         if (mExpandedViewContainerLocation == null) {
             return;
         }
 
-        if (usingActivityView()) {
-            float desiredHeight = mOverflowHeight;
-            if (!mIsOverflow) {
-                desiredHeight = Math.max(mBubble.getDesiredHeight(mContext), mMinHeight);
-            }
+        if (mBubble != null || mIsOverflow) {
+            float desiredHeight = mIsOverflow
+                    ? mOverflowHeight
+                    : mBubble.getDesiredHeight(mContext);
+            desiredHeight = Math.max(desiredHeight, mMinHeight);
             float height = Math.min(desiredHeight, getMaxExpandedHeight());
             height = Math.max(height, mMinHeight);
-            ViewGroup.LayoutParams lp = mActivityView.getLayoutParams();
+            FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mTaskView.getLayoutParams();
             mNeedsNewHeight = lp.height != height;
-            if (!mKeyboardVisible) {
-                // If the keyboard is visible... don't adjust the height because that will cause
-                // a configuration change and the keyboard will be lost.
+            if (!mImeVisible) {
+                // If the ime is visible... don't adjust the height because that will cause
+                // a configuration change and the ime will be lost.
                 lp.height = (int) height;
-                mActivityView.setLayoutParams(lp);
+                mTaskView.setLayoutParams(lp);
                 mNeedsNewHeight = false;
             }
             if (DEBUG_BUBBLE_EXPANDED_VIEW) {
@@ -688,18 +565,17 @@
     }
 
     private int getMaxExpandedHeight() {
-        mWindowManager.getDefaultDisplay().getRealSize(mDisplaySize);
-        int bottomInset = getRootWindowInsets() != null
-                ? getRootWindowInsets().getStableInsetBottom()
+        int expandedContainerY = mExpandedViewContainerLocation != null
+                // Remove top insets back here because availableRect.height would account for that
+                ? mExpandedViewContainerLocation[1] - mPositioner.getInsets().top
                 : 0;
-
-        return mDisplaySize.y
-                - mExpandedViewContainerLocation[1]
+        return mPositioner.getAvailableRect().height()
+                - expandedContainerY
                 - getPaddingTop()
                 - getPaddingBottom()
                 - mSettingsIconHeight
                 - mPointerHeight
-                - mPointerMargin - bottomInset;
+                - mPointerMargin;
     }
 
     /**
@@ -714,24 +590,35 @@
             Log.d(TAG, "updateView: bubble="
                     + getBubbleKey());
         }
-
         mExpandedViewContainerLocation = containerLocationOnScreen;
-
-        if (usingActivityView()
-                && mActivityView.getVisibility() == VISIBLE
-                && mActivityView.isAttachedToWindow()) {
-            mActivityView.onLocationChanged();
+        if (mTaskView != null
+                && mTaskView.getVisibility() == VISIBLE
+                && mTaskView.isAttachedToWindow()) {
             updateHeight();
+            mTaskView.onLocationChanged();
         }
     }
 
     /**
-     * Set the x position that the tip of the triangle should point to.
+     * Set the position that the tip of the triangle should point to.
      */
-    public void setPointerPosition(float x) {
-        float halfPointerWidth = mPointerWidth / 2f;
-        float pointerLeft = x - halfPointerWidth - mExpandedViewPadding;
-        mPointerView.setTranslationX(pointerLeft);
+    public void setPointerPosition(float x, float y, boolean isLandscape, boolean onLeft) {
+        // Pointer gets drawn in the padding
+        int paddingLeft = (isLandscape && onLeft) ? mPointerHeight : 0;
+        int paddingRight = (isLandscape && !onLeft) ? mPointerHeight : 0;
+        int paddingTop = isLandscape ? 0 : mExpandedViewPadding;
+        setPadding(paddingLeft, paddingTop, paddingRight, 0);
+
+        if (isLandscape) {
+            // TODO: why setY vs setTranslationY ? linearlayout?
+            mPointerView.setY(y - (mPointerWidth / 2f));
+            mPointerView.setTranslationX(onLeft ? -mPointerHeight : x - mExpandedViewPadding);
+        } else {
+            mPointerView.setTranslationY(0f);
+            mPointerView.setTranslationX(x - mExpandedViewPadding - (mPointerWidth / 2f));
+        }
+        mCurrentPointer = isLandscape ? onLeft ? mLeftPointer : mRightPointer : mTopPointer;
+        updatePointerView();
         mPointerView.setVisibility(VISIBLE);
     }
 
@@ -744,65 +631,19 @@
     }
 
     /**
-     * Removes and releases an ActivityView if one was previously created for this bubble.
+     * Cleans up anything related to the task and TaskView.
      */
     public void cleanUpExpandedState() {
         if (DEBUG_BUBBLE_EXPANDED_VIEW) {
-            Log.d(TAG, "cleanUpExpandedState: mActivityViewStatus=" + mActivityViewStatus
-                    + ", bubble=" + getBubbleKey());
+            Log.d(TAG, "cleanUpExpandedState: bubble=" + getBubbleKey() + " task=" + mTaskId);
         }
-        if (mActivityView == null) {
-            return;
+        if (mTaskView != null) {
+            mTaskView.release();
         }
-        mActivityView.release();
-        if (mTaskId != -1) {
-            try {
-                ActivityTaskManager.getService().removeTask(mTaskId);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed to remove taskId " + mTaskId);
-            }
-            mTaskId = -1;
+        if (mTaskView != null) {
+            removeView(mTaskView);
+            mTaskView = null;
         }
-        removeView(mActivityView);
-
-        mActivityView = null;
-    }
-
-    /**
-     * Called when the last task is removed from a {@link android.hardware.display.VirtualDisplay}
-     * which {@link ActivityView} uses.
-     */
-    void notifyDisplayEmpty() {
-        if (DEBUG_BUBBLE_EXPANDED_VIEW) {
-            Log.d(TAG, "notifyDisplayEmpty: bubble="
-                    + getBubbleKey()
-                    + " mActivityViewStatus=" + mActivityViewStatus);
-        }
-        if (mActivityViewStatus == ActivityViewStatus.ACTIVITY_STARTED) {
-            mActivityViewStatus = ActivityViewStatus.INITIALIZED;
-        }
-    }
-
-    private boolean usingActivityView() {
-        return (mPendingIntent != null || mBubble.hasMetadataShortcutId())
-                && mActivityView != null;
-    }
-
-    /**
-     * @return the display id of the virtual display.
-     */
-    public int getVirtualDisplayId() {
-        if (usingActivityView()) {
-            return mActivityView.getVirtualDisplayId();
-        }
-        return INVALID_DISPLAY;
-    }
-
-    private VirtualDisplay getVirtualDisplay() {
-        if (usingActivityView()) {
-            return mActivityView.getVirtualDisplay();
-        }
-        return null;
     }
 
     /**
@@ -812,7 +653,6 @@
             @NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
         pw.print("BubbleExpandedView");
         pw.print("  taskId:               "); pw.println(mTaskId);
-        pw.print("  activityViewStatus:   "); pw.println(mActivityViewStatus);
         pw.print("  stackView:            "); pw.println(mStackView);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java
index 1fa3aaa..d8b3250 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java
@@ -18,6 +18,8 @@
 
 import static android.graphics.Paint.ANTI_ALIAS_FLAG;
 import static android.graphics.Paint.FILTER_BITMAP_FLAG;
+import static com.android.systemui.Interpolators.ALPHA_IN;
+import static com.android.systemui.Interpolators.ALPHA_OUT;
 
 import android.animation.ArgbEvaluator;
 import android.content.Context;
@@ -34,6 +36,7 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.ShapeDrawable;
 import android.text.TextUtils;
+import android.util.TypedValue;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -55,6 +58,12 @@
     /** Max width of the flyout, in terms of percent of the screen width. */
     private static final float FLYOUT_MAX_WIDTH_PERCENT = .6f;
 
+    /** Translation Y of fade animation. */
+    private static final float FLYOUT_FADE_Y = 40f;
+
+    private static final long FLYOUT_FADE_OUT_DURATION = 150L;
+    private static final long FLYOUT_FADE_IN_DURATION = 250L;
+
     private final int mFlyoutPadding;
     private final int mFlyoutSpaceFromBubble;
     private final int mPointerSize;
@@ -103,6 +112,9 @@
     /** The bounds of the flyout background, kept up to date as it transitions to the 'new' dot. */
     private final RectF mBgRect = new RectF();
 
+    /** The y position of the flyout, relative to the top of the screen. */
+    private float mFlyoutY = 0f;
+
     /**
      * Percent progress in the transition from flyout to 'new' dot. These two values are the inverse
      * of each other (if we're 40% transitioned to the dot, we're 60% flyout), but it makes the code
@@ -212,18 +224,47 @@
         super.onDraw(canvas);
     }
 
-    /** Configures the flyout, collapsed into to dot form. */
-    void setupFlyoutStartingAsDot(
-            Bubble.FlyoutMessage flyoutMessage,
-            PointF stackPos,
-            float parentWidth,
-            boolean arrowPointingLeft,
-            int dotColor,
-            @Nullable Runnable onLayoutComplete,
-            @Nullable Runnable onHide,
-            float[] dotCenter,
-            boolean hideDot) {
+    void updateFontSize(float fontScale) {
+        final float fontSize = mContext.getResources()
+                .getDimensionPixelSize(com.android.internal.R.dimen.text_size_body_2_material);
+        final float newFontSize = fontSize * fontScale;
+        mMessageText.setTextSize(TypedValue.COMPLEX_UNIT_PX, newFontSize);
+        mSenderText.setTextSize(TypedValue.COMPLEX_UNIT_PX, newFontSize);
+    }
 
+    /*
+     * Fade animation for consecutive flyouts.
+     */
+    void animateUpdate(Bubble.FlyoutMessage flyoutMessage, float parentWidth, float stackY) {
+        final Runnable afterFadeOut = () -> {
+            updateFlyoutMessage(flyoutMessage, parentWidth);
+            // Wait for TextViews to layout with updated height.
+            post(() -> {
+                mFlyoutY = stackY + (mBubbleSize - mFlyoutTextContainer.getHeight()) / 2f;
+                fade(true /* in */, () -> {} /* after */);
+            } /* after */ );
+        };
+        fade(false /* in */, afterFadeOut);
+    }
+
+    /*
+     * Fade-out above or fade-in from below.
+     */
+    private void fade(boolean in, Runnable afterFade) {
+        setAlpha(in ? 0f : 1f);
+        setTranslationY(in ? mFlyoutY + FLYOUT_FADE_Y : mFlyoutY);
+        animate()
+                .alpha(in ? 1f : 0f)
+                .setDuration(in ? FLYOUT_FADE_IN_DURATION : FLYOUT_FADE_OUT_DURATION)
+                .setInterpolator(in ? ALPHA_IN : ALPHA_OUT);
+        animate()
+                .translationY(in ? mFlyoutY : mFlyoutY - FLYOUT_FADE_Y)
+                .setDuration(in ? FLYOUT_FADE_IN_DURATION : FLYOUT_FADE_OUT_DURATION)
+                .setInterpolator(in ? ALPHA_IN : ALPHA_OUT)
+                .withEndAction(afterFade);
+    }
+
+    private void updateFlyoutMessage(Bubble.FlyoutMessage flyoutMessage, float parentWidth) {
         final Drawable senderAvatar = flyoutMessage.senderAvatar;
         if (senderAvatar != null && flyoutMessage.isGroupChat) {
             mSenderAvatar.setVisibility(VISIBLE);
@@ -247,6 +288,27 @@
             mSenderText.setVisibility(GONE);
         }
 
+        // Set the flyout TextView's max width in terms of percent, and then subtract out the
+        // padding so that the entire flyout view will be the desired width (rather than the
+        // TextView being the desired width + extra padding).
+        mMessageText.setMaxWidth(maxTextViewWidth);
+        mMessageText.setText(flyoutMessage.message);
+    }
+
+    /** Configures the flyout, collapsed into dot form. */
+    void setupFlyoutStartingAsDot(
+            Bubble.FlyoutMessage flyoutMessage,
+            PointF stackPos,
+            float parentWidth,
+            boolean arrowPointingLeft,
+            int dotColor,
+            @Nullable Runnable onLayoutComplete,
+            @Nullable Runnable onHide,
+            float[] dotCenter,
+            boolean hideDot)  {
+
+        updateFlyoutMessage(flyoutMessage, parentWidth);
+
         mArrowPointingLeft = arrowPointingLeft;
         mDotColor = dotColor;
         mOnHide = onHide;
@@ -254,24 +316,12 @@
 
         setCollapsePercent(1f);
 
-        // Set the flyout TextView's max width in terms of percent, and then subtract out the
-        // padding so that the entire flyout view will be the desired width (rather than the
-        // TextView being the desired width + extra padding).
-        mMessageText.setMaxWidth(maxTextViewWidth);
-        mMessageText.setText(flyoutMessage.message);
-
-        // Wait for the TextView to lay out so we know its line count.
+        // Wait for TextViews to layout with updated height.
         post(() -> {
-            float restingTranslationY;
-            // Multi line flyouts get top-aligned to the bubble.
-            if (mMessageText.getLineCount() > 1) {
-                restingTranslationY = stackPos.y + mBubbleIconTopPadding;
-            } else {
-                // Single line flyouts are vertically centered with respect to the bubble.
-                restingTranslationY =
-                        stackPos.y + (mBubbleSize - mFlyoutTextContainer.getHeight()) / 2f;
-            }
-            setTranslationY(restingTranslationY);
+            // Flyout is vertically centered with respect to the bubble.
+            mFlyoutY =
+                    stackPos.y + (mBubbleSize - mFlyoutTextContainer.getHeight()) / 2f;
+            setTranslationY(mFlyoutY);
 
             // Calculate the translation required to position the flyout next to the bubble stack,
             // with the desired padding.
@@ -291,7 +341,7 @@
             final float dotPositionY = stackPos.y + mDotCenter[1] - adjustmentForScaleAway;
 
             final float distanceFromFlyoutLeftToDotCenterX = mRestingTranslationX - dotPositionX;
-            final float distanceFromLayoutTopToDotCenterY = restingTranslationY - dotPositionY;
+            final float distanceFromLayoutTopToDotCenterY = mFlyoutY - dotPositionY;
 
             mTranslationXWhenDot = -distanceFromFlyoutLeftToDotCenterX;
             mTranslationYWhenDot = -distanceFromLayoutTopToDotCenterY;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLogger.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLogger.java
index 86ba8c5..48c809d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLogger.java
@@ -19,17 +19,22 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.UiEvent;
 import com.android.internal.logging.UiEventLogger;
+import com.android.internal.util.FrameworkStatsLog;
 
 /**
- * Interface for handling bubble-specific logging.
+ * Implementation of UiEventLogger for logging bubble UI events.
+ *
+ * See UiEventReported atom in atoms.proto for more context.
  */
-public interface BubbleLogger extends UiEventLogger {
+public class BubbleLogger {
+
+    private final UiEventLogger mUiEventLogger;
 
     /**
      * Bubble UI event.
      */
     @VisibleForTesting
-    enum Event implements UiEventLogger.UiEventEnum {
+    public enum Event implements UiEventLogger.UiEventEnum {
 
         @UiEvent(doc = "User dismissed the bubble via gesture, add bubble to overflow.")
         BUBBLE_OVERFLOW_ADD_USER_GESTURE(483),
@@ -70,23 +75,80 @@
         }
     }
 
+    public BubbleLogger(UiEventLogger uiEventLogger) {
+        mUiEventLogger = uiEventLogger;
+    }
+
     /**
      * @param b Bubble involved in this UI event
      * @param e UI event
      */
-    void log(Bubble b, UiEventEnum e);
+    public void log(Bubble b, UiEventLogger.UiEventEnum e) {
+        mUiEventLogger.logWithInstanceId(e, b.getAppUid(), b.getPackageName(), b.getInstanceId());
+    }
 
     /**
-     *
      * @param b Bubble removed from overflow
-     * @param r Reason that bubble was removed from overflow
+     * @param r Reason that bubble was removed
      */
-    void logOverflowRemove(Bubble b, @BubbleController.DismissReason int r);
+    public void logOverflowRemove(Bubble b, @BubbleController.DismissReason int r) {
+        if (r == BubbleController.DISMISS_NOTIF_CANCEL) {
+            log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_CANCEL);
+        } else if (r == BubbleController.DISMISS_GROUP_CANCELLED) {
+            log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_GROUP_CANCEL);
+        } else if (r == BubbleController.DISMISS_NO_LONGER_BUBBLE) {
+            log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_NO_LONGER_BUBBLE);
+        } else if (r == BubbleController.DISMISS_BLOCKED) {
+            log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_BLOCKED);
+        }
+    }
 
     /**
-     *
      * @param b Bubble added to overflow
      * @param r Reason that bubble was added to overflow
      */
-    void logOverflowAdd(Bubble b, @BubbleController.DismissReason int r);
-}
+    public void logOverflowAdd(Bubble b, @BubbleController.DismissReason int r) {
+        if (r == BubbleController.DISMISS_AGED) {
+            log(b, Event.BUBBLE_OVERFLOW_ADD_AGED);
+        } else if (r == BubbleController.DISMISS_USER_GESTURE) {
+            log(b, Event.BUBBLE_OVERFLOW_ADD_USER_GESTURE);
+        }
+    }
+
+    void logStackUiChanged(String packageName, int action, int bubbleCount, float normalX,
+            float normalY) {
+        FrameworkStatsLog.write(FrameworkStatsLog.BUBBLE_UI_CHANGED,
+                packageName,
+                null /* notification channel */,
+                0 /* notification ID */,
+                0 /* bubble position */,
+                bubbleCount,
+                action,
+                normalX,
+                normalY,
+                false /* unread bubble */,
+                false /* on-going bubble */,
+                false /* isAppForeground (unused) */);
+    }
+
+    void logShowOverflow(String packageName, int currentUserId) {
+        mUiEventLogger.log(BubbleLogger.Event.BUBBLE_OVERFLOW_SELECTED, currentUserId,
+                packageName);
+    }
+
+    void logBubbleUiChanged(Bubble bubble, String packageName, int action, int bubbleCount,
+            float normalX, float normalY, int index) {
+        FrameworkStatsLog.write(FrameworkStatsLog.BUBBLE_UI_CHANGED,
+                packageName,
+                bubble.getChannelId() /* notification channel */,
+                bubble.getNotificationId() /* notification ID */,
+                index,
+                bubbleCount,
+                action,
+                normalX,
+                normalY,
+                bubble.showInShade() /* isUnread */,
+                false /* isOngoing (unused) */,
+                false /* isAppForeground (unused) */);
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java
deleted file mode 100644
index ea612af..0000000
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.os.UserHandle;
-
-import com.android.internal.logging.UiEventLoggerImpl;
-import com.android.systemui.shared.system.SysUiStatsLog;
-
-/**
- * Implementation of UiEventLogger for logging bubble UI events.
- *
- * See UiEventReported atom in atoms.proto for more context.
- */
-public class BubbleLoggerImpl extends UiEventLoggerImpl implements BubbleLogger {
-
-    /**
-     * @param b Bubble involved in this UI event
-     * @param e UI event
-     */
-    public void log(Bubble b, UiEventEnum e) {
-        logWithInstanceId(e, b.getAppUid(), b.getPackageName(), b.getInstanceId());
-    }
-
-    /**
-     * @param b Bubble removed from overflow
-     * @param r Reason that bubble was removed
-     */
-    public void logOverflowRemove(Bubble b, @BubbleController.DismissReason int r) {
-        if (r == BubbleController.DISMISS_NOTIF_CANCEL) {
-            log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_CANCEL);
-        } else if (r == BubbleController.DISMISS_GROUP_CANCELLED) {
-            log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_GROUP_CANCEL);
-        } else if (r == BubbleController.DISMISS_NO_LONGER_BUBBLE) {
-            log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_NO_LONGER_BUBBLE);
-        } else if (r == BubbleController.DISMISS_BLOCKED) {
-            log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_BLOCKED);
-        }
-    }
-
-    /**
-     * @param b Bubble added to overflow
-     * @param r Reason that bubble was added to overflow
-     */
-    public void logOverflowAdd(Bubble b, @BubbleController.DismissReason int r) {
-        if (r == BubbleController.DISMISS_AGED) {
-            log(b, Event.BUBBLE_OVERFLOW_ADD_AGED);
-        } else if (r == BubbleController.DISMISS_USER_GESTURE) {
-            log(b, Event.BUBBLE_OVERFLOW_ADD_USER_GESTURE);
-        }
-    }
-
-    void logStackUiChanged(String packageName, int action, int bubbleCount, float normalX,
-            float normalY) {
-        SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
-                packageName,
-                null /* notification channel */,
-                0 /* notification ID */,
-                0 /* bubble position */,
-                bubbleCount,
-                action,
-                normalX,
-                normalY,
-                false /* unread bubble */,
-                false /* on-going bubble */,
-                false /* isAppForeground (unused) */);
-    }
-
-    void logShowOverflow(String packageName, int currentUserId) {
-        super.log(BubbleLogger.Event.BUBBLE_OVERFLOW_SELECTED, currentUserId,
-                packageName);
-    }
-
-    void logBubbleUiChanged(Bubble bubble, String packageName, int action, int bubbleCount,
-            float normalX, float normalY, int index) {
-        SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
-                packageName,
-                bubble.getChannelId() /* notification channel */,
-                bubble.getNotificationId() /* notification ID */,
-                index,
-                bubbleCount,
-                action,
-                normalX,
-                normalY,
-                bubble.showInShade() /* isUnread */,
-                false /* isOngoing (unused) */,
-                false /* isAppForeground (unused) */);
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt
index bf7c860..102055d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.bubbles
 
+import android.app.ActivityTaskManager.INVALID_TASK_ID
 import android.content.Context
 import android.content.res.Configuration
 import android.graphics.Bitmap
@@ -37,8 +38,8 @@
     private val stack: BubbleStackView
 ) : BubbleViewProvider {
 
-    private lateinit var bitmap : Bitmap
-    private lateinit var dotPath : Path
+    private lateinit var bitmap: Bitmap
+    private lateinit var dotPath: Path
 
     private var bitmapSize = 0
     private var iconBitmapSize = 0
@@ -167,8 +168,8 @@
         return KEY
     }
 
-    override fun getDisplayId(): Int {
-        return expandedView.virtualDisplayId
+    override fun getTaskId(): Int {
+        return if (expandedView != null) expandedView.getTaskId() else INVALID_TASK_ID
     }
 
     companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
index 5fdda97..fc3f5b6 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
@@ -22,11 +22,13 @@
 
 import android.app.Activity;
 import android.content.Context;
+import android.content.Intent;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Color;
 import android.os.Bundle;
+import android.os.IBinder;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.LayoutInflater;
@@ -47,13 +49,14 @@
 import java.util.List;
 import java.util.function.Consumer;
 
-import javax.inject.Inject;
 
 /**
  * Activity for showing aged out bubbles.
  * Must be public to be accessible to androidx...AppComponentFactory
  */
 public class BubbleOverflowActivity extends Activity {
+    static final String EXTRA_BUBBLE_CONTROLLER = "bubble_controller";
+
     private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleOverflowActivity" : TAG_BUBBLES;
 
     private LinearLayout mEmptyState;
@@ -93,11 +96,6 @@
         }
     }
 
-    @Inject
-    public BubbleOverflowActivity(Bubbles bubbles) {
-        mBubbles = bubbles;
-    }
-
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -109,6 +107,15 @@
         mEmptyStateSubtitle = findViewById(R.id.bubble_overflow_empty_subtitle);
         mEmptyStateImage = findViewById(R.id.bubble_overflow_empty_state_image);
 
+        Intent intent = getIntent();
+        if (intent != null && intent.getExtras() != null) {
+            IBinder binder = intent.getExtras().getBinder(EXTRA_BUBBLE_CONTROLLER);
+            if (binder instanceof ObjectWrapper) {
+                mBubbles = ((ObjectWrapper<Bubbles>) binder).get();
+            }
+        } else {
+            Log.w(TAG, "Bubble overflow activity created without bubble controller!");
+        }
         updateOverflow();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubblePositioner.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubblePositioner.java
new file mode 100644
index 0000000..029caee
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubblePositioner.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.view.WindowMetrics;
+
+import androidx.annotation.VisibleForTesting;
+
+/**
+ * Keeps track of display size, configuration, and specific bubble sizes. One place for all
+ * placement and positioning calculations to refer to.
+ */
+public class BubblePositioner {
+
+    private WindowManager mWindowManager;
+    private Rect mPositionRect;
+    private int mOrientation;
+    private Insets mInsets;
+
+    public BubblePositioner(Context context, WindowManager windowManager) {
+        mWindowManager = windowManager;
+        update(Configuration.ORIENTATION_UNDEFINED);
+    }
+
+    public void update(int orientation) {
+        WindowMetrics windowMetrics = mWindowManager.getCurrentWindowMetrics();
+        mPositionRect = new Rect(windowMetrics.getBounds());
+        WindowInsets metricInsets = windowMetrics.getWindowInsets();
+
+        Insets insets = metricInsets.getInsetsIgnoringVisibility(WindowInsets.Type.navigationBars()
+                | WindowInsets.Type.statusBars()
+                | WindowInsets.Type.displayCutout());
+        update(orientation, insets, windowMetrics.getBounds());
+    }
+
+    @VisibleForTesting
+    public void update(int orientation, Insets insets, Rect bounds) {
+        mOrientation = orientation;
+        mInsets = insets;
+
+        mPositionRect = new Rect(bounds);
+        mPositionRect.left += mInsets.left;
+        mPositionRect.top += mInsets.top;
+        mPositionRect.right -= mInsets.right;
+        mPositionRect.bottom -= mInsets.bottom;
+    }
+
+    /**
+     * @return a rect of available screen space for displaying bubbles in the correct orientation,
+     * accounting for system bars and cutouts.
+     */
+    public Rect getAvailableRect() {
+        return mPositionRect;
+    }
+
+    /**
+     * @return the current orientation.
+     */
+    public int getOrientation() {
+        return mOrientation;
+    }
+
+    /**
+     * @return the relevant insets (status bar, nav bar, cutouts).
+     */
+    public Insets getInsets() {
+        return mInsets;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 0dcd1d2..0714c5e 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -27,7 +27,6 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.annotation.SuppressLint;
-import android.app.ActivityView;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -36,9 +35,9 @@
 import android.content.res.TypedArray;
 import android.graphics.ColorMatrix;
 import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Insets;
 import android.graphics.Outline;
 import android.graphics.Paint;
-import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.RectF;
@@ -48,7 +47,6 @@
 import android.provider.Settings;
 import android.util.Log;
 import android.view.Choreographer;
-import android.view.DisplayCutout;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.SurfaceControl;
@@ -58,7 +56,6 @@
 import android.view.ViewOutlineProvider;
 import android.view.ViewTreeObserver;
 import android.view.WindowInsets;
-import android.view.WindowManager;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 import android.view.animation.AccelerateDecelerateInterpolator;
@@ -74,18 +71,13 @@
 import androidx.dynamicanimation.animation.SpringForce;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.systemui.Interpolators;
-import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.bubbles.animation.AnimatableScaleMatrix;
 import com.android.systemui.bubbles.animation.ExpandedAnimationController;
 import com.android.systemui.bubbles.animation.PhysicsAnimationLayout;
 import com.android.systemui.bubbles.animation.StackAnimationController;
-import com.android.systemui.model.SysUiState;
-import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.shared.system.SysUiStatsLog;
-import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
-import com.android.systemui.util.RelativeTouchListener;
 import com.android.wm.shell.animation.PhysicsAnimator;
 import com.android.wm.shell.common.FloatingContentCoordinator;
 import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
@@ -121,6 +113,8 @@
     /** Duration of the flyout alpha animations. */
     private static final int FLYOUT_ALPHA_ANIMATION_DURATION = 100;
 
+    private static final int FADE_IN_DURATION = 320;
+
     /** Percent to darken the bubbles when they're in the dismiss target. */
     private static final float DARKEN_PERCENT = 0.3f;
 
@@ -192,8 +186,6 @@
         }
     };
 
-    private Point mDisplaySize;
-
     private final BubbleData mBubbleData;
 
     private final ValueAnimator mDesaturateAndDarkenAnimator;
@@ -249,8 +241,8 @@
     private int mBubblePaddingTop;
     private int mBubbleTouchPadding;
     private int mExpandedViewPadding;
+    private int mPointerHeight;
     private int mCornerRadius;
-    private int mStatusBarHeight;
     private int mImeOffset;
     @Nullable private BubbleViewProvider mExpandedBubble;
     private boolean mIsExpanded;
@@ -302,7 +294,7 @@
                 pw.println("  expandedViewAlpha:  " + expandedView.getAlpha());
                 pw.println("  expandedViewTaskId: " + expandedView.getTaskId());
 
-                final ActivityView av = expandedView.getActivityView();
+                final View av = expandedView.getTaskView();
 
                 if (av != null) {
                     pw.println("  activityViewVis:    " + av.getVisibility());
@@ -323,8 +315,6 @@
     /** Callback to run when we want to unbubble the given notification's conversation. */
     private Consumer<String> mUnbubbleConversationCallback;
 
-    private SysUiState mSysUiState;
-
     private boolean mViewUpdatedRequested = false;
     private boolean mIsExpansionAnimating = false;
     private boolean mIsBubbleSwitchAnimating = false;
@@ -332,8 +322,6 @@
     /** The view to desaturate/darken when magneted to the dismiss target. */
     @Nullable private View mDesaturateAndDarkenTargetView;
 
-    private LayoutInflater mInflater;
-
     private Rect mTempRect = new Rect();
 
     private final List<Rect> mSystemGestureExclusionRects = Collections.singletonList(new Rect());
@@ -401,6 +389,11 @@
     public final Consumer<Boolean> mOnImeVisibilityChanged;
 
     /**
+     * Callback to run when the bubble expand status changes.
+     */
+    private final Consumer<Boolean> mOnBubbleExpandChanged;
+
+    /**
      * Callback to run to ask BubbleController to hide the current IME.
      */
     private final Runnable mHideCurrentInputMethodCallback;
@@ -660,7 +653,7 @@
                                     viewInitialX + dx, velX, velY) <= 0;
                     updateBubbleIcons();
                     logBubbleEvent(null /* no bubble associated with bubble stack move */,
-                            SysUiStatsLog.BUBBLE_UICHANGED__ACTION__STACK_MOVED);
+                            FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__STACK_MOVED);
                 }
                 mDismissView.hide();
             }
@@ -724,13 +717,11 @@
         }
     };
 
-    private DismissView mDismissView;
-    private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
-
     @Nullable
     private BubbleOverflow mBubbleOverflow;
     private StackEducationView mStackEduView;
     private ManageEducationView mManageEduView;
+    private DismissView mDismissView;
 
     private ViewGroup mManageMenu;
     private ImageView mManageSettingsIcon;
@@ -738,20 +729,20 @@
     private boolean mShowingManage = false;
     private PhysicsAnimator.SpringConfig mManageSpringConfig = new PhysicsAnimator.SpringConfig(
             SpringForce.STIFFNESS_MEDIUM, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
+    private BubblePositioner mPositioner;
+
     @SuppressLint("ClickableViewAccessibility")
     public BubbleStackView(Context context, BubbleData data,
             @Nullable SurfaceSynchronizer synchronizer,
             FloatingContentCoordinator floatingContentCoordinator,
-            SysUiState sysUiState,
             Runnable allBubblesAnimatedOutAction,
             Consumer<Boolean> onImeVisibilityChanged,
-            Runnable hideCurrentInputMethodCallback) {
+            Runnable hideCurrentInputMethodCallback,
+            Consumer<Boolean> onBubbleExpandChanged,
+            BubblePositioner positioner) {
         super(context);
 
         mBubbleData = data;
-        mInflater = LayoutInflater.from(context);
-
-        mSysUiState = sysUiState;
 
         Resources res = getResources();
         mMaxBubbles = res.getInteger(R.integer.bubbles_max_rendered);
@@ -759,15 +750,11 @@
         mBubbleElevation = res.getDimensionPixelSize(R.dimen.bubble_elevation);
         mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
         mBubbleTouchPadding = res.getDimensionPixelSize(R.dimen.bubble_touch_padding);
+        mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height);
 
-        mStatusBarHeight =
-                res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
         mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset);
 
-        mDisplaySize = new Point();
-        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
-        // We use the real size & subtract screen decorations / window insets ourselves when needed
-        wm.getDefaultDisplay().getRealSize(mDisplaySize);
+        mPositioner = positioner;
 
         mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding);
         int elevation = res.getDimensionPixelSize(R.dimen.bubble_elevation);
@@ -784,11 +771,10 @@
         };
 
         mStackAnimationController = new StackAnimationController(
-                floatingContentCoordinator, this::getBubbleCount, onBubbleAnimatedOut);
+                floatingContentCoordinator, this::getBubbleCount, onBubbleAnimatedOut, mPositioner);
 
         mExpandedAnimationController = new ExpandedAnimationController(
-                mDisplaySize, mExpandedViewPadding, res.getConfiguration().orientation,
-                onBubbleAnimatedOut);
+                mPositioner, mExpandedViewPadding, onBubbleAnimatedOut);
         mSurfaceSynchronizer = synchronizer != null ? synchronizer : DEFAULT_SURFACE_SYNCHRONIZER;
 
         // Force LTR by default since most of the Bubbles UI is positioned manually by the user, or
@@ -823,10 +809,10 @@
         mAnimatingOutSurfaceContainer.addView(mAnimatingOutSurfaceView);
 
         mAnimatingOutSurfaceContainer.setPadding(
-                mExpandedViewPadding,
-                mExpandedViewPadding,
-                mExpandedViewPadding,
-                mExpandedViewPadding);
+                mExpandedViewContainer.getPaddingLeft(),
+                mExpandedViewContainer.getPaddingTop(),
+                mExpandedViewContainer.getPaddingRight(),
+                mExpandedViewContainer.getPaddingBottom());
 
         setUpManageMenu();
 
@@ -865,49 +851,28 @@
 
         mOnImeVisibilityChanged = onImeVisibilityChanged;
         mHideCurrentInputMethodCallback = hideCurrentInputMethodCallback;
+        mOnBubbleExpandChanged = onBubbleExpandChanged;
 
         setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> {
             onImeVisibilityChanged.accept(insets.getInsets(WindowInsets.Type.ime()).bottom > 0);
-
             if (!mIsExpanded || mIsExpansionAnimating) {
                 return view.onApplyWindowInsets(insets);
             }
-            mExpandedAnimationController.updateYPosition(
-                    // Update the insets after we're done translating otherwise position
-                    // calculation for them won't be correct.
-                    () -> {
-                        if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
-                            mExpandedBubble.getExpandedView().updateInsets(insets);
-                        }
-                    });
             return view.onApplyWindowInsets(insets);
         });
 
         mOrientationChangedListener =
                 (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
-                    mExpandedAnimationController.updateResources(mOrientation, mDisplaySize);
-                    mStackAnimationController.updateResources(mOrientation);
+                    onDisplaySizeChanged();
+                    mExpandedAnimationController.updateResources();
+                    mStackAnimationController.updateResources();
                     mBubbleOverflow.updateResources();
 
-                    // Need to update the padding around the view
-                    WindowInsets insets = getRootWindowInsets();
-                    int leftPadding = mExpandedViewPadding;
-                    int rightPadding = mExpandedViewPadding;
-                    if (insets != null) {
-                        // Can't have the expanded view overlaying notches
-                        int cutoutLeft = 0;
-                        int cutoutRight = 0;
-                        DisplayCutout cutout = insets.getDisplayCutout();
-                        if (cutout != null) {
-                            cutoutLeft = cutout.getSafeInsetLeft();
-                            cutoutRight = cutout.getSafeInsetRight();
-                        }
-                        // Or overlaying nav or status bar
-                        leftPadding += Math.max(cutoutLeft, insets.getStableInsetLeft());
-                        rightPadding += Math.max(cutoutRight, insets.getStableInsetRight());
+                    if (mRelativeStackPositionBeforeRotation != null) {
+                        mStackAnimationController.setStackPosition(
+                                mRelativeStackPositionBeforeRotation);
+                        mRelativeStackPositionBeforeRotation = null;
                     }
-                    mExpandedViewContainer.setPadding(leftPadding, mExpandedViewPadding,
-                            rightPadding, mExpandedViewPadding);
 
                     if (mIsExpanded) {
                         // Re-draw bubble row and pointer for new orientation.
@@ -917,15 +882,10 @@
                         mExpandedAnimationController.expandFromStack(() -> {
                             afterExpandedViewAnimation();
                         } /* after */);
-                        mExpandedViewContainer.setTranslationX(0);
+                        mExpandedViewContainer.setTranslationX(0f);
                         mExpandedViewContainer.setTranslationY(getExpandedViewY());
                         mExpandedViewContainer.setAlpha(1f);
                     }
-                    if (mRelativeStackPositionBeforeRotation != null) {
-                        mStackAnimationController.setStackPosition(
-                                mRelativeStackPositionBeforeRotation);
-                        mRelativeStackPositionBeforeRotation = null;
-                    }
                     removeOnLayoutChangeListener(mOrientationChangedListener);
                 };
 
@@ -974,7 +934,7 @@
 
         animate()
                 .setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED)
-                .setDuration(CollapsedStatusBarFragment.FADE_IN_DURATION);
+                .setDuration(FADE_IN_DURATION);
     }
 
     /**
@@ -1066,7 +1026,7 @@
                         mBubbleData.setExpanded(false);
                         mContext.startActivityAsUser(intent, bubble.getUser());
                         logBubbleEvent(bubble,
-                                SysUiStatsLog.BUBBLE_UICHANGED__ACTION__HEADER_GO_TO_SETTINGS);
+                                FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__HEADER_GO_TO_SETTINGS);
                     }
                 });
 
@@ -1082,8 +1042,7 @@
      * Whether the educational view should show for the expanded view "manage" menu.
      */
     private boolean shouldShowManageEdu() {
-        final boolean seen = Prefs.getBoolean(mContext,
-                Prefs.Key.HAS_SEEN_BUBBLES_MANAGE_EDUCATION, false /* default */);
+        final boolean seen = getPrefBoolean(ManageEducationViewKt.PREF_MANAGED_EDUCATION);
         final boolean shouldShow = (!seen || BubbleDebugConfig.forceShowUserEducation(mContext))
                 && mExpandedBubble != null;
         if (BubbleDebugConfig.DEBUG_USER_EDUCATION) {
@@ -1107,8 +1066,7 @@
      * Whether education view should show for the collapsed stack.
      */
     private boolean shouldShowStackEdu() {
-        final boolean seen = Prefs.getBoolean(getContext(),
-                Prefs.Key.HAS_SEEN_BUBBLES_EDUCATION, false /* default */);
+        final boolean seen = getPrefBoolean(StackEducationViewKt.PREF_STACK_EDUCATION);
         final boolean shouldShow = !seen || BubbleDebugConfig.forceShowUserEducation(mContext);
         if (BubbleDebugConfig.DEBUG_USER_EDUCATION) {
             Log.d(TAG, "Show stack edu: " + shouldShow);
@@ -1116,6 +1074,11 @@
         return shouldShow;
     }
 
+    private boolean getPrefBoolean(String key) {
+        return mContext.getSharedPreferences(mContext.getPackageName(), Context.MODE_PRIVATE)
+                .getBoolean(key, false /* default */);
+    }
+
     /**
      * @return true if education view for collapsed stack should show and was not showing before.
      */
@@ -1156,6 +1119,10 @@
         addView(mFlyout, new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
     }
 
+    void updateFlyout(float fontScale) {
+        mFlyout.updateFontSize(fontScale);
+    }
+
     private void updateOverflow() {
         mBubbleOverflow.update();
         mBubbleContainer.reorderView(mBubbleOverflow.getIconView(),
@@ -1185,26 +1152,16 @@
     }
 
     /** Respond to the phone being rotated by repositioning the stack and hiding any flyouts. */
-    public void onOrientationChanged(int orientation) {
-        mOrientation = orientation;
-
-        // Display size is based on the rotation device was in when requested, we should update it
-        // We use the real size & subtract screen decorations / window insets ourselves when needed
-        WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
-        wm.getDefaultDisplay().getRealSize(mDisplaySize);
-
-        // Some resources change depending on orientation
+    public void onOrientationChanged() {
         Resources res = getContext().getResources();
-        mStatusBarHeight = res.getDimensionPixelSize(
-                com.android.internal.R.dimen.status_bar_height);
         mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
 
         mRelativeStackPositionBeforeRotation = mStackAnimationController.getRelativeStackPosition();
-        addOnLayoutChangeListener(mOrientationChangedListener);
-        hideFlyoutImmediate();
-
         mManageMenu.setVisibility(View.INVISIBLE);
         mShowingManage = false;
+
+        addOnLayoutChangeListener(mOrientationChangedListener);
+        hideFlyoutImmediate();
     }
 
     /** Tells the views with locale-dependent layout direction to resolve the new direction. */
@@ -1224,11 +1181,7 @@
     public void onDisplaySizeChanged() {
         updateOverflow();
 
-        WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
-        wm.getDefaultDisplay().getRealSize(mDisplaySize);
         Resources res = getContext().getResources();
-        mStatusBarHeight = res.getDimensionPixelSize(
-                com.android.internal.R.dimen.status_bar_height);
         mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
         mBubbleSize = getResources().getDimensionPixelSize(R.dimen.individual_bubble_size);
         for (Bubble b : mBubbleData.getBubbles()) {
@@ -1238,8 +1191,8 @@
             }
             b.getIconView().setLayoutParams(new LayoutParams(mBubbleSize, mBubbleSize));
         }
-        mExpandedAnimationController.updateResources(mOrientation, mDisplaySize);
-        mStackAnimationController.updateResources(mOrientation);
+        mExpandedAnimationController.updateResources();
+        mStackAnimationController.updateResources();
         mDismissView.updateResources();
         mMagneticTarget.setMagneticFieldRadiusPx(mBubbleSize * 2);
     }
@@ -1250,7 +1203,15 @@
 
         mTempRect.setEmpty();
         getTouchableRegion(mTempRect);
-        inoutInfo.touchableRegion.set(mTempRect);
+        if (mIsExpanded && mExpandedBubble != null
+                && mExpandedBubble.getExpandedView() != null
+                && mExpandedBubble.getExpandedView().getTaskView() != null) {
+            inoutInfo.touchableRegion.set(mTempRect);
+            mExpandedBubble.getExpandedView().getTaskView().getBoundsOnScreen(mTempRect);
+            inoutInfo.touchableRegion.op(mTempRect, Region.Op.DIFFERENCE);
+        } else {
+            inoutInfo.touchableRegion.set(mTempRect);
+        }
     }
 
     @Override
@@ -1438,13 +1399,6 @@
     }
 
     /**
-     * The {@link BadgedImageView} that is expanded, null if one does not exist.
-     */
-    View getExpandedBubbleView() {
-        return mExpandedBubble != null ? mExpandedBubble.getIconView() : null;
-    }
-
-    /**
      * The {@link Bubble} that is expanded, null if one does not exist.
      */
     @Nullable
@@ -1484,7 +1438,7 @@
                 new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
         animateInFlyoutForBubble(bubble);
         requestUpdate();
-        logBubbleEvent(bubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__POSTED);
+        logBubbleEvent(bubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__POSTED);
     }
 
     // via BubbleData.Listener
@@ -1504,7 +1458,7 @@
                     bubble.cleanupViews();
                 }
                 updatePointerPosition();
-                logBubbleEvent(bubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
+                logBubbleEvent(bubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
                 return;
             }
         }
@@ -1522,7 +1476,7 @@
     void updateBubble(Bubble bubble) {
         animateInFlyoutForBubble(bubble);
         requestUpdate();
-        logBubbleEvent(bubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__UPDATED);
+        logBubbleEvent(bubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__UPDATED);
     }
 
     public void updateBubbleOrder(List<Bubble> bubbles) {
@@ -1559,7 +1513,7 @@
             return;
         }
 
-        if (bubbleToSelect.getKey() == BubbleOverflow.KEY) {
+        if (bubbleToSelect.getKey().equals(BubbleOverflow.KEY)) {
             mBubbleData.setShowingOverflow(true);
         } else {
             mBubbleData.setShowingOverflow(false);
@@ -1618,8 +1572,9 @@
                 requestUpdate();
 
                 logBubbleEvent(previouslySelected,
-                        SysUiStatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
-                logBubbleEvent(bubbleToSelect, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED);
+                        FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
+                logBubbleEvent(bubbleToSelect,
+                        FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED);
                 notifyExpansionChanged(previouslySelected, false /* expanded */);
                 notifyExpansionChanged(bubbleToSelect, true /* expanded */);
             });
@@ -1649,30 +1604,21 @@
 
         hideCurrentInputMethod();
 
-        mSysUiState
-                .setFlag(QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED, shouldExpand)
-                .commitUpdate(mContext.getDisplayId());
+        mOnBubbleExpandChanged.accept(shouldExpand);
 
         if (mIsExpanded) {
             animateCollapse();
-            logBubbleEvent(mExpandedBubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
+            logBubbleEvent(mExpandedBubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
         } else {
             animateExpansion();
             // TODO: move next line to BubbleData
-            logBubbleEvent(mExpandedBubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED);
-            logBubbleEvent(mExpandedBubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__STACK_EXPANDED);
+            logBubbleEvent(mExpandedBubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED);
+            logBubbleEvent(mExpandedBubble,
+                    FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__STACK_EXPANDED);
         }
         notifyExpansionChanged(mExpandedBubble, mIsExpanded);
     }
 
-    void showExpandedViewContents(int displayId) {
-        if (mExpandedBubble != null
-                && mExpandedBubble.getExpandedView() != null
-                && mExpandedBubble.getExpandedView().getVirtualDisplayId() == displayId) {
-            mExpandedBubble.setContentVisibility(true);
-        }
-    }
-
     /**
      * Asks the BubbleController to hide the IME from anywhere, whether it's focused on Bubbles or
      * not.
@@ -1696,7 +1642,8 @@
 
     private void animateExpansion() {
         cancelDelayedExpandCollapseSwitchAnimations();
-
+        final boolean isLandscape =
+                mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE;
         mIsExpanded = true;
         if (mStackEduView != null) {
             mStackEduView.hide(true /* fromExpansion */);
@@ -1707,26 +1654,26 @@
         updateOverflowVisibility();
         updatePointerPosition();
         mExpandedAnimationController.expandFromStack(() -> {
-            afterExpandedViewAnimation();
             if (mIsExpanded && mExpandedBubble.getExpandedView() != null) {
                 maybeShowManageEdu();
             }
         } /* after */);
 
-        mExpandedViewContainer.setTranslationX(0);
+        mExpandedViewContainer.setTranslationX(0f);
         mExpandedViewContainer.setTranslationY(getExpandedViewY());
         mExpandedViewContainer.setAlpha(1f);
 
         // X-value of the bubble we're expanding, once it's settled in its row.
-        final float bubbleWillBeAtX =
-                mExpandedAnimationController.getBubbleLeft(
+        final float bubbleWillBeAt =
+                mExpandedAnimationController.getBubbleXOrYForOrientation(
                         mBubbleData.getBubbles().indexOf(mExpandedBubble));
 
         // How far horizontally the bubble will be animating. We'll wait a bit longer for bubbles
         // that are animating farther, so that the expanded view doesn't move as much.
-        final float horizontalDistanceAnimated =
-                Math.abs(bubbleWillBeAtX
-                        - mStackAnimationController.getStackPosition().x);
+        final float relevantStackPosition = isLandscape
+                ? mStackAnimationController.getStackPosition().y
+                : mStackAnimationController.getStackPosition().x;
+        final float distanceAnimated = Math.abs(bubbleWillBeAt - relevantStackPosition);
 
         // Wait for the path animation target to reach its end, and add a small amount of extra time
         // if the bubble is moving a lot horizontally.
@@ -1736,13 +1683,26 @@
         if (getWidth() > 0) {
             startDelay = (long)
                     (ExpandedAnimationController.EXPAND_COLLAPSE_TARGET_ANIM_DURATION
-                            + (horizontalDistanceAnimated / getWidth()) * 30);
+                            + (distanceAnimated / getWidth()) * 30);
         }
 
         // Set the pivot point for the scale, so the expanded view animates out from the bubble.
-        mExpandedViewContainerMatrix.setScale(
-                0f, 0f,
-                bubbleWillBeAtX + mBubbleSize / 2f, getExpandedViewY());
+        if (isLandscape) {
+            float pivotX;
+            float pivotY = bubbleWillBeAt + mBubbleSize / 2f;
+            if (mStackOnLeftOrWillBe) {
+                pivotX = mPositioner.getAvailableRect().left + mBubbleSize + mExpandedViewPadding;
+            } else {
+                pivotX = mPositioner.getAvailableRect().right - mBubbleSize - mExpandedViewPadding;
+            }
+            mExpandedViewContainerMatrix.setScale(
+                    0f, 0f,
+                    pivotX, pivotY);
+        } else {
+            mExpandedViewContainerMatrix.setScale(
+                    0f, 0f,
+                    bubbleWillBeAt + mBubbleSize / 2f, getExpandedViewY());
+        }
         mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
 
         if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
@@ -1762,19 +1722,20 @@
                         if (mExpandedBubble == null || mExpandedBubble.getIconView() == null) {
                             return;
                         }
+                        float translation = isLandscape
+                                ? mExpandedBubble.getIconView().getTranslationY()
+                                : mExpandedBubble.getIconView().getTranslationX();
                         mExpandedViewContainerMatrix.postTranslate(
-                                mExpandedBubble.getIconView().getTranslationX()
-                                        - bubbleWillBeAtX,
+                                translation - bubbleWillBeAt,
                                 0);
                         mExpandedViewContainer.setAnimationMatrix(
                                 mExpandedViewContainerMatrix);
                     })
                     .withEndActions(() -> {
+                        afterExpandedViewAnimation();
                         if (mExpandedBubble != null
                                 && mExpandedBubble.getExpandedView() != null) {
                             mExpandedBubble.getExpandedView()
-                                    .setContentVisibility(true);
-                            mExpandedBubble.getExpandedView()
                                     .setSurfaceZOrderedOnTop(false);
                         }
                     })
@@ -1813,16 +1774,29 @@
         // We want to visually collapse into this bubble during the animation.
         final View expandingFromBubble = mExpandedBubble.getIconView();
 
-        // X-value the bubble is animating from (back into the stack).
-        final float expandingFromBubbleAtX =
-                mExpandedAnimationController.getBubbleLeft(
+        // Value the bubble is animating from (back into the stack).
+        final float expandingFromBubbleAt =
+                mExpandedAnimationController.getBubbleXOrYForOrientation(
                         mBubbleData.getBubbles().indexOf(mExpandedBubble));
-
-        // Set the pivot point.
-        mExpandedViewContainerMatrix.setScale(
-                1f, 1f,
-                expandingFromBubbleAtX + mBubbleSize / 2f,
-                getExpandedViewY());
+        final boolean isLandscape =
+                mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE;
+        if (isLandscape) {
+            float pivotX;
+            float pivotY = expandingFromBubbleAt + mBubbleSize / 2f;
+            if (mStackOnLeftOrWillBe) {
+                pivotX = mPositioner.getAvailableRect().left + mBubbleSize + mExpandedViewPadding;
+            } else {
+                pivotX = mPositioner.getAvailableRect().right - mBubbleSize - mExpandedViewPadding;
+            }
+            mExpandedViewContainerMatrix.setScale(
+                    1f, 1f,
+                    pivotX, pivotY);
+        } else {
+            mExpandedViewContainerMatrix.setScale(
+                    1f, 1f,
+                    expandingFromBubbleAt + mBubbleSize / 2f,
+                    getExpandedViewY());
+        }
 
         PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel();
         PhysicsAnimator.getInstance(mExpandedViewContainerMatrix)
@@ -1831,9 +1805,15 @@
                 .addUpdateListener((target, values) -> {
                     if (expandingFromBubble != null) {
                         // Follow the bubble as it translates!
-                        mExpandedViewContainerMatrix.postTranslate(
-                                expandingFromBubble.getTranslationX()
-                                        - expandingFromBubbleAtX, 0f);
+                        if (isLandscape) {
+                            mExpandedViewContainerMatrix.postTranslate(
+                                    0f, expandingFromBubble.getTranslationY()
+                                            - expandingFromBubbleAt);
+                        } else {
+                            mExpandedViewContainerMatrix.postTranslate(
+                                    expandingFromBubble.getTranslationX()
+                                            - expandingFromBubbleAt, 0f);
+                        }
                     }
 
                     mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
@@ -1877,26 +1857,55 @@
         // The surface contains a screenshot of the animating out bubble, so we just need to animate
         // it out (and then release the GraphicBuffer).
         PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer).cancel();
-        PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer)
+        PhysicsAnimator animator = PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer)
                 .spring(DynamicAnimation.SCALE_X, 0f, mScaleOutSpringConfig)
                 .spring(DynamicAnimation.SCALE_Y, 0f, mScaleOutSpringConfig)
-                .spring(DynamicAnimation.TRANSLATION_Y,
-                        mAnimatingOutSurfaceContainer.getTranslationY() - mBubbleSize * 2,
-                        mTranslateSpringConfig)
-                .withEndActions(this::releaseAnimatingOutBubbleBuffer)
-                .start();
+                .withEndActions(this::releaseAnimatingOutBubbleBuffer);
+
+        if (mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE) {
+            float translationX = mStackAnimationController.isStackOnLeftSide()
+                    ? mAnimatingOutSurfaceContainer.getTranslationX() + mBubbleSize * 2
+                    : mAnimatingOutSurfaceContainer.getTranslationX();
+            animator.spring(DynamicAnimation.TRANSLATION_X,
+                    translationX,
+                    mTranslateSpringConfig)
+                    .start();
+        } else {
+            animator.spring(DynamicAnimation.TRANSLATION_Y,
+                    mAnimatingOutSurfaceContainer.getTranslationY() - mBubbleSize * 2,
+                    mTranslateSpringConfig)
+                    .start();
+        }
 
         boolean isOverflow = mExpandedBubble != null
                 && mExpandedBubble.getKey().equals(BubbleOverflow.KEY);
-        float expandingFromBubbleDestinationX =
-                mExpandedAnimationController.getBubbleLeft(isOverflow ? getBubbleCount()
+        float expandingFromBubbleDestination =
+                mExpandedAnimationController.getBubbleXOrYForOrientation(isOverflow
+                        ? getBubbleCount()
                         : mBubbleData.getBubbles().indexOf(mExpandedBubble));
 
         mExpandedViewContainer.setAlpha(1f);
         mExpandedViewContainer.setVisibility(View.VISIBLE);
 
-        mExpandedViewContainerMatrix.setScale(
-                0f, 0f, expandingFromBubbleDestinationX + mBubbleSize / 2f, getExpandedViewY());
+        if (mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE) {
+            float pivotX;
+            float pivotY = expandingFromBubbleDestination + mBubbleSize / 2f;
+            if (mStackOnLeftOrWillBe) {
+                pivotX = mPositioner.getAvailableRect().left + mBubbleSize + mExpandedViewPadding;
+            } else {
+                pivotX = mPositioner.getAvailableRect().right - mBubbleSize - mExpandedViewPadding;
+
+            }
+            mExpandedViewContainerMatrix.setScale(
+                    0f, 0f,
+                    pivotX, pivotY);
+        } else {
+            mExpandedViewContainerMatrix.setScale(
+                    0f, 0f,
+                    expandingFromBubbleDestination + mBubbleSize / 2f,
+                    getExpandedViewY());
+        }
+
         mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
 
         mDelayedAnimationHandler.postDelayed(() -> {
@@ -1918,7 +1927,6 @@
                     })
                     .withEndActions(() -> {
                         if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
-                            mExpandedBubble.getExpandedView().setContentVisibility(true);
                             mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(false);
                         }
 
@@ -1954,13 +1962,6 @@
         }
     }
 
-    /** Return the BubbleView at the given index from the bubble container. */
-    public BadgedImageView getBubbleAt(int i) {
-        return getBubbleCount() > i
-                ? (BadgedImageView) mBubbleContainer.getChildAt(i)
-                : null;
-    }
-
     /** Moves the bubbles out of the way if they're going to be over the keyboard. */
     public void onImeVisibilityChanged(boolean visible, int height) {
         mStackAnimationController.setImeHeight(visible ? height + mImeOffset : 0);
@@ -1982,6 +1983,9 @@
                                 FLYOUT_IME_ANIMATION_SPRING_CONFIG)
                         .start();
             }
+        } else if (mIsExpanded && mExpandedBubble != null
+                && mExpandedBubble.getExpandedView() != null) {
+            mExpandedBubble.getExpandedView().setImeVisible(visible);
         }
     }
 
@@ -2189,14 +2193,15 @@
      * Calculates the y position of the expanded view when it is expanded.
      */
     float getExpandedViewY() {
-        return getStatusBarHeight() + mBubbleSize + mBubblePaddingTop;
+        final int top = mPositioner.getAvailableRect().top;
+        if (mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE) {
+            return top + mExpandedViewPadding;
+        } else {
+            return top + mBubbleSize + mBubblePaddingTop;
+        }
     }
 
-    /**
-     * Animates in the flyout for the given bubble, if available, and then hides it after some time.
-     */
-    @VisibleForTesting
-    void animateInFlyoutForBubble(Bubble bubble) {
+    private boolean shouldShowFlyout(Bubble bubble) {
         Bubble.FlyoutMessage flyoutMessage = bubble.getFlyoutMessage();
         final BadgedImageView bubbleView = bubble.getIconView();
         if (flyoutMessage == null
@@ -2208,11 +2213,22 @@
                 || mIsGestureInProgress
                 || mBubbleToExpandAfterFlyoutCollapse != null
                 || bubbleView == null) {
-            if (bubbleView != null) {
+            if (bubbleView != null && mFlyout.getVisibility() != VISIBLE) {
                 bubbleView.removeDotSuppressionFlag(BadgedImageView.SuppressionFlag.FLYOUT_VISIBLE);
             }
             // Skip the message if none exists, we're expanded or animating expansion, or we're
             // about to expand a bubble from the previous tapped flyout, or if bubble view is null.
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Animates in the flyout for the given bubble, if available, and then hides it after some time.
+     */
+    @VisibleForTesting
+    void animateInFlyoutForBubble(Bubble bubble) {
+        if (!shouldShowFlyout(bubble)) {
             return;
         }
 
@@ -2230,25 +2246,23 @@
             }
 
             // Stop suppressing the dot now that the flyout has morphed into the dot.
-            bubbleView.removeDotSuppressionFlag(
-                    BadgedImageView.SuppressionFlag.FLYOUT_VISIBLE);
-
-            mFlyout.setVisibility(INVISIBLE);
-
+            if (bubble.getIconView() != null) {
+                bubble.getIconView().removeDotSuppressionFlag(
+                        BadgedImageView.SuppressionFlag.FLYOUT_VISIBLE);
+            }
             // Hide the stack after a delay, if needed.
             updateTemporarilyInvisibleAnimation(false /* hideImmediately */);
         };
-        mFlyout.setVisibility(INVISIBLE);
 
         // Suppress the dot when we are animating the flyout.
-        bubbleView.addDotSuppressionFlag(
+        bubble.getIconView().addDotSuppressionFlag(
                 BadgedImageView.SuppressionFlag.FLYOUT_VISIBLE);
 
         // Start flyout expansion. Post in case layout isn't complete and getWidth returns 0.
         post(() -> {
             // An auto-expanding bubble could have been posted during the time it takes to
             // layout.
-            if (isExpanded()) {
+            if (isExpanded() || bubble.getIconView() == null) {
                 return;
             }
             final Runnable expandFlyoutAfterDelay = () -> {
@@ -2265,23 +2279,26 @@
                 mFlyout.postDelayed(mAnimateInFlyout, 200);
             };
 
-            if (bubble.getIconView() == null) {
-                return;
-            }
 
-            mFlyout.setupFlyoutStartingAsDot(flyoutMessage,
-                    mStackAnimationController.getStackPosition(), getWidth(),
-                    mStackAnimationController.isStackOnLeftSide(),
-                    bubble.getIconView().getDotColor() /* dotColor */,
-                    expandFlyoutAfterDelay /* onLayoutComplete */,
-                    mAfterFlyoutHidden,
-                    bubble.getIconView().getDotCenter(),
-                    !bubble.showDot());
+            if (mFlyout.getVisibility() == View.VISIBLE) {
+                mFlyout.animateUpdate(bubble.getFlyoutMessage(), getWidth(),
+                        mStackAnimationController.getStackPosition().y);
+            } else {
+                mFlyout.setVisibility(INVISIBLE);
+                mFlyout.setupFlyoutStartingAsDot(bubble.getFlyoutMessage(),
+                        mStackAnimationController.getStackPosition(), getWidth(),
+                        mStackAnimationController.isStackOnLeftSide(),
+                        bubble.getIconView().getDotColor() /* dotColor */,
+                        expandFlyoutAfterDelay /* onLayoutComplete */,
+                        mAfterFlyoutHidden,
+                        bubble.getIconView().getDotCenter(),
+                        !bubble.showDot());
+            }
             mFlyout.bringToFront();
         });
         mFlyout.removeCallbacks(mHideFlyout);
         mFlyout.postDelayed(mHideFlyout, FLYOUT_HIDE_AFTER);
-        logBubbleEvent(bubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__FLYOUT);
+        logBubbleEvent(bubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__FLYOUT);
     }
 
     /** Hide the flyout immediately and cancel any pending hide runnables. */
@@ -2337,19 +2354,6 @@
         }
     }
 
-    private int getStatusBarHeight() {
-        if (getRootWindowInsets() != null) {
-            WindowInsets insets = getRootWindowInsets();
-            return Math.max(
-                    mStatusBarHeight,
-                    insets.getDisplayCutout() != null
-                            ? insets.getDisplayCutout().getSafeInsetTop()
-                            : 0);
-        }
-
-        return 0;
-    }
-
     private void requestUpdate() {
         if (mViewUpdatedRequested || mIsExpansionAnimating) {
             return;
@@ -2389,7 +2393,6 @@
         final float targetY = mTempRect.bottom - mManageMenu.getHeight();
 
         final float xOffsetForAnimation = (isLtr ? 1 : -1) * mManageMenu.getWidth() / 4f;
-
         if (show) {
             mManageMenu.setScaleX(0.5f);
             mManageMenu.setScaleY(0.5f);
@@ -2406,6 +2409,8 @@
                     .withEndActions(() -> {
                         View child = mManageMenu.getChildAt(0);
                         child.requestAccessibilityFocus();
+                        // Update the AV's obscured touchable region for the new visibility state.
+                        mExpandedBubble.getExpandedView().updateObscuredTouchableRegion();
                     })
                     .start();
 
@@ -2417,12 +2422,15 @@
                     .spring(DynamicAnimation.SCALE_Y, 0.5f)
                     .spring(DynamicAnimation.TRANSLATION_X, targetX - xOffsetForAnimation)
                     .spring(DynamicAnimation.TRANSLATION_Y, targetY + mManageMenu.getHeight() / 4f)
-                    .withEndActions(() -> mManageMenu.setVisibility(View.INVISIBLE))
+                    .withEndActions(() -> {
+                        mManageMenu.setVisibility(View.INVISIBLE);
+                        if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
+                            // Update the AV's obscured touchable region for the new state.
+                            mExpandedBubble.getExpandedView().updateObscuredTouchableRegion();
+                        }
+                    })
                     .start();
         }
-
-        // Update the AV's obscured touchable region for the new menu visibility state.
-        mExpandedBubble.getExpandedView().updateObscuredTouchableRegion();
     }
 
     private void updateExpandedBubble() {
@@ -2442,7 +2450,6 @@
             mExpandedViewContainer.setAlpha(0f);
             mExpandedViewContainer.addView(bev);
             bev.setManageClickListener((view) -> showManageMenu(!mShowingManage));
-            bev.populateExpandedView();
 
             if (!mIsExpansionAnimating) {
                 mSurfaceSynchronizer.syncSurfaceAndRun(() -> {
@@ -2495,11 +2502,11 @@
         PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer).cancel();
         mAnimatingOutSurfaceContainer.setScaleX(1f);
         mAnimatingOutSurfaceContainer.setScaleY(1f);
-        mAnimatingOutSurfaceContainer.setTranslationX(0);
+        mAnimatingOutSurfaceContainer.setTranslationX(mExpandedViewContainer.getPaddingLeft());
         mAnimatingOutSurfaceContainer.setTranslationY(0);
 
         final int[] activityViewLocation =
-                mExpandedBubble.getExpandedView().getActivityViewLocationOnScreen();
+                mExpandedBubble.getExpandedView().getTaskViewLocationOnScreen();
         final int[] surfaceViewLocation = mAnimatingOutSurfaceView.getLocationOnScreen();
 
         // Translate the surface to overlap the real ActivityView.
@@ -2553,9 +2560,22 @@
             Log.d(TAG, "updateExpandedView: mIsExpanded=" + mIsExpanded);
         }
 
+        // Need to update the padding around the view for any insets
+        Insets insets = mPositioner.getInsets();
+        int leftPadding = insets.left + mExpandedViewPadding;
+        int rightPadding = insets.right + mExpandedViewPadding;
+        if (mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE) {
+            if (!mStackAnimationController.isStackOnLeftSide()) {
+                rightPadding += mPointerHeight + mBubbleSize;
+            } else {
+                leftPadding += mPointerHeight + mBubbleSize;
+            }
+        }
+        mExpandedViewContainer.setPadding(leftPadding, 0, rightPadding, 0);
         mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
         if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
             mExpandedViewContainer.setTranslationY(getExpandedViewY());
+            mExpandedViewContainer.setTranslationX(0f);
             mExpandedBubble.getExpandedView().updateView(
                     mExpandedViewContainer.getLocationOnScreen());
         }
@@ -2598,12 +2618,27 @@
         if (index == -1) {
             return;
         }
-        float bubbleLeftFromScreenLeft = mExpandedAnimationController.getBubbleLeft(index);
-        float halfBubble = mBubbleSize / 2f;
-        float bubbleCenter = bubbleLeftFromScreenLeft + halfBubble;
-        // Padding might be adjusted for insets, so get it directly from the view
-        bubbleCenter -= mExpandedViewContainer.getPaddingLeft();
-        mExpandedBubble.getExpandedView().setPointerPosition(bubbleCenter);
+        float bubblePosition = mExpandedAnimationController.getBubbleXOrYForOrientation(index);
+        if (mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE) {
+            float x = mStackOnLeftOrWillBe
+                    ? mPositioner.getAvailableRect().left
+                    : mPositioner.getAvailableRect().right
+                            - mExpandedViewContainer.getPaddingRight()
+                            - mPointerHeight;
+            float bubbleCenter = bubblePosition - getExpandedViewY() + (mBubbleSize / 2f);
+            mExpandedBubble.getExpandedView().setPointerPosition(
+                    x,
+                    bubbleCenter,
+                    true,
+                    mStackOnLeftOrWillBe);
+        } else {
+            float bubbleCenter = bubblePosition + (mBubbleSize / 2f);
+            mExpandedBubble.getExpandedView().setPointerPosition(
+                    bubbleCenter,
+                    getExpandedViewY(),
+                    false,
+                    mStackOnLeftOrWillBe);
+        }
     }
 
     /**
@@ -2632,7 +2667,7 @@
      * @return the normalized x-axis position of the bubble stack rounded to 4 decimal places.
      */
     public float getNormalizedXPosition() {
-        return new BigDecimal(getStackPosition().x / mDisplaySize.x)
+        return new BigDecimal(getStackPosition().x / mPositioner.getAvailableRect().width())
                 .setScale(4, RoundingMode.CEILING.HALF_UP)
                 .floatValue();
     }
@@ -2641,7 +2676,7 @@
      * @return the normalized y-axis position of the bubble stack rounded to 4 decimal places.
      */
     public float getNormalizedYPosition() {
-        return new BigDecimal(getStackPosition().y / mDisplaySize.y)
+        return new BigDecimal(getStackPosition().y / mPositioner.getAvailableRect().height())
                 .setScale(4, RoundingMode.CEILING.HALF_UP)
                 .floatValue();
     }
@@ -2675,17 +2710,6 @@
                 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() || mExpandedBubble == null || mExpandedBubble.getExpandedView() == null) {
-            return false;
-        }
-        return mExpandedBubble.getExpandedView().performBackPressIfNeeded();
-    }
-
     /** For debugging only */
     List<Bubble> getBubblesOnScreen() {
         List<Bubble> bubbles = new ArrayList<>();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTaskView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTaskView.java
deleted file mode 100644
index 06205c5..0000000
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTaskView.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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 static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
-import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.ActivityOptions;
-import android.app.PendingIntent;
-import android.window.TaskEmbedder;
-import android.window.TaskOrganizerTaskEmbedder;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ShortcutInfo;
-import android.graphics.Matrix;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.view.IWindow;
-import android.view.SurfaceControl;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
-
-import dalvik.system.CloseGuard;
-
-
-public class BubbleTaskView extends SurfaceView implements SurfaceHolder.Callback,
-        TaskEmbedder.Host {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleTaskView" : TAG_BUBBLES;
-
-    private final CloseGuard mGuard = CloseGuard.get();
-    private boolean mOpened; // Protected by mGuard.
-
-    private TaskEmbedder mTaskEmbedder;
-    private final SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
-    private final Rect mTmpRect = new Rect();
-
-    public BubbleTaskView(Context context) {
-        super(context);
-
-        mTaskEmbedder = new TaskOrganizerTaskEmbedder(context, this);
-        setUseAlpha();
-        getHolder().addCallback(this);
-
-        mOpened = true;
-        mGuard.open("release");
-    }
-
-    public void setCallback(TaskEmbedder.Listener callback) {
-        if (callback == null) {
-            mTaskEmbedder.setListener(null);
-            return;
-        }
-        mTaskEmbedder.setListener(callback);
-    }
-
-    public void startShortcutActivity(@NonNull ShortcutInfo shortcut,
-            @NonNull ActivityOptions options, @Nullable Rect sourceBounds) {
-        mTaskEmbedder.startShortcutActivity(shortcut, options, sourceBounds);
-    }
-
-    public void startActivity(@NonNull PendingIntent pendingIntent, @Nullable Intent fillInIntent,
-            @NonNull ActivityOptions options) {
-        mTaskEmbedder.startActivity(pendingIntent, fillInIntent, options);
-    }
-
-    public void onLocationChanged() {
-        mTaskEmbedder.notifyBoundsChanged();
-    }
-
-    @Override
-    public Rect getScreenBounds() {
-        getBoundsOnScreen(mTmpRect);
-        return mTmpRect;
-    }
-
-    @Override
-    public void onTaskBackgroundColorChanged(TaskEmbedder ts, int bgColor) {
-        setResizeBackgroundColor(bgColor);
-    }
-
-    @Override
-    public Region getTapExcludeRegion() {
-        // Not used
-        return null;
-    }
-
-    @Override
-    public Matrix getScreenToTaskMatrix() {
-        // Not used
-        return null;
-    }
-
-    @Override
-    public IWindow getWindow() {
-        // Not used
-        return null;
-    }
-
-    @Override
-    public Point getPositionInWindow() {
-        // Not used
-        return null;
-    }
-
-    @Override
-    public boolean canReceivePointerEvents() {
-        // Not used
-        return false;
-    }
-
-    public void release() {
-        if (!mTaskEmbedder.isInitialized()) {
-            throw new IllegalStateException(
-                    "Trying to release container that is not initialized.");
-        }
-        performRelease();
-    }
-
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            if (mGuard != null) {
-                mGuard.warnIfOpen();
-                performRelease();
-            }
-        } finally {
-            super.finalize();
-        }
-    }
-
-    private void performRelease() {
-        if (!mOpened) {
-            return;
-        }
-        getHolder().removeCallback(this);
-        mTaskEmbedder.release();
-        mTaskEmbedder.setListener(null);
-
-        mGuard.close();
-        mOpened = false;
-    }
-
-    @Override
-    public void surfaceCreated(SurfaceHolder holder) {
-        if (!mTaskEmbedder.isInitialized()) {
-            mTaskEmbedder.initialize(getSurfaceControl());
-        } else {
-            mTmpTransaction.reparent(mTaskEmbedder.getSurfaceControl(),
-                    getSurfaceControl()).apply();
-        }
-        mTaskEmbedder.start();
-    }
-
-    @Override
-    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
-        mTaskEmbedder.resizeTask(width, height);
-        mTaskEmbedder.notifyBoundsChanged();
-    }
-
-    @Override
-    public void surfaceDestroyed(SurfaceHolder holder) {
-        mTaskEmbedder.stop();
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
index 28757fa..010a29e 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
@@ -22,8 +22,6 @@
 import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
 
 import android.annotation.NonNull;
-import android.app.Notification;
-import android.app.Person;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
@@ -36,8 +34,6 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.os.AsyncTask;
-import android.os.Parcelable;
-import android.text.TextUtils;
 import android.util.Log;
 import android.util.PathParser;
 import android.view.LayoutInflater;
@@ -47,10 +43,8 @@
 import com.android.internal.graphics.ColorUtils;
 import com.android.launcher3.icons.BitmapInfo;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import java.lang.ref.WeakReference;
-import java.util.List;
 import java.util.Objects;
 
 /**
@@ -208,73 +202,6 @@
         }
     }
 
-
-    /**
-     * Returns our best guess for the most relevant text summary of the latest update to this
-     * notification, based on its type. Returns null if there should not be an update message.
-     */
-    @NonNull
-    static Bubble.FlyoutMessage extractFlyoutMessage(NotificationEntry entry) {
-        Objects.requireNonNull(entry);
-        final Notification underlyingNotif = entry.getSbn().getNotification();
-        final Class<? extends Notification.Style> style = underlyingNotif.getNotificationStyle();
-
-        Bubble.FlyoutMessage bubbleMessage = new Bubble.FlyoutMessage();
-        bubbleMessage.isGroupChat = underlyingNotif.extras.getBoolean(
-                Notification.EXTRA_IS_GROUP_CONVERSATION);
-        try {
-            if (Notification.BigTextStyle.class.equals(style)) {
-                // Return the big text, it is big so probably important. If it's not there use the
-                // normal text.
-                CharSequence bigText =
-                        underlyingNotif.extras.getCharSequence(Notification.EXTRA_BIG_TEXT);
-                bubbleMessage.message = !TextUtils.isEmpty(bigText)
-                        ? bigText
-                        : underlyingNotif.extras.getCharSequence(Notification.EXTRA_TEXT);
-                return bubbleMessage;
-            } else if (Notification.MessagingStyle.class.equals(style)) {
-                final List<Notification.MessagingStyle.Message> messages =
-                        Notification.MessagingStyle.Message.getMessagesFromBundleArray(
-                                (Parcelable[]) underlyingNotif.extras.get(
-                                        Notification.EXTRA_MESSAGES));
-
-                final Notification.MessagingStyle.Message latestMessage =
-                        Notification.MessagingStyle.findLatestIncomingMessage(messages);
-                if (latestMessage != null) {
-                    bubbleMessage.message = latestMessage.getText();
-                    Person sender = latestMessage.getSenderPerson();
-                    bubbleMessage.senderName = sender != null ? sender.getName() : null;
-                    bubbleMessage.senderAvatar = null;
-                    bubbleMessage.senderIcon = sender != null ? sender.getIcon() : null;
-                    return bubbleMessage;
-                }
-            } else if (Notification.InboxStyle.class.equals(style)) {
-                CharSequence[] lines =
-                        underlyingNotif.extras.getCharSequenceArray(Notification.EXTRA_TEXT_LINES);
-
-                // Return the last line since it should be the most recent.
-                if (lines != null && lines.length > 0) {
-                    bubbleMessage.message = lines[lines.length - 1];
-                    return bubbleMessage;
-                }
-            } else if (Notification.MediaStyle.class.equals(style)) {
-                // Return nothing, media updates aren't typically useful as a text update.
-                return bubbleMessage;
-            } else {
-                // Default to text extra.
-                bubbleMessage.message =
-                        underlyingNotif.extras.getCharSequence(Notification.EXTRA_TEXT);
-                return bubbleMessage;
-            }
-        } catch (ClassCastException | NullPointerException | ArrayIndexOutOfBoundsException e) {
-            // No use crashing, we'll just return null and the caller will assume there's no update
-            // message.
-            e.printStackTrace();
-        }
-
-        return bubbleMessage;
-    }
-
     @Nullable
     static Drawable loadSenderAvatar(@NonNull final Context context, @Nullable final Icon icon) {
         Objects.requireNonNull(context);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java
index 916ad18..5cc24ce 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java
@@ -48,5 +48,5 @@
 
     boolean showDot();
 
-    int getDisplayId();
+    int getTaskId();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java
index 34828b3..4882abc 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java
@@ -17,14 +17,13 @@
 package com.android.systemui.bubbles;
 
 import android.annotation.NonNull;
-import android.content.Context;
-import android.view.Display;
 
 import androidx.annotation.MainThread;
 
 import com.android.systemui.statusbar.ScrimView;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.wm.shell.ShellTaskOrganizer;
 
 import java.util.List;
 
@@ -57,12 +56,6 @@
      */
     ScrimView getScrimForBubble();
 
-    /**
-     * @return the display id of the expanded view, if the stack is expanded and not occluded by the
-     * status bar, otherwise returns {@link Display#INVALID_DISPLAY}.
-     */
-    int getExpandedDisplayId(Context context);
-
     /** @return Bubbles for updating overflow. */
     List<Bubble> getOverflowBubbles();
 
@@ -77,13 +70,6 @@
      */
     void expandStackAndSelectBubble(NotificationEntry entry);
 
-
-    /**
-     * Directs a back gesture at the bubble stack. When opened, the current expanded bubble
-     * is forwarded a back key down/up pair.
-     */
-    void performBackPressIfNeeded();
-
     /** Promote the provided bubbles when overflow view. */
     void promoteBubbleFromOverflow(Bubble bubble);
 
@@ -142,4 +128,10 @@
 
     /** Set a listener to be notified of when overflow view update. */
     void setOverflowListener(BubbleData.Listener listener);
+
+    /** The task listener for events in bubble tasks. **/
+    ShellTaskOrganizer getTaskOrganizer();
+
+    /** Contains information to help position things on the screen.  */
+    BubblePositioner getPositioner();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt b/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt
index 26a9773..3db07c2 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt
@@ -25,8 +25,6 @@
 import android.widget.TextView
 import com.android.internal.util.ContrastColorUtil
 import com.android.systemui.Interpolators
-import com.android.systemui.Prefs
-import com.android.systemui.Prefs.Key.HAS_SEEN_BUBBLES_MANAGE_EDUCATION
 import com.android.systemui.R
 
 /**
@@ -38,8 +36,8 @@
     private val TAG = if (BubbleDebugConfig.TAG_WITH_CLASS_NAME) "BubbleManageEducationView"
         else BubbleDebugConfig.TAG_BUBBLES
 
-    private val ANIMATE_DURATION : Long = 200
-    private val ANIMATE_DURATION_SHORT : Long = 40
+    private val ANIMATE_DURATION: Long = 200
+    private val ANIMATE_DURATION_SHORT: Long = 40
 
     private val manageView by lazy { findViewById<View>(R.id.manage_education_view) }
     private val manageButton by lazy { findViewById<Button>(R.id.manage) }
@@ -50,7 +48,7 @@
     private var isHiding = false
 
     init {
-        LayoutInflater.from(context).inflate(R.layout.bubbles_manage_button_education, this);
+        LayoutInflater.from(context).inflate(R.layout.bubbles_manage_button_education, this)
         visibility = View.GONE
         elevation = resources.getDimensionPixelSize(R.dimen.bubble_elevation).toFloat()
 
@@ -95,7 +93,7 @@
      *
      * @param show whether the user education view should show or not.
      */
-    fun show(expandedView: BubbleExpandedView, rect : Rect) {
+    fun show(expandedView: BubbleExpandedView, rect: Rect) {
         if (visibility == VISIBLE) return
 
         alpha = 0f
@@ -136,10 +134,13 @@
             .withEndAction {
                 isHiding = false
                 visibility = GONE
-            };
+            }
     }
 
     private fun setShouldShow(shouldShow: Boolean) {
-        Prefs.putBoolean(context, HAS_SEEN_BUBBLES_MANAGE_EDUCATION, !shouldShow)
+        context.getSharedPreferences(context.packageName, Context.MODE_PRIVATE)
+                .edit().putBoolean(PREF_MANAGED_EDUCATION, !shouldShow).apply()
     }
-}
\ No newline at end of file
+}
+
+const val PREF_MANAGED_EDUCATION: String = "HasSeenBubblesManageOnboarding"
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/ObjectWrapper.java b/packages/SystemUI/src/com/android/systemui/bubbles/ObjectWrapper.java
new file mode 100644
index 0000000..f054122
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/ObjectWrapper.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.os.Binder;
+import android.os.IBinder;
+
+// Copied from Launcher3
+/**
+ * Utility class to pass non-parcealable objects within same process using parcealable payload.
+ *
+ * It wraps the object in a binder as binders are singleton within a process
+ */
+public class ObjectWrapper<T> extends Binder {
+
+    private T mObject;
+
+    public ObjectWrapper(T object) {
+        mObject = object;
+    }
+
+    public T get() {
+        return mObject;
+    }
+
+    public void clear() {
+        mObject = null;
+    }
+
+    public static IBinder wrap(Object obj) {
+        return new ObjectWrapper<>(obj);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/RelativeTouchListener.kt b/packages/SystemUI/src/com/android/systemui/bubbles/RelativeTouchListener.kt
similarity index 99%
rename from packages/SystemUI/src/com/android/systemui/util/RelativeTouchListener.kt
rename to packages/SystemUI/src/com/android/systemui/bubbles/RelativeTouchListener.kt
index 8880df9..b1291a5 100644
--- a/packages/SystemUI/src/com/android/systemui/util/RelativeTouchListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/RelativeTouchListener.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.util
+package com.android.systemui.bubbles
 
 import android.graphics.PointF
 import android.os.Handler
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/StackEducationView.kt b/packages/SystemUI/src/com/android/systemui/bubbles/StackEducationView.kt
index 3e4c729..216df2e 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/StackEducationView.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/StackEducationView.kt
@@ -24,21 +24,19 @@
 import android.widget.TextView
 import com.android.internal.util.ContrastColorUtil
 import com.android.systemui.Interpolators
-import com.android.systemui.Prefs
-import com.android.systemui.Prefs.Key.HAS_SEEN_BUBBLES_EDUCATION
 import com.android.systemui.R
 
 /**
  * User education view to highlight the collapsed stack of bubbles.
  * Shown only the first time a user taps the stack.
  */
-class StackEducationView constructor(context: Context) : LinearLayout(context){
+class StackEducationView constructor(context: Context) : LinearLayout(context) {
 
     private val TAG = if (BubbleDebugConfig.TAG_WITH_CLASS_NAME) "BubbleStackEducationView"
         else BubbleDebugConfig.TAG_BUBBLES
 
-    private val ANIMATE_DURATION : Long = 200
-    private val ANIMATE_DURATION_SHORT : Long = 40
+    private val ANIMATE_DURATION: Long = 200
+    private val ANIMATE_DURATION_SHORT: Long = 40
 
     private val view by lazy { findViewById<View>(R.id.stack_education_layout) }
     private val titleTextView by lazy { findViewById<TextView>(R.id.stack_education_title) }
@@ -47,7 +45,7 @@
     private var isHiding = false
 
     init {
-        LayoutInflater.from(context).inflate(R.layout.bubble_stack_user_education, this);
+        LayoutInflater.from(context).inflate(R.layout.bubble_stack_user_education, this)
 
         visibility = View.GONE
         elevation = resources.getDimensionPixelSize(R.dimen.bubble_elevation).toFloat()
@@ -93,7 +91,7 @@
      *
      * @return true if user education was shown, false otherwise.
      */
-    fun show(stackPosition: PointF) : Boolean{
+    fun show(stackPosition: PointF): Boolean {
         if (visibility == VISIBLE) return false
 
         setAlpha(0f)
@@ -129,6 +127,9 @@
     }
 
     private fun setShouldShow(shouldShow: Boolean) {
-        Prefs.putBoolean(context, HAS_SEEN_BUBBLES_EDUCATION, !shouldShow)
+        context.getSharedPreferences(context.packageName, Context.MODE_PRIVATE)
+                .edit().putBoolean(PREF_STACK_EDUCATION, !shouldShow).apply()
     }
-}
\ No newline at end of file
+}
+
+const val PREF_STACK_EDUCATION: String = "HasSeenBubblesOnboarding"
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/TaskView.java b/packages/SystemUI/src/com/android/systemui/bubbles/TaskView.java
new file mode 100644
index 0000000..85616d1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/TaskView.java
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutInfo;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.os.Handler;
+import android.view.SurfaceControl;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.window.WindowContainerToken;
+import android.window.WindowContainerTransaction;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+
+import dalvik.system.CloseGuard;
+
+import java.io.PrintWriter;
+import java.util.concurrent.Executor;
+
+/**
+ * View that can display a task.
+ */
+// TODO: Place in com.android.wm.shell vs. com.android.wm.shell.bubbles on shell migration.
+public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
+        ShellTaskOrganizer.TaskListener {
+
+    public interface Listener {
+        /** Called when the container is ready for launching activities. */
+        default void onInitialized() {}
+
+        /** Called when the container can no longer launch activities. */
+        default void onReleased() {}
+
+        /** Called when a task is created inside the container. */
+        default void onTaskCreated(int taskId, ComponentName name) {}
+
+        /** Called when a task visibility changes. */
+        default void onTaskVisibilityChanged(int taskId, boolean visible) {}
+
+        /** Called when a task is about to be removed from the stack inside the container. */
+        default void onTaskRemovalStarted(int taskId) {}
+
+        /** Called when a task is created inside the container. */
+        default void onBackPressedOnTaskRoot(int taskId) {}
+    }
+
+    private final CloseGuard mGuard = CloseGuard.get();
+
+    private final ShellTaskOrganizer mTaskOrganizer;
+
+    private ActivityManager.RunningTaskInfo mTaskInfo;
+    private WindowContainerToken mTaskToken;
+    private SurfaceControl mTaskLeash;
+    private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
+    private boolean mSurfaceCreated;
+    private boolean mIsInitialized;
+    private Listener mListener;
+    private final Executor mExecutor;
+
+    private final Rect mTmpRect = new Rect();
+    private final Rect mTmpRootRect = new Rect();
+
+    public TaskView(Context context, ShellTaskOrganizer organizer, Executor executor) {
+        super(context, null, 0, 0, true /* disableBackgroundLayer */);
+
+        mExecutor = executor;
+        mTaskOrganizer = organizer;
+        setUseAlpha();
+        getHolder().addCallback(this);
+        mGuard.open("release");
+    }
+
+    /**
+     * Only one listener may be set on the view, throws an exception otherwise.
+     */
+    public void setListener(Listener listener) {
+        if (mListener != null) {
+            throw new IllegalStateException(
+                    "Trying to set a listener when one has already been set");
+        }
+        mListener = listener;
+    }
+
+    /**
+     * Launch an activity represented by {@link ShortcutInfo}.
+     * <p>The owner of this container must be allowed to access the shortcut information,
+     * as defined in {@link LauncherApps#hasShortcutHostPermission()} to use this method.
+     *
+     * @param shortcut the shortcut used to launch the activity.
+     * @param options options for the activity.
+     * @param sourceBounds the rect containing the source bounds of the clicked icon to open
+     *                     this shortcut.
+     */
+    public void startShortcutActivity(@NonNull ShortcutInfo shortcut,
+            @NonNull ActivityOptions options, @Nullable Rect sourceBounds) {
+        prepareActivityOptions(options);
+        LauncherApps service = mContext.getSystemService(LauncherApps.class);
+        try {
+            service.startShortcut(shortcut, sourceBounds, options.toBundle());
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Launch a new activity.
+     *
+     * @param pendingIntent Intent used to launch an activity.
+     * @param fillInIntent Additional Intent data, see {@link Intent#fillIn Intent.fillIn()}
+     * @param options options for the activity.
+     */
+    public void startActivity(@NonNull PendingIntent pendingIntent, @Nullable Intent fillInIntent,
+            @NonNull ActivityOptions options) {
+        prepareActivityOptions(options);
+        try {
+            pendingIntent.send(mContext, 0 /* code */, fillInIntent,
+                    null /* onFinished */, null /* handler */, null /* requiredPermission */,
+                    options.toBundle());
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private void prepareActivityOptions(ActivityOptions options) {
+        final Binder launchCookie = new Binder();
+        mTaskOrganizer.setPendingLaunchCookieListener(launchCookie, this);
+        options.setLaunchCookie(launchCookie);
+        options.setLaunchWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        options.setTaskAlwaysOnTop(true);
+    }
+
+    /**
+     * Call when view position or size has changed. Do not call when animating.
+     */
+    public void onLocationChanged() {
+        if (mTaskToken == null) {
+            return;
+        }
+        // Update based on the screen bounds
+        getBoundsOnScreen(mTmpRect);
+        getRootView().getBoundsOnScreen(mTmpRootRect);
+        if (!mTmpRootRect.contains(mTmpRect)) {
+            mTmpRect.offsetTo(0, 0);
+        }
+
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        wct.setBounds(mTaskToken, mTmpRect);
+        // TODO(b/151449487): Enable synchronization
+        mTaskOrganizer.applyTransaction(wct);
+    }
+
+    /**
+     * Release this container if it is initialized.
+     */
+    public void release() {
+        performRelease();
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            if (mGuard != null) {
+                mGuard.warnIfOpen();
+                performRelease();
+            }
+        } finally {
+            super.finalize();
+        }
+    }
+
+    private void performRelease() {
+        getHolder().removeCallback(this);
+        mTaskOrganizer.removeListener(this);
+        resetTaskInfo();
+        mGuard.close();
+        if (mListener != null && mIsInitialized) {
+            mListener.onReleased();
+            mIsInitialized = false;
+        }
+    }
+
+    private void resetTaskInfo() {
+        mTaskInfo = null;
+        mTaskToken = null;
+        mTaskLeash = null;
+    }
+
+    private void updateTaskVisibility() {
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        wct.setHidden(mTaskToken, !mSurfaceCreated /* hidden */);
+        mTaskOrganizer.applyTransaction(wct);
+        // TODO(b/151449487): Only call callback once we enable synchronization
+        if (mListener != null) {
+            mListener.onTaskVisibilityChanged(mTaskInfo.taskId, mSurfaceCreated);
+        }
+    }
+
+    @Override
+    public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo,
+            SurfaceControl leash) {
+        mExecutor.execute(() -> {
+            mTaskInfo = taskInfo;
+            mTaskToken = taskInfo.token;
+            mTaskLeash = leash;
+
+            if (mSurfaceCreated) {
+                // Surface is ready, so just reparent the task to this surface control
+                mTransaction.reparent(mTaskLeash, getSurfaceControl())
+                        .show(mTaskLeash)
+                        .apply();
+            } else {
+                // The surface has already been destroyed before the task has appeared,
+                // so go ahead and hide the task entirely
+                updateTaskVisibility();
+            }
+
+            // TODO: Synchronize show with the resize
+            onLocationChanged();
+            setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
+
+            if (mListener != null) {
+                mListener.onTaskCreated(taskInfo.taskId, taskInfo.baseActivity);
+            }
+        });
+    }
+
+    @Override
+    public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+        mExecutor.execute(() -> {
+            if (mTaskToken == null || !mTaskToken.equals(taskInfo.token)) return;
+
+            if (mListener != null) {
+                mListener.onTaskRemovalStarted(taskInfo.taskId);
+            }
+
+            // Unparent the task when this surface is destroyed
+            mTransaction.reparent(mTaskLeash, null).apply();
+            resetTaskInfo();
+        });
+    }
+
+    @Override
+    public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
+        mExecutor.execute(() -> {
+            mTaskInfo.taskDescription = taskInfo.taskDescription;
+            setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
+        });
+    }
+
+    @Override
+    public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) {
+        mExecutor.execute(() -> {
+            if (mTaskToken == null || !mTaskToken.equals(taskInfo.token)) return;
+            if (mListener != null) {
+                mListener.onBackPressedOnTaskRoot(taskInfo.taskId);
+            }
+        });
+    }
+
+    @Override
+    public void dump(@androidx.annotation.NonNull PrintWriter pw, String prefix) {
+        final String innerPrefix = prefix + "  ";
+        final String childPrefix = innerPrefix + "  ";
+        pw.println(prefix + this);
+    }
+
+    @Override
+    public String toString() {
+        return "TaskView" + ":" + (mTaskInfo != null ? mTaskInfo.taskId : "null");
+    }
+
+    @Override
+    public void surfaceCreated(SurfaceHolder holder) {
+        mSurfaceCreated = true;
+        if (mListener != null && !mIsInitialized) {
+            mIsInitialized = true;
+            mListener.onInitialized();
+        }
+        if (mTaskToken == null) {
+            // Nothing to update, task is not yet available
+            return;
+        }
+        // Reparent the task when this surface is created
+        mTransaction.reparent(mTaskLeash, getSurfaceControl())
+                .show(mTaskLeash)
+                .apply();
+        updateTaskVisibility();
+    }
+
+    @Override
+    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+        if (mTaskToken == null) {
+            return;
+        }
+        onLocationChanged();
+    }
+
+    @Override
+    public void surfaceDestroyed(SurfaceHolder holder) {
+        mSurfaceCreated = false;
+        if (mTaskToken == null) {
+            // Nothing to update, task is not yet available
+            return;
+        }
+
+        // Unparent the task when this surface is destroyed
+        mTransaction.reparent(mTaskLeash, null).apply();
+        updateTaskVisibility();
+    }
+}
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 9f88ee5..5a70401 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
@@ -19,11 +19,9 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Path;
-import android.graphics.Point;
 import android.graphics.PointF;
-import android.view.DisplayCutout;
+import android.graphics.Rect;
 import android.view.View;
-import android.view.WindowInsets;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -32,6 +30,7 @@
 
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
+import com.android.systemui.bubbles.BubblePositioner;
 import com.android.wm.shell.animation.PhysicsAnimator;
 import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
 
@@ -58,6 +57,9 @@
     /** Duration of the expand/collapse target path animation. */
     public static final int EXPAND_COLLAPSE_TARGET_ANIM_DURATION = 175;
 
+    /** Damping ratio for expand/collapse spring. */
+    private static final float DAMPING_RATIO_MEDIUM_LOW_BOUNCY = 0.65f;
+
     /** Stiffness for the expand/collapse path-following animation. */
     private static final int EXPAND_COLLAPSE_ANIM_STIFFNESS = 1000;
 
@@ -80,16 +82,8 @@
     private float mBubblePaddingTop;
     /** Size of each bubble. */
     private float mBubbleSizePx;
-    /** Space between bubbles in row above expanded view. */
-    private float mSpaceBetweenBubbles;
-    /** Height of the status bar. */
-    private float mStatusBarHeight;
-    /** Size of display. */
-    private Point mDisplaySize;
     /** Max number of bubbles shown in row above expanded view. */
     private int mBubblesMaxRendered;
-    /** What the current screen orientation is. */
-    private int mScreenOrientation;
 
     private boolean mAnimatingExpand = false;
 
@@ -101,7 +95,8 @@
     private boolean mPreparingToCollapse = false;
 
     private boolean mAnimatingCollapse = false;
-    private @Nullable Runnable mAfterExpand;
+    @Nullable
+    private Runnable mAfterExpand;
     private Runnable mAfterCollapse;
     private PointF mCollapsePoint;
 
@@ -135,9 +130,12 @@
      */
     private Runnable mOnBubbleAnimatedOutAction;
 
-    public ExpandedAnimationController(Point displaySize, int expandedViewPadding,
-            int orientation, Runnable onBubbleAnimatedOutAction) {
-        updateResources(orientation, displaySize);
+    private BubblePositioner mPositioner;
+
+    public ExpandedAnimationController(BubblePositioner positioner, int expandedViewPadding,
+            Runnable onBubbleAnimatedOutAction) {
+        mPositioner = positioner;
+        updateResources();
         mExpandedViewPadding = expandedViewPadding;
         mOnBubbleAnimatedOutAction = onBubbleAnimatedOutAction;
     }
@@ -149,7 +147,8 @@
     private boolean mBubbleDraggedOutEnough = false;
 
     /** End action to run when the lead bubble's expansion animation completes. */
-    @Nullable private Runnable mLeadBubbleEndAction;
+    @Nullable
+    private Runnable mLeadBubbleEndAction;
 
     /**
      * Animates expanding the bubbles into a row along the top of the screen, optionally running an
@@ -197,28 +196,17 @@
 
     /**
      * Update effective screen width based on current orientation.
-     * @param orientation Landscape or portrait.
-     * @param displaySize Updated display size.
      */
-    public void updateResources(int orientation, Point displaySize) {
-        mScreenOrientation = orientation;
-        mDisplaySize = displaySize;
+    public void updateResources() {
         if (mLayout == null) {
             return;
         }
         Resources res = mLayout.getContext().getResources();
         mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
-        mStatusBarHeight = res.getDimensionPixelSize(
-                com.android.internal.R.dimen.status_bar_height);
         mStackOffsetPx = res.getDimensionPixelSize(R.dimen.bubble_stack_offset);
         mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
         mBubbleSizePx = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
         mBubblesMaxRendered = res.getInteger(R.integer.bubbles_max_rendered);
-
-        // Includes overflow button.
-        float totalGapWidth = getWidthForDisplayingBubbles() - (mExpandedViewPadding * 2)
-                - (mBubblesMaxRendered + 1) * mBubbleSizePx;
-        mSpaceBetweenBubbles = totalGapWidth / mBubblesMaxRendered;
     }
 
     /**
@@ -267,20 +255,27 @@
                 // If we're expanding, first draw a line from the bubble's current position to the
                 // top of the screen.
                 path.lineTo(bubble.getTranslationX(), expandedY);
-
                 // Then, draw a line across the screen to the bubble's resting position.
-                path.lineTo(getBubbleLeft(index), expandedY);
+                if (mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE) {
+                    Rect availableRect = mPositioner.getAvailableRect();
+                    boolean onLeft = mCollapsePoint != null
+                            && mCollapsePoint.x < (availableRect.width() / 2f);
+                    float translationX = onLeft
+                            ? availableRect.left + mExpandedViewPadding
+                            : availableRect.right - mBubbleSizePx - mExpandedViewPadding;
+                    path.lineTo(translationX, getBubbleXOrYForOrientation(index));
+                } else {
+                    path.lineTo(getBubbleXOrYForOrientation(index), expandedY);
+                }
             } else {
-                final float sideMultiplier =
-                        mLayout.isFirstChildXLeftOfCenter(mCollapsePoint.x) ? -1 : 1;
-                final float stackedX = mCollapsePoint.x + (sideMultiplier * index * mStackOffsetPx);
+                final float stackedX = mCollapsePoint.x;
 
                 // If we're collapsing, draw a line from the bubble's current position to the side
                 // of the screen where the bubble will be stacked.
                 path.lineTo(stackedX, expandedY);
 
                 // Then, draw a line down to the stack position.
-                path.lineTo(stackedX, mCollapsePoint.y);
+                path.lineTo(stackedX, mCollapsePoint.y + index * mStackOffsetPx);
             }
 
             // The lead bubble should be the bubble with the longest distance to travel when we're
@@ -410,7 +405,8 @@
         updateBubblePositions();
     }
 
-    @Nullable public View getDraggedOutBubble() {
+    @Nullable
+    public View getDraggedOutBubble() {
         return mMagnetizedBubbleDraggingOut == null
                 ? null
                 : mMagnetizedBubbleDraggingOut.getUnderlyingObject();
@@ -429,7 +425,7 @@
         final int index = mLayout.indexOfChild(bubbleView);
 
         animationForChildAtIndex(index)
-                .position(getBubbleLeft(index), getExpandedY())
+                .position(getBubbleXOrYForOrientation(index), getExpandedY())
                 .withPositionStartVelocities(velX, velY)
                 .start(() -> bubbleView.setTranslationZ(0f) /* after */);
 
@@ -456,15 +452,7 @@
 
     /** The Y value of the row of expanded bubbles. */
     public float getExpandedY() {
-        if (mLayout == null || mLayout.getRootWindowInsets() == null) {
-            return 0;
-        }
-        final WindowInsets insets = mLayout.getRootWindowInsets();
-        return mBubblePaddingTop + Math.max(
-                mStatusBarHeight,
-                insets.getDisplayCutout() != null
-                        ? insets.getDisplayCutout().getSafeInsetTop()
-                        : 0);
+        return mPositioner.getAvailableRect().top + mBubblePaddingTop;
     }
 
     /** Description of current animation controller state. */
@@ -478,7 +466,7 @@
 
     @Override
     void onActiveControllerForLayout(PhysicsAnimationLayout layout) {
-        updateResources(mScreenOrientation, mDisplaySize);
+        updateResources();
 
         // Ensure that all child views are at 1x scale, and visible, in case they were animating
         // in.
@@ -510,7 +498,7 @@
     @Override
     SpringForce getSpringForce(DynamicAnimation.ViewProperty property, View view) {
         return new SpringForce()
-                .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)
+                .setDampingRatio(DAMPING_RATIO_MEDIUM_LOW_BOUNCY)
                 .setStiffness(SpringForce.STIFFNESS_LOW);
     }
 
@@ -523,7 +511,7 @@
         } else if (mAnimatingCollapse) {
             startOrUpdatePathAnimation(false /* expanding */);
         } else {
-            child.setTranslationX(getBubbleLeft(index));
+            child.setTranslationX(getBubbleXOrYForOrientation(index));
 
             // If we're preparing to collapse, don't start animations since the collapse animation
             // will take over and animate the new bubble into the correct (stacked) position.
@@ -592,76 +580,56 @@
                 return;
             }
 
-            animationForChild(bubble)
-                    .translationX(getBubbleLeft(i))
-                    .start();
-        }
-    }
-
-    /**
-     * @param index Bubble index in row.
-     * @return Bubble left x from left edge of screen.
-     */
-    public float getBubbleLeft(int index) {
-        final float bubbleFromRowLeft = index * (mBubbleSizePx + mSpaceBetweenBubbles);
-        return getRowLeft() + bubbleFromRowLeft;
-    }
-
-    /**
-     * When expanded, the bubbles are centered in the screen. In portrait, all available space is
-     * used. In landscape we have too much space so the value is restricted. This method accounts
-     * for window decorations (nav bar, cutouts).
-     *
-     * @return the desired width to display the expanded bubbles in.
-     */
-    public float getWidthForDisplayingBubbles() {
-        final float availableWidth = getAvailableScreenWidth(true /* includeStableInsets */);
-        if (mScreenOrientation == Configuration.ORIENTATION_LANDSCAPE) {
-            // display size y in landscape will be the smaller dimension of the screen
-            return Math.max(mDisplaySize.y, availableWidth * CENTER_BUBBLES_LANDSCAPE_PERCENT);
-        } else {
-            return availableWidth;
-        }
-    }
-
-    /**
-     * Determines the available screen width without the cutout.
-     *
-     * @param subtractStableInsets Whether or not stable insets should also be removed from the
-     *                             returned width.
-     * @return the total screen width available accounting for cutouts and insets,
-     * iff {@param includeStableInsets} is true.
-     */
-    private float getAvailableScreenWidth(boolean subtractStableInsets) {
-        float availableSize = mDisplaySize.x;
-        WindowInsets insets = mLayout != null ? mLayout.getRootWindowInsets() : null;
-        if (insets != null) {
-            int cutoutLeft = 0;
-            int cutoutRight = 0;
-            DisplayCutout cutout = insets.getDisplayCutout();
-            if (cutout != null) {
-                cutoutLeft = cutout.getSafeInsetLeft();
-                cutoutRight = cutout.getSafeInsetRight();
+            if (mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE) {
+                Rect availableRect = mPositioner.getAvailableRect();
+                boolean onLeft = mCollapsePoint != null
+                        && mCollapsePoint.x < (availableRect.width() / 2f);
+                animationForChild(bubble)
+                        .translationX(onLeft
+                                ? availableRect.left + mExpandedViewPadding
+                                : availableRect.right - mBubbleSizePx - mExpandedViewPadding)
+                        .translationY(getBubbleXOrYForOrientation(i))
+                        .start();
+            } else {
+                animationForChild(bubble)
+                        .translationX(getBubbleXOrYForOrientation(i))
+                        .translationY(getExpandedY())
+                        .start();
             }
-            final int stableLeft = subtractStableInsets ? insets.getStableInsetLeft() : 0;
-            final int stableRight = subtractStableInsets ? insets.getStableInsetRight() : 0;
-            availableSize -= Math.max(stableLeft, cutoutLeft);
-            availableSize -= Math.max(stableRight, cutoutRight);
         }
-        return availableSize;
     }
 
-    private float getRowLeft() {
+    /**
+     * When bubbles are expanded in portrait, they display at the top of the screen in a horizontal
+     * row. When in landscape, they show at the left or right side in a vertical row. This method
+     * accounts for screen orientation and will return an x or y value for the position of the
+     * bubble in the row.
+     *
+     * @param index Bubble index in row.
+     * @return the y position of the bubble if {@link Configuration#ORIENTATION_LANDSCAPE} and the
+     * x position if {@link Configuration#ORIENTATION_PORTRAIT}.
+     */
+    public float getBubbleXOrYForOrientation(int index) {
         if (mLayout == null) {
             return 0;
         }
-        float rowWidth = (mLayout.getChildCount() * mBubbleSizePx)
-                + ((mLayout.getChildCount() - 1) * mSpaceBetweenBubbles);
-
-        // This display size we're using includes the size of the insets, we want the true
-        // center of the display minus the notch here, which means we should include the
-        // stable insets (e.g. status bar, nav bar) in this calculation.
-        final float trueCenter = getAvailableScreenWidth(false /* subtractStableInsets */) / 2f;
-        return trueCenter - (rowWidth / 2f);
+        Rect availableRect = mPositioner.getAvailableRect();
+        final boolean isLandscape =
+                mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE;
+        final float availableSpace = isLandscape
+                ? availableRect.height()
+                : availableRect.width();
+        final float spaceForMaxBubbles = (mExpandedViewPadding * 2)
+                + (mBubblesMaxRendered + 1) * mBubbleSizePx;
+        final float spaceBetweenBubbles =
+                (availableSpace - spaceForMaxBubbles) / mBubblesMaxRendered;
+        final float expandedStackSize = (mLayout.getChildCount() * mBubbleSizePx)
+                + ((mLayout.getChildCount() - 1) * spaceBetweenBubbles);
+        final float centerPosition = isLandscape
+                ? availableRect.centerY()
+                : availableRect.centerX();
+        final float rowStart = centerPosition - (expandedStackSize / 2f);
+        final float positionInBar = index * (mBubbleSizePx + spaceBetweenBubbles);
+        return rowStart + positionInBar;
     }
 }
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 b7490a5..31e1ca8 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
@@ -24,7 +24,6 @@
 import android.provider.Settings;
 import android.util.Log;
 import android.view.View;
-import android.view.WindowInsets;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -35,6 +34,7 @@
 import androidx.dynamicanimation.animation.SpringForce;
 
 import com.android.systemui.R;
+import com.android.systemui.bubbles.BubblePositioner;
 import com.android.systemui.bubbles.BubbleStackView;
 import com.android.wm.shell.animation.PhysicsAnimator;
 import com.android.wm.shell.common.FloatingContentCoordinator;
@@ -59,12 +59,6 @@
 
     private static final String TAG = "Bubbs.StackCtrl";
 
-    /** Scale factor to use initially for new bubbles being animated in. */
-    private static final float ANIMATE_IN_STARTING_SCALE = 1.15f;
-
-    /** Translation factor (multiplied by stack offset) to use for bubbles being animated in/out. */
-    private static final int ANIMATE_TRANSLATION_FACTOR = 4;
-
     /** Values to use for animating bubbles in. */
     private static final float ANIMATE_IN_STIFFNESS = 1000f;
     private static final int ANIMATE_IN_START_DELAY = 25;
@@ -72,9 +66,9 @@
     /**
      * Values to use for the default {@link SpringForce} provided to the physics animation layout.
      */
-    public static final int DEFAULT_STIFFNESS = 12000;
+    public static final int SPRING_TO_TOUCH_STIFFNESS = 12000;
     public static final float IME_ANIMATION_STIFFNESS = SpringForce.STIFFNESS_LOW;
-    private static final int FLING_FOLLOW_STIFFNESS = 500;
+    private static final int CHAIN_STIFFNESS = 600;
     public static final float DEFAULT_BOUNCINESS = 0.9f;
 
     private final PhysicsAnimator.SpringConfig mAnimateOutSpringConfig =
@@ -198,10 +192,8 @@
     private int mBubblePaddingTop;
     /** How far offscreen the stack rests. */
     private int mBubbleOffscreen;
-    /** How far down the screen the stack starts, when there is no pre-existing location. */
-    private int mStackStartingVerticalOffset;
-    /** Height of the status bar. */
-    private float mStatusBarHeight;
+    /** Contains display size, orientation, and inset information. */
+    private BubblePositioner mPositioner;
 
     /** FloatingContentCoordinator instance for resolving floating content conflicts. */
     private FloatingContentCoordinator mFloatingContentCoordinator;
@@ -266,10 +258,12 @@
     public StackAnimationController(
             FloatingContentCoordinator floatingContentCoordinator,
             IntSupplier bubbleCountSupplier,
-            Runnable onBubbleAnimatedOutAction) {
+            Runnable onBubbleAnimatedOutAction,
+            BubblePositioner positioner) {
         mFloatingContentCoordinator = floatingContentCoordinator;
         mBubbleCountSupplier = bubbleCountSupplier;
         mOnBubbleAnimatedOutAction = onBubbleAnimatedOutAction;
+        mPositioner = positioner;
     }
 
     /**
@@ -583,45 +577,12 @@
      * be animated or dragged beyond them.
      */
     public RectF getAllowableStackPositionRegion() {
-        final WindowInsets insets = mLayout.getRootWindowInsets();
-        final RectF allowableRegion = new RectF();
-        if (insets != null) {
-            allowableRegion.left =
-                    -mBubbleOffscreen
-                            + Math.max(
-                            insets.getSystemWindowInsetLeft(),
-                            insets.getDisplayCutout() != null
-                                    ? insets.getDisplayCutout().getSafeInsetLeft()
-                                    : 0);
-            allowableRegion.right =
-                    mLayout.getWidth()
-                            - mBubbleSize
-                            + mBubbleOffscreen
-                            - Math.max(
-                            insets.getSystemWindowInsetRight(),
-                            insets.getDisplayCutout() != null
-                                    ? insets.getDisplayCutout().getSafeInsetRight()
-                                    : 0);
-
-            allowableRegion.top =
-                    mBubblePaddingTop
-                            + Math.max(
-                            mStatusBarHeight,
-                            insets.getDisplayCutout() != null
-                                    ? insets.getDisplayCutout().getSafeInsetTop()
-                                    : 0);
-            allowableRegion.bottom =
-                    mLayout.getHeight()
-                            - mBubbleSize
-                            - mBubblePaddingTop
-                            - (mImeHeight != UNSET ? mImeHeight + mBubblePaddingTop : 0f)
-                            - Math.max(
-                            insets.getStableInsetBottom(),
-                            insets.getDisplayCutout() != null
-                                    ? insets.getDisplayCutout().getSafeInsetBottom()
-                                    : 0);
-        }
-
+        final RectF allowableRegion = new RectF(mPositioner.getAvailableRect());
+        allowableRegion.left -= mBubbleOffscreen;
+        allowableRegion.top += mBubblePaddingTop;
+        allowableRegion.right += mBubbleOffscreen - mBubbleSize;
+        allowableRegion.bottom -= mBubblePaddingTop + mBubbleSize
+                + (mImeHeight != UNSET ? mImeHeight + mBubblePaddingTop : 0f);
         return allowableRegion;
     }
 
@@ -629,7 +590,7 @@
     public void moveStackFromTouch(float x, float y) {
         // Begin the spring-to-touch catch up animation if needed.
         if (mSpringToTouchOnNextMotionEvent) {
-            springStack(x, y, DEFAULT_STIFFNESS);
+            springStack(x, y, SPRING_TO_TOUCH_STIFFNESS);
             mSpringToTouchOnNextMotionEvent = false;
             mFirstBubbleSpringingToTouch = true;
         } else if (mFirstBubbleSpringingToTouch) {
@@ -744,15 +705,13 @@
 
     @Override
     float getOffsetForChainedPropertyAnimation(DynamicAnimation.ViewProperty property) {
-        if (property.equals(DynamicAnimation.TRANSLATION_X)) {
+        if (property.equals(DynamicAnimation.TRANSLATION_Y)) {
             // If we're in the dismiss target, have the bubbles pile on top of each other with no
             // offset.
             if (isStackStuckToTarget()) {
                 return 0f;
             } else {
-                // Offset to the left if we're on the left, or the right otherwise.
-                return mLayout.isFirstChildXLeftOfCenter(mStackPosition.x)
-                        ? -mStackOffset : mStackOffset;
+                return mStackOffset;
             }
         } else {
             return 0f;
@@ -762,14 +721,12 @@
     @Override
     SpringForce getSpringForce(DynamicAnimation.ViewProperty property, View view) {
         final ContentResolver contentResolver = mLayout.getContext().getContentResolver();
-        final float stiffness = Settings.Secure.getFloat(contentResolver, "bubble_stiffness",
-                mIsMovingFromFlinging ? FLING_FOLLOW_STIFFNESS : DEFAULT_STIFFNESS /* default */);
         final float dampingRatio = Settings.Secure.getFloat(contentResolver, "bubble_damping",
                 DEFAULT_BOUNCINESS);
 
         return new SpringForce()
                 .setDampingRatio(dampingRatio)
-                .setStiffness(stiffness);
+                .setStiffness(CHAIN_STIFFNESS);
     }
 
     @Override
@@ -828,22 +785,15 @@
         mBubbleBitmapSize = res.getDimensionPixelSize(R.dimen.bubble_bitmap_size);
         mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
         mBubbleOffscreen = res.getDimensionPixelSize(R.dimen.bubble_stack_offscreen);
-        mStackStartingVerticalOffset =
-                res.getDimensionPixelSize(R.dimen.bubble_stack_starting_offset_y);
-        mStatusBarHeight =
-                res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
     }
 
     /**
-     * Update effective screen width based on current orientation.
-     * @param orientation Landscape or portrait.
+     * Update resources.
      */
-    public void updateResources(int orientation) {
+    public void updateResources() {
         if (mLayout != null) {
             Resources res = mLayout.getContext().getResources();
             mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
-            mStatusBarHeight = res.getDimensionPixelSize(
-                    com.android.internal.R.dimen.status_bar_height);
         }
     }
 
@@ -980,19 +930,19 @@
             return;
         }
 
-        final float xOffset =
-                getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_X);
+        final float yOffset =
+                getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_Y);
 
         // Position the new bubble in the correct position, scaled down completely.
-        child.setTranslationX(mStackPosition.x + xOffset * index);
-        child.setTranslationY(mStackPosition.y);
+        child.setTranslationX(mStackPosition.x);
+        child.setTranslationY(mStackPosition.y + yOffset * index);
         child.setScaleX(0f);
         child.setScaleY(0f);
 
         // Push the subsequent views out of the way, if there are subsequent views.
         if (index + 1 < mLayout.getChildCount()) {
             animationForChildAtIndex(index + 1)
-                    .translationX(mStackPosition.x + xOffset * (index + 1))
+                    .translationY(mStackPosition.y + yOffset * (index + 1))
                     .withStiffness(SpringForce.STIFFNESS_LOW)
                     .start();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
index d2eaf0d..6b5f237 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
@@ -19,14 +19,15 @@
 import android.app.INotificationManager;
 import android.content.Context;
 import android.content.pm.LauncherApps;
+import android.os.Handler;
 import android.view.WindowManager;
 
+import com.android.internal.logging.UiEventLogger;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.bubbles.BubbleController;
-import com.android.systemui.bubbles.BubbleData;
-import com.android.systemui.bubbles.BubbleDataRepository;
 import com.android.systemui.bubbles.Bubbles;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -40,6 +41,7 @@
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.WindowManagerShellWrapper;
 import com.android.wm.shell.common.FloatingContentCoordinator;
 
@@ -59,7 +61,6 @@
             NotificationShadeWindowController notificationShadeWindowController,
             StatusBarStateController statusBarStateController,
             ShadeController shadeController,
-            BubbleData data,
             ConfigurationController configurationController,
             NotificationInterruptStateProvider interruptionStateProvider,
             ZenModeController zenModeController,
@@ -70,19 +71,20 @@
             FeatureFlags featureFlags,
             DumpManager dumpManager,
             FloatingContentCoordinator floatingContentCoordinator,
-            BubbleDataRepository bubbleDataRepository,
             SysUiState sysUiState,
             INotificationManager notifManager,
             IStatusBarService statusBarService,
             WindowManager windowManager,
             WindowManagerShellWrapper windowManagerShellWrapper,
-            LauncherApps launcherApps) {
-        return new BubbleController(
+            LauncherApps launcherApps,
+            UiEventLogger uiEventLogger,
+            @Main Handler mainHandler,
+            ShellTaskOrganizer organizer) {
+        return BubbleController.create(
                 context,
                 notificationShadeWindowController,
                 statusBarStateController,
                 shadeController,
-                data,
                 null /* synchronizer */,
                 configurationController,
                 interruptionStateProvider,
@@ -94,12 +96,14 @@
                 featureFlags,
                 dumpManager,
                 floatingContentCoordinator,
-                bubbleDataRepository,
                 sysUiState,
                 notifManager,
                 statusBarService,
                 windowManager,
                 windowManagerShellWrapper,
-                launcherApps);
+                launcherApps,
+                uiEventLogger,
+                mainHandler,
+                organizer);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubblePersistentRepository.kt b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubblePersistentRepository.kt
index f447965..ce0786d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubblePersistentRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubblePersistentRepository.kt
@@ -18,16 +18,11 @@
 import android.content.Context
 import android.util.AtomicFile
 import android.util.Log
-import com.android.systemui.dagger.SysUISingleton
 import java.io.File
 import java.io.FileOutputStream
 import java.io.IOException
-import javax.inject.Inject
 
-@SysUISingleton
-class BubblePersistentRepository @Inject constructor(
-    context: Context
-) {
+class BubblePersistentRepository(context: Context) {
 
     private val bubbleFile: AtomicFile = AtomicFile(File(context.filesDir,
             "overflow_bubbles.xml"), "overflow-bubbles")
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleVolatileRepository.kt b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleVolatileRepository.kt
index c6d5732..e0a7c78 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleVolatileRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleVolatileRepository.kt
@@ -19,8 +19,6 @@
 import android.os.UserHandle
 import com.android.internal.annotations.VisibleForTesting
 import com.android.systemui.bubbles.ShortcutKey
-import com.android.systemui.dagger.SysUISingleton
-import javax.inject.Inject
 
 private const val CAPACITY = 16
 
@@ -28,10 +26,7 @@
  * BubbleVolatileRepository holds the most updated snapshot of list of bubbles for in-memory
  * manipulation.
  */
-@SysUISingleton
-class BubbleVolatileRepository @Inject constructor(
-    private val launcherApps: LauncherApps
-) {
+class BubbleVolatileRepository(private val launcherApps: LauncherApps) {
     /**
      * An ordered set of bubbles based on their natural ordering.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
index 28bcf3a..d13e194 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
@@ -19,13 +19,13 @@
 import android.app.Activity;
 
 import com.android.systemui.ForegroundServicesDialog;
-import com.android.systemui.bubbles.BubbleOverflowActivity;
 import com.android.systemui.keyguard.WorkLockActivity;
 import com.android.systemui.screenrecord.ScreenRecordDialog;
 import com.android.systemui.settings.BrightnessDialog;
 import com.android.systemui.tuner.TunerActivity;
 import com.android.systemui.usb.UsbDebuggingActivity;
 import com.android.systemui.usb.UsbDebuggingSecondaryUserActivity;
+import com.android.systemui.user.CreateUserActivity;
 
 import dagger.Binds;
 import dagger.Module;
@@ -67,12 +67,6 @@
     @ClassKey(ScreenRecordDialog.class)
     public abstract Activity bindScreenRecordDialog(ScreenRecordDialog activity);
 
-    /** Inject into BubbleOverflowActivity. */
-    @Binds
-    @IntoMap
-    @ClassKey(BubbleOverflowActivity.class)
-    public abstract Activity bindBubbleOverflowActivity(BubbleOverflowActivity activity);
-
     /** Inject into UsbDebuggingActivity. */
     @Binds
     @IntoMap
@@ -85,4 +79,10 @@
     @ClassKey(UsbDebuggingSecondaryUserActivity.class)
     public abstract Activity bindUsbDebuggingSecondaryUserActivity(
             UsbDebuggingSecondaryUserActivity activity);
+
+    /** Inject into CreateUserActivity. */
+    @Binds
+    @IntoMap
+    @ClassKey(CreateUserActivity.class)
+    public abstract Activity bindCreateUserActivity(CreateUserActivity activity);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index e303754..3aa4626 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -38,7 +38,6 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.UiEventLogger;
-import com.android.internal.logging.UiEventLoggerImpl;
 import com.android.internal.util.NotificationMessagingUtil;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.ViewMediatorCallback;
@@ -66,6 +65,7 @@
 import com.android.systemui.shared.plugins.PluginManagerImpl;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.DevicePolicyManagerWrapper;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.systemui.shared.system.WindowManagerWrapper;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -263,6 +263,13 @@
         return ActivityManagerWrapper.getInstance();
     }
 
+    /** */
+    @Provides
+    @SysUISingleton
+    public TaskStackChangeListeners provideTaskStackChangeListeners() {
+        return TaskStackChangeListeners.getInstance();
+    }
+
     /** Provides and initializes the {#link BroadcastDispatcher} for SystemUI */
     @Provides
     @SysUISingleton
@@ -332,13 +339,6 @@
         return Choreographer.getInstance();
     }
 
-    /** Provides an instance of {@link com.android.internal.logging.UiEventLogger} */
-    @Provides
-    @SysUISingleton
-    static UiEventLogger provideUiEventLogger() {
-        return new UiEventLoggerImpl();
-    }
-
     /** */
     @Provides
     @SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java
index c5dc8cc..554d9cb 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java
@@ -16,9 +16,20 @@
 
 package com.android.systemui.dagger;
 
+import android.content.Context;
+import android.util.DisplayMetrics;
+
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.UiEventLoggerImpl;
 import com.android.systemui.util.concurrency.GlobalConcurrencyModule;
+import com.android.wm.shell.WindowManagerShellWrapper;
+import com.android.wm.shell.animation.FlingAnimationUtils;
+import com.android.wm.shell.common.FloatingContentCoordinator;
+
+import javax.inject.Singleton;
 
 import dagger.Module;
+import dagger.Provides;
 
 /**
  * Supplies globally scoped instances that should be available in all versions of SystemUI
@@ -39,4 +50,38 @@
         FrameworkServicesModule.class,
         GlobalConcurrencyModule.class})
 public class GlobalModule {
+
+    // TODO(b/161980186): Currently only used by Bubbles, can move back to WMShellBaseModule once
+    //                    Bubbles has migrated over
+    @Singleton
+    @Provides
+    static FloatingContentCoordinator provideFloatingContentCoordinator() {
+        return new FloatingContentCoordinator();
+    }
+
+    // TODO(b/161980186): Currently only used by Bubbles, can move back to WMShellBaseModule once
+    //                    Bubbles has migrated over
+    @Singleton
+    @Provides
+    static WindowManagerShellWrapper provideWindowManagerShellWrapper() {
+        return new WindowManagerShellWrapper();
+    }
+
+    // TODO(b/162923491): This should not be a singleton at all, the display metrics can change and
+    //                    callers should be creating a new builder on demand
+    @Singleton
+    @Provides
+    static FlingAnimationUtils.Builder provideFlingAnimationUtilsBuilder(
+            Context context) {
+        DisplayMetrics displayMetrics = new DisplayMetrics();
+        context.getDisplay().getMetrics(displayMetrics);
+        return new FlingAnimationUtils.Builder(displayMetrics);
+    }
+
+    /** Provides an instance of {@link com.android.internal.logging.UiEventLogger} */
+    @Provides
+    @Singleton
+    static UiEventLogger provideUiEventLogger() {
+        return new UiEventLoggerImpl();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
index 00fdf55..d648c94 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
@@ -52,7 +52,7 @@
     WMComponent.Builder getWMComponentBuilder();
 
     /**
-     * Builder for a SysuiComponent.
+     * Builder for a SysUIComponent.
      */
     SysUIComponent.Builder getSysUIComponent();
 
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index 4bea067..b098579 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -22,8 +22,15 @@
 import com.android.systemui.SystemUIAppComponentFactory;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.KeyguardSliceProvider;
+import com.android.systemui.shared.system.InputConsumerController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.util.InjectionInflationController;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.onehanded.OneHanded;
+import com.android.wm.shell.pip.Pip;
+import com.android.wm.shell.splitscreen.SplitScreen;
+
+import java.util.Optional;
 
 import dagger.BindsInstance;
 import dagger.Subcomponent;
@@ -43,15 +50,35 @@
     /**
      * Builder for a SysUIComponent.
      */
+    @SysUISingleton
     @Subcomponent.Builder
     interface Builder {
         @BindsInstance
-        Builder setStubAPIClass(WMComponent.StubAPIClass stubAPIClass);
+        Builder setPip(Optional<Pip> p);
+
+        @BindsInstance
+        Builder setSplitScreen(Optional<SplitScreen> s);
+
+        @BindsInstance
+        Builder setOneHanded(Optional<OneHanded> o);
+
+        @BindsInstance
+        Builder setInputConsumerController(InputConsumerController i);
+
+        @BindsInstance
+        Builder setShellTaskOrganizer(ShellTaskOrganizer s);
 
         SysUIComponent build();
     }
 
     /**
+     * Initializes all the SysUI components.
+     */
+    default void init() {
+        // Do nothing
+    }
+
+    /**
      * Provides a BootCompleteCache.
      */
     @SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
index 2c0b04f..7ca8e63 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
@@ -33,6 +33,7 @@
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.dock.DockManagerImpl;
 import com.android.systemui.doze.DozeHost;
+import com.android.systemui.media.dagger.MediaModule;
 import com.android.systemui.plugins.qs.QSFactory;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.power.EnhancedEstimates;
@@ -61,7 +62,6 @@
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.wmshell.WMShellModule;
 
 import javax.inject.Named;
 
@@ -74,9 +74,9 @@
  * overridden by the System UI implementation.
  */
 @Module(includes = {
-            QSModule.class,
-            WMShellModule.class
-        })
+        MediaModule.class,
+        QSModule.class
+})
 public abstract class SystemUIDefaultModule {
 
     @SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 8f4e738..a982ec5 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -21,6 +21,7 @@
 import com.android.systemui.BootCompleteCacheImpl;
 import com.android.systemui.appops.dagger.AppOpsModule;
 import com.android.systemui.assist.AssistModule;
+import com.android.systemui.bubbles.Bubbles;
 import com.android.systemui.controls.dagger.ControlsModule;
 import com.android.systemui.demomode.dagger.DemoModeModule;
 import com.android.systemui.doze.dagger.DozeComponent;
@@ -41,8 +42,10 @@
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.dagger.SmartRepliesInflationModule;
 import com.android.systemui.statusbar.policy.dagger.StatusBarPolicyModule;
 import com.android.systemui.tuner.dagger.TunerModule;
+import com.android.systemui.user.UserModule;
 import com.android.systemui.util.concurrency.SysUIConcurrencyModule;
 import com.android.systemui.util.dagger.UtilModule;
 import com.android.systemui.util.sensors.SensorModule;
@@ -73,19 +76,23 @@
             SensorModule.class,
             SettingsModule.class,
             SettingsUtilModule.class,
+            SmartRepliesInflationModule.class,
             StatusBarPolicyModule.class,
             SysUIConcurrencyModule.class,
             TunerModule.class,
+            UserModule.class,
             UtilModule.class,
             VolumeModule.class
         },
-        subcomponents = {StatusBarComponent.class,
-                NotificationRowComponent.class,
-                DozeComponent.class,
-                ExpandableNotificationRowComponent.class,
-                KeyguardBouncerComponent.class,
-                NotificationShelfComponent.class,
-                FragmentService.FragmentCreator.class})
+        subcomponents = {
+            StatusBarComponent.class,
+            NotificationRowComponent.class,
+            DozeComponent.class,
+            ExpandableNotificationRowComponent.class,
+            KeyguardBouncerComponent.class,
+            NotificationShelfComponent.class,
+            FragmentService.FragmentCreator.class
+        })
 public abstract class SystemUIModule {
 
     @Binds
@@ -119,6 +126,9 @@
     @BindsOptionalOf
     abstract StatusBar optionalStatusBar();
 
+    @BindsOptionalOf
+    abstract Bubbles optionalBubbles();
+
     @SysUISingleton
     @Binds
     abstract SystemClock bindSystemClock(SystemClockImpl systemClock);
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
index ad90eff..e3bd1b2 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
@@ -16,7 +16,15 @@
 
 package com.android.systemui.dagger;
 
-import javax.inject.Inject;
+import com.android.systemui.shared.system.InputConsumerController;
+import com.android.systemui.wmshell.WMShellModule;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.onehanded.OneHanded;
+import com.android.wm.shell.pip.Pip;
+import com.android.wm.shell.splitscreen.SplitScreen;
+
+import java.util.Optional;
 
 import dagger.Subcomponent;
 
@@ -24,7 +32,7 @@
  * Dagger Subcomponent for WindowManager.
  */
 @WMSingleton
-@Subcomponent(modules = {})
+@Subcomponent(modules = {WMShellModule.class})
 public interface WMComponent {
 
     /**
@@ -35,18 +43,36 @@
         WMComponent build();
     }
 
-
     /**
-     *  Example class used for passing an API to SysUI from WMShell.
-     *
-     *  TODO: Remove this once real WM classes are ready to go.
-     **/
-    @WMSingleton
-    class StubAPIClass {
-        @Inject
-        StubAPIClass() {}
+     * Initializes all the WMShell components before starting any of the SystemUI components.
+     */
+    default void init() {
+        // This is to prevent circular init problem by separating registration step out of its
+        // constructor. And make sure the initialization of DisplayImeController won't depend on
+        // specific feature anymore.
+        getDisplayImeController().startMonitorDisplays();
+        getShellTaskOrganizer().registerOrganizer();
     }
 
-    /** Create a StubAPIClass. */
-    StubAPIClass createStubAPIClass();
+    // Required components to be initialized at start up
+    @WMSingleton
+    ShellTaskOrganizer getShellTaskOrganizer();
+
+    @WMSingleton
+    DisplayImeController getDisplayImeController();
+
+    @WMSingleton
+    InputConsumerController getInputConsumerController();
+
+    // TODO(b/162923491): We currently pass the instances through to SysUI, but that may change
+    //                    depending on the threading mechanism we go with
+
+    @WMSingleton
+    Optional<OneHanded> getOneHanded();
+
+    @WMSingleton
+    Optional<Pip> getPip();
+
+    @WMSingleton
+    Optional<SplitScreen> getSplitScreen();
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/RootView.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/RootView.java
similarity index 88%
rename from packages/SystemUI/src/com/android/keyguard/dagger/RootView.java
rename to packages/SystemUI/src/com/android/systemui/dagger/qualifiers/RootView.java
index 5ebff09..e6c46c0 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/RootView.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/RootView.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.keyguard.dagger;
+package com.android.systemui.dagger.qualifiers;
 
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 6154a4e..f470a6b 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -123,6 +123,14 @@
     }
 
     /**
+     * Appends dozing event to the logs
+     * @param suppressed true if dozing is suppressed
+     */
+    public void traceDozingSuppressed(boolean suppressed) {
+        mLogger.logDozingSuppressed(suppressed);
+    }
+
+    /**
      * Appends fling event to the logs
      */
     public void traceFling(boolean expand, boolean aboveThreshold, boolean thresholdNeeded,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
index 46cec95..0c9e143 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
@@ -64,6 +64,14 @@
         })
     }
 
+    fun logDozingSuppressed(isDozingSuppressed: Boolean) {
+        buffer.log(TAG, INFO, {
+            bool1 = isDozingSuppressed
+        }, {
+            "DozingSuppressed=$bool1"
+        })
+    }
+
     fun logFling(
         expand: Boolean,
         aboveThreshold: Boolean,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index 1e0460b..befb648 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -413,6 +413,7 @@
         pw.print(" state="); pw.println(mState);
         pw.print(" wakeLockHeldForCurrentState="); pw.println(mWakeLockHeldForCurrentState);
         pw.print(" wakeLock="); pw.println(mWakeLock);
+        pw.print(" isDozeSuppressed="); pw.println(mDozeHost.isDozeSuppressed());
         pw.println("Parts:");
         for (Part p : mParts) {
             p.dump(pw);
diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeScope.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeScope.java
index 7a8b816..435859a 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeScope.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeScope.java
@@ -24,7 +24,7 @@
 import javax.inject.Scope;
 
 /**
- * Scope annotation for singleton items within the StatusBarComponent.
+ * Scope annotation for singleton items within the DozeComponent.
  */
 @Documented
 @Retention(RUNTIME)
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index b55b29a..a330be6 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -140,8 +140,14 @@
         d.setContentView(R.layout.shutdown_dialog);
         d.setCancelable(false);
 
-        int color = Utils.getColorAttrDefaultColor(mContext,
-                com.android.systemui.R.attr.wallpaperTextColor);
+        int color;
+        if (mBlurUtils.supportsBlursOnWindows()) {
+            color = Utils.getColorAttrDefaultColor(mContext,
+                    com.android.systemui.R.attr.wallpaperTextColor);
+        } else {
+            color = mContext.getResources().getColor(
+                    com.android.systemui.R.color.global_actions_shutdown_ui_text);
+        }
 
         ProgressBar bar = d.findViewById(R.id.progress);
         bar.getIndeterminateDrawable().setTint(color);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index 5f726cd..37bcb16 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -316,7 +316,8 @@
             }
             mDatePattern = getContext().getString(R.string.system_ui_aod_date_pattern);
             mPendingIntent = PendingIntent.getActivity(getContext(), 0,
-                    new Intent(getContext(), KeyguardSliceProvider.class), 0);
+                    new Intent(getContext(), KeyguardSliceProvider.class),
+                    PendingIntent.FLAG_IMMUTABLE);
             try {
                 //TODO(b/168778439): Remove this whole try catch. This is for debugging in dogfood.
                 mMediaManager.addCallback(this);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
index c281ece..7585110 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
@@ -30,8 +30,8 @@
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
 
 public class WorkLockActivityController {
     private static final String TAG = WorkLockActivityController.class.getSimpleName();
@@ -40,16 +40,16 @@
     private final IActivityTaskManager mIatm;
 
     public WorkLockActivityController(Context context) {
-        this(context, ActivityManagerWrapper.getInstance(), ActivityTaskManager.getService());
+        this(context, TaskStackChangeListeners.getInstance(), ActivityTaskManager.getService());
     }
 
     @VisibleForTesting
     WorkLockActivityController(
-            Context context, ActivityManagerWrapper am, IActivityTaskManager iAtm) {
+            Context context, TaskStackChangeListeners tscl, IActivityTaskManager iAtm) {
         mContext = context;
         mIatm = iAtm;
 
-        am.registerTaskStackListener(mLockListener);
+        tscl.registerTaskStackListener(mLockListener);
     }
 
     private void startWorkChallengeInTask(int taskId, int userId) {
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 6db4086..e3ee2a1 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -108,6 +108,18 @@
         return buffer;
     }
 
+    /** Provides a logging buffer for all logs related to Toasts shown by SystemUI. */
+    @Provides
+    @SysUISingleton
+    @ToastLog
+    public static LogBuffer provideToastLogBuffer(
+            LogcatEchoTracker bufferFilter,
+            DumpManager dumpManager) {
+        LogBuffer buffer = new LogBuffer("ToastLog", 50, 10, bufferFilter);
+        buffer.attach(dumpManager);
+        return buffer;
+    }
+
     /** Allows logging buffers to be tweaked via adb on debug builds but not on prod builds. */
     @Provides
     @SysUISingleton
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/RootView.java b/packages/SystemUI/src/com/android/systemui/log/dagger/ToastLog.java
similarity index 77%
copy from packages/SystemUI/src/com/android/keyguard/dagger/RootView.java
copy to packages/SystemUI/src/com/android/systemui/log/dagger/ToastLog.java
index 5ebff09..8671dbf 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/RootView.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/ToastLog.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open 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,17 +14,20 @@
  * limitations under the License.
  */
 
-package com.android.keyguard.dagger;
+package com.android.systemui.log.dagger;
 
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
+import com.android.systemui.log.LogBuffer;
+
 import java.lang.annotation.Documented;
 import java.lang.annotation.Retention;
 
 import javax.inject.Qualifier;
 
+/** A {@link LogBuffer} for ToastLog-related messages. */
 @Qualifier
 @Documented
 @Retention(RUNTIME)
-public @interface RootView {
+public @interface ToastLog {
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
index 094ece2..6fb8650 100644
--- a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
@@ -18,6 +18,7 @@
 
 import android.view.View
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.media.dagger.MediaModule.KEYGUARD
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.NotificationLockscreenUserManager
 import com.android.systemui.statusbar.StatusBarState
@@ -25,6 +26,7 @@
 import com.android.systemui.statusbar.notification.stack.MediaHeaderView
 import com.android.systemui.statusbar.phone.KeyguardBypassController
 import javax.inject.Inject
+import javax.inject.Named
 
 /**
  * A class that controls the media notifications on the lock screen, handles its visibility and
@@ -32,7 +34,7 @@
  */
 @SysUISingleton
 class KeyguardMediaController @Inject constructor(
-    private val mediaHost: MediaHost,
+    @param:Named(KEYGUARD) private val mediaHost: MediaHost,
     private val bypassController: KeyguardBypassController,
     private val statusBarStateController: SysuiStatusBarStateController,
     private val notifLockscreenUserManager: NotificationLockscreenUserManager
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
index d80aafb..cb14f31 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
@@ -282,10 +282,12 @@
                 scrollXAmount = -1 * relativePos
             }
             if (scrollXAmount != 0) {
+                val dx = if (isRtl) -scrollXAmount else scrollXAmount
+                val newScrollX = scrollView.relativeScrollX + dx
                 // Delay the scrolling since scrollView calls springback which cancels
                 // the animation again..
                 mainExecutor.execute {
-                    scrollView.smoothScrollBy(if (isRtl) -scrollXAmount else scrollXAmount, 0)
+                    scrollView.smoothScrollTo(newScrollX, scrollView.scrollY)
                 }
             }
             val currentTranslation = scrollView.getContentTranslation()
@@ -553,4 +555,4 @@
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index f6571ef..c18a6a4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -42,10 +42,10 @@
 import androidx.constraintlayout.widget.ConstraintSet;
 
 import com.android.settingslib.Utils;
-import com.android.settingslib.media.MediaOutputSliceConstants;
 import com.android.settingslib.widget.AdaptiveIcon;
 import com.android.systemui.R;
 import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.media.dialog.MediaOutputDialogFactory;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 import com.android.systemui.util.animation.TransitionLayout;
@@ -93,7 +93,7 @@
     private int mAlbumArtRadius;
     // This will provide the corners for the album art.
     private final ViewOutlineProvider mViewOutlineProvider;
-
+    private final MediaOutputDialogFactory mMediaOutputDialogFactory;
     /**
      * Initialize a new control panel
      * @param context
@@ -104,7 +104,8 @@
     public MediaControlPanel(Context context, @Background Executor backgroundExecutor,
             ActivityStarter activityStarter, MediaViewController mediaViewController,
             SeekBarViewModel seekBarViewModel, Lazy<MediaDataManager> lazyMediaDataManager,
-            KeyguardDismissUtil keyguardDismissUtil) {
+            KeyguardDismissUtil keyguardDismissUtil, MediaOutputDialogFactory
+            mediaOutputDialogFactory) {
         mContext = context;
         mBackgroundExecutor = backgroundExecutor;
         mActivityStarter = activityStarter;
@@ -112,6 +113,7 @@
         mMediaViewController = mediaViewController;
         mMediaDataManagerLazy = lazyMediaDataManager;
         mKeyguardDismissUtil = keyguardDismissUtil;
+        mMediaOutputDialogFactory = mediaOutputDialogFactory;
         loadDimens();
 
         mViewOutlineProvider = new ViewOutlineProvider() {
@@ -273,13 +275,7 @@
         setVisibleAndAlpha(collapsedSet, R.id.media_seamless, true /*visible */);
         setVisibleAndAlpha(expandedSet, R.id.media_seamless, true /*visible */);
         mViewHolder.getSeamless().setOnClickListener(v -> {
-            final Intent intent = new Intent()
-                    .setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT)
-                    .putExtra(MediaOutputSliceConstants.EXTRA_PACKAGE_NAME,
-                            data.getPackageName())
-                    .putExtra(MediaOutputSliceConstants.KEY_MEDIA_SESSION_TOKEN, mToken);
-            mActivityStarter.startActivity(intent, false, true /* dismissShade */,
-                    Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+            mMediaOutputDialogFactory.create(data.getPackageName(), true);
         });
 
         ImageView iconView = mViewHolder.getSeamlessIcon();
@@ -358,7 +354,15 @@
         final MediaController controller = getController();
         mBackgroundExecutor.execute(() -> mSeekBarViewModel.updateController(controller));
 
+        // Guts label
+        boolean isDismissible = data.isClearable();
+        mViewHolder.getSettingsText().setText(isDismissible
+                ? R.string.controls_media_close_session
+                : R.string.controls_media_active_session);
+
         // Dismiss
+        mViewHolder.getDismissLabel().setAlpha(isDismissible ? 1 : DISABLED_ALPHA);
+        mViewHolder.getDismiss().setEnabled(isDismissible);
         mViewHolder.getDismiss().setOnClickListener(v -> {
             if (mKey != null) {
                 closeGuts();
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
index ce184aa..857c50f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
@@ -11,7 +11,7 @@
 import java.util.Objects
 import javax.inject.Inject
 
-class MediaHost @Inject constructor(
+class MediaHost constructor(
     private val state: MediaHostStateHolder,
     private val mediaHierarchyManager: MediaHierarchyManager,
     private val mediaDataManager: MediaDataManager,
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
index 51dbfa7..ce72991 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
@@ -46,7 +46,7 @@
     /**
      * Callback representing that a media object is now expired:
      * @param token Media session unique identifier
-     * @param pauseTimeuot True when expired for {@code PAUSED_MEDIA_TIMEOUT}
+     * @param pauseTimeout True when expired for {@code PAUSED_MEDIA_TIMEOUT}
      */
     lateinit var timeoutCallback: (String, Boolean) -> Unit
 
@@ -57,11 +57,10 @@
         // Having an old key means that we're migrating from/to resumption. We should update
         // the old listener to make sure that events will be dispatched to the new location.
         val migrating = oldKey != null && key != oldKey
-        var wasPlaying = false
         if (migrating) {
             val reusedListener = mediaListeners.remove(oldKey)
             if (reusedListener != null) {
-                wasPlaying = reusedListener.playing ?: false
+                val wasPlaying = reusedListener.playing ?: false
                 if (DEBUG) Log.d(TAG, "migrating key $oldKey to $key, for resumption")
                 reusedListener.mediaData = data
                 reusedListener.key = key
@@ -159,9 +158,8 @@
                         Log.v(TAG, "Execute timeout for $key")
                     }
                     timedOut = true
-                    if (dispatchEvents) {
-                        timeoutCallback(key, timedOut)
-                    }
+                    // this event is async, so it's safe even when `dispatchEvents` is false
+                    timeoutCallback(key, timedOut)
                 }, PAUSED_MEDIA_TIMEOUT)
             } else {
                 expireMediaTimeout(key, "playback started - $state, $key")
diff --git a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
index 11551ac..16327bd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
@@ -23,7 +23,6 @@
 import android.widget.ImageView
 import android.widget.SeekBar
 import android.widget.TextView
-import androidx.constraintlayout.widget.ConstraintSet
 import com.android.systemui.R
 import com.android.systemui.util.animation.TransitionLayout
 
@@ -61,8 +60,10 @@
     val action4 = itemView.requireViewById<ImageButton>(R.id.action4)
 
     // Settings screen
+    val settingsText = itemView.requireViewById<TextView>(R.id.remove_text)
     val cancel = itemView.requireViewById<View>(R.id.cancel)
-    val dismiss = itemView.requireViewById<View>(R.id.dismiss)
+    val dismiss = itemView.requireViewById<ViewGroup>(R.id.dismiss)
+    val dismissLabel = dismiss.getChildAt(0)
     val settings = itemView.requireViewById<View>(R.id.settings)
 
     init {
diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
index 9e326aa..c824458 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
@@ -91,9 +91,9 @@
         }
     private var playbackState: PlaybackState? = null
     private var callback = object : MediaController.Callback() {
-        override fun onPlaybackStateChanged(state: PlaybackState) {
+        override fun onPlaybackStateChanged(state: PlaybackState?) {
             playbackState = state
-            if (PlaybackState.STATE_NONE.equals(playbackState)) {
+            if (playbackState == null || PlaybackState.STATE_NONE.equals(playbackState)) {
                 clearController()
             } else {
                 checkIfPollingNeeded()
diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
new file mode 100644
index 0000000..57ac9df
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.media.dagger;
+
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.media.MediaDataManager;
+import com.android.systemui.media.MediaHierarchyManager;
+import com.android.systemui.media.MediaHost;
+import com.android.systemui.media.MediaHostStatesManager;
+
+import javax.inject.Named;
+
+import dagger.Module;
+import dagger.Provides;
+
+/** Dagger module for the media package. */
+@Module
+public interface MediaModule {
+    String QS_PANEL = "media_qs_panel";
+    String QUICK_QS_PANEL = "media_quick_qs_panel";
+    String KEYGUARD = "media_keyguard";
+
+    /** */
+    @Provides
+    @SysUISingleton
+    @Named(QS_PANEL)
+    static MediaHost providesQSMediaHost(MediaHost.MediaHostStateHolder stateHolder,
+            MediaHierarchyManager hierarchyManager, MediaDataManager dataManager,
+            MediaHostStatesManager statesManager) {
+        return new MediaHost(stateHolder, hierarchyManager, dataManager, statesManager);
+    }
+
+    /** */
+    @Provides
+    @SysUISingleton
+    @Named(QUICK_QS_PANEL)
+    static MediaHost providesQuickQSMediaHost(MediaHost.MediaHostStateHolder stateHolder,
+            MediaHierarchyManager hierarchyManager, MediaDataManager dataManager,
+            MediaHostStatesManager statesManager) {
+        return new MediaHost(stateHolder, hierarchyManager, dataManager, statesManager);
+    }
+
+    /** */
+    @Provides
+    @SysUISingleton
+    @Named(KEYGUARD)
+    static MediaHost providesKeyguardMediaHost(MediaHost.MediaHostStateHolder stateHolder,
+            MediaHierarchyManager hierarchyManager, MediaDataManager dataManager,
+            MediaHostStatesManager statesManager) {
+        return new MediaHost(stateHolder, hierarchyManager, dataManager, statesManager);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index d1630eb..d26f7ab 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -109,34 +109,45 @@
             super.onBind(device, topMargin, bottomMargin);
             final boolean currentlyConnected = isCurrentlyConnected(device);
             if (currentlyConnected) {
-                mConnectedItem = mFrameLayout;
+                mConnectedItem = mContainerLayout;
+            }
+            mBottomDivider.setVisibility(View.GONE);
+            mCheckBox.setVisibility(View.GONE);
+            if (currentlyConnected && mController.isActiveRemoteDevice(device)) {
+                // Init active device layout
+                mDivider.setVisibility(View.VISIBLE);
+                mDivider.setTransitionAlpha(1);
+                mAddIcon.setVisibility(View.VISIBLE);
+                mAddIcon.setTransitionAlpha(1);
+                mAddIcon.setOnClickListener(v -> onEndItemClick());
+            } else {
+                // Init non-active device layout
+                mDivider.setVisibility(View.GONE);
+                mAddIcon.setVisibility(View.GONE);
             }
             if (mController.isTransferring()) {
                 if (device.getState() == MediaDeviceState.STATE_CONNECTING
                         && !mController.hasAdjustVolumeUserRestriction()) {
-                    setTwoLineLayout(device, null /* title */, true /* bFocused */,
-                            false /* showSeekBar*/, true /* showProgressBar */,
-                            false /* showSubtitle */);
+                    setTwoLineLayout(device, true /* bFocused */, false /* showSeekBar*/,
+                            true /* showProgressBar */, false /* showSubtitle */);
                 } else {
                     setSingleLineLayout(getItemTitle(device), false /* bFocused */);
                 }
             } else {
                 // Set different layout for each device
                 if (device.getState() == MediaDeviceState.STATE_CONNECTING_FAILED) {
-                    setTwoLineLayout(device, null /* title */, false /* bFocused */,
-                            false /* showSeekBar*/, false /* showProgressBar */,
+                    setTwoLineLayout(device, false /* bFocused */,
+                            false /* showSeekBar */, false /* showProgressBar */,
                             true /* showSubtitle */);
                     mSubTitleText.setText(R.string.media_output_dialog_connect_failed);
-                    mFrameLayout.setOnClickListener(v -> onItemClick(v, device));
-                } else if (!mController.hasAdjustVolumeUserRestriction()
-                        && currentlyConnected) {
-                    setTwoLineLayout(device, null /* title */, true /* bFocused */,
-                            true /* showSeekBar*/, false /* showProgressBar */,
-                            false /* showSubtitle */);
+                    mContainerLayout.setOnClickListener(v -> onItemClick(v, device));
+                } else if (!mController.hasAdjustVolumeUserRestriction() && currentlyConnected) {
+                    setTwoLineLayout(device, true /* bFocused */, true /* showSeekBar */,
+                            false /* showProgressBar */, false /* showSubtitle */);
                     initSeekbar(device);
                 } else {
                     setSingleLineLayout(getItemTitle(device), false /* bFocused */);
-                    mFrameLayout.setOnClickListener(v -> onItemClick(v, device));
+                    mContainerLayout.setOnClickListener(v -> onItemClick(v, device));
                 }
             }
         }
@@ -145,13 +156,17 @@
         void onBind(int customizedItem, boolean topMargin, boolean bottomMargin) {
             super.onBind(customizedItem, topMargin, bottomMargin);
             if (customizedItem == CUSTOMIZED_ITEM_PAIR_NEW) {
+                mCheckBox.setVisibility(View.GONE);
+                mDivider.setVisibility(View.GONE);
+                mAddIcon.setVisibility(View.GONE);
+                mBottomDivider.setVisibility(View.GONE);
                 setSingleLineLayout(mContext.getText(R.string.media_output_dialog_pairing_new),
                         false /* bFocused */);
                 final Drawable d = mContext.getDrawable(R.drawable.ic_add);
                 d.setColorFilter(new PorterDuffColorFilter(
                         Utils.getColorAccentDefaultColor(mContext), PorterDuff.Mode.SRC_IN));
                 mTitleIcon.setImageDrawable(d);
-                mFrameLayout.setOnClickListener(v -> onItemClick(CUSTOMIZED_ITEM_PAIR_NEW));
+                mContainerLayout.setOnClickListener(v -> onItemClick(CUSTOMIZED_ITEM_PAIR_NEW));
             }
         }
 
@@ -173,5 +188,9 @@
                 mController.launchBluetoothPairing();
             }
         }
+
+        private void onEndItemClick() {
+            mController.launchMediaOutputGroupDialog();
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index 2d3e77d..536b759 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -24,8 +24,9 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.FrameLayout;
+import android.widget.CheckBox;
 import android.widget.ImageView;
+import android.widget.LinearLayout;
 import android.widget.ProgressBar;
 import android.widget.RelativeLayout;
 import android.widget.SeekBar;
@@ -44,19 +45,17 @@
 public abstract class MediaOutputBaseAdapter extends
         RecyclerView.Adapter<MediaOutputBaseAdapter.MediaDeviceBaseViewHolder> {
 
-    private static final String FONT_SELECTED_TITLE = "sans-serif-medium";
-    private static final String FONT_TITLE = "sans-serif";
-
     static final int CUSTOMIZED_ITEM_PAIR_NEW = 1;
+    static final int CUSTOMIZED_ITEM_GROUP = 2;
 
     final MediaOutputController mController;
 
-    private boolean mIsDragging;
     private int mMargin;
     private boolean mIsAnimating;
 
     Context mContext;
     View mHolderView;
+    boolean mIsDragging;
 
     public MediaOutputBaseAdapter(MediaOutputController controller) {
         mController = controller;
@@ -99,27 +98,33 @@
 
         private static final int ANIM_DURATION = 200;
 
-        final FrameLayout mFrameLayout;
+        final LinearLayout mContainerLayout;
         final TextView mTitleText;
         final TextView mTwoLineTitleText;
         final TextView mSubTitleText;
         final ImageView mTitleIcon;
-        final ImageView mEndIcon;
+        final ImageView mAddIcon;
         final ProgressBar mProgressBar;
         final SeekBar mSeekBar;
         final RelativeLayout mTwoLineLayout;
+        final View mDivider;
+        final View mBottomDivider;
+        final CheckBox mCheckBox;
 
         MediaDeviceBaseViewHolder(View view) {
             super(view);
-            mFrameLayout = view.requireViewById(R.id.device_container);
+            mContainerLayout = view.requireViewById(R.id.device_container);
             mTitleText = view.requireViewById(R.id.title);
             mSubTitleText = view.requireViewById(R.id.subtitle);
             mTwoLineLayout = view.requireViewById(R.id.two_line_layout);
             mTwoLineTitleText = view.requireViewById(R.id.two_line_title);
             mTitleIcon = view.requireViewById(R.id.title_icon);
-            mEndIcon = view.requireViewById(R.id.end_icon);
             mProgressBar = view.requireViewById(R.id.volume_indeterminate_progress);
             mSeekBar = view.requireViewById(R.id.volume_seekbar);
+            mDivider = view.requireViewById(R.id.end_divider);
+            mBottomDivider = view.requireViewById(R.id.bottom_divider);
+            mAddIcon = view.requireViewById(R.id.add_icon);
+            mCheckBox = view.requireViewById(R.id.check_box);
         }
 
         void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin) {
@@ -132,11 +137,11 @@
         }
 
         private void setMargin(boolean topMargin, boolean bottomMargin) {
-            ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mFrameLayout
+            ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mContainerLayout
                     .getLayoutParams();
             params.topMargin = topMargin ? mMargin : 0;
             params.bottomMargin = bottomMargin ? mMargin : 0;
-            mFrameLayout.setLayoutParams(params);
+            mContainerLayout.setLayoutParams(params);
         }
 
         void setSingleLineLayout(CharSequence title, boolean bFocused) {
@@ -146,13 +151,26 @@
             mTitleText.setTranslationY(0);
             mTitleText.setText(title);
             if (bFocused) {
-                mTitleText.setTypeface(Typeface.create(FONT_SELECTED_TITLE, Typeface.NORMAL));
+                mTitleText.setTypeface(Typeface.create(mContext.getString(
+                        com.android.internal.R.string.config_headlineFontFamilyMedium),
+                        Typeface.NORMAL));
             } else {
-                mTitleText.setTypeface(Typeface.create(FONT_TITLE, Typeface.NORMAL));
+                mTitleText.setTypeface(Typeface.create(mContext.getString(
+                        com.android.internal.R.string.config_headlineFontFamily), Typeface.NORMAL));
             }
         }
 
-        void setTwoLineLayout(MediaDevice device, CharSequence title, boolean bFocused,
+        void setTwoLineLayout(MediaDevice device, boolean bFocused, boolean showSeekBar,
+                boolean showProgressBar, boolean showSubtitle) {
+            setTwoLineLayout(device, null, bFocused, showSeekBar, showProgressBar, showSubtitle);
+        }
+
+        void setTwoLineLayout(CharSequence title, boolean bFocused, boolean showSeekBar,
+                boolean showProgressBar, boolean showSubtitle) {
+            setTwoLineLayout(null, title, bFocused, showSeekBar, showProgressBar, showSubtitle);
+        }
+
+        private void setTwoLineLayout(MediaDevice device, CharSequence title, boolean bFocused,
                 boolean showSeekBar, boolean showProgressBar, boolean showSubtitle) {
             mTitleText.setVisibility(View.GONE);
             mTwoLineLayout.setVisibility(View.VISIBLE);
@@ -168,18 +186,21 @@
             }
 
             if (bFocused) {
-                mTwoLineTitleText.setTypeface(Typeface.create(FONT_SELECTED_TITLE,
+                mTwoLineTitleText.setTypeface(Typeface.create(mContext.getString(
+                        com.android.internal.R.string.config_headlineFontFamilyMedium),
                         Typeface.NORMAL));
             } else {
-                mTwoLineTitleText.setTypeface(Typeface.create(FONT_TITLE, Typeface.NORMAL));
+                mTwoLineTitleText.setTypeface(Typeface.create(mContext.getString(
+                        com.android.internal.R.string.config_headlineFontFamily), Typeface.NORMAL));
             }
         }
 
         void initSeekbar(MediaDevice device) {
             mSeekBar.setMax(device.getMaxVolume());
             mSeekBar.setMin(0);
-            if (mSeekBar.getProgress() != device.getCurrentVolume()) {
-                mSeekBar.setProgress(device.getCurrentVolume());
+            final int currentVolume = device.getCurrentVolume();
+            if (mSeekBar.getProgress() != currentVolume) {
+                mSeekBar.setProgress(currentVolume);
             }
             mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                 @Override
@@ -213,7 +234,9 @@
             }
             mIsAnimating = true;
             // Animation for title text
-            toTitleText.setTypeface(Typeface.create(FONT_SELECTED_TITLE, Typeface.NORMAL));
+            toTitleText.setTypeface(Typeface.create(mContext.getString(
+                    com.android.internal.R.string.config_headlineFontFamilyMedium),
+                    Typeface.NORMAL));
             toTitleText.animate()
                     .setDuration(ANIM_DURATION)
                     .translationY(-delta)
@@ -234,7 +257,9 @@
                         public void onAnimationEnd(Animator animation) {
                             final TextView fromTitleText = from.requireViewById(
                                     R.id.two_line_title);
-                            fromTitleText.setTypeface(Typeface.create(FONT_TITLE, Typeface.NORMAL));
+                            fromTitleText.setTypeface(Typeface.create(mContext.getString(
+                                    com.android.internal.R.string.config_headlineFontFamily),
+                                    Typeface.NORMAL));
                             fromTitleText.animate()
                                     .setDuration(ANIM_DURATION)
                                     .translationY(delta)
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index 3b82999..78939df 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -49,7 +49,7 @@
  * Base dialog for media output UI
  */
 public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
-        MediaOutputController.Callback {
+        MediaOutputController.Callback, Window.Callback {
 
     private static final String TAG = "MediaOutputDialog";
 
@@ -119,6 +119,8 @@
         // Init device list
         mDevicesRecyclerView.setLayoutManager(mLayoutManager);
         mDevicesRecyclerView.setAdapter(mAdapter);
+        // Init header icon
+        mHeaderIcon.setOnClickListener(v -> onHeaderIconClick());
         // Init bottom buttons
         mDoneButton.setOnClickListener(v -> dismiss());
         mStopButton.setOnClickListener(v -> {
@@ -210,4 +212,15 @@
     public void dismissDialog() {
         dismiss();
     }
+
+    @Override
+    public void onWindowFocusChanged(boolean hasFocus) {
+        super.onWindowFocusChanged(hasFocus);
+        if (!hasFocus && isShowing()) {
+            dismiss();
+        }
+    }
+
+    void onHeaderIconClick() {
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index b1f1bda..80928d6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -18,10 +18,7 @@
 
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.media.MediaMetadata;
 import android.media.MediaRoute2Info;
@@ -49,6 +46,8 @@
 import com.android.settingslib.utils.ThreadUtils;
 import com.android.systemui.R;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.phone.ShadeController;
 
 import java.util.ArrayList;
@@ -61,7 +60,7 @@
 /**
  * Controller for media output dialog
  */
-public class MediaOutputController implements LocalMediaManager.DeviceCallback{
+public class MediaOutputController implements LocalMediaManager.DeviceCallback {
 
     private static final String TAG = "MediaOutputController";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -71,6 +70,9 @@
     private final MediaSessionManager mMediaSessionManager;
     private final ShadeController mShadeController;
     private final ActivityStarter mActivityStarter;
+    private final List<MediaDevice> mGroupMediaDevices = new CopyOnWriteArrayList<>();
+    private final boolean mAboveStatusbar;
+    private final NotificationEntryManager mNotificationEntryManager;
     @VisibleForTesting
     final List<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>();
 
@@ -82,13 +84,16 @@
 
     @Inject
     public MediaOutputController(@NonNull Context context, String packageName,
-            MediaSessionManager mediaSessionManager, LocalBluetoothManager
-            lbm, ShadeController shadeController, ActivityStarter starter) {
+            boolean aboveStatusbar, MediaSessionManager mediaSessionManager, LocalBluetoothManager
+            lbm, ShadeController shadeController, ActivityStarter starter,
+            NotificationEntryManager notificationEntryManager) {
         mContext = context;
         mPackageName = packageName;
         mMediaSessionManager = mediaSessionManager;
         mShadeController = shadeController;
         mActivityStarter = starter;
+        mAboveStatusbar = aboveStatusbar;
+        mNotificationEntryManager = notificationEntryManager;
         InfoMediaManager imm = new InfoMediaManager(mContext, packageName, null, lbm);
         mLocalMediaManager = new LocalMediaManager(mContext, lbm, imm, packageName);
     }
@@ -194,7 +199,7 @@
         if (DEBUG) {
             Log.d(TAG, "Media meta data does not contain icon information");
         }
-        return getPackageIcon();
+        return getNotificationIcon();
     }
 
     IconCompat getDeviceIconCompat(MediaDevice device) {
@@ -210,24 +215,15 @@
         return BluetoothUtils.createIconWithDrawable(drawable);
     }
 
-    private IconCompat getPackageIcon() {
+    IconCompat getNotificationIcon() {
         if (TextUtils.isEmpty(mPackageName)) {
             return null;
         }
-        try {
-            final Drawable drawable = mContext.getPackageManager().getApplicationIcon(mPackageName);
-            if (drawable instanceof BitmapDrawable) {
-                return IconCompat.createWithBitmap(((BitmapDrawable) drawable).getBitmap());
-            }
-            final Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
-                    drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
-            final Canvas canvas = new Canvas(bitmap);
-            drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
-            drawable.draw(canvas);
-            return IconCompat.createWithBitmap(bitmap);
-        } catch (PackageManager.NameNotFoundException e) {
-            if (DEBUG) {
-                Log.e(TAG, "Package is not found. Unable to get package icon.");
+        for (NotificationEntry entry
+                : mNotificationEntryManager.getActiveNotificationsForCurrentUser()) {
+            if (entry.getSbn().getNotification().hasMediaSession()
+                    && TextUtils.equals(entry.getSbn().getPackageName(), mPackageName)) {
+                return IconCompat.createFromIcon(entry.getSbn().getNotification().getLargeIcon());
             }
         }
         return null;
@@ -271,6 +267,42 @@
         mMediaDevices.addAll(targetMediaDevices);
     }
 
+    List<MediaDevice> getGroupMediaDevices() {
+        final List<MediaDevice> selectedDevices = getSelectedMediaDevice();
+        final List<MediaDevice> selectableDevices = getSelectableMediaDevice();
+        if (mGroupMediaDevices.isEmpty()) {
+            mGroupMediaDevices.addAll(selectedDevices);
+            mGroupMediaDevices.addAll(selectableDevices);
+            return mGroupMediaDevices;
+        }
+        // To keep the same list order
+        final Collection<MediaDevice> sourceDevices = new ArrayList<>();
+        final Collection<MediaDevice> targetMediaDevices = new ArrayList<>();
+        sourceDevices.addAll(selectedDevices);
+        sourceDevices.addAll(selectableDevices);
+        for (MediaDevice originalDevice : mGroupMediaDevices) {
+            for (MediaDevice newDevice : sourceDevices) {
+                if (TextUtils.equals(originalDevice.getId(), newDevice.getId())) {
+                    targetMediaDevices.add(newDevice);
+                    sourceDevices.remove(newDevice);
+                    break;
+                }
+            }
+        }
+        // Add new devices at the end of list if necessary
+        if (!sourceDevices.isEmpty()) {
+            targetMediaDevices.addAll(sourceDevices);
+        }
+        mGroupMediaDevices.clear();
+        mGroupMediaDevices.addAll(targetMediaDevices);
+
+        return mGroupMediaDevices;
+    }
+
+    void resetGroupMediaDevices() {
+        mGroupMediaDevices.clear();
+    }
+
     void connectDevice(MediaDevice device) {
         ThreadUtils.postOnBackgroundThread(() -> {
             mLocalMediaManager.connectDevice(device);
@@ -309,15 +341,6 @@
         return mLocalMediaManager.getDeselectableMediaDevice();
     }
 
-    boolean isDeviceIncluded(Collection<MediaDevice> deviceCollection, MediaDevice targetDevice) {
-        for (MediaDevice device : deviceCollection) {
-            if (TextUtils.equals(device.getId(), targetDevice.getId())) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     void adjustSessionVolume(String sessionId, int volume) {
         mLocalMediaManager.adjustSessionVolume(sessionId, volume);
     }
@@ -407,6 +430,16 @@
         mActivityStarter.dismissKeyguardThenExecute(postKeyguardAction, null, true);
     }
 
+    void launchMediaOutputDialog() {
+        mCallback.dismissDialog();
+        new MediaOutputDialog(mContext, mAboveStatusbar, this);
+    }
+
+    void launchMediaOutputGroupDialog() {
+        mCallback.dismissDialog();
+        new MediaOutputGroupDialog(mContext, mAboveStatusbar, this);
+    }
+
     boolean isActiveRemoteDevice(@NonNull MediaDevice device) {
         final List<String> features = device.getFeatures();
         return (features.contains(MediaRoute2Info.FEATURE_REMOTE_PLAYBACK)
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
index 4cdca4c..7d1a7ce 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
@@ -20,6 +20,7 @@
 import android.media.session.MediaSessionManager
 import com.android.settingslib.bluetooth.LocalBluetoothManager
 import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.notification.NotificationEntryManager
 import com.android.systemui.statusbar.phone.ShadeController
 import javax.inject.Inject
 
@@ -31,7 +32,8 @@
     private val mediaSessionManager: MediaSessionManager,
     private val lbm: LocalBluetoothManager?,
     private val shadeController: ShadeController,
-    private val starter: ActivityStarter
+    private val starter: ActivityStarter,
+    private val notificationEntryManager: NotificationEntryManager
 ) {
     companion object {
         var mediaOutputDialog: MediaOutputDialog? = null
@@ -40,9 +42,8 @@
     /** Creates a [MediaOutputDialog] for the given package. */
     fun create(packageName: String, aboveStatusBar: Boolean) {
         mediaOutputDialog?.dismiss()
-
-        mediaOutputDialog = MediaOutputController(context, packageName, mediaSessionManager, lbm,
-                shadeController, starter).run {
+        mediaOutputDialog = MediaOutputController(context, packageName, aboveStatusBar,
+                mediaSessionManager, lbm, shadeController, starter, notificationEntryManager).run {
             MediaOutputDialog(context, aboveStatusBar, this) }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java
new file mode 100644
index 0000000..ceb4495
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.media.dialog;
+
+import android.content.res.ColorStateList;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.SeekBar;
+
+import androidx.annotation.NonNull;
+
+import com.android.settingslib.bluetooth.BluetoothUtils;
+import com.android.settingslib.media.MediaDevice;
+import com.android.systemui.R;
+
+import java.util.List;
+
+/**
+ * Adapter for media output dynamic group dialog.
+ */
+public class MediaOutputGroupAdapter extends MediaOutputBaseAdapter {
+
+    private static final String TAG = "MediaOutputGroupAdapter";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    private final List<MediaDevice> mGroupMediaDevices;
+
+    public MediaOutputGroupAdapter(MediaOutputController controller) {
+        super(controller);
+        mGroupMediaDevices = controller.getGroupMediaDevices();
+    }
+
+    @Override
+    public MediaDeviceBaseViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup,
+            int viewType) {
+        super.onCreateViewHolder(viewGroup, viewType);
+
+        return new GroupViewHolder(mHolderView);
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull MediaDeviceBaseViewHolder viewHolder, int position) {
+        // Add "Group"
+        if (position == 0) {
+            viewHolder.onBind(CUSTOMIZED_ITEM_GROUP, true /* topMargin */,
+                    false /* bottomMargin */);
+            return;
+        }
+        // Add available devices
+        final int newPosition = position - 1;
+        final int size = mGroupMediaDevices.size();
+        if (newPosition < size) {
+            viewHolder.onBind(mGroupMediaDevices.get(newPosition), false /* topMargin */,
+                    newPosition == (size - 1) /* bottomMargin */);
+            return;
+        }
+        if (DEBUG) {
+            Log.d(TAG, "Incorrect position: " + position);
+        }
+    }
+
+    @Override
+    public int getItemCount() {
+        // Require extra item for group volume operation
+        return mGroupMediaDevices.size() + 1;
+    }
+
+    @Override
+    CharSequence getItemTitle(MediaDevice device) {
+        return super.getItemTitle(device);
+    }
+
+    class GroupViewHolder extends MediaDeviceBaseViewHolder {
+
+        GroupViewHolder(View view) {
+            super(view);
+        }
+
+        @Override
+        void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin) {
+            super.onBind(device, topMargin, bottomMargin);
+            mDivider.setVisibility(View.GONE);
+            mAddIcon.setVisibility(View.GONE);
+            mBottomDivider.setVisibility(View.GONE);
+            mCheckBox.setVisibility(View.VISIBLE);
+            mCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
+                onCheckBoxClicked(isChecked, device);
+            });
+            setTwoLineLayout(device, false /* bFocused */, true /* showSeekBar */,
+                    false /* showProgressBar */, false /* showSubtitle*/);
+            initSeekbar(device);
+            final List<MediaDevice> selectedDevices = mController.getSelectedMediaDevice();
+            if (isDeviceIncluded(mController.getSelectableMediaDevice(), device)) {
+                mCheckBox.setButtonDrawable(R.drawable.ic_check_box);
+                mCheckBox.setChecked(false);
+                mCheckBox.setEnabled(true);
+            } else if (isDeviceIncluded(selectedDevices, device)) {
+                if (selectedDevices.size() == 1 || !isDeviceIncluded(
+                        mController.getDeselectableMediaDevice(), device)) {
+                    mCheckBox.setButtonDrawable(getDisabledCheckboxDrawable());
+                    mCheckBox.setChecked(true);
+                    mCheckBox.setEnabled(false);
+                } else {
+                    mCheckBox.setButtonDrawable(R.drawable.ic_check_box);
+                    mCheckBox.setChecked(true);
+                    mCheckBox.setEnabled(true);
+                }
+            }
+        }
+
+        @Override
+        void onBind(int customizedItem, boolean topMargin, boolean bottomMargin) {
+            super.onBind(customizedItem, topMargin, bottomMargin);
+            if (customizedItem == CUSTOMIZED_ITEM_GROUP) {
+                setTwoLineLayout(mContext.getText(R.string.media_output_dialog_group),
+                        true /* bFocused */, true /* showSeekBar */, false /* showProgressBar */,
+                        false /* showSubtitle*/);
+                mTitleIcon.setImageDrawable(getSpeakerDrawable());
+                mBottomDivider.setVisibility(View.VISIBLE);
+                mCheckBox.setVisibility(View.GONE);
+                mDivider.setVisibility(View.GONE);
+                mAddIcon.setVisibility(View.GONE);
+                initSessionSeekbar();
+            }
+        }
+
+        private void onCheckBoxClicked(boolean isChecked, MediaDevice device) {
+            if (isChecked && isDeviceIncluded(mController.getSelectableMediaDevice(), device)) {
+                mController.addDeviceToPlayMedia(device);
+            } else if (!isChecked && isDeviceIncluded(mController.getDeselectableMediaDevice(),
+                    device)) {
+                mController.removeDeviceFromPlayMedia(device);
+            }
+        }
+
+        private void initSessionSeekbar() {
+            mSeekBar.setMax(mController.getSessionVolumeMax());
+            mSeekBar.setMin(0);
+            final int currentVolume = mController.getSessionVolume();
+            if (mSeekBar.getProgress() != currentVolume) {
+                mSeekBar.setProgress(currentVolume);
+            }
+            mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+                @Override
+                public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+                    if (!fromUser) {
+                        return;
+                    }
+                    mController.adjustSessionVolume(progress);
+                }
+
+                @Override
+                public void onStartTrackingTouch(SeekBar seekBar) {
+                    mIsDragging = true;
+                }
+
+                @Override
+                public void onStopTrackingTouch(SeekBar seekBar) {
+                    mIsDragging = false;
+                }
+            });
+        }
+
+        private Drawable getDisabledCheckboxDrawable() {
+            final Drawable drawable = mContext.getDrawable(R.drawable.ic_check_box_blue_24dp)
+                    .mutate();
+            final Bitmap checkbox = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
+                    drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
+            final Canvas canvas = new Canvas(checkbox);
+            TypedValue value = new TypedValue();
+            mContext.getTheme().resolveAttribute(android.R.attr.disabledAlpha, value, true);
+            drawable.setAlpha((int) (value.getFloat() * 255));
+            drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+            drawable.draw(canvas);
+
+            return drawable;
+        }
+
+        private Drawable getSpeakerDrawable() {
+            final Drawable drawable = mContext.getDrawable(R.drawable.ic_speaker_group_black_24dp)
+                    .mutate();
+            final ColorStateList list = mContext.getResources().getColorStateList(
+                            R.color.advanced_icon_color, mContext.getTheme());
+            drawable.setColorFilter(new PorterDuffColorFilter(list.getDefaultColor(),
+                    PorterDuff.Mode.SRC_IN));
+            return BluetoothUtils.buildAdvancedDrawable(mContext, drawable);
+        }
+
+        private boolean isDeviceIncluded(List<MediaDevice> deviceList, MediaDevice targetDevice) {
+            for (MediaDevice device : deviceList) {
+                if (TextUtils.equals(device.getId(), targetDevice.getId())) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java
new file mode 100644
index 0000000..4079304
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.media.dialog;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.view.View;
+import android.view.WindowManager;
+
+import androidx.core.graphics.drawable.IconCompat;
+
+import com.android.systemui.R;
+
+/**
+ * Dialog for media output group.
+ */
+public class MediaOutputGroupDialog extends MediaOutputBaseDialog {
+
+    MediaOutputGroupDialog(Context context, boolean aboveStatusbar, MediaOutputController
+            mediaOutputController) {
+        super(context, mediaOutputController);
+        mMediaOutputController.resetGroupMediaDevices();
+        mAdapter = new MediaOutputGroupAdapter(mMediaOutputController);
+        if (!aboveStatusbar) {
+            getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
+        }
+        show();
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+
+    @Override
+    int getHeaderIconRes() {
+        return R.drawable.ic_arrow_back;
+    }
+
+    @Override
+    IconCompat getHeaderIcon() {
+        return null;
+    }
+
+    @Override
+    int getHeaderIconSize() {
+        return mContext.getResources().getDimensionPixelSize(
+                    R.dimen.media_output_dialog_header_back_icon_size);
+    }
+
+    @Override
+    CharSequence getHeaderText() {
+        return mContext.getString(R.string.media_output_dialog_add_output);
+    }
+
+    @Override
+    CharSequence getHeaderSubtitle() {
+        final int size = mMediaOutputController.getSelectedMediaDevice().size();
+        if (size == 1) {
+            return mContext.getText(R.string.media_output_dialog_single_device);
+        }
+        return mContext.getString(R.string.media_output_dialog_multiple_devices, size);
+    }
+
+    @Override
+    int getStopButtonVisibility() {
+        return View.VISIBLE;
+    }
+
+    @Override
+    void onHeaderIconClick() {
+        mMediaOutputController.launchMediaOutputDialog();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java
index 6cbf065..df9e7a4 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java
@@ -47,6 +47,7 @@
 import com.android.systemui.navigationbar.buttons.KeyButtonDrawable;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
 import com.android.systemui.statusbar.policy.RotationLockController;
 
@@ -156,7 +157,7 @@
             throw e.rethrowFromSystemServer();
         }
 
-        ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
+        TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
     }
 
     void unregisterListeners() {
@@ -171,7 +172,7 @@
             throw e.rethrowFromSystemServer();
         }
 
-        ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskStackListener);
+        TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
     }
 
     void addRotationCallback(Consumer<Integer> watcher) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
index af851a7..4efe4d8 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
@@ -58,7 +58,6 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
-import com.android.systemui.bubbles.Bubbles;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.shared.system.QuickStepContract;
 
@@ -426,12 +425,6 @@
         if (getDisplay() != null) {
             displayId = getDisplay().getDisplayId();
         }
-        // Bubbles will give us a valid display id if it should get the back event
-        Bubbles Bubbles = Dependency.get(Bubbles.class);
-        int bubbleDisplayId = Bubbles.getExpandedDisplayId(mContext);
-        if (mCode == KeyEvent.KEYCODE_BACK && bubbleDisplayId != INVALID_DISPLAY) {
-            displayId = bubbleDisplayId;
-        }
         if (displayId != INVALID_DISPLAY) {
             ev.setDisplayId(displayId);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 1538e9e..18cc746 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -15,8 +15,6 @@
  */
 package com.android.systemui.navigationbar.gestural;
 
-import static android.view.Display.INVALID_DISPLAY;
-
 import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -57,7 +55,6 @@
 import com.android.systemui.R;
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.bubbles.Bubbles;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.navigationbar.NavigationBarView;
 import com.android.systemui.navigationbar.NavigationModeController;
@@ -71,6 +68,7 @@
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.SysUiStatsLog;
 import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.systemui.shared.tracing.ProtoTraceable;
 import com.android.systemui.tracing.ProtoTracer;
 import com.android.systemui.tracing.nano.EdgeBackGestureHandlerProto;
@@ -387,7 +385,7 @@
             mGestureNavigationSettingsObserver.unregister();
             mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this);
             mPluginManager.removePluginListener(this);
-            ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskStackListener);
+            TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
             DeviceConfig.removeOnPropertiesChangedListener(mOnPropertiesChangedListener);
 
             try {
@@ -403,7 +401,7 @@
             updateDisplaySize();
             mContext.getSystemService(DisplayManager.class).registerDisplayListener(this,
                     mContext.getMainThreadHandler());
-            ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
+            TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
             DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
                     runnable -> (mContext.getMainThreadHandler()).post(runnable),
                     mOnPropertiesChangedListener);
@@ -605,6 +603,11 @@
             return;
         }
         mLogGesture = false;
+        String logPackageName = "";
+        // Due to privacy, only top 100 most used apps by all users can be logged.
+        if (mUseMLModel && mVocab.containsKey(mPackageName) && mVocab.get(mPackageName) < 100) {
+            logPackageName = mPackageName;
+        }
         SysUiStatsLog.write(SysUiStatsLog.BACK_GESTURE_REPORTED_REPORTED, backType,
                 (int) mDownPoint.y, mIsOnLeftEdge
                         ? SysUiStatsLog.BACK_GESTURE__X_LOCATION__LEFT
@@ -613,7 +616,7 @@
                 (int) mEndPoint.x, (int) mEndPoint.y,
                 mEdgeWidthLeft + mLeftInset,
                 mDisplaySize.x - (mEdgeWidthRight + mRightInset),
-                mUseMLModel ? mMLResults : -2);
+                mUseMLModel ? mMLResults : -2, logPackageName);
     }
 
     private void onMotionEvent(MotionEvent ev) {
@@ -728,13 +731,7 @@
                 KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
                 InputDevice.SOURCE_KEYBOARD);
 
-        // Bubbles will give us a valid display id if it should get the back event
-        final int bubbleDisplayId = Dependency.get(Bubbles.class).getExpandedDisplayId(mContext);
-        if (bubbleDisplayId != INVALID_DISPLAY) {
-            ev.setDisplayId(bubbleDisplayId);
-        } else {
-            ev.setDisplayId(mContext.getDisplay().getDisplayId());
-        }
+        ev.setDisplayId(mContext.getDisplay().getDisplayId());
         InputManager.getInstance().injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
index 284f41a..fbbda5f 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.navigationbar.gestural;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -334,6 +336,7 @@
                 .getDimension(R.dimen.navigation_edge_action_drag_threshold);
         setVisibility(GONE);
 
+        boolean isPrimaryDisplay = mContext.getDisplayId() == DEFAULT_DISPLAY;
         mRegionSamplingHelper = new RegionSamplingHelper(this,
                 new RegionSamplingHelper.SamplingCallback() {
                     @Override
@@ -345,8 +348,14 @@
                     public Rect getSampledRegion(View sampledView) {
                         return mSamplingRect;
                     }
+
+                    @Override
+                    public boolean isSamplingEnabled() {
+                        return isPrimaryDisplay;
+                    }
                 });
         mRegionSamplingHelper.setWindowVisible(true);
+        mShowProtection = !isPrimaryDisplay;
     }
 
     @Override
@@ -366,11 +375,6 @@
         updateIsDark(animate);
     }
 
-    private void setShowProtection(boolean showProtection) {
-        mShowProtection = showProtection;
-        invalidate();
-    }
-
     @Override
     public void setIsLeftPanel(boolean isLeftPanel) {
         mIsLeftPanel = isLeftPanel;
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
new file mode 100644
index 0000000..6a78c64
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.people;
+
+import android.app.Activity;
+import android.app.INotificationManager;
+import android.app.people.IPeopleManager;
+import android.content.Context;
+import android.content.pm.LauncherApps;
+import android.content.pm.PackageManager;
+import android.content.pm.ShortcutInfo;
+import android.os.Bundle;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.Log;
+import android.view.ViewGroup;
+
+import com.android.systemui.R;
+
+import java.util.List;
+
+/**
+ * Shows the user their tiles for their priority People (go/live-status).
+ */
+public class PeopleSpaceActivity extends Activity {
+
+    private static String sTAG = "PeopleSpaceActivity";
+
+    private ViewGroup mPeopleSpaceLayout;
+    private IPeopleManager mPeopleManager;
+    private INotificationManager mNotificationManager;
+    private PackageManager mPackageManager;
+    private LauncherApps mLauncherApps;
+    private Context mContext;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.people_space_activity);
+        mPeopleSpaceLayout = findViewById(R.id.people_space_layout);
+        mContext = getApplicationContext();
+        mNotificationManager =
+                INotificationManager.Stub.asInterface(
+                        ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+        mPackageManager = getPackageManager();
+        mPeopleManager = IPeopleManager.Stub.asInterface(
+                ServiceManager.getService(Context.PEOPLE_SERVICE));
+        mLauncherApps = mContext.getSystemService(LauncherApps.class);
+        setTileViewsWithPriorityConversations();
+    }
+
+    /**
+     * Retrieves all priority conversations and sets a {@link PeopleSpaceTileView}s for each
+     * priority conversation.
+     */
+    private void setTileViewsWithPriorityConversations() {
+        try {
+            List<ShortcutInfo> shortcutInfos =
+                    PeopleSpaceUtils.getShortcutInfos(
+                            mContext, mNotificationManager, mPeopleManager);
+            for (ShortcutInfo conversation : shortcutInfos) {
+                PeopleSpaceTileView tileView = new PeopleSpaceTileView(mContext,
+                        mPeopleSpaceLayout,
+                        conversation.getId());
+                setTileView(tileView, conversation);
+            }
+        } catch (Exception e) {
+            Log.e(sTAG, "Couldn't retrieve conversations", e);
+        }
+    }
+
+    /** Sets {@code tileView} with the data in {@code conversation}. */
+    private void setTileView(PeopleSpaceTileView tileView,
+            ShortcutInfo shortcutInfo) {
+        try {
+            int userId = UserHandle.getUserHandleForUid(
+                    shortcutInfo.getUserId()).getIdentifier();
+
+            String pkg = shortcutInfo.getPackage();
+            long lastInteraction = mPeopleManager.getLastInteraction(
+                    pkg, userId,
+                    shortcutInfo.getId());
+            String status = lastInteraction != 0l ? mContext.getString(
+                    R.string.last_interaction_status,
+                    PeopleSpaceUtils.getLastInteractionString(
+                            lastInteraction)) : mContext.getString(R.string.basic_status);
+            tileView.setStatus(status);
+
+            tileView.setName(shortcutInfo.getLabel().toString());
+            tileView.setPackageIcon(mPackageManager.getApplicationIcon(pkg));
+            tileView.setPersonIcon(mLauncherApps.getShortcutIconDrawable(shortcutInfo, 0));
+            tileView.setOnClickListener(mLauncherApps, shortcutInfo);
+        } catch (Exception e) {
+            Log.e(sTAG, "Couldn't retrieve shortcut information", e);
+        }
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        // Refresh tile views to sync new conversations.
+        setTileViewsWithPriorityConversations();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java
new file mode 100644
index 0000000..d5ef190
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.people;
+
+import android.content.Context;
+import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutInfo;
+import android.graphics.drawable.Drawable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+
+/**
+ * PeopleSpaceTileView renders an individual person's tile with associated status.
+ */
+public class PeopleSpaceTileView extends LinearLayout {
+
+    private View mTileView;
+    private TextView mNameView;
+    private TextView mStatusView;
+    private ImageView mPackageIconView;
+    private ImageView mPersonIconView;
+
+    public PeopleSpaceTileView(Context context, ViewGroup view, String shortcutId) {
+        super(context);
+        mTileView = view.findViewWithTag(shortcutId);
+        if (mTileView == null) {
+            LayoutInflater inflater = LayoutInflater.from(context);
+            mTileView = inflater.inflate(R.layout.people_space_tile_view, view, false);
+            view.addView(mTileView, LayoutParams.MATCH_PARENT,
+                    LayoutParams.MATCH_PARENT);
+            mTileView.setTag(shortcutId);
+        }
+        mNameView = mTileView.findViewById(R.id.tile_view_name);
+        mStatusView = mTileView.findViewById(R.id.tile_view_status);
+        mPackageIconView = mTileView.findViewById(R.id.tile_view_package_icon);
+        mPersonIconView = mTileView.findViewById(R.id.tile_view_person_icon);
+    }
+
+    /** Sets the name text on the tile. */
+    public void setName(String name) {
+        mNameView.setText(name);
+    }
+
+    /** Sets the status text on the tile. */
+    public void setStatus(String status) {
+        mStatusView.setText(status);
+    }
+
+    /** Sets the package drawable on the tile. */
+    public void setPackageIcon(Drawable drawable) {
+        mPackageIconView.setImageDrawable(drawable);
+    }
+
+    /** Sets the person drawable on the tile. */
+    public void setPersonIcon(Drawable drawable) {
+        mPersonIconView.setImageDrawable(drawable);
+    }
+
+    /** Sets the click listener of the tile. */
+    public void setOnClickListener(LauncherApps launcherApps, ShortcutInfo shortcutInfo) {
+        mTileView.setOnClickListener(v -> launcherApps.startShortcut(shortcutInfo, null, null));
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
new file mode 100644
index 0000000..9d46b32
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.people;
+
+import android.app.INotificationManager;
+import android.app.people.ConversationChannel;
+import android.app.people.IPeopleManager;
+import android.content.Context;
+import android.content.pm.ShortcutInfo;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.icu.text.MeasureFormat;
+import android.icu.util.Measure;
+import android.icu.util.MeasureUnit;
+import android.provider.Settings;
+import android.service.notification.ConversationChannelWrapper;
+
+import java.time.Duration;
+import java.util.List;
+import java.util.Locale;
+import java.util.stream.Collectors;
+
+/** Utils class for People Space. */
+public class PeopleSpaceUtils {
+    private static final String TAG = "PeopleSpaceUtils";
+
+    /** Turns on debugging information about People Space. */
+    public static final boolean DEBUG = true;
+
+    /** Returns a list of {@link ShortcutInfo} corresponding to user's conversations. */
+    public static List<ShortcutInfo> getShortcutInfos(
+            Context context,
+            INotificationManager notificationManager,
+            IPeopleManager peopleManager
+    ) throws Exception {
+        boolean showAllConversations = Settings.Global.getInt(context.getContentResolver(),
+                Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE) == 0;
+        List<ConversationChannelWrapper> conversations =
+                notificationManager.getConversations(
+                        !showAllConversations /* priority only */).getList();
+        List<ShortcutInfo> shortcutInfos = conversations.stream().filter(
+                c -> shouldKeepConversation(c)).map(
+                    c -> c.getShortcutInfo()).collect(Collectors.toList());
+        if (showAllConversations) {
+            List<ConversationChannel> recentConversations =
+                    peopleManager.getRecentConversations().getList();
+            List<ShortcutInfo> recentShortcuts = recentConversations.stream().map(
+                    c -> c.getShortcutInfo()).collect(Collectors.toList());
+            shortcutInfos.addAll(recentShortcuts);
+        }
+        return shortcutInfos;
+    }
+
+    /** Converts {@code drawable} to a {@link Bitmap}. */
+    public static Bitmap convertDrawableToBitmap(Drawable drawable) {
+        if (drawable instanceof BitmapDrawable) {
+            return ((BitmapDrawable) drawable).getBitmap();
+        }
+        // We use max below because the drawable might have no intrinsic width/height (e.g. if the
+        // drawable is a solid color).
+        Bitmap bitmap =
+                Bitmap.createBitmap(
+                        Math.max(drawable.getIntrinsicWidth(), 1),
+                        Math.max(drawable.getIntrinsicHeight(), 1),
+                        Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(bitmap);
+        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+        drawable.draw(canvas);
+        return bitmap;
+    }
+
+    /** Returns a readable representation of {@code lastInteraction}. */
+    public static String getLastInteractionString(long lastInteraction) {
+        long now = System.currentTimeMillis();
+        Duration durationSinceLastInteraction = Duration.ofMillis(
+                now - lastInteraction);
+        MeasureFormat formatter = MeasureFormat.getInstance(Locale.getDefault(),
+                MeasureFormat.FormatWidth.WIDE);
+        if (durationSinceLastInteraction.toDays() >= 1) {
+            return
+                    formatter
+                            .formatMeasures(new Measure(durationSinceLastInteraction.toDays(),
+                                    MeasureUnit.DAY));
+        } else if (durationSinceLastInteraction.toHours() >= 1) {
+            return formatter.formatMeasures(new Measure(durationSinceLastInteraction.toHours(),
+                    MeasureUnit.HOUR));
+        } else if (durationSinceLastInteraction.toMinutes() >= 1) {
+            return formatter.formatMeasures(new Measure(durationSinceLastInteraction.toMinutes(),
+                    MeasureUnit.MINUTE));
+        } else {
+            return formatter.formatMeasures(
+                    new Measure(durationSinceLastInteraction.toMillis() / 1000,
+                            MeasureUnit.SECOND));
+        }
+    }
+
+    /**
+     * Returns whether the {@code conversation} should be kept for display in the People Space.
+     *
+     * <p>A valid {@code conversation} must:
+     *     <ul>
+     *         <li>Have a non-null {@link ShortcutInfo}
+     *         <li>Have an associated label in the {@link ShortcutInfo}
+     *     </ul>
+     * </li>
+     */
+    public static boolean shouldKeepConversation(ConversationChannelWrapper conversation) {
+        ShortcutInfo shortcutInfo = conversation.getShortcutInfo();
+        return shortcutInfo != null && shortcutInfo.getLabel().length() != 0;
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
new file mode 100644
index 0000000..85801f9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.people.widget;
+
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProvider;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+import android.widget.RemoteViews;
+
+import com.android.systemui.R;
+import com.android.systemui.people.PeopleSpaceUtils;
+
+/** People Space Widget Provider class. */
+public class PeopleSpaceWidgetProvider extends AppWidgetProvider {
+    private static final String TAG = "PeopleSpaceWidgetPvd";
+    private static final boolean DEBUG = PeopleSpaceUtils.DEBUG;
+
+    /** Called when widget updates. */
+    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
+        super.onUpdate(context, appWidgetManager, appWidgetIds);
+
+        if (DEBUG) Log.d(TAG, "onUpdate called");
+        // Perform this loop procedure for each App Widget that belongs to this provider
+        for (int appWidgetId : appWidgetIds) {
+            RemoteViews views =
+                    new RemoteViews(context.getPackageName(), R.layout.people_space_widget);
+
+            Intent intent = new Intent(context, PeopleSpaceWidgetService.class);
+            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
+            views.setRemoteAdapter(R.id.widget_list_view, intent);
+
+            // Tell the AppWidgetManager to perform an update on the current app widget
+            appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widget_list_view);
+            appWidgetManager.updateAppWidget(appWidgetId, views);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java
new file mode 100644
index 0000000..093925a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.people.widget;
+
+import android.app.INotificationManager;
+import android.app.people.IPeopleManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.LauncherApps;
+import android.content.pm.PackageManager;
+import android.content.pm.ShortcutInfo;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.Log;
+import android.widget.RemoteViews;
+import android.widget.RemoteViewsService;
+
+import com.android.systemui.R;
+import com.android.systemui.people.PeopleSpaceTileView;
+import com.android.systemui.people.PeopleSpaceUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** People Space Widget RemoteViewsFactory class. */
+public class PeopleSpaceWidgetRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {
+    private static final String TAG = "PeopleSpaceWRVFactory";
+    private static final boolean DEBUG = PeopleSpaceUtils.DEBUG;
+
+    private IPeopleManager mPeopleManager;
+    private INotificationManager mNotificationManager;
+    private PackageManager mPackageManager;
+    private LauncherApps mLauncherApps;
+    private List<ShortcutInfo> mShortcutInfos = new ArrayList<>();
+    private Context mContext;
+
+    public PeopleSpaceWidgetRemoteViewsFactory(Context context, Intent intent) {
+        this.mContext = context;
+    }
+
+    @Override
+    public void onCreate() {
+        if (DEBUG) Log.d(TAG, "onCreate called");
+        mNotificationManager =
+                INotificationManager.Stub.asInterface(
+                        ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+        mPackageManager = mContext.getPackageManager();
+        mPeopleManager = IPeopleManager.Stub.asInterface(
+                ServiceManager.getService(Context.PEOPLE_SERVICE));
+        mLauncherApps = mContext.getSystemService(LauncherApps.class);
+        setTileViewsWithPriorityConversations();
+    }
+
+    /**
+     * Retrieves all priority conversations and sets a {@link PeopleSpaceTileView}s for each
+     * priority conversation.
+     */
+    private void setTileViewsWithPriorityConversations() {
+        try {
+            mShortcutInfos =
+                    PeopleSpaceUtils.getShortcutInfos(
+                            mContext, mNotificationManager, mPeopleManager);
+        } catch (Exception e) {
+            Log.e(TAG, "Couldn't retrieve conversations", e);
+        }
+    }
+
+    @Override
+    public void onDataSetChanged() {
+        if (DEBUG) Log.d(TAG, "onDataSetChanged called");
+        setTileViewsWithPriorityConversations();
+    }
+
+    @Override
+    public void onDestroy() {
+        mShortcutInfos.clear();
+    }
+
+    @Override
+    public int getCount() {
+        return mShortcutInfos.size();
+    }
+
+    @Override
+    public RemoteViews getViewAt(int i) {
+        if (DEBUG) Log.d(TAG, "getViewAt called, index: " + i);
+
+        RemoteViews personView =
+                new RemoteViews(mContext.getPackageName(), R.layout.people_space_widget_item);
+        try {
+            ShortcutInfo shortcutInfo = mShortcutInfos.get(i);
+            int userId = UserHandle.getUserHandleForUid(
+                    shortcutInfo.getUserId()).getIdentifier();
+            String pkg = shortcutInfo.getPackage();
+            long lastInteraction = mPeopleManager.getLastInteraction(
+                    pkg, userId,
+                    shortcutInfo.getId());
+
+            String status = lastInteraction != 0L ? mContext.getString(
+                    R.string.last_interaction_status,
+                    PeopleSpaceUtils.getLastInteractionString(
+                            lastInteraction)) : mContext.getString(R.string.basic_status);
+
+            personView.setTextViewText(R.id.status, status);
+            personView.setTextViewText(R.id.name, shortcutInfo.getLabel().toString());
+
+            personView.setImageViewBitmap(
+                    R.id.package_icon,
+                    PeopleSpaceUtils.convertDrawableToBitmap(
+                            mPackageManager.getApplicationIcon(pkg)
+                    )
+            );
+            personView.setImageViewBitmap(
+                    R.id.person_icon,
+                    PeopleSpaceUtils.convertDrawableToBitmap(
+                            mLauncherApps.getShortcutIconDrawable(shortcutInfo, 0)
+                    )
+            );
+        } catch (Exception e) {
+            Log.e(TAG, "Couldn't retrieve shortcut information", e);
+        }
+        return personView;
+    }
+
+    @Override
+    public RemoteViews getLoadingView() {
+        return null;
+    }
+
+    @Override
+    public int getViewTypeCount() {
+        return 1;
+    }
+
+    @Override
+    public long getItemId(int i) {
+        return i;
+    }
+
+    @Override
+    public boolean hasStableIds() {
+        return true;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetService.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetService.java
new file mode 100644
index 0000000..c0e4347
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetService.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.people.widget;
+import android.content.Intent;
+import android.util.Log;
+import android.widget.RemoteViewsService;
+
+import com.android.systemui.people.PeopleSpaceUtils;
+
+/** People Space Widget Service class. */
+public class PeopleSpaceWidgetService extends RemoteViewsService {
+    private static final String TAG = "PeopleSpaceWidgetSvc";
+    private static final boolean DEBUG = PeopleSpaceUtils.DEBUG;
+
+    @Override
+    public RemoteViewsFactory onGetViewFactory(Intent intent) {
+        if (DEBUG) Log.d(TAG, "onGetViewFactory called");
+        return new PeopleSpaceWidgetRemoteViewsFactory(this.getApplicationContext(), intent);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index f56e6cd..dc5ba69 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -69,8 +69,10 @@
         private const val ALL_INDICATORS =
                 SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED
         private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED
+        private const val LOCATION = SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_ENABLED
         private const val DEFAULT_ALL_INDICATORS = false
         private const val DEFAULT_MIC_CAMERA = true
+        private const val DEFAULT_LOCATION = false
     }
 
     @VisibleForTesting
@@ -88,6 +90,11 @@
         return true
     }
 
+    private fun isLocationEnabled(): Boolean {
+        return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+                LOCATION, DEFAULT_LOCATION)
+    }
+
     private var currentUserIds = emptyList<Int>()
     private var listening = false
     private val callbacks = mutableListOf<WeakReference<Callback>>()
@@ -107,13 +114,15 @@
         private set
     var micCameraAvailable = isMicCameraEnabled()
         private set
+    var locationAvailable = isLocationEnabled()
 
     private val devicePropertiesChangedListener =
             object : DeviceConfig.OnPropertiesChangedListener {
         override fun onPropertiesChanged(properties: DeviceConfig.Properties) {
             if (DeviceConfig.NAMESPACE_PRIVACY.equals(properties.getNamespace()) &&
                     (properties.keyset.contains(ALL_INDICATORS) ||
-                    properties.keyset.contains(MIC_CAMERA))) {
+                            properties.keyset.contains(MIC_CAMERA) ||
+                            properties.keyset.contains(LOCATION))) {
 
                 // Running on the ui executor so can iterate on callbacks
                 if (properties.keyset.contains(ALL_INDICATORS)) {
@@ -126,6 +135,10 @@
 //                    micCameraAvailable = properties.getBoolean(MIC_CAMERA, DEFAULT_MIC_CAMERA)
 //                    callbacks.forEach { it.get()?.onFlagMicCameraChanged(micCameraAvailable) }
 //                }
+                if (properties.keyset.contains(LOCATION)) {
+                    locationAvailable = properties.getBoolean(LOCATION, DEFAULT_LOCATION)
+                    callbacks.forEach { it.get()?.onFlagLocationChanged(locationAvailable) }
+                }
                 internalUiExecutor.updateListeningState()
             }
         }
@@ -139,7 +152,8 @@
             active: Boolean
         ) {
             // Check if we care about this code right now
-            if (!allIndicatorsAvailable && code in OPS_LOCATION) {
+            if (!allIndicatorsAvailable &&
+                    (code in OPS_LOCATION && !locationAvailable)) {
                 return
             }
             val userId = UserHandle.getUserId(uid)
@@ -195,7 +209,8 @@
      * main thread.
      */
     private fun setListeningState() {
-        val listen = !callbacks.isEmpty() and (allIndicatorsAvailable || micCameraAvailable)
+        val listen = !callbacks.isEmpty() and
+                (allIndicatorsAvailable || micCameraAvailable || locationAvailable)
         if (listening == listen) return
         listening = listen
         if (listening) {
@@ -258,7 +273,9 @@
             AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE
             else -> return null
         }
-        if (type == PrivacyType.TYPE_LOCATION && !allIndicatorsAvailable) return null
+        if (type == PrivacyType.TYPE_LOCATION && (!allIndicatorsAvailable && !locationAvailable)) {
+            return null
+        }
         val app = PrivacyApplication(appOpItem.packageName, appOpItem.uid)
         return PrivacyItem(type, app)
     }
@@ -271,6 +288,9 @@
 
         @JvmDefault
         fun onFlagMicCameraChanged(flag: Boolean) {}
+
+        @JvmDefault
+        fun onFlagLocationChanged(flag: Boolean) {}
     }
 
     private class NotifyChangesToCallback(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt
index dc157a8..8107647 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt
@@ -33,7 +33,7 @@
         private const val NUM_LINES = 2
     }
 
-    protected val mRecords = ArrayList<QSPanel.TileRecord>()
+    protected val mRecords = ArrayList<QSPanelControllerBase.TileRecord>()
     private var _listening = false
     private var smallTileSize = 0
     private val twoLineHeight
@@ -50,17 +50,17 @@
         updateResources()
     }
 
-    override fun addTile(tile: QSPanel.TileRecord) {
+    override fun addTile(tile: QSPanelControllerBase.TileRecord) {
         mRecords.add(tile)
         tile.tile.setListening(this, _listening)
         addTileView(tile)
     }
 
-    protected fun addTileView(tile: QSPanel.TileRecord) {
+    protected fun addTileView(tile: QSPanelControllerBase.TileRecord) {
         addView(tile.tileView)
     }
 
-    override fun removeTile(tile: QSPanel.TileRecord) {
+    override fun removeTile(tile: QSPanelControllerBase.TileRecord) {
         mRecords.remove(tile)
         tile.tile.setListening(this, false)
         removeView(tile.tileView)
@@ -72,7 +72,7 @@
         super.removeAllViews()
     }
 
-    override fun getOffsetTop(tile: QSPanel.TileRecord?) = top
+    override fun getOffsetTop(tile: QSPanelControllerBase.TileRecord?) = top
 
     override fun updateResources(): Boolean {
         with(mContext.resources) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 04f379e..3062a77 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -26,7 +26,7 @@
 import com.android.systemui.R;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.qs.QSPanel.QSTileLayout;
-import com.android.systemui.qs.QSPanel.TileRecord;
+import com.android.systemui.qs.QSPanelControllerBase.TileRecord;
 
 import java.util.ArrayList;
 import java.util.Set;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index 9dcc924..4d41950 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -28,12 +28,17 @@
 import com.android.systemui.qs.QSPanel.QSTileLayout;
 import com.android.systemui.qs.TouchAnimator.Builder;
 import com.android.systemui.qs.TouchAnimator.Listener;
+import com.android.systemui.qs.dagger.QSScope;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerService.Tunable;
 
 import java.util.ArrayList;
 import java.util.Collection;
 
+import javax.inject.Inject;
+
+/** */
+@QSScope
 public class QSAnimator implements Callback, PageListener, Listener, OnLayoutChangeListener,
         OnAttachStateChangeListener, Tunable {
 
@@ -53,6 +58,9 @@
     private final ArrayList<View> mQuickQsViews = new ArrayList<>();
     private final QuickQSPanel mQuickQsPanel;
     private final QSPanel mQsPanel;
+    private final QSPanelController mQsPanelController;
+    private final QuickQSPanelController mQuickQSPanelController;
+    private final QSSecurityFooter mSecurityFooter;
     private final QS mQs;
 
     private PagedTileLayout mPagedLayout;
@@ -78,10 +86,19 @@
     private QSTileHost mHost;
     private boolean mShowCollapsedOnKeyguard;
 
-    public QSAnimator(QS qs, QuickQSPanel quickPanel, QSPanel panel) {
+    @Inject
+    public QSAnimator(QS qs, QuickQSPanel quickPanel, QSPanel panel,
+            QSPanelController qsPanelController, QuickQSPanelController quickQSPanelController,
+            QSTileHost qsTileHost,
+            QSSecurityFooter securityFooter) {
         mQs = qs;
         mQuickQsPanel = quickPanel;
         mQsPanel = panel;
+        mQsPanelController = qsPanelController;
+        mQuickQSPanelController = quickQSPanelController;
+        mSecurityFooter = securityFooter;
+        mHost = qsTileHost;
+        mHost.addCallback(this);
         mQsPanel.addOnAttachStateChangeListener(this);
         qs.getView().addOnLayoutChangeListener(this);
         if (mQsPanel.isAttachedToWindow()) {
@@ -134,12 +151,6 @@
                 && !mShowCollapsedOnKeyguard ? View.INVISIBLE : View.VISIBLE);
     }
 
-    public void setHost(QSTileHost qsh) {
-        mHost = qsh;
-        qsh.addCallback(this);
-        updateAnimators();
-    }
-
     @Override
     public void onViewAttachedToWindow(View v) {
         Dependency.get(TunerService.class).addTunable(this, ALLOW_FANCY_ANIMATION,
@@ -148,9 +159,7 @@
 
     @Override
     public void onViewDetachedFromWindow(View v) {
-        if (mHost != null) {
-            mHost.removeCallback(this);
-        }
+        mHost.removeCallback(this);
         Dependency.get(TunerService.class).removeTunable(this);
     }
 
@@ -185,8 +194,7 @@
         TouchAnimator.Builder translationXBuilder = new Builder();
         TouchAnimator.Builder translationYBuilder = new Builder();
 
-        if (mQsPanel.getHost() == null) return;
-        Collection<QSTile> tiles = mQsPanel.getHost().getTiles();
+        Collection<QSTile> tiles = mHost.getTiles();
         int count = 0;
         int[] loc1 = new int[2];
         int[] loc2 = new int[2];
@@ -206,7 +214,7 @@
         firstPageBuilder.addFloat(tileLayout, "translationY", heightDiff, 0);
 
         for (QSTile tile : tiles) {
-            QSTileView tileView = mQsPanel.getTileView(tile);
+            QSTileView tileView = mQsPanelController.getTileView(tile);
             if (tileView == null) {
                 Log.e(TAG, "tileView is null " + tile.getTileSpec());
                 continue;
@@ -217,7 +225,7 @@
             // This case: less tiles to animate in small displays.
             if (count < mQuickQsPanel.getTileLayout().getNumVisibleTiles() && mAllowFancy) {
                 // Quick tiles.
-                QSTileView quickTileView = mQuickQsPanel.getTileView(tile);
+                QSTileView quickTileView = mQuickQSPanelController.getTileView(tile);
                 if (quickTileView == null) continue;
 
                 lastX = loc1[0];
@@ -302,16 +310,12 @@
 
             // Fade in the security footer and the divider as we reach the final position
             builder = new Builder().setStartDelay(EXPANDED_TILE_DELAY);
-            if (mQsPanel.getSecurityFooter() != null) {
-                builder.addFloat(mQsPanel.getSecurityFooter().getView(), "alpha", 0, 1);
-            }
+            builder.addFloat(mSecurityFooter.getView(), "alpha", 0, 1);
             if (mQsPanel.getDivider() != null) {
                 builder.addFloat(mQsPanel.getDivider(), "alpha", 0, 1);
             }
             mAllPagesDelayedAnimator = builder.build();
-            if (mQsPanel.getSecurityFooter() != null) {
-                mAllViews.add(mQsPanel.getSecurityFooter().getView());
-            }
+            mAllViews.add(mSecurityFooter.getView());
             if (mQsPanel.getDivider() != null) {
                 mAllViews.add(mQsPanel.getDivider());
             }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index ac55fa0..7e2433a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -23,6 +23,7 @@
 import android.content.res.Configuration;
 import android.graphics.Point;
 import android.util.AttributeSet;
+import android.util.Pair;
 import android.view.View;
 import android.widget.FrameLayout;
 
@@ -282,7 +283,7 @@
             View view = getChildAt(i);
             if (view == mStatusBarBackground || view == mBackgroundGradient
                     || view == mQSCustomizer) {
-                // Some views are always full width
+                // Some views are always full width or have dependent padding
                 continue;
             }
             LayoutParams lp = (LayoutParams) view.getLayoutParams();
@@ -291,6 +292,9 @@
             if (view == mQSPanelContainer) {
                 // QS panel lays out some of its content full width
                 mQSPanel.setContentMargins(mContentPaddingStart, mContentPaddingEnd);
+                Pair<Integer, Integer> margins = mQSPanel.getVisualSideMargins();
+                // Apply paddings based on QSPanel
+                mQSCustomizer.setContentPaddings(margins.first, margins.second);
             } else if (view == mHeader) {
                 // The header contains the QQS panel which needs to have special padding, to
                 // visually align them.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java
index 1e239b1..34ebe3e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java
@@ -16,24 +16,25 @@
 
 package com.android.systemui.qs;
 
-import com.android.systemui.R;
+import com.android.systemui.qs.dagger.QSScope;
 import com.android.systemui.util.ViewController;
 
 import javax.inject.Inject;
 
-class QSContainerImplController extends ViewController<QSContainerImpl> {
+/** */
+@QSScope
+public class QSContainerImplController extends ViewController<QSContainerImpl> {
     private final QuickStatusBarHeaderController mQuickStatusBarHeaderController;
 
-    private QSContainerImplController(QSContainerImpl view,
-            QuickStatusBarHeaderController.Builder quickStatusBarHeaderControllerBuilder) {
+    @Inject
+    QSContainerImplController(QSContainerImpl view,
+            QuickStatusBarHeaderController quickStatusBarHeaderController) {
         super(view);
-        mQuickStatusBarHeaderController = quickStatusBarHeaderControllerBuilder
-                .setQuickStatusBarHeader(mView.findViewById(R.id.header)).build();
+        mQuickStatusBarHeaderController = quickStatusBarHeaderController;
     }
 
     @Override
-    public void init() {
-        super.init();
+    public void initInternal() {
         mQuickStatusBarHeaderController.init();
     }
 
@@ -49,23 +50,7 @@
     protected void onViewDetached() {
     }
 
-    static class Builder {
-        private final QuickStatusBarHeaderController.Builder mQuickStatusBarHeaderControllerBuilder;
-        private QSContainerImpl mView;
-
-        @Inject
-        Builder(
-                QuickStatusBarHeaderController.Builder quickStatusBarHeaderControllerBuilder) {
-            mQuickStatusBarHeaderControllerBuilder = quickStatusBarHeaderControllerBuilder;
-        }
-
-        public Builder setQSContainerImpl(QSContainerImpl view) {
-            mView = view;
-            return this;
-        }
-
-        public QSContainerImplController build() {
-            return new QSContainerImplController(mView, mQuickStatusBarHeaderControllerBuilder);
-        }
+    public QSContainerImpl getView() {
+        return mView;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index 2be8a97..cfcceb2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
@@ -76,7 +76,7 @@
     private int mOpenY;
     private boolean mAnimatingOpen;
     private boolean mSwitchState;
-    private View mFooter;
+    private QSFooter mFooter;
 
     public QSDetail(Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
@@ -120,7 +120,8 @@
         mDetailDoneButton.setOnClickListener(doneListener);
     }
 
-    public void setQsPanel(QSPanel panel, QuickStatusBarHeader header, View footer) {
+    /** */
+    public void setQsPanel(QSPanel panel, QuickStatusBarHeader header, QSFooter footer) {
         mQsPanel = panel;
         mHeader = header;
         mFooter = footer;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
similarity index 61%
rename from packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
rename to packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
index 84563a0..8b9dae1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -11,19 +11,14 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT 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.systemui.qs;
 
 import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
 
-import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
-
-import android.content.ClipData;
-import android.content.ClipboardManager;
 import android.content.Context;
-import android.content.Intent;
 import android.content.res.Configuration;
 import android.database.ContentObserver;
 import android.graphics.PorterDuff.Mode;
@@ -36,50 +31,27 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
-import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.View;
-import android.view.View.OnClickListener;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.TextView;
-import android.widget.Toast;
 
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto;
-import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.settingslib.Utils;
 import com.android.settingslib.development.DevelopmentSettingsEnabler;
 import com.android.settingslib.drawable.UserIconDrawable;
-import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.R.dimen;
-import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.qs.TouchAnimator.Builder;
-import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.phone.MultiUserSwitch;
 import com.android.systemui.statusbar.phone.SettingsButton;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.UserInfoController;
-import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener;
-import com.android.systemui.tuner.TunerService;
 
-import javax.inject.Inject;
-import javax.inject.Named;
-
-public class QSFooterImpl extends FrameLayout implements QSFooter,
-        OnClickListener, OnUserInfoChangedListener {
-
-    private static final String TAG = "QSFooterImpl";
-
-    private final ActivityStarter mActivityStarter;
-    private final UserInfoController mUserInfoController;
-    private final DeviceProvisionedController mDeviceProvisionedController;
-    private final UserTracker mUserTracker;
+/** */
+public class QSFooterView extends FrameLayout {
     private SettingsButton mSettingsButton;
     protected View mSettingsContainer;
     private PageIndicator mPageIndicator;
@@ -87,7 +59,6 @@
     private boolean mShouldShowBuildText;
 
     private boolean mQsDisabled;
-    private QSPanel mQsPanel;
     private QuickQSPanel mQuickQsPanel;
 
     private boolean mExpanded;
@@ -117,39 +88,19 @@
         }
     };
 
-    @Inject
-    public QSFooterImpl(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
-            ActivityStarter activityStarter, UserInfoController userInfoController,
-            DeviceProvisionedController deviceProvisionedController, UserTracker userTracker) {
+    public QSFooterView(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mActivityStarter = activityStarter;
-        mUserInfoController = userInfoController;
-        mDeviceProvisionedController = deviceProvisionedController;
-        mUserTracker = userTracker;
-    }
-
-    @VisibleForTesting
-    public QSFooterImpl(Context context, AttributeSet attrs) {
-        this(context, attrs,
-                Dependency.get(ActivityStarter.class),
-                Dependency.get(UserInfoController.class),
-                Dependency.get(DeviceProvisionedController.class),
-                Dependency.get(UserTracker.class));
     }
 
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
         mEdit = findViewById(android.R.id.edit);
-        mEdit.setOnClickListener(view ->
-                mActivityStarter.postQSRunnableDismissingKeyguard(() ->
-                        mQsPanel.showEdit(view)));
 
         mPageIndicator = findViewById(R.id.footer_page_indicator);
 
         mSettingsButton = findViewById(R.id.settings_button);
         mSettingsContainer = findViewById(R.id.settings_button_container);
-        mSettingsButton.setOnClickListener(this);
 
         mMultiUserSwitch = findViewById(R.id.multi_user_switch);
         mMultiUserAvatar = mMultiUserSwitch.findViewById(R.id.multi_user_avatar);
@@ -157,19 +108,6 @@
         mActionsContainer = findViewById(R.id.qs_footer_actions_container);
         mEditContainer = findViewById(R.id.qs_footer_actions_edit_container);
         mBuildText = findViewById(R.id.build);
-        mBuildText.setOnLongClickListener(view -> {
-            CharSequence buildText = mBuildText.getText();
-            if (!TextUtils.isEmpty(buildText)) {
-                ClipboardManager service =
-                        mUserTracker.getUserContext().getSystemService(ClipboardManager.class);
-                String label = mContext.getString(R.string.build_number_clip_data_label);
-                service.setPrimaryClip(ClipData.newPlainText(label, buildText));
-                Toast.makeText(mContext, R.string.build_number_copy_toast, Toast.LENGTH_SHORT)
-                        .show();
-                return true;
-            }
-            return false;
-        });
 
         // RenderThread is doing more harm than good when touching the header (to expand quick
         // settings), so disable it for this view
@@ -180,7 +118,6 @@
         addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight,
                 oldBottom) -> updateAnimator(right - left));
         setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
-        updateEverything();
         setBuildText();
     }
 
@@ -249,24 +186,22 @@
                 .build();
     }
 
-    @Override
-    public void setKeyguardShowing(boolean keyguardShowing) {
+    /** */
+    public void setKeyguardShowing() {
         setExpansion(mExpansionAmount);
     }
 
-    @Override
     public void setExpandClickListener(OnClickListener onClickListener) {
         mExpandClickListener = onClickListener;
     }
 
-    @Override
-    public void setExpanded(boolean expanded) {
+    void setExpanded(boolean expanded, boolean isTunerEnabled) {
         if (mExpanded == expanded) return;
         mExpanded = expanded;
-        updateEverything();
+        updateEverything(isTunerEnabled);
     }
 
-    @Override
+    /** */
     public void setExpansion(float headerExpansionFraction) {
         mExpansionAmount = headerExpansionFraction;
         if (mSettingsCogAnimator != null) mSettingsCogAnimator.setPosition(headerExpansionFraction);
@@ -287,18 +222,16 @@
     @Override
     @VisibleForTesting
     public void onDetachedFromWindow() {
-        setListening(false);
         mContext.getContentResolver().unregisterContentObserver(mDeveloperSettingsObserver);
         super.onDetachedFromWindow();
     }
 
-    @Override
+    /** */
     public void setListening(boolean listening) {
         if (listening == mListening) {
             return;
         }
         mListening = listening;
-        updateListeners();
     }
 
     @Override
@@ -318,17 +251,16 @@
         info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND);
     }
 
-    @Override
-    public void disable(int state1, int state2, boolean animate) {
+    void disable(int state2, boolean isTunerEnabled) {
         final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0;
         if (disabled == mQsDisabled) return;
         mQsDisabled = disabled;
-        updateEverything();
+        updateEverything(isTunerEnabled);
     }
 
-    public void updateEverything() {
+    void updateEverything(boolean isTunerEnabled) {
         post(() -> {
-            updateVisibilities();
+            updateVisibilities(isTunerEnabled);
             updateClickabilities();
             setClickable(false);
         });
@@ -341,11 +273,10 @@
         mBuildText.setLongClickable(mBuildText.getVisibility() == View.VISIBLE);
     }
 
-    private void updateVisibilities() {
+    private void updateVisibilities(boolean isTunerEnabled) {
         mSettingsContainer.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
         mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility(
-                TunerService.isTunerEnabled(mContext, mUserTracker.getUserHandle()) ? View.VISIBLE
-                        : View.INVISIBLE);
+                isTunerEnabled ? View.VISIBLE : View.INVISIBLE);
         final boolean isDemo = UserManager.isDeviceInDemoMode(mContext);
         mMultiUserSwitch.setVisibility(showUserSwitcher() ? View.VISIBLE : View.INVISIBLE);
         mEditContainer.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE);
@@ -358,78 +289,22 @@
         return mExpanded && mMultiUserSwitch.isMultiUserEnabled();
     }
 
-    private void updateListeners() {
-        if (mListening) {
-            mUserInfoController.addCallback(this);
-        } else {
-            mUserInfoController.removeCallback(this);
-        }
-    }
-
-    @Override
+    /** */
     public void setQSPanel(final QSPanel qsPanel) {
-        mQsPanel = qsPanel;
-        if (mQsPanel != null) {
+        if (qsPanel != null) {
             mMultiUserSwitch.setQsPanel(qsPanel);
-            mQsPanel.setFooterPageIndicator(mPageIndicator);
+            qsPanel.setFooterPageIndicator(mPageIndicator);
         }
     }
 
-    @Override
     public void setQQSPanel(@Nullable QuickQSPanel panel) {
         mQuickQsPanel = panel;
     }
 
-    @Override
-    public void onClick(View v) {
-        // Don't do anything until view are unhidden
-        if (!mExpanded) {
-            return;
-        }
 
-        if (v == mSettingsButton) {
-            if (!mDeviceProvisionedController.isCurrentUserSetup()) {
-                // If user isn't setup just unlock the device and dump them back at SUW.
-                mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
-                });
-                return;
-            }
-            MetricsLogger.action(mContext,
-                    mExpanded ? MetricsProto.MetricsEvent.ACTION_QS_EXPANDED_SETTINGS_LAUNCH
-                            : MetricsProto.MetricsEvent.ACTION_QS_COLLAPSED_SETTINGS_LAUNCH);
-            if (mSettingsButton.isTunerClick()) {
-                mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
-                    if (TunerService.isTunerEnabled(mContext, mUserTracker.getUserHandle())) {
-                        TunerService.showResetRequest(mContext, mUserTracker.getUserHandle(),
-                                () -> {
-                                    // Relaunch settings so that the tuner disappears.
-                                    startSettingsActivity();
-                                });
-                    } else {
-                        Toast.makeText(getContext(), R.string.tuner_toast,
-                                Toast.LENGTH_LONG).show();
-                        TunerService.setTunerEnabled(mContext, mUserTracker.getUserHandle(), true);
-                    }
-                    startSettingsActivity();
-
-                });
-            } else {
-                startSettingsActivity();
-            }
-        }
-    }
-
-    private void startSettingsActivity() {
-        mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS),
-                true /* dismissShade */);
-    }
-
-    @Override
-    public void onUserInfoChanged(String name, Drawable picture, String userAccount) {
-        if (picture != null &&
-                UserManager.get(mContext).isGuestUser(KeyguardUpdateMonitor.getCurrentUser()) &&
-                !(picture instanceof UserIconDrawable)) {
-            picture = picture.getConstantState().newDrawable(mContext.getResources()).mutate();
+    void onUserInfoChanged(Drawable picture, boolean isGuestUser) {
+        if (picture != null && isGuestUser && !(picture instanceof UserIconDrawable)) {
+            picture = picture.getConstantState().newDrawable(getResources()).mutate();
             picture.setColorFilter(
                     Utils.getColorAttrDefaultColor(mContext, android.R.attr.colorForeground),
                     Mode.SRC_IN);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
new file mode 100644
index 0000000..e3af04b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.qs;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.os.UserManager;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.annotation.Nullable;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.R;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.qs.dagger.QSScope;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.phone.SettingsButton;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.UserInfoController;
+import com.android.systemui.tuner.TunerService;
+import com.android.systemui.util.ViewController;
+
+import javax.inject.Inject;
+
+/**
+ * Controller for {@link QSFooterView}.
+ */
+@QSScope
+public class QSFooterViewController extends ViewController<QSFooterView> implements QSFooter {
+
+    private final UserManager mUserManager;
+    private final UserInfoController mUserInfoController;
+    private final ActivityStarter mActivityStarter;
+    private final DeviceProvisionedController mDeviceProvisionedController;
+    private final UserTracker mUserTracker;
+    private final QSPanelController mQsPanelController;
+    private final TunerService mTunerService;
+    private final MetricsLogger mMetricsLogger;
+    private final SettingsButton mSettingsButton;
+    private final TextView mBuildText;
+    private final View mEdit;
+
+    private final UserInfoController.OnUserInfoChangedListener mOnUserInfoChangedListener =
+            new UserInfoController.OnUserInfoChangedListener() {
+        @Override
+        public void onUserInfoChanged(String name, Drawable picture, String userAccount) {
+            boolean isGuestUser = mUserManager.isGuestUser(KeyguardUpdateMonitor.getCurrentUser());
+            mView.onUserInfoChanged(picture, isGuestUser);
+        }
+    };
+
+    private final View.OnClickListener mSettingsOnClickListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            // Don't do anything until view are unhidden
+            if (!mExpanded) {
+                return;
+            }
+
+            if (v == mSettingsButton) {
+                if (!mDeviceProvisionedController.isCurrentUserSetup()) {
+                    // If user isn't setup just unlock the device and dump them back at SUW.
+                    mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
+                    });
+                    return;
+                }
+                mMetricsLogger.action(
+                        mExpanded ? MetricsProto.MetricsEvent.ACTION_QS_EXPANDED_SETTINGS_LAUNCH
+                                : MetricsProto.MetricsEvent.ACTION_QS_COLLAPSED_SETTINGS_LAUNCH);
+                if (mSettingsButton.isTunerClick()) {
+                    mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
+                        if (isTunerEnabled()) {
+                            mTunerService.showResetRequest(
+                                    mUserTracker.getUserHandle(),
+                                    () -> {
+                                        // Relaunch settings so that the tuner disappears.
+                                        startSettingsActivity();
+                                    });
+                        } else {
+                            Toast.makeText(getContext(), R.string.tuner_toast,
+                                    Toast.LENGTH_LONG).show();
+                            mTunerService.setTunerEnabled(mUserTracker.getUserHandle(), true);
+                        }
+                        startSettingsActivity();
+
+                    });
+                } else {
+                    startSettingsActivity();
+                }
+            }
+        }
+    };
+
+    private boolean mListening;
+    private boolean mExpanded;
+
+    @Inject
+    QSFooterViewController(QSFooterView view, UserManager userManager,
+            UserInfoController userInfoController, ActivityStarter activityStarter,
+            DeviceProvisionedController deviceProvisionedController, UserTracker userTracker,
+            QSPanelController qsPanelController, TunerService tunerService,
+            MetricsLogger metricsLogger) {
+        super(view);
+        mUserManager = userManager;
+        mUserInfoController = userInfoController;
+        mActivityStarter = activityStarter;
+        mDeviceProvisionedController = deviceProvisionedController;
+        mUserTracker = userTracker;
+        mQsPanelController = qsPanelController;
+        mTunerService = tunerService;
+        mMetricsLogger = metricsLogger;
+
+        mSettingsButton = mView.findViewById(R.id.settings_button);
+        mBuildText = mView.findViewById(R.id.build);
+        mEdit = mView.findViewById(android.R.id.edit);
+    }
+
+
+    @Override
+    protected void onViewAttached() {
+        mSettingsButton.setOnClickListener(mSettingsOnClickListener);
+        mBuildText.setOnLongClickListener(view -> {
+            CharSequence buildText = mBuildText.getText();
+            if (!TextUtils.isEmpty(buildText)) {
+                ClipboardManager service =
+                        mUserTracker.getUserContext().getSystemService(ClipboardManager.class);
+                String label = getResources().getString(R.string.build_number_clip_data_label);
+                service.setPrimaryClip(ClipData.newPlainText(label, buildText));
+                Toast.makeText(getContext(), R.string.build_number_copy_toast, Toast.LENGTH_SHORT)
+                        .show();
+                return true;
+            }
+            return false;
+        });
+
+        mEdit.setOnClickListener(view ->
+                mActivityStarter.postQSRunnableDismissingKeyguard(() ->
+                        mQsPanelController.showEdit(view)));
+
+        mView.updateEverything(isTunerEnabled());
+    }
+
+    @Override
+    protected void onViewDetached() {
+        setListening(false);
+    }
+
+
+    @Override
+    public void setQSPanel(@Nullable QSPanel panel) {
+        mView.setQSPanel(panel);
+    }
+
+    @Override
+    public void setVisibility(int visibility) {
+        mView.setVisibility(visibility);
+    }
+
+    @Override
+    public void setExpanded(boolean expanded) {
+        mExpanded = expanded;
+        mView.setExpanded(expanded, isTunerEnabled());
+    }
+
+
+    @Override
+    public int getHeight() {
+        return mView.getHeight();
+    }
+
+    @Override
+    public void setExpansion(float expansion) {
+        mView.setExpansion(expansion);
+    }
+
+    @Override
+    public void setListening(boolean listening) {
+        if (mListening == listening) {
+            return;
+        }
+
+        mListening = listening;
+        if (mListening) {
+            mUserInfoController.addCallback(mOnUserInfoChangedListener);
+        } else {
+            mUserInfoController.removeCallback(mOnUserInfoChangedListener);
+        }
+    }
+
+    @Override
+    public void setKeyguardShowing(boolean keyguardShowing) {
+        mView.setKeyguardShowing();
+    }
+
+    /** */
+    @Override
+    public void setExpandClickListener(View.OnClickListener onClickListener) {
+        mView.setExpandClickListener(onClickListener);
+    }
+
+    @Override
+    public void setQQSPanel(@Nullable QuickQSPanel panel) {
+        mView.setQQSPanel(panel);
+    }
+
+    @Override
+    public void disable(int state1, int state2, boolean animate) {
+        mView.disable(state2, isTunerEnabled());
+    }
+
+
+    private void startSettingsActivity() {
+        mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS),
+                true /* dismissShade */);
+    }
+
+    private boolean isTunerEnabled() {
+        return mTunerService.isTunerEnabled(mUserTracker.getUserHandle());
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 3a78365..1a7d366 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -35,11 +35,11 @@
 
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
-import com.android.systemui.R.id;
 import com.android.systemui.media.MediaHost;
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.customize.QSCustomizer;
+import com.android.systemui.qs.dagger.QSFragmentComponent;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
@@ -70,7 +70,6 @@
     private HeightListener mPanelView;
     protected QuickStatusBarHeader mHeader;
     private QSCustomizer mQSCustomizer;
-    protected QSPanel mQSPanel;
     protected NonInterceptingScrollView mQSPanelScrollView;
     private QSDetail mQSDetail;
     private boolean mListening;
@@ -82,7 +81,7 @@
 
     private final RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
     private final InjectionInflationController mInjectionInflater;
-    private final QSContainerImplController.Builder mQSContainerImplControllerBuilder;
+    private final QSFragmentComponent.Factory mQsComponentFactory;
     private final QSTileHost mHost;
     private boolean mShowCollapsedOnKeyguard;
     private boolean mLastKeyguardAndExpanded;
@@ -96,15 +95,17 @@
     private int[] mTmpLocation = new int[2];
     private int mLastViewHeight;
     private float mLastHeaderTranslation;
+    private QSPanelController mQSPanelController;
+    private QuickQSPanelController mQuickQSPanelController;
 
     @Inject
     public QSFragment(RemoteInputQuickSettingsDisabler remoteInputQsDisabler,
             InjectionInflationController injectionInflater, QSTileHost qsTileHost,
             StatusBarStateController statusBarStateController, CommandQueue commandQueue,
-            QSContainerImplController.Builder qsContainerImplControllerBuilder) {
+            QSFragmentComponent.Factory qsComponentFactory) {
         mRemoteInputQuickSettingsDisabler = remoteInputQsDisabler;
         mInjectionInflater = injectionInflater;
-        mQSContainerImplControllerBuilder = qsContainerImplControllerBuilder;
+        mQsComponentFactory = qsComponentFactory;
         commandQueue.observe(getLifecycle(), this);
         mHost = qsTileHost;
         mStatusBarStateController = statusBarStateController;
@@ -120,8 +121,13 @@
 
     @Override
     public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
-        super.onViewCreated(view, savedInstanceState);
-        mQSPanel = view.findViewById(R.id.quick_settings_panel);
+        QSFragmentComponent qsFragmentComponent = mQsComponentFactory.create(this);
+        mQSPanelController = qsFragmentComponent.getQSPanelController();
+        mQuickQSPanelController = qsFragmentComponent.getQuickQSPanelController();
+
+        mQSPanelController.init();
+        mQuickQSPanelController.init();
+
         mQSPanelScrollView = view.findViewById(R.id.expanded_qs_scroll_view);
         mQSPanelScrollView.addOnLayoutChangeListener(
                 (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
@@ -135,18 +141,15 @@
         });
         mQSDetail = view.findViewById(R.id.qs_detail);
         mHeader = view.findViewById(R.id.header);
-        mQSPanel.setHeaderContainer(view.findViewById(R.id.header_text_container));
-        mFooter = view.findViewById(R.id.qs_footer);
-        mContainer = view.findViewById(id.quick_settings_container);
+        mQSPanelController.setHeaderContainer(view.findViewById(R.id.header_text_container));
+        mFooter = qsFragmentComponent.getQSFooter();
 
-        mQSContainerImplController = mQSContainerImplControllerBuilder
-                .setQSContainerImpl((QSContainerImpl) view)
-                .build();
+        mQSContainerImplController = qsFragmentComponent.getQSContainerImplController();
         mQSContainerImplController.init();
+        mContainer = mQSContainerImplController.getView();
 
-        mQSDetail.setQsPanel(mQSPanel, mHeader, (View) mFooter);
-        mQSAnimator = new QSAnimator(this, mHeader.findViewById(R.id.quick_qs_panel), mQSPanel);
-
+        mQSDetail.setQsPanel(mQSPanelController.getView(), mHeader, mFooter);
+        mQSAnimator = qsFragmentComponent.getQSAnimator();
 
         mQSCustomizer = view.findViewById(R.id.qs_customize);
         mQSCustomizer.setQs(this);
@@ -156,7 +159,7 @@
             setEditLocation(view);
             mQSCustomizer.restoreInstanceState(savedInstanceState);
             if (mQsExpanded) {
-                mQSPanel.getTileLayout().restoreInstanceState(savedInstanceState);
+                mQSPanelController.getTileLayout().restoreInstanceState(savedInstanceState);
             }
         }
         setHost(mHost);
@@ -188,7 +191,7 @@
         outState.putBoolean(EXTRA_LISTENING, mListening);
         mQSCustomizer.saveInstanceState(outState);
         if (mQsExpanded) {
-            mQSPanel.getTileLayout().saveInstanceState(outState);
+            mQSPanelController.getTileLayout().saveInstanceState(outState);
         }
     }
 
@@ -249,14 +252,10 @@
     }
 
     public void setHost(QSTileHost qsh) {
-        mQSPanel.setHost(qsh, mQSCustomizer);
-        mHeader.setQSPanel(mQSPanel);
-        mFooter.setQSPanel(mQSPanel);
+        mQSPanelController.setCustomizer(mQSCustomizer);
+        mHeader.setQSPanel(mQSPanelController.getView());
+        mFooter.setQSPanel(mQSPanelController.getView());
         mQSDetail.setHost(qsh);
-
-        if (mQSAnimator != null) {
-            mQSAnimator.setHost(qsh);
-        }
     }
 
     @Override
@@ -278,7 +277,7 @@
     private void updateQsState() {
         final boolean expandVisually = mQsExpanded || mStackScrollerOverscrolling
                 || mHeaderAnimating;
-        mQSPanel.setExpanded(mQsExpanded);
+        mQSPanelController.setExpanded(mQsExpanded);
         mQSDetail.setExpanded(mQsExpanded);
         boolean keyguardShowing = isKeyguardShowing();
         mHeader.setVisibility((mQsExpanded || !keyguardShowing || mHeaderAnimating
@@ -294,7 +293,8 @@
                 : View.INVISIBLE);
         mFooter.setExpanded((keyguardShowing && !mHeaderAnimating && !mShowCollapsedOnKeyguard)
                 || (mQsExpanded && !mStackScrollerOverscrolling));
-        mQSPanel.setVisibility(!mQsDisabled && expandVisually ? View.VISIBLE : View.INVISIBLE);
+        mQSPanelController.setVisibility(
+                !mQsDisabled && expandVisually ? View.VISIBLE : View.INVISIBLE);
     }
 
     private boolean isKeyguardShowing() {
@@ -317,8 +317,12 @@
         }
     }
 
+    public QSPanelController getQSPanelController() {
+        return mQSPanelController;
+    }
+
     public QSPanel getQsPanel() {
-        return mQSPanel;
+        return mQSPanelController.getView();
     }
 
     public QSCustomizer getCustomizer() {
@@ -327,7 +331,7 @@
 
     @Override
     public boolean isShowingDetail() {
-        return mQSPanel.isShowingCustomize() || mQSDetail.isShowingDetail();
+        return mQSPanelController.isShowingCustomize() || mQSDetail.isShowingDetail();
     }
 
     @Override
@@ -339,7 +343,7 @@
     public void setExpanded(boolean expanded) {
         if (DEBUG) Log.d(TAG, "setExpanded " + expanded);
         mQsExpanded = expanded;
-        mQSPanel.setListening(mListening, mQsExpanded);
+        mQSPanelController.setListening(mListening, mQsExpanded);
         updateQsState();
     }
 
@@ -368,7 +372,7 @@
         mListening = listening;
         mQSContainerImplController.setListening(listening);
         mFooter.setListening(listening);
-        mQSPanel.setListening(mListening, mQsExpanded);
+        mQSPanelController.setListening(mListening, mQsExpanded);
     }
 
     @Override
@@ -406,11 +410,15 @@
         float panelTranslationY = translationScaleY * heightDiff;
 
         // Let the views animate their contents correctly by giving them the necessary context.
-        mHeader.setExpansion(onKeyguardAndExpanded, expansion,
-                panelTranslationY);
+        mHeader.setExpansion(onKeyguardAndExpanded, expansion, panelTranslationY);
+        if (expansion < 1 && expansion > 0.99) {
+            if (mQuickQSPanelController.switchTileLayout(false)) {
+                mHeader.updateResources();
+            }
+        }
         mFooter.setExpansion(onKeyguardAndExpanded ? 1 : expansion);
-        mQSPanel.getQsTileRevealController().setExpansion(expansion);
-        mQSPanel.getTileLayout().setExpansion(expansion);
+        mQSPanelController.getQsTileRevealController().setExpansion(expansion);
+        mQSPanelController.getTileLayout().setExpansion(expansion);
         mQSPanelScrollView.setTranslationY(translationScaleY * heightDiff);
         if (fullyCollapsed) {
             mQSPanelScrollView.setScrollY(0);
@@ -448,7 +456,8 @@
             float expandedMediaPosition = absoluteBottomPosition - mQSPanelScrollView.getScrollY()
                     + mQSPanelScrollView.getScrollRange();
             // The expanded media host should never move below the laid out position
-            pinToBottom(expandedMediaPosition, mQSPanel.getMediaHost(), true /* expanded */);
+            pinToBottom(
+                    expandedMediaPosition, mQSPanelController.getMediaHost(), true /* expanded */);
             // The expanded media host should never move above the laid out position
             pinToBottom(absoluteBottomPosition, mHeader.getHeaderQsPanel().getMediaHost(),
                     false /* expanded */);
@@ -538,7 +547,7 @@
 
     @Override
     public void closeDetail() {
-        mQSPanel.closeDetail();
+        mQSPanelController.closeDetail();
     }
 
     public void notifyCustomizeChanged() {
@@ -553,8 +562,8 @@
     }
 
     /**
-     * The height this view wants to be. This is different from {@link #getMeasuredHeight} such that
-     * during closing the detail panel, this already returns the smaller height.
+     * The height this view wants to be. This is different from {@link View#getMeasuredHeight} such
+     * that during closing the detail panel, this already returns the smaller height.
      */
     @Override
     public int getDesiredHeight() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index fdc0a60e..1b17a2a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -16,20 +16,20 @@
 
 package com.android.systemui.qs;
 
+import static com.android.systemui.media.dagger.MediaModule.QS_PANEL;
 import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
 import static com.android.systemui.util.Utils.useQsMediaPlayer;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
-import android.metrics.LogMaker;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
 import android.util.AttributeSet;
+import android.util.Pair;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -41,41 +41,30 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.widget.RemeasuringLinearLayout;
 import com.android.systemui.Dependency;
-import com.android.systemui.Dumpable;
 import com.android.systemui.R;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.dump.DumpManager;
 import com.android.systemui.media.MediaHierarchyManager;
 import com.android.systemui.media.MediaHost;
 import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QSTile;
-import com.android.systemui.plugins.qs.QSTileView;
-import com.android.systemui.qs.QSHost.Callback;
 import com.android.systemui.qs.customize.QSCustomizer;
-import com.android.systemui.qs.external.CustomTile;
 import com.android.systemui.qs.logging.QSLogger;
-import com.android.systemui.settings.BrightnessController;
 import com.android.systemui.settings.ToggleSliderView;
-import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.policy.BrightnessMirrorController;
 import com.android.systemui.statusbar.policy.BrightnessMirrorController.BrightnessMirrorListener;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerService.Tunable;
 import com.android.systemui.util.animation.DisappearParameters;
 
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
 import java.util.function.Consumer;
-import java.util.stream.Collectors;
 
 import javax.inject.Inject;
 import javax.inject.Named;
 
 /** View that represents the quick settings tile panel (when expanded/pulled down). **/
-public class QSPanel extends LinearLayout implements Tunable, Callback, BrightnessMirrorListener,
-        Dumpable {
+public class QSPanel extends LinearLayout implements Tunable, BrightnessMirrorListener {
 
     public static final String QS_SHOW_BRIGHTNESS = "qs_show_brightness";
     public static final String QS_SHOW_HEADER = "qs_show_header";
@@ -83,20 +72,15 @@
     private static final String TAG = "QSPanel";
 
     protected final Context mContext;
-    protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
-    private final BroadcastDispatcher mBroadcastDispatcher;
     protected final MediaHost mMediaHost;
 
     /**
      * The index where the content starts that needs to be moved between parents
      */
     private final int mMovableContentStartIndex;
-    private String mCachedSpecs = "";
 
     @Nullable
     protected View mBrightnessView;
-    @Nullable
-    private BrightnessController mBrightnessController;
 
     private final H mHandler = new H();
     private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
@@ -110,14 +94,14 @@
     protected boolean mListening;
 
     private QSDetail.Callback mCallback;
-    private final DumpManager mDumpManager;
     private final QSLogger mQSLogger;
     protected final UiEventLogger mUiEventLogger;
     protected QSTileHost mHost;
-    private final UserTracker mUserTracker;
+    private final List<OnConfigurationChangedListener> mOnConfigurationChangedListeners =
+            new ArrayList<>();
 
     @Nullable
-    protected QSSecurityFooter mSecurityFooter;
+    protected View mSecurityFooter;
 
     @Nullable
     protected View mFooter;
@@ -154,12 +138,9 @@
     public QSPanel(
             @Named(VIEW_CONTEXT) Context context,
             AttributeSet attrs,
-            DumpManager dumpManager,
-            BroadcastDispatcher broadcastDispatcher,
             QSLogger qsLogger,
-            MediaHost mediaHost,
-            UiEventLogger uiEventLogger,
-            UserTracker userTracker
+            @Named(QS_PANEL) MediaHost mediaHost,
+            UiEventLogger uiEventLogger
     ) {
         super(context, attrs);
         mUsingMediaPlayer = useQsMediaPlayer(context);
@@ -172,16 +153,14 @@
         });
         mContext = context;
         mQSLogger = qsLogger;
-        mDumpManager = dumpManager;
-        mBroadcastDispatcher = broadcastDispatcher;
         mUiEventLogger = uiEventLogger;
-        mUserTracker = userTracker;
 
         setOrientation(VERTICAL);
 
         addViewsAboveTiles();
         mMovableContentStartIndex = getChildCount();
         mRegularTileLayout = createRegularTileLayout();
+        mTileLayout = mRegularTileLayout;
 
         if (mUsingMediaPlayer) {
             mHorizontalLinearLayout = new RemeasuringLinearLayout(mContext);
@@ -207,35 +186,27 @@
 
             initMediaHostState();
         }
-        addSecurityFooter();
         if (mRegularTileLayout instanceof PagedTileLayout) {
             mQsTileRevealController = new QSTileRevealController(mContext, this,
                     (PagedTileLayout) mRegularTileLayout);
         }
-        mQSLogger.logAllTilesChangeListening(mListening, getDumpableTag(), mCachedSpecs);
-        updateResources();
+        mQSLogger.logAllTilesChangeListening(mListening, getDumpableTag(), "");
     }
 
     protected void onMediaVisibilityChanged(Boolean visible) {
-        switchTileLayout();
         if (mMediaVisibilityChangedListener != null) {
             mMediaVisibilityChangedListener.accept(visible);
         }
     }
 
-    protected void addSecurityFooter() {
-        mSecurityFooter = new QSSecurityFooter(this, mContext, mUserTracker);
-    }
-
     protected void addViewsAboveTiles() {
         mBrightnessView = LayoutInflater.from(mContext).inflate(
             R.layout.quick_settings_brightness_dialog, this, false);
         addView(mBrightnessView);
-        mBrightnessController = new BrightnessController(getContext(),
-                findViewById(R.id.brightness_slider), mBroadcastDispatcher);
     }
 
-    protected QSTileLayout createRegularTileLayout() {
+    /** */
+    public QSTileLayout createRegularTileLayout() {
         if (mRegularTileLayout == null) {
             mRegularTileLayout = (QSTileLayout) LayoutInflater.from(mContext).inflate(
                     R.layout.qs_paged_tile_layout, this, false);
@@ -335,37 +306,10 @@
     }
 
     @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        final TunerService tunerService = Dependency.get(TunerService.class);
-        tunerService.addTunable(this, QS_SHOW_BRIGHTNESS);
-
-        if (mHost != null) {
-            setTiles(mHost.getTiles());
-        }
-        if (mBrightnessMirrorController != null) {
-            mBrightnessMirrorController.addCallback(this);
-        }
-        mDumpManager.registerDumpable(getDumpableTag(), this);
-    }
-
-    @Override
     protected void onDetachedFromWindow() {
-        Dependency.get(TunerService.class).removeTunable(this);
-        if (mHost != null) {
-            mHost.removeCallback(this);
-        }
         if (mTileLayout != null) {
             mTileLayout.setListening(false);
         }
-        for (TileRecord record : mRecords) {
-            record.tile.removeCallbacks();
-        }
-        mRecords.clear();
-        if (mBrightnessMirrorController != null) {
-            mBrightnessMirrorController.removeCallback(this);
-        }
-        mDumpManager.unregisterDumpable(getDumpableTag());
         super.onDetachedFromWindow();
     }
 
@@ -374,11 +318,6 @@
     }
 
     @Override
-    public void onTilesChanged() {
-        setTiles(mHost.getTiles());
-    }
-
-    @Override
     public void onTuningChanged(String key, String newValue) {
         if (QS_SHOW_BRIGHTNESS.equals(key) && mBrightnessView != null) {
             updateViewVisibilityForTuningValue(mBrightnessView, newValue);
@@ -389,8 +328,8 @@
         view.setVisibility(TunerService.parseIntegerSwitch(newValue, true) ? VISIBLE : GONE);
     }
 
-    public void openDetails(String subPanel) {
-        QSTile tile = getTile(subPanel);
+    /** */
+    public void openDetails(QSTile tile) {
         // If there's no tile with that name (as defined in QSFactoryImpl or other QSFactory),
         // QSFactory will not be able to create a tile and getTile will return null
         if (tile != null) {
@@ -398,15 +337,6 @@
         }
     }
 
-    private QSTile getTile(String subPanel) {
-        for (int i = 0; i < mRecords.size(); i++) {
-            if (subPanel.equals(mRecords.get(i).tile.getTileSpec())) {
-                return mRecords.get(i).tile;
-            }
-        }
-        return mHost.createTile(subPanel);
-    }
-
     public void setBrightnessMirror(BrightnessMirrorController c) {
         if (mBrightnessMirrorController != null) {
             mBrightnessMirrorController.removeCallback(this);
@@ -432,17 +362,8 @@
         mCallback = callback;
     }
 
-    public void setHost(QSTileHost host, QSCustomizer customizer) {
-        mHost = host;
-        mHost.addCallback(this);
-        setTiles(mHost.getTiles());
-        if (mSecurityFooter != null) {
-            mSecurityFooter.setHostEnvironment(host);
-        }
+    void setCustomizer(QSCustomizer customizer) {
         mCustomizePanel = customizer;
-        if (mCustomizePanel != null) {
-            mCustomizePanel.setHost(mHost);
-        }
     }
 
     /**
@@ -481,9 +402,6 @@
 
         updatePageIndicator();
 
-        if (mListening) {
-            refreshAllTiles();
-        }
         if (mTileLayout != null) {
             mTileLayout.updateResources();
         }
@@ -504,20 +422,21 @@
                 res.getDimensionPixelSize(R.dimen.qs_panel_padding_bottom));
     }
 
+    void addOnConfigurationChangedListener(OnConfigurationChangedListener listener) {
+        mOnConfigurationChangedListeners.add(listener);
+    }
+
+    void removeOnConfigurationChangedListener(OnConfigurationChangedListener listener) {
+        mOnConfigurationChangedListeners.remove(listener);
+    }
+
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
-        if (mSecurityFooter != null) {
-            mSecurityFooter.onConfigurationChanged();
-        }
-        updateResources();
+        mOnConfigurationChangedListeners.forEach(
+                listener -> listener.onConfigurationChange(newConfig));
 
         updateBrightnessMirror();
-
-        if (newConfig.orientation != mLastOrientation) {
-            mLastOrientation = newConfig.orientation;
-            switchTileLayout();
-        }
     }
 
     @Override
@@ -525,14 +444,9 @@
         super.onFinishInflate();
         mFooter = findViewById(R.id.qs_footer);
         mDivider = findViewById(R.id.divider);
-        switchTileLayout(true /* force */);
     }
 
-    boolean switchTileLayout() {
-        return switchTileLayout(false /* force */);
-    }
-
-    private boolean switchTileLayout(boolean force) {
+    boolean switchTileLayout(boolean force, List<QSPanelControllerBase.TileRecord> records) {
         /** Whether or not the QuickQSPanel currently contains a media player. */
         boolean horizontal = shouldUseHorizontalLayout();
         if (mDivider != null) {
@@ -560,13 +474,12 @@
             reAttachMediaHost();
             if (mTileLayout != null) {
                 mTileLayout.setListening(false);
-                for (TileRecord record : mRecords) {
+                for (QSPanelControllerBase.TileRecord record : records) {
                     mTileLayout.removeTile(record);
                     record.tile.removeCallback(record.callback);
                 }
             }
             mTileLayout = newLayout;
-            if (mHost != null) setTiles(mHost.getTiles());
             newLayout.setListening(mListening);
             if (needsDynamicRowsAndColumns()) {
                 newLayout.setMinRows(horizontal ? 2 : 1);
@@ -615,20 +528,20 @@
         index++;
 
         if (mSecurityFooter != null) {
-            View view = mSecurityFooter.getView();
-            LinearLayout.LayoutParams layoutParams = (LayoutParams) view.getLayoutParams();
+            LinearLayout.LayoutParams layoutParams =
+                    (LayoutParams) mSecurityFooter.getLayoutParams();
             if (mUsingHorizontalLayout && mHeaderContainer != null) {
                 // Adding the security view to the header, that enables us to avoid scrolling
                 layoutParams.width = 0;
                 layoutParams.weight = 1.6f;
-                switchToParent(view, mHeaderContainer, 1 /* always in second place */);
+                switchToParent(mSecurityFooter, mHeaderContainer, 1 /* always in second place */);
             } else {
                 layoutParams.width = LayoutParams.WRAP_CONTENT;
                 layoutParams.weight = 0;
-                switchToParent(view, parent, index);
+                switchToParent(mSecurityFooter, parent, index);
                 index++;
             }
-            view.setLayoutParams(layoutParams);
+            mSecurityFooter.setLayoutParams(layoutParams);
         }
 
         if (mFooter != null) {
@@ -701,14 +614,6 @@
         if (!mExpanded && mTileLayout instanceof PagedTileLayout) {
             ((PagedTileLayout) mTileLayout).setCurrentItem(0, false);
         }
-        mMetricsLogger.visibility(MetricsEvent.QS_PANEL, mExpanded);
-        if (!mExpanded) {
-            mUiEventLogger.log(closePanelEvent());
-            closeDetail();
-        } else {
-            mUiEventLogger.log(openPanelEvent());
-            logTiles();
-        }
     }
 
     public void setPageListener(final PagedTileLayout.PageListener pageListener) {
@@ -721,56 +626,16 @@
         return mExpanded;
     }
 
-    public void setListening(boolean listening) {
+    /** */
+    public void setListening(boolean listening, String cachedSpecs) {
         if (mListening == listening) return;
         mListening = listening;
         if (mTileLayout != null) {
-            mQSLogger.logAllTilesChangeListening(listening, getDumpableTag(), mCachedSpecs);
+            mQSLogger.logAllTilesChangeListening(listening, getDumpableTag(), cachedSpecs);
             mTileLayout.setListening(listening);
         }
-        if (mListening) {
-            refreshAllTiles();
-        }
     }
 
-    private String getTilesSpecs() {
-        return mRecords.stream()
-                .map(tileRecord ->  tileRecord.tile.getTileSpec())
-                .collect(Collectors.joining(","));
-    }
-
-    public void setListening(boolean listening, boolean expanded) {
-        setListening(listening && expanded);
-        if (mSecurityFooter != null) {
-            mSecurityFooter.setListening(listening);
-        }
-        // Set the listening as soon as the QS fragment starts listening regardless of the expansion,
-        // so it will update the current brightness before the slider is visible.
-        setBrightnessListening(listening);
-    }
-
-    public void setBrightnessListening(boolean listening) {
-        if (mBrightnessController == null) {
-            return;
-        }
-        if (listening) {
-            mBrightnessController.registerCallbacks();
-        } else {
-            mBrightnessController.unregisterCallbacks();
-        }
-    }
-
-    public void refreshAllTiles() {
-        if (mBrightnessController != null) {
-            mBrightnessController.checkRestrictionAndSetEnabled();
-        }
-        for (TileRecord r : mRecords) {
-            r.tile.refreshState();
-        }
-        if (mSecurityFooter != null) {
-            mSecurityFooter.refreshState();
-        }
-    }
 
     public void showDetailAdapter(boolean show, DetailAdapter adapter, int[] locationInWindow) {
         int xInWindow = locationInWindow[0];
@@ -792,33 +657,10 @@
         mHandler.obtainMessage(H.SHOW_DETAIL, show ? 1 : 0, 0, r).sendToTarget();
     }
 
-    public void setTiles(Collection<QSTile> tiles) {
-        setTiles(tiles, false);
-    }
-
-    public void setTiles(Collection<QSTile> tiles, boolean collapsedView) {
-        if (!collapsedView) {
-            mQsTileRevealController.updateRevealedTiles(tiles);
-        }
-        for (TileRecord record : mRecords) {
-            mTileLayout.removeTile(record);
-            record.tile.removeCallback(record.callback);
-        }
-        mRecords.clear();
-        mCachedSpecs = "";
-        for (QSTile tile : tiles) {
-            addTile(tile, collapsedView);
-        }
-    }
-
-    protected void drawTile(TileRecord r, QSTile.State state) {
+    protected void drawTile(QSPanelControllerBase.TileRecord r, QSTile.State state) {
         r.tileView.onStateChanged(state);
     }
 
-    protected QSTileView createTileView(QSTile tile, boolean collapsedView) {
-        return mHost.createTileView(tile, collapsedView);
-    }
-
     protected QSEvent openPanelEvent() {
         return QSEvent.QS_PANEL_EXPANDED;
     }
@@ -835,14 +677,15 @@
         return mExpanded;
     }
 
-    protected TileRecord addTile(final QSTile tile, boolean collapsedView) {
-        final TileRecord r = new TileRecord();
-        r.tile = tile;
-        r.tileView = createTileView(tile, collapsedView);
+    void updateRevealedTiles(Collection<QSTile> tiles) {
+        mQsTileRevealController.updateRevealedTiles(tiles);
+    }
+
+    void addTile(QSPanelControllerBase.TileRecord tileRecord) {
         final QSTile.Callback callback = new QSTile.Callback() {
             @Override
             public void onStateChanged(QSTile.State state) {
-                drawTile(r, state);
+                drawTile(tileRecord, state);
             }
 
             @Override
@@ -850,22 +693,22 @@
                 // Both the collapsed and full QS panels get this callback, this check determines
                 // which one should handle showing the detail.
                 if (shouldShowDetail()) {
-                    QSPanel.this.showDetail(show, r);
+                    QSPanel.this.showDetail(show, tileRecord);
                 }
             }
 
             @Override
             public void onToggleStateChanged(boolean state) {
-                if (mDetailRecord == r) {
+                if (mDetailRecord == tileRecord) {
                     fireToggleStateChanged(state);
                 }
             }
 
             @Override
             public void onScanStateChanged(boolean state) {
-                r.scanState = state;
-                if (mDetailRecord == r) {
-                    fireScanStateChanged(r.scanState);
+                tileRecord.scanState = state;
+                if (mDetailRecord == tileRecord) {
+                    fireScanStateChanged(tileRecord.scanState);
                 }
             }
 
@@ -877,20 +720,20 @@
                 }
             }
         };
-        r.tile.addCallback(callback);
-        r.callback = callback;
-        r.tileView.init(r.tile);
-        r.tile.refreshState();
-        mRecords.add(r);
-        mCachedSpecs = getTilesSpecs();
+
+        tileRecord.tile.addCallback(callback);
+        tileRecord.callback = callback;
+        tileRecord.tileView.init(tileRecord.tile);
+        tileRecord.tile.refreshState();
 
         if (mTileLayout != null) {
-            mTileLayout.addTile(r);
+            mTileLayout.addTile(tileRecord);
         }
-
-        return r;
     }
 
+    void removeTile(QSPanelControllerBase.TileRecord tileRecord) {
+        mTileLayout.removeTile(tileRecord);
+    }
 
     public void showEdit(final View v) {
         v.post(new Runnable() {
@@ -923,8 +766,8 @@
     }
 
     protected void handleShowDetail(Record r, boolean show) {
-        if (r instanceof TileRecord) {
-            handleShowDetailTile((TileRecord) r, show);
+        if (r instanceof QSPanelControllerBase.TileRecord) {
+            handleShowDetailTile((QSPanelControllerBase.TileRecord) r, show);
         } else {
             int x = 0;
             int y = 0;
@@ -936,7 +779,7 @@
         }
     }
 
-    private void handleShowDetailTile(TileRecord r, boolean show) {
+    private void handleShowDetailTile(QSPanelControllerBase.TileRecord r, boolean show) {
         if ((mDetailRecord != null) == show && mDetailRecord == r) return;
 
         if (show) {
@@ -957,8 +800,8 @@
     protected void setDetailRecord(Record r) {
         if (r == mDetailRecord) return;
         mDetailRecord = r;
-        final boolean scanState = mDetailRecord instanceof TileRecord
-                && ((TileRecord) mDetailRecord).scanState;
+        final boolean scanState = mDetailRecord instanceof QSPanelControllerBase.TileRecord
+                && ((QSPanelControllerBase.TileRecord) mDetailRecord).scanState;
         fireScanStateChanged(scanState);
     }
 
@@ -970,15 +813,6 @@
         }
         mGridContentVisible = visible;
     }
-
-    private void logTiles() {
-        for (int i = 0; i < mRecords.size(); i++) {
-            QSTile tile = mRecords.get(i).tile;
-            mMetricsLogger.write(tile.populate(new LogMaker(tile.getMetricsCategory())
-                    .setType(MetricsEvent.TYPE_OPEN)));
-        }
-    }
-
     private void fireShowingDetail(DetailAdapter detail, int x, int y) {
         if (mCallback != null) {
             mCallback.onShowingDetail(detail, x, y);
@@ -997,46 +831,15 @@
         }
     }
 
-    public void clickTile(ComponentName tile) {
-        final String spec = CustomTile.toSpec(tile);
-        final int N = mRecords.size();
-        for (int i = 0; i < N; i++) {
-            if (mRecords.get(i).tile.getTileSpec().equals(spec)) {
-                mRecords.get(i).tile.click();
-                break;
-            }
-        }
-    }
-
     QSTileLayout getTileLayout() {
         return mTileLayout;
     }
 
-    QSTileView getTileView(QSTile tile) {
-        for (TileRecord r : mRecords) {
-            if (r.tile == tile) {
-                return r.tileView;
-            }
-        }
-        return null;
-    }
-
-    @Nullable
-    public QSSecurityFooter getSecurityFooter() {
-        return mSecurityFooter;
-    }
-
     @Nullable
     public View getDivider() {
         return mDivider;
     }
 
-    public void showDeviceMonitoringDialog() {
-        if (mSecurityFooter != null) {
-            mSecurityFooter.showDeviceMonitoringDialog();
-        }
-    }
-
     public void setContentMargins(int startMargin, int endMargin) {
         // Only some views actually want this content padding, others want to go all the way
         // to the edge like the brightness slider
@@ -1080,6 +883,10 @@
         updateTileLayoutMargins();
     }
 
+    public Pair<Integer, Integer> getVisualSideMargins() {
+        return new Pair(mVisualMarginStart, mUsingHorizontalLayout ? 0 : mVisualMarginEnd);
+    }
+
     private void updateTileLayoutMargins() {
         int marginEnd = mVisualMarginEnd;
         if (mUsingHorizontalLayout) {
@@ -1115,9 +922,11 @@
      */
     protected void updateMargins(View view, int start, int end) {
         LayoutParams lp = (LayoutParams) view.getLayoutParams();
-        lp.setMarginStart(start);
-        lp.setMarginEnd(end);
-        view.setLayoutParams(lp);
+        if (lp != null) {
+            lp.setMarginStart(start);
+            lp.setMarginEnd(end);
+            view.setLayoutParams(lp);
+        }
     }
 
     public MediaHost getMediaHost() {
@@ -1135,6 +944,14 @@
         mMediaVisibilityChangedListener = visibilityChangedListener;
     }
 
+    public boolean isListening() {
+        return mListening;
+    }
+
+    public void setSecurityFooter(View view) {
+        mSecurityFooter = view;
+    }
+
     private class H extends Handler {
         private static final int SHOW_DETAIL = 1;
         private static final int SET_TILE_VISIBILITY = 2;
@@ -1150,46 +967,32 @@
         }
     }
 
-    @Override
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.println(getClass().getSimpleName() + ":");
-        pw.println("  Tile records:");
-        for (TileRecord record : mRecords) {
-            if (record.tile instanceof Dumpable) {
-                pw.print("    "); ((Dumpable) record.tile).dump(fd, pw, args);
-                pw.print("    "); pw.println(record.tileView.toString());
-            }
-        }
-    }
-
-
     protected static class Record {
         DetailAdapter detailAdapter;
         int x;
         int y;
     }
 
-    public static final class TileRecord extends Record {
-        public QSTile tile;
-        public com.android.systemui.plugins.qs.QSTileView tileView;
-        public boolean scanState;
-        public QSTile.Callback callback;
-    }
-
     public interface QSTileLayout {
-
+        /** */
         default void saveInstanceState(Bundle outState) {}
 
+        /** */
         default void restoreInstanceState(Bundle savedInstanceState) {}
 
-        void addTile(TileRecord tile);
+        /** */
+        void addTile(QSPanelControllerBase.TileRecord tile);
 
-        void removeTile(TileRecord tile);
+        /** */
+        void removeTile(QSPanelControllerBase.TileRecord tile);
 
-        int getOffsetTop(TileRecord tile);
+        /** */
+        int getOffsetTop(QSPanelControllerBase.TileRecord tile);
 
+        /** */
         boolean updateResources();
 
+        /** */
         void setListening(boolean listening);
 
         /**
@@ -1216,4 +1019,8 @@
 
         int getNumVisibleTiles();
     }
+
+    interface OnConfigurationChangedListener {
+        void onConfigurationChange(Configuration newConfig);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
new file mode 100644
index 0000000..f222b0d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.qs;
+
+import static com.android.systemui.qs.QSPanel.QS_SHOW_BRIGHTNESS;
+
+import android.annotation.NonNull;
+import android.content.res.Configuration;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.R;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.media.MediaHost;
+import com.android.systemui.plugins.qs.QSTile;
+import com.android.systemui.qs.customize.QSCustomizer;
+import com.android.systemui.qs.dagger.QSScope;
+import com.android.systemui.settings.BrightnessController;
+import com.android.systemui.statusbar.policy.BrightnessMirrorController;
+import com.android.systemui.tuner.TunerService;
+
+import javax.inject.Inject;
+
+/**
+ * Controller for {@link QSPanel}.
+ */
+@QSScope
+public class QSPanelController extends QSPanelControllerBase<QSPanel> {
+    private final QSSecurityFooter mQsSecurityFooter;
+    private final TunerService mTunerService;
+    private final BrightnessController mBrightnessController;
+
+    private final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener =
+            new QSPanel.OnConfigurationChangedListener() {
+        @Override
+        public void onConfigurationChange(Configuration newConfig) {
+            mView.updateResources();
+            mQsSecurityFooter.onConfigurationChanged();
+            if (mView.isListening()) {
+                refreshAllTiles();
+            }
+        }
+    };
+    private BrightnessMirrorController mBrightnessMirrorController;
+
+    @Inject
+    QSPanelController(QSPanel view, QSSecurityFooter qsSecurityFooter, TunerService tunerService,
+            QSTileHost qstileHost, DumpManager dumpManager,
+            MetricsLogger metricsLogger, UiEventLogger uiEventLogger,
+            BrightnessController.Factory brightnessControllerFactory) {
+        super(view, qstileHost, metricsLogger, uiEventLogger, dumpManager);
+        mQsSecurityFooter = qsSecurityFooter;
+        mTunerService = tunerService;
+        mQsSecurityFooter.setHostEnvironment(qstileHost);
+        mBrightnessController = brightnessControllerFactory.create(
+                mView.findViewById(R.id.brightness_slider));
+    }
+
+    @Override
+    protected void onViewAttached() {
+        super.onViewAttached();
+        mTunerService.addTunable(mView, QS_SHOW_BRIGHTNESS);
+        mView.updateResources();
+        if (mView.isListening()) {
+            refreshAllTiles();
+        }
+        mView.addOnConfigurationChangedListener(mOnConfigurationChangedListener);
+        mView.setSecurityFooter(mQsSecurityFooter.getView());
+        switchTileLayout(true);
+        if (mBrightnessMirrorController != null) {
+            mBrightnessMirrorController.addCallback(mView);
+        }
+    }
+
+    @Override
+    protected void onViewDetached() {
+        mTunerService.removeTunable(mView);
+        mView.removeOnConfigurationChangedListener(mOnConfigurationChangedListener);
+        if (mBrightnessMirrorController != null) {
+            mBrightnessMirrorController.removeCallback(mView);
+        }
+        super.onViewDetached();
+    }
+
+    /** TODO(b/168904199): Remove this method once view is controllerized. */
+    QSPanel getView() {
+        return mView;
+    }
+
+    /**
+     * Set the header container of quick settings.
+     */
+    public void setHeaderContainer(@NonNull ViewGroup headerContainer) {
+        mView.setHeaderContainer(headerContainer);
+    }
+
+    public QSPanel.QSTileLayout getTileLayout() {
+        return mView.getTileLayout();
+    }
+
+    /** */
+    public void setCustomizer(QSCustomizer customizer) {
+        mView.setCustomizer(customizer);
+    }
+
+    /** */
+    public boolean isShowingCustomize() {
+        return mView.isShowingCustomize();
+    }
+
+    /** */
+    public void setVisibility(int visibility) {
+        mView.setVisibility(visibility);
+    }
+
+    /** */
+    public void setListening(boolean listening, boolean expanded) {
+        setListening(listening && expanded);
+        if (mView.isListening()) {
+            refreshAllTiles();
+        }
+
+        mQsSecurityFooter.setListening(listening);
+
+        // Set the listening as soon as the QS fragment starts listening regardless of the
+        //expansion, so it will update the current brightness before the slider is visible.
+        if (listening) {
+            mBrightnessController.registerCallbacks();
+        } else {
+            mBrightnessController.unregisterCallbacks();
+        }
+    }
+
+    /** */
+    public QSTileRevealController getQsTileRevealController() {
+        return mView.getQsTileRevealController();
+    }
+
+    /** */
+    public MediaHost getMediaHost() {
+        return mView.getMediaHost();
+    }
+
+    /** */
+    public void setBrightnessMirror(BrightnessMirrorController brightnessMirrorController) {
+        mBrightnessMirrorController = brightnessMirrorController;
+        mView.setBrightnessMirror(brightnessMirrorController);
+    }
+
+    /** Get the QSTileHost this panel uses. */
+    public QSTileHost getHost() {
+        return mHost;
+    }
+
+
+    /** Open the details for a specific tile.. */
+    public void openDetails(String subPanel) {
+        QSTile tile = getTile(subPanel);
+        if (tile != null) {
+            mView.openDetails(tile);
+        }
+    }
+
+    /** Show the device monitoring dialog. */
+    public void showDeviceMonitoringDialog() {
+        mQsSecurityFooter.showDeviceMonitoringDialog();
+    }
+
+    /** Update appearance of QSPanel. */
+    public void updateResources() {
+        mView.updateResources();
+    }
+
+    /** Update state of all tiles. */
+    public void refreshAllTiles() {
+        mBrightnessController.checkRestrictionAndSetEnabled();
+        super.refreshAllTiles();
+        mQsSecurityFooter.refreshState();
+    }
+
+    /** Start customizing the Quick Settings. */
+    public void showEdit(View view) {
+        mView.showEdit(view);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
new file mode 100644
index 0000000..fe92827
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.qs;
+
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+import android.content.ComponentName;
+import android.content.res.Configuration;
+import android.metrics.LogMaker;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.Dumpable;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.media.MediaHost;
+import com.android.systemui.plugins.qs.QSTile;
+import com.android.systemui.plugins.qs.QSTileView;
+import com.android.systemui.qs.external.CustomTile;
+import com.android.systemui.util.ViewController;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.stream.Collectors;
+
+/**
+ * Controller for QSPanel views.
+ *
+ * @param <T> Type of QSPanel.
+ */
+public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewController<T>
+        implements Dumpable{
+    protected final QSTileHost mHost;
+    private final MediaHost mMediaHost;
+    private final MetricsLogger mMetricsLogger;
+    private final UiEventLogger mUiEventLogger;
+    private final DumpManager mDumpManager;
+    protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
+
+    private int mLastOrientation;
+
+    private final QSHost.Callback mQSHostCallback = this::setTiles;
+
+    private final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener =
+            new QSPanel.OnConfigurationChangedListener() {
+                @Override
+                public void onConfigurationChange(Configuration newConfig) {
+                    if (newConfig.orientation != mLastOrientation) {
+                        mLastOrientation = newConfig.orientation;
+                        switchTileLayout(false);
+                    }
+                }
+            };
+    private String mCachedSpecs = "";
+
+    protected QSPanelControllerBase(T view, QSTileHost host,
+            MetricsLogger metricsLogger, UiEventLogger uiEventLogger, DumpManager dumpManager) {
+        super(view);
+        mHost = host;
+        mMediaHost = mView.getMediaHost();
+        mMetricsLogger = metricsLogger;
+        mUiEventLogger = uiEventLogger;
+        mDumpManager = dumpManager;
+    }
+
+    @Override
+    protected void onViewAttached() {
+        mView.addOnConfigurationChangedListener(mOnConfigurationChangedListener);
+        mHost.addCallback(mQSHostCallback);
+        mMediaHost.addVisibilityChangeListener(aBoolean -> {
+            switchTileLayout(false);
+            return null;
+        });
+        setTiles();
+        switchTileLayout(true);
+        mDumpManager.registerDumpable(mView.getDumpableTag(), this);
+    }
+
+    @Override
+    protected void onViewDetached() {
+        mView.removeOnConfigurationChangedListener(mOnConfigurationChangedListener);
+        mHost.removeCallback(mQSHostCallback);
+
+        for (TileRecord record : mRecords) {
+            record.tile.removeCallbacks();
+        }
+        mRecords.clear();
+        mDumpManager.unregisterDumpable(mView.getDumpableTag());
+    }
+
+    /** */
+    public void setTiles() {
+        setTiles(mHost.getTiles(), false);
+    }
+
+    /** */
+    public void setTiles(Collection<QSTile> tiles, boolean collapsedView) {
+        if (!collapsedView) {
+            mView.updateRevealedTiles(tiles);
+        }
+        for (QSPanelControllerBase.TileRecord record : mRecords) {
+            mView.removeTile(record);
+            record.tile.removeCallback(record.callback);
+        }
+        mRecords.clear();
+        mCachedSpecs = "";
+        for (QSTile tile : tiles) {
+            addTile(tile, collapsedView);
+        }
+    }
+
+    /** */
+    public void refreshAllTiles() {
+        for (QSPanelControllerBase.TileRecord r : mRecords) {
+            r.tile.refreshState();
+        }
+    }
+
+    private void addTile(final QSTile tile, boolean collapsedView) {
+        final TileRecord r = new TileRecord();
+        r.tile = tile;
+        r.tileView = mHost.createTileView(tile, collapsedView);
+        mView.addTile(r);
+        mRecords.add(r);
+        mCachedSpecs = getTilesSpecs();
+
+    }
+
+    /** */
+    public void clickTile(ComponentName tile) {
+        final String spec = CustomTile.toSpec(tile);
+        for (TileRecord record : mRecords) {
+            if (record.tile.getTileSpec().equals(spec)) {
+                record.tile.click();
+                break;
+            }
+        }
+    }
+    protected QSTile getTile(String subPanel) {
+        for (int i = 0; i < mRecords.size(); i++) {
+            if (subPanel.equals(mRecords.get(i).tile.getTileSpec())) {
+                return mRecords.get(i).tile;
+            }
+        }
+        return mHost.createTile(subPanel);
+    }
+
+
+    QSTileView getTileView(QSTile tile) {
+        for (QSPanelControllerBase.TileRecord r : mRecords) {
+            if (r.tile == tile) {
+                return r.tileView;
+            }
+        }
+        return null;
+    }
+
+    private String getTilesSpecs() {
+        return mRecords.stream()
+                .map(tileRecord ->  tileRecord.tile.getTileSpec())
+                .collect(Collectors.joining(","));
+    }
+
+
+    /** */
+    public void setExpanded(boolean expanded) {
+        mView.setExpanded(expanded);
+        mMetricsLogger.visibility(MetricsEvent.QS_PANEL, expanded);
+        if (!expanded) {
+            mUiEventLogger.log(mView.closePanelEvent());
+            closeDetail();
+        } else {
+            mUiEventLogger.log(mView.openPanelEvent());
+            logTiles();
+        }
+    }
+
+    /** */
+    public void closeDetail() {
+        mView.closeDetail();
+    }
+
+    /** */
+    public void openDetails(String subPanel) {
+        QSTile tile = getTile(subPanel);
+        // If there's no tile with that name (as defined in QSFactoryImpl or other QSFactory),
+        // QSFactory will not be able to create a tile and getTile will return null
+        if (tile != null) {
+            mView.showDetailAdapter(
+                    true, tile.getDetailAdapter(), new int[]{mView.getWidth() / 2, 0});
+        }
+    }
+
+
+    void setListening(boolean listening) {
+        mView.setListening(listening, mCachedSpecs);
+    }
+
+    boolean switchTileLayout(boolean force) {
+        if (mView.switchTileLayout(force, mRecords)) {
+            setTiles();
+            return true;
+        }
+        return false;
+    }
+
+    private void logTiles() {
+        for (int i = 0; i < mRecords.size(); i++) {
+            QSTile tile = mRecords.get(i).tile;
+            mMetricsLogger.write(tile.populate(new LogMaker(tile.getMetricsCategory())
+                    .setType(MetricsEvent.TYPE_OPEN)));
+        }
+    }
+
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println(getClass().getSimpleName() + ":");
+        pw.println("  Tile records:");
+        for (QSPanelControllerBase.TileRecord record : mRecords) {
+            if (record.tile instanceof Dumpable) {
+                pw.print("    "); ((Dumpable) record.tile).dump(fd, pw, args);
+                pw.print("    "); pw.println(record.tileView.toString());
+            }
+        }
+    }
+
+    /** */
+    public static final class TileRecord extends QSPanel.Record {
+        public QSTile tile;
+        public com.android.systemui.plugins.qs.QSTileView tileView;
+        public boolean scanState;
+        public QSTile.Callback callback;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index 0891972..c90182b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -44,11 +44,15 @@
 import com.android.systemui.FontSizeUtils;
 import com.android.systemui.R;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.qs.dagger.QSScope;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 import com.android.systemui.statusbar.policy.SecurityController;
 
-public class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListener {
+import javax.inject.Inject;
+
+@QSScope
+class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListener {
     protected static final String TAG = "QSSecurityFooter";
     protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     private static final boolean DEBUG_FORCE_VISIBLE = false;
@@ -72,12 +76,13 @@
     private int mFooterTextId;
     private int mFooterIconId;
 
+    @Inject
     public QSSecurityFooter(QSPanel qsPanel, Context context, UserTracker userTracker) {
         mRootView = LayoutInflater.from(context)
                 .inflate(R.layout.quick_settings_footer, qsPanel, false);
         mRootView.setOnClickListener(this);
-        mFooterText = (TextView) mRootView.findViewById(R.id.footer_text);
-        mFooterIcon = (ImageView) mRootView.findViewById(R.id.footer_icon);
+        mFooterText = mRootView.findViewById(R.id.footer_text);
+        mFooterIcon = mRootView.findViewById(R.id.footer_icon);
         mFooterIconId = R.drawable.ic_info_outline;
         mContext = context;
         mMainHandler = new Handler(Looper.myLooper());
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileRevealController.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileRevealController.java
index 2f012e6..3d4a417 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileRevealController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileRevealController.java
@@ -8,11 +8,16 @@
 
 import com.android.systemui.Prefs;
 import com.android.systemui.plugins.qs.QSTile;
+import com.android.systemui.qs.dagger.QSScope;
 
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Set;
 
+import javax.inject.Inject;
+
+/** */
+@QSScope
 public class QSTileRevealController {
     private static final long QS_REVEAL_TILES_DELAY = 500L;
 
@@ -34,6 +39,7 @@
         }
     };
 
+    @Inject
     QSTileRevealController(Context context, QSPanel qsPanel, PagedTileLayout pagedTileLayout) {
         mContext = context;
         mQSPanel = qsPanel;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index ea036f6..84a5b6f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.qs;
 
+import static com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL;
 import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
 
 import android.content.Context;
@@ -27,23 +28,13 @@
 import android.widget.LinearLayout;
 
 import com.android.internal.logging.UiEventLogger;
-import com.android.systemui.Dependency;
 import com.android.systemui.R;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.dump.DumpManager;
 import com.android.systemui.media.MediaHierarchyManager;
 import com.android.systemui.media.MediaHost;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.qs.QSTile.SignalState;
 import com.android.systemui.plugins.qs.QSTile.State;
-import com.android.systemui.qs.customize.QSCustomizer;
 import com.android.systemui.qs.logging.QSLogger;
-import com.android.systemui.settings.UserTracker;
-import com.android.systemui.tuner.TunerService;
-import com.android.systemui.tuner.TunerService.Tunable;
-
-import java.util.ArrayList;
-import java.util.Collection;
 
 import javax.inject.Inject;
 import javax.inject.Named;
@@ -67,15 +58,10 @@
     public QuickQSPanel(
             @Named(VIEW_CONTEXT) Context context,
             AttributeSet attrs,
-            DumpManager dumpManager,
-            BroadcastDispatcher broadcastDispatcher,
             QSLogger qsLogger,
-            MediaHost mediaHost,
-            UiEventLogger uiEventLogger,
-            UserTracker userTracker
-    ) {
-        super(context, attrs, dumpManager, broadcastDispatcher, qsLogger, mediaHost, uiEventLogger,
-                userTracker);
+            @Named(QUICK_QS_PANEL) MediaHost mediaHost,
+            UiEventLogger uiEventLogger) {
+        super(context, attrs, qsLogger, mediaHost, uiEventLogger);
         sDefaultMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns);
         applyBottomMargin((View) mRegularTileLayout);
     }
@@ -88,17 +74,12 @@
     }
 
     @Override
-    protected void addSecurityFooter() {
-        // No footer needed
-    }
-
-    @Override
     protected void addViewsAboveTiles() {
         // Nothing to add above the tiles
     }
 
     @Override
-    protected TileLayout createRegularTileLayout() {
+    public TileLayout createRegularTileLayout() {
         return new QuickQSPanel.HeaderTileLayout(mContext, mUiEventLogger);
     }
 
@@ -108,6 +89,7 @@
     }
 
     @Override
+
     protected void initMediaHostState() {
         mMediaHost.setExpansion(0.0f);
         mMediaHost.setShowsOnlyActiveMedia(true);
@@ -131,18 +113,6 @@
     }
 
     @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        Dependency.get(TunerService.class).addTunable(mNumTiles, NUM_QUICK_TILES);
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        Dependency.get(TunerService.class).removeTunable(mNumTiles);
-    }
-
-    @Override
     protected String getDumpableTag() {
         return TAG;
     }
@@ -157,7 +127,7 @@
     }
 
     @Override
-    protected void drawTile(TileRecord r, State state) {
+    protected void drawTile(QSPanelControllerBase.TileRecord r, State state) {
         if (state instanceof SignalState) {
             SignalState copy = new SignalState();
             state.copyTo(copy);
@@ -169,17 +139,8 @@
         super.drawTile(r, state);
     }
 
-    @Override
-    public void setHost(QSTileHost host, QSCustomizer customizer) {
-        super.setHost(host, customizer);
-        setTiles(mHost.getTiles());
-    }
-
     public void setMaxTiles(int maxTiles) {
         mMaxTiles = maxTiles;
-        if (mHost != null) {
-            setTiles(mHost.getTiles());
-        }
     }
 
     @Override
@@ -190,25 +151,6 @@
         }
     }
 
-    @Override
-    public void setTiles(Collection<QSTile> tiles) {
-        ArrayList<QSTile> quickTiles = new ArrayList<>();
-        for (QSTile tile : tiles) {
-            quickTiles.add(tile);
-            if (quickTiles.size() == mMaxTiles) {
-                break;
-            }
-        }
-        super.setTiles(quickTiles, true);
-    }
-
-    private final Tunable mNumTiles = new Tunable() {
-        @Override
-        public void onTuningChanged(String key, String newValue) {
-            setMaxTiles(parseNumTiles(newValue));
-        }
-    };
-
     public int getNumQuickTiles() {
         return mMaxTiles;
     }
@@ -306,7 +248,7 @@
         }
 
         @Override
-        protected void addTileView(TileRecord tile) {
+        protected void addTileView(QSPanelControllerBase.TileRecord tile) {
             addView(tile.tileView, getChildCount(), generateTileLayoutParams());
         }
 
@@ -369,7 +311,7 @@
         private void setAccessibilityOrder() {
             if (mRecords != null && mRecords.size() > 0) {
                 View previousView = this;
-                for (TileRecord record : mRecords) {
+                for (QSPanelControllerBase.TileRecord record : mRecords) {
                     if (record.tileView.getVisibility() == GONE) continue;
                     previousView = record.tileView.updateAccessibilityOrder(previousView);
                 }
@@ -381,7 +323,7 @@
         @Override
         protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
             // Measure each QS tile.
-            for (TileRecord record : mRecords) {
+            for (QSPanelControllerBase.TileRecord record : mRecords) {
                 if (record.tileView.getVisibility() == GONE) continue;
                 record.tileView.measure(exactly(mCellWidth), exactly(mCellHeight));
             }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
new file mode 100644
index 0000000..97b6e99
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.qs;
+
+import static com.android.systemui.qs.QuickQSPanel.NUM_QUICK_TILES;
+import static com.android.systemui.qs.QuickQSPanel.parseNumTiles;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.plugins.qs.QSTile;
+import com.android.systemui.qs.dagger.QSScope;
+import com.android.systemui.tuner.TunerService;
+import com.android.systemui.tuner.TunerService.Tunable;
+
+import java.util.ArrayList;
+
+import javax.inject.Inject;
+
+/** Controller for {@link QuickQSPanel}. */
+@QSScope
+public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel> {
+    private final Tunable mNumTiles =
+            (key, newValue) -> setMaxTiles(parseNumTiles(newValue));
+
+    private final TunerService mTunerService;
+
+    @Inject
+    QuickQSPanelController(QuickQSPanel view, TunerService tunerService, QSTileHost qsTileHost,
+            MetricsLogger metricsLogger, UiEventLogger uiEventLogger,
+            DumpManager dumpManager) {
+        super(view, qsTileHost, metricsLogger, uiEventLogger, dumpManager);
+        mTunerService = tunerService;
+    }
+
+    @Override
+    protected void onViewAttached() {
+        super.onViewAttached();
+        mTunerService.addTunable(mNumTiles, NUM_QUICK_TILES);
+
+    }
+
+    @Override
+    protected void onViewDetached() {
+        super.onViewDetached();
+        mTunerService.removeTunable(mNumTiles);
+    }
+
+    public boolean isListening() {
+        return mView.isListening();
+    }
+
+    private void setMaxTiles(int parseNumTiles) {
+        mView.setMaxTiles(parseNumTiles);
+        setTiles();
+    }
+
+    @Override
+    public void setTiles() {
+        ArrayList<QSTile> quickTiles = new ArrayList<>();
+        for (QSTile tile : mHost.getTiles()) {
+            quickTiles.add(tile);
+            if (quickTiles.size() == mView.getNumQuickTiles()) {
+                break;
+            }
+        }
+        super.setTiles(quickTiles, true);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index a9fbc74..5757602 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -353,11 +353,7 @@
             mPrivacyChip.setExpanded(expansionFraction > 0.5);
             mPrivacyChipAlphaAnimator.setPosition(keyguardExpansionFraction);
         }
-        if (expansionFraction < 1 && expansionFraction > 0.99) {
-            if (mHeaderQsPanel.switchTileLayout()) {
-                updateResources();
-            }
-        }
+
         mKeyguardExpansionFraction = keyguardExpansionFraction;
     }
 
@@ -446,7 +442,6 @@
     public void setQSPanel(final QSPanel qsPanel) {
         //host.setHeaderView(mExpandIndicator);
         mHeaderQsPanel.setQSPanelAndHeader(qsPanel, this);
-        mHeaderQsPanel.setHost(qsPanel.getHost(), null /* No customization in header */);
 
         Rect tintArea = new Rect(0, 0, 0, 0);
         int colorForeground = Utils.getColorAttrDefaultColor(getContext(),
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
index 676a300..febb71c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
@@ -44,6 +44,7 @@
 import com.android.systemui.privacy.PrivacyItem;
 import com.android.systemui.privacy.PrivacyItemController;
 import com.android.systemui.qs.carrier.QSCarrierGroupController;
+import com.android.systemui.qs.dagger.QSScope;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -64,6 +65,7 @@
 /**
  * Controller for {@link QuickStatusBarHeader}.
  */
+@QSScope
 class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader> {
     private static final String TAG = "QuickStatusBarHeader";
 
@@ -74,7 +76,7 @@
     private final ActivityStarter mActivityStarter;
     private final UiEventLogger mUiEventLogger;
     private final QSCarrierGroupController mQSCarrierGroupController;
-    private final QuickQSPanel mHeaderQsPanel;
+    private final QuickQSPanelController mHeaderQsPanelController;
     private final LifecycleRegistry mLifecycle;
     private final OngoingPrivacyChip mPrivacyChip;
     private final Clock mClockView;
@@ -93,6 +95,7 @@
     private AlarmClockInfo mNextAlarm;
     private boolean mAllIndicatorsEnabled;
     private boolean mMicCameraIndicatorsEnabled;
+    private boolean mLocationIndicatorsEnabled;
     private boolean mPrivacyChipLogged;
     private int mRingerMode = AudioManager.RINGER_MODE_NORMAL;
 
@@ -156,6 +159,14 @@
             }
         }
 
+        @Override
+        public void onFlagLocationChanged(boolean flag) {
+            if (mLocationIndicatorsEnabled != flag) {
+                mLocationIndicatorsEnabled = flag;
+                update();
+            }
+        }
+
         private void update() {
             StatusIconContainer iconContainer = mView.requireViewById(R.id.statusIcons);
             iconContainer.setIgnoredSlots(getIgnoredIconSlots());
@@ -194,13 +205,14 @@
         }
     };
 
-    private QuickStatusBarHeaderController(QuickStatusBarHeader view,
+    @Inject
+    QuickStatusBarHeaderController(QuickStatusBarHeader view,
             ZenModeController zenModeController, NextAlarmController nextAlarmController,
             PrivacyItemController privacyItemController, RingerModeTracker ringerModeTracker,
             ActivityStarter activityStarter, UiEventLogger uiEventLogger,
             QSTileHost qsTileHost, StatusBarIconController statusBarIconController,
             CommandQueue commandQueue, DemoModeController demoModeController,
-            UserTracker userTracker,
+            UserTracker userTracker, QuickQSPanelController quickQSPanelController,
             QSCarrierGroupController.Builder qsCarrierGroupControllerBuilder) {
         super(view);
         mZenModeController = zenModeController;
@@ -215,6 +227,7 @@
         mDemoModeController = demoModeController;
         mUserTracker = userTracker;
         mLifecycle = new LifecycleRegistry(mLifecycleOwner);
+        mHeaderQsPanelController = quickQSPanelController;
 
         mQSCarrierGroupController = qsCarrierGroupControllerBuilder
                 .setQSCarrierGroup(mView.findViewById(R.id.carrier_group))
@@ -222,7 +235,6 @@
 
 
         mPrivacyChip = mView.findViewById(R.id.privacy_chip);
-        mHeaderQsPanel = mView.findViewById(R.id.quick_qs_panel);
         mNextAlarmContainer = mView.findViewById(R.id.alarm_container);
         mClockView = mView.findViewById(R.id.clock);
         mRingerContainer = mView.findViewById(R.id.ringer_container);
@@ -252,6 +264,7 @@
 
         mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable();
         mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
+        mLocationIndicatorsEnabled = mPrivacyItemController.getLocationAvailable();
 
         setChipVisibility(mPrivacyChip.getVisibility() == View.VISIBLE);
 
@@ -280,8 +293,12 @@
         }
         mListening = listening;
 
-        mHeaderQsPanel.setListening(listening);
-        if (mHeaderQsPanel.switchTileLayout()) {
+        mHeaderQsPanelController.setListening(listening);
+        if (mHeaderQsPanelController.isListening()) {
+            mHeaderQsPanelController.refreshAllTiles();
+        }
+
+        if (mHeaderQsPanelController.switchTileLayout(false)) {
             mView.updateResources();
         }
 
@@ -292,6 +309,7 @@
             // Get the most up to date info
             mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable();
             mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
+            mLocationIndicatorsEnabled = mPrivacyItemController.getLocationAvailable();
             mPrivacyItemController.addCallback(mPICCallback);
         } else {
             mZenModeController.removeCallback(mZenModeControllerCallback);
@@ -323,7 +341,7 @@
                     com.android.internal.R.string.status_bar_camera));
             ignored.add(mView.getResources().getString(
                     com.android.internal.R.string.status_bar_microphone));
-            if (mAllIndicatorsEnabled) {
+            if (mAllIndicatorsEnabled || mLocationIndicatorsEnabled) {
                 ignored.add(mView.getResources().getString(
                         com.android.internal.R.string.status_bar_location));
             }
@@ -333,7 +351,7 @@
     }
 
     private boolean getChipEnabled() {
-        return mMicCameraIndicatorsEnabled || mAllIndicatorsEnabled;
+        return mMicCameraIndicatorsEnabled || mLocationIndicatorsEnabled || mAllIndicatorsEnabled;
     }
 
     private boolean isZenOverridingRinger() {
@@ -369,55 +387,4 @@
             mClockView.onDemoModeFinished();
         }
     }
-
-    static class Builder {
-        private final ZenModeController mZenModeController;
-        private final NextAlarmController mNextAlarmController;
-        private final PrivacyItemController mPrivacyItemController;
-        private final RingerModeTracker mRingerModeTracker;
-        private final ActivityStarter mActivityStarter;
-        private final UiEventLogger mUiEventLogger;
-        private final QSTileHost mQsTileHost;
-        private final StatusBarIconController mStatusBarIconController;
-        private final CommandQueue mCommandQueue;
-        private final DemoModeController mDemoModeController;
-        private final UserTracker mUserTracker;
-        private final QSCarrierGroupController.Builder mQSCarrierGroupControllerBuilder;
-        private QuickStatusBarHeader mView;
-
-        @Inject
-        Builder(ZenModeController zenModeController, NextAlarmController nextAlarmController,
-                PrivacyItemController privacyItemController, RingerModeTracker ringerModeTracker,
-                ActivityStarter activityStarter, UiEventLogger uiEventLogger, QSTileHost qsTileHost,
-                StatusBarIconController statusBarIconController, CommandQueue commandQueue,
-                DemoModeController demoModeController, UserTracker userTracker,
-                QSCarrierGroupController.Builder qsCarrierGroupControllerBuilder) {
-            mZenModeController = zenModeController;
-            mNextAlarmController = nextAlarmController;
-            mPrivacyItemController = privacyItemController;
-            mRingerModeTracker = ringerModeTracker;
-            mActivityStarter = activityStarter;
-            mUiEventLogger = uiEventLogger;
-            mQsTileHost = qsTileHost;
-            mStatusBarIconController = statusBarIconController;
-            mCommandQueue = commandQueue;
-            mDemoModeController = demoModeController;
-            mUserTracker = userTracker;
-            mQSCarrierGroupControllerBuilder = qsCarrierGroupControllerBuilder;
-        }
-
-        public Builder setQuickStatusBarHeader(QuickStatusBarHeader view) {
-            mView = view;
-            return this;
-        }
-
-
-        QuickStatusBarHeaderController build() {
-            return new QuickStatusBarHeaderController(mView, mZenModeController,
-                    mNextAlarmController, mPrivacyItemController, mRingerModeTracker,
-                    mActivityStarter, mUiEventLogger, mQsTileHost, mStatusBarIconController,
-                    mCommandQueue, mDemoModeController, mUserTracker,
-                    mQSCarrierGroupControllerBuilder);
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 694492a..4ab7afd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -11,7 +11,7 @@
 
 import com.android.systemui.R;
 import com.android.systemui.qs.QSPanel.QSTileLayout;
-import com.android.systemui.qs.QSPanel.TileRecord;
+import com.android.systemui.qs.QSPanelControllerBase.TileRecord;
 
 import java.util.ArrayList;
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index e5ed88c..8097958 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -74,9 +74,9 @@
     private final ScreenLifecycle mScreenLifecycle;
     private final TileQueryHelper mTileQueryHelper;
     private final View mTransparentView;
+    private final QSTileHost mHost;
 
     private boolean isShown;
-    private QSTileHost mHost;
     private RecyclerView mRecyclerView;
     private TileAdapter mTileAdapter;
     private Toolbar mToolbar;
@@ -95,6 +95,7 @@
             KeyguardStateController keyguardStateController,
             ScreenLifecycle screenLifecycle,
             TileQueryHelper tileQueryHelper,
+            QSTileHost qsTileHost,
             UiEventLogger uiEventLogger) {
         super(new ContextThemeWrapper(context, R.style.edit_theme), attrs);
 
@@ -132,12 +133,15 @@
         layout.setSpanSizeLookup(mTileAdapter.getSizeLookup());
         mRecyclerView.setLayoutManager(layout);
         mRecyclerView.addItemDecoration(mTileAdapter.getItemDecoration());
+        mRecyclerView.addItemDecoration(mTileAdapter.getMarginItemDecoration());
         DefaultItemAnimator animator = new DefaultItemAnimator();
         animator.setMoveDuration(TileAdapter.MOVE_DURATION);
         mRecyclerView.setItemAnimator(animator);
         mLightBarController = lightBarController;
         mKeyguardStateController = keyguardStateController;
         mScreenLifecycle = screenLifecycle;
+        mHost = qsTileHost;
+        mTileAdapter.setHost(mHost);
         updateNavBackDrop(getResources().getConfiguration());
     }
 
@@ -169,11 +173,6 @@
         mLightBarController.setQsCustomizing(mIsShowingNavBackdrop && isShown);
     }
 
-    public void setHost(QSTileHost host) {
-        mHost = host;
-        mTileAdapter.setHost(host);
-    }
-
     public void setContainer(NotificationsQuickSettingsContainer notificationsQsContainer) {
         mNotifQsContainer = notificationsQsContainer;
     }
@@ -221,6 +220,22 @@
         }
     }
 
+    /**
+     * Sets the padding for the RecyclerView. Also, updates the margin between the tiles in the
+     * {@link TileAdapter}.
+     */
+    public void setContentPaddings(int paddingStart, int paddingEnd) {
+        int halfMargin = mContext.getResources()
+                .getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal) / 2;
+        mTileAdapter.changeHalfMargin(halfMargin);
+        mRecyclerView.setPaddingRelative(
+                paddingStart,
+                mRecyclerView.getPaddingTop(),
+                paddingEnd,
+                mRecyclerView.getPaddingBottom()
+        );
+    }
+
     private void queryTiles() {
         mTileQueryHelper.queryTiles(mHost);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index e049025..b471dfa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -18,6 +18,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Canvas;
+import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.view.LayoutInflater;
@@ -75,6 +76,7 @@
     private final List<TileInfo> mTiles = new ArrayList<>();
     private final ItemTouchHelper mItemTouchHelper;
     private final ItemDecoration mDecoration;
+    private final MarginTileDecoration mMarginDecoration;
     private final int mMinNumTiles;
     private int mEditIndex;
     private int mTileDividerIndex;
@@ -97,6 +99,7 @@
         mUiEventLogger = uiEventLogger;
         mItemTouchHelper = new ItemTouchHelper(mCallbacks);
         mDecoration = new TileItemDecoration(context);
+        mMarginDecoration = new MarginTileDecoration();
         mMinNumTiles = context.getResources().getInteger(R.integer.quick_settings_min_num_tiles);
         mAccessibilityDelegate = new TileAdapterDelegate();
     }
@@ -123,6 +126,14 @@
         return mDecoration;
     }
 
+    public ItemDecoration getMarginItemDecoration() {
+        return mMarginDecoration;
+    }
+
+    public void changeHalfMargin(int halfMargin) {
+        mMarginDecoration.setHalfMargin(halfMargin);
+    }
+
     public void saveSpecs(QSTileHost host) {
         List<String> newSpecs = new ArrayList<>();
         clearAccessibilityState();
@@ -596,7 +607,6 @@
             mDrawable = context.getDrawable(R.drawable.qs_customize_tile_decoration);
         }
 
-
         @Override
         public void onDraw(Canvas c, RecyclerView parent, State state) {
             super.onDraw(c, parent, state);
@@ -630,6 +640,25 @@
         }
     }
 
+    private static class MarginTileDecoration extends ItemDecoration {
+        private int mHalfMargin;
+
+        public void setHalfMargin(int halfMargin) {
+            mHalfMargin = halfMargin;
+        }
+
+        @Override
+        public void getItemOffsets(@NonNull Rect outRect, @NonNull View view,
+                @NonNull RecyclerView parent, @NonNull State state) {
+            if (view instanceof TextView) {
+                super.getItemOffsets(outRect, view, parent, state);
+            } else {
+                outRect.left = mHalfMargin;
+                outRect.right = mHalfMargin;
+            }
+        }
+    }
+
     private final ItemTouchHelper.Callback mCallbacks = new ItemTouchHelper.Callback() {
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java
new file mode 100644
index 0000000..51b2c8d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.qs.dagger;
+
+import com.android.systemui.qs.QSAnimator;
+import com.android.systemui.qs.QSContainerImplController;
+import com.android.systemui.qs.QSFooter;
+import com.android.systemui.qs.QSFragment;
+import com.android.systemui.qs.QSPanelController;
+import com.android.systemui.qs.QuickQSPanelController;
+
+import dagger.BindsInstance;
+import dagger.Subcomponent;
+
+/**
+ * Dagger Subcomponent for {@link QSFragment}.
+ */
+@Subcomponent(modules = {QSFragmentModule.class})
+@QSScope
+public interface QSFragmentComponent {
+    /** Factory for building a {@link QSFragmentComponent}. */
+    @Subcomponent.Factory
+    interface Factory {
+        QSFragmentComponent create(@BindsInstance QSFragment qsFragment);
+    }
+
+    /** Construct a {@link QSPanelController}. */
+    QSPanelController getQSPanelController();
+
+    /** Construct a {@link QuickQSPanelController}. */
+    QuickQSPanelController getQuickQSPanelController();
+
+    /** Construct a {@link QSAnimator}. */
+    QSAnimator getQSAnimator();
+
+    /** Construct a {@link QSContainerImplController}. */
+    QSContainerImplController getQSContainerImplController();
+
+    /** Construct a {@link QSFooter} */
+    QSFooter getQSFooter();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
new file mode 100644
index 0000000..4bf4eff
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.qs.dagger;
+
+import android.view.View;
+
+import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.RootView;
+import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.qs.QSContainerImpl;
+import com.android.systemui.qs.QSFooter;
+import com.android.systemui.qs.QSFooterView;
+import com.android.systemui.qs.QSFooterViewController;
+import com.android.systemui.qs.QSFragment;
+import com.android.systemui.qs.QSPanel;
+import com.android.systemui.qs.QuickQSPanel;
+import com.android.systemui.qs.QuickStatusBarHeader;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Dagger Module for {@link QSFragmentComponent}.
+ */
+@Module
+public interface QSFragmentModule {
+    /** */
+    @Provides
+    @RootView
+    static View provideRootView(QSFragment qsFragment) {
+        return qsFragment.getView();
+    }
+
+    /** */
+    @Provides
+    static QSPanel provideQSPanel(@RootView View view) {
+        return view.findViewById(R.id.quick_settings_panel);
+    }
+
+    /** */
+    @Provides
+    static QSContainerImpl providesQSContainerImpl(@RootView View view) {
+        return view.findViewById(R.id.quick_settings_container);
+    }
+
+    /** */
+    @Binds
+    QS bindQS(QSFragment qsFragment);
+
+    /** */
+    @Provides
+    static QuickStatusBarHeader providesQuickStatusBarHeader(@RootView View view) {
+        return view.findViewById(R.id.header);
+    }
+
+    /** */
+    @Provides
+    static QuickQSPanel providesQuickQSPanel(QuickStatusBarHeader quickStatusBarHeader) {
+        return quickStatusBarHeader.findViewById(R.id.quick_qs_panel);
+    }
+
+    /** */
+    @Provides
+    static QSFooterView providesQSFooterView(@RootView View view) {
+        return view.findViewById(R.id.qs_footer);
+    }
+
+    /** */
+    @Provides
+    @QSScope
+    static QSFooter providesQSFooter(QSFooterViewController qsFooterViewController) {
+        qsFooterViewController.init();
+        return qsFooterViewController;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
index 8ff96c8..7c799ae 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
@@ -21,6 +21,7 @@
 import android.os.Handler;
 
 import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.media.dagger.MediaModule;
 import com.android.systemui.qs.AutoAddTracker;
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.QSTileHost;
@@ -37,8 +38,8 @@
 /**
  * Module for QS dependencies
  */
-// TODO: Add other QS classes
-@Module
+@Module(subcomponents = {QSFragmentComponent.class},
+        includes = {MediaModule.class})
 public interface QSModule {
 
     @Provides
@@ -59,7 +60,6 @@
         return manager;
     }
 
-
     /** */
     @Binds
     QSHost provideQsHost(QSTileHost controllerImpl);
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/RootView.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSScope.java
similarity index 74%
copy from packages/SystemUI/src/com/android/keyguard/dagger/RootView.java
copy to packages/SystemUI/src/com/android/systemui/qs/dagger/QSScope.java
index 5ebff09..f615eab 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/RootView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSScope.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open 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,17 +14,19 @@
  * limitations under the License.
  */
 
-package com.android.keyguard.dagger;
+package com.android.systemui.qs.dagger;
 
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
 import java.lang.annotation.Documented;
 import java.lang.annotation.Retention;
 
-import javax.inject.Qualifier;
+import javax.inject.Scope;
 
-@Qualifier
+/**
+ * Scope annotation for singleton items within the {@link QSFragmentComponent}.
+ */
 @Documented
 @Retention(RUNTIME)
-public @interface RootView {
-}
+@Scope
+public @interface QSScope {}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
index 201ed9c..8ddd4c9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
@@ -155,7 +155,7 @@
                     }
                     view.setActivated(true);
                 }
-                switchTo(tag);
+                onUserListItemClicked(tag);
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 0ae1170..ddf30ad 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -35,12 +35,14 @@
 
 import android.annotation.FloatRange;
 import android.app.ActivityTaskManager;
+import android.app.PictureInPictureParams;
 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.pm.ActivityInfo;
 import android.graphics.Bitmap;
 import android.graphics.Insets;
 import android.graphics.Rect;
@@ -93,7 +95,6 @@
 import com.android.wm.shell.onehanded.OneHandedEvents;
 import com.android.wm.shell.pip.Pip;
 import com.android.wm.shell.pip.PipAnimationController;
-import com.android.wm.shell.pip.phone.PipUtils;
 import com.android.wm.shell.splitscreen.SplitScreen;
 
 import java.io.FileDescriptor;
@@ -149,7 +150,6 @@
     private int mConnectionBackoffAttempts;
     private boolean mBound;
     private boolean mIsEnabled;
-    private boolean mHasPipFeature;
     private int mCurrentBoundedUserId = -1;
     private float mNavBarButtonAlpha;
     private boolean mInputFocusTransferStarted;
@@ -166,7 +166,7 @@
             if (!verifyCaller("startScreenPinning")) {
                 return;
             }
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 mHandler.post(() -> {
                     mStatusBarOptionalLazy.ifPresent(
@@ -183,7 +183,7 @@
             if (!verifyCaller("stopScreenPinning")) {
                 return;
             }
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 mHandler.post(() -> {
                     try {
@@ -203,7 +203,7 @@
             if (!verifyCaller("onStatusBarMotionEvent")) {
                 return;
             }
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 // TODO move this logic to message queue
                 mStatusBarOptionalLazy.ifPresent(statusBarLazy -> {
@@ -238,7 +238,7 @@
             if (!verifyCaller("onOverviewShown")) {
                 return;
             }
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 mHandler.post(() -> {
                     for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
@@ -255,7 +255,7 @@
             if (!verifyCaller("getNonMinimizedSplitScreenSecondaryBounds")) {
                 return null;
             }
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 return mSplitScreenOptional.map(splitScreen ->
                         splitScreen.getDividerView().getNonMinimizedSplitScreenSecondaryBounds())
@@ -270,7 +270,7 @@
             if (!verifyCaller("setNavBarButtonAlpha")) {
                 return;
             }
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 mNavBarButtonAlpha = alpha;
                 mHandler.post(() -> notifyNavBarButtonAlphaChanged(alpha, animate));
@@ -289,7 +289,7 @@
             if (!verifyCaller("onAssistantProgress")) {
                 return;
             }
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 mHandler.post(() -> notifyAssistantProgress(progress));
             } finally {
@@ -302,7 +302,7 @@
             if (!verifyCaller("onAssistantGestureCompletion")) {
                 return;
             }
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 mHandler.post(() -> notifyAssistantGestureCompletion(velocity));
             } finally {
@@ -315,7 +315,7 @@
             if (!verifyCaller("startAssistant")) {
                 return;
             }
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 mHandler.post(() -> notifyStartAssistant(bundle));
             } finally {
@@ -328,7 +328,7 @@
             if (!verifyCaller("monitorGestureInput")) {
                 return null;
             }
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 final InputMonitor monitor =
                         InputManager.getInstance().monitorGestureInput(name, displayId);
@@ -346,7 +346,7 @@
             if (!verifyCaller("notifyAccessibilityButtonClicked")) {
                 return;
             }
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 AccessibilityManager.getInstance(mContext)
                         .notifyAccessibilityButtonClicked(displayId);
@@ -360,7 +360,7 @@
             if (!verifyCaller("notifyAccessibilityButtonLongClicked")) {
                 return;
             }
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 final Intent intent =
                         new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON);
@@ -375,12 +375,10 @@
 
         @Override
         public void setShelfHeight(boolean visible, int shelfHeight) {
-            if (!verifyCaller("setShelfHeight") || !mHasPipFeature) {
-                Log.w(TAG_OPS,
-                        "ByPass setShelfHeight, FEATURE_PICTURE_IN_PICTURE:" + mHasPipFeature);
+            if (!verifyCaller("setShelfHeight")) {
                 return;
             }
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 mPipOptional.ifPresent(
                         pip -> pip.setShelfHeight(visible, shelfHeight));
@@ -403,12 +401,10 @@
 
         @Override
         public void notifySwipeToHomeFinished() {
-            if (!verifyCaller("notifySwipeToHomeFinished") || !mHasPipFeature) {
-                Log.w(TAG_OPS, "ByPass notifySwipeToHomeFinished, FEATURE_PICTURE_IN_PICTURE:"
-                        + mHasPipFeature);
+            if (!verifyCaller("notifySwipeToHomeFinished")) {
                 return;
             }
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 mPipOptional.ifPresent(
                         pip -> pip.setPinnedStackAnimationType(
@@ -420,13 +416,11 @@
 
         @Override
         public void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) {
-            if (!verifyCaller("setPinnedStackAnimationListener") || !mHasPipFeature) {
-                Log.w(TAG_OPS, "ByPass setPinnedStackAnimationListener, FEATURE_PICTURE_IN_PICTURE:"
-                        + mHasPipFeature);
+            if (!verifyCaller("setPinnedStackAnimationListener")) {
                 return;
             }
             mIPinnedStackAnimationListener = listener;
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 mPipOptional.ifPresent(
                         pip -> pip.setPinnedStackAnimationListener(mPinnedStackAnimationCallback));
@@ -440,7 +434,7 @@
             if (!verifyCaller("onQuickSwitchToNewTask")) {
                 return;
             }
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 mHandler.post(() -> notifyQuickSwitchToNewTask(rotation));
             } finally {
@@ -453,7 +447,7 @@
             if (!verifyCaller("startOneHandedMode")) {
                 return;
             }
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 mOneHandedOptional.ifPresent(oneHanded -> oneHanded.startOneHanded());
             } finally {
@@ -466,7 +460,7 @@
             if (!verifyCaller("stopOneHandedMode")) {
                 return;
             }
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 mOneHandedOptional.ifPresent(oneHanded -> oneHanded.stopOneHanded(
                                 OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_GESTURE_OUT));
@@ -495,7 +489,7 @@
             if (!verifyCaller("expandNotificationPanel")) {
                 return;
             }
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN);
             } finally {
@@ -503,6 +497,38 @@
             }
         }
 
+        @Override
+        public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
+                PictureInPictureParams pictureInPictureParams,
+                int launcherRotation, int shelfHeight) {
+            if (!verifyCaller("startSwipePipToHome")) {
+                return null;
+            }
+            final long binderToken = Binder.clearCallingIdentity();
+            try {
+                return mPipOptional.map(pip ->
+                        pip.startSwipePipToHome(componentName, activityInfo,
+                                pictureInPictureParams, launcherRotation, shelfHeight))
+                        .orElse(null);
+            } finally {
+                Binder.restoreCallingIdentity(binderToken);
+            }
+        }
+
+        @Override
+        public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) {
+            if (!verifyCaller("stopSwipePipToHome")) {
+                return;
+            }
+            final long binderToken = Binder.clearCallingIdentity();
+            try {
+                mPipOptional.ifPresent(pip -> pip.stopSwipePipToHome(
+                        componentName, destinationBounds));
+            } finally {
+                Binder.restoreCallingIdentity(binderToken);
+            }
+        }
+
         private boolean verifyCaller(String reason) {
             final int callerId = Binder.getCallingUserHandle().getIdentifier();
             if (callerId != mCurrentBoundedUserId) {
@@ -616,7 +642,6 @@
         super(broadcastDispatcher);
         mContext = context;
         mPipOptional = pipOptional;
-        mHasPipFeature = PipUtils.hasSystemFeature(mContext);
         mStatusBarOptionalLazy = statusBarOptionalLazy;
         mHandler = new Handler();
         mNavBarControllerLazy = navBarControllerLazy;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
index 9c5a3de..ccf2598 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
@@ -67,6 +67,7 @@
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
 
 import java.io.PrintWriter;
 import java.util.Collections;
@@ -365,7 +366,7 @@
             mOverviewProxyListenerRegistered = true;
         }
         if (!mTaskListenerRegistered) {
-            ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskListener);
+            TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskListener);
             mTaskListenerRegistered = true;
         }
     }
@@ -377,7 +378,7 @@
             mOverviewProxyListenerRegistered = false;
         }
         if (mTaskListenerRegistered) {
-            ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskListener);
+            TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskListener);
             mTaskListenerRegistered = false;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
index 1a9abb9..e6f43c1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
@@ -132,7 +132,7 @@
         mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
         mMediaRecorder.setVideoEncodingProfileLevel(
                 MediaCodecInfo.CodecProfileLevel.AVCProfileHigh,
-                MediaCodecInfo.CodecProfileLevel.AVCLevel42);
+                MediaCodecInfo.CodecProfileLevel.AVCLevel3);
         mMediaRecorder.setVideoSize(screenWidth, screenHeight);
         mMediaRecorder.setVideoFrameRate(refereshRate);
         mMediaRecorder.setVideoEncodingBitRate(vidBitRate);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java
index 3fd7f945..5c26d94 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java
@@ -16,12 +16,12 @@
 
 package com.android.systemui.screenshot;
 
-import static com.android.systemui.screenshot.GlobalScreenshot.ACTION_TYPE_EDIT;
-import static com.android.systemui.screenshot.GlobalScreenshot.ACTION_TYPE_SHARE;
-import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ACTION_INTENT;
-import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_DISALLOW_ENTER_PIP;
-import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
-import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED;
+import static com.android.systemui.screenshot.ScreenshotController.ACTION_TYPE_EDIT;
+import static com.android.systemui.screenshot.ScreenshotController.ACTION_TYPE_SHARE;
+import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ACTION_INTENT;
+import static com.android.systemui.screenshot.ScreenshotController.EXTRA_DISALLOW_ENTER_PIP;
+import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ID;
+import static com.android.systemui.screenshot.ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED;
 import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_SCREENSHOT;
 
 import android.app.ActivityOptions;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java
index 9028bb5..35839f3 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java
@@ -16,10 +16,10 @@
 
 package com.android.systemui.screenshot;
 
-import static com.android.systemui.screenshot.GlobalScreenshot.ACTION_TYPE_DELETE;
-import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
-import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED;
-import static com.android.systemui.screenshot.GlobalScreenshot.SCREENSHOT_URI_ID;
+import static com.android.systemui.screenshot.ScreenshotController.ACTION_TYPE_DELETE;
+import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ID;
+import static com.android.systemui.screenshot.ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED;
+import static com.android.systemui.screenshot.ScreenshotController.SCREENSHOT_URI_ID;
 
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
deleted file mode 100644
index 2b4fa2a..0000000
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ /dev/null
@@ -1,1123 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.screenshot;
-
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ValueAnimator;
-import android.annotation.Nullable;
-import android.annotation.SuppressLint;
-import android.app.ActivityManager;
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.graphics.Insets;
-import android.graphics.Outline;
-import android.graphics.PixelFormat;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.InsetDrawable;
-import android.graphics.drawable.LayerDrawable;
-import android.media.MediaActionSound;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.provider.Settings;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.util.MathUtils;
-import android.view.Display;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.SurfaceControl;
-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.view.accessibility.AccessibilityManager;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-import android.widget.FrameLayout;
-import android.widget.HorizontalScrollView;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.Toast;
-
-import com.android.internal.logging.UiEventLogger;
-import com.android.systemui.R;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.shared.system.QuickStepContract;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.function.Consumer;
-
-import javax.inject.Inject;
-
-/**
- * Class for handling device screen shots
- */
-@SysUISingleton
-public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInsetsListener {
-
-    /**
-     * POD used in the AsyncTask which saves an image in the background.
-     */
-    static class SaveImageInBackgroundData {
-        public Bitmap image;
-        public Consumer<Uri> finisher;
-        public GlobalScreenshot.ActionsReadyListener mActionsReadyListener;
-        public int errorMsgResId;
-
-        void clearImage() {
-            image = null;
-        }
-    }
-
-    /**
-     * Structure returned by the SaveImageInBackgroundTask
-     */
-    static class SavedImageData {
-        public Uri uri;
-        public Notification.Action shareAction;
-        public Notification.Action editAction;
-        public Notification.Action deleteAction;
-        public List<Notification.Action> smartActions;
-
-        /**
-         * Used to reset the return data on error
-         */
-        public void reset() {
-            uri = null;
-            shareAction = null;
-            editAction = null;
-            deleteAction = null;
-            smartActions = null;
-        }
-    }
-
-    abstract static class ActionsReadyListener {
-        abstract void onActionsReady(SavedImageData imageData);
-    }
-
-    // These strings are used for communicating the action invoked to
-    // ScreenshotNotificationSmartActionsProvider.
-    static final String EXTRA_ACTION_TYPE = "android:screenshot_action_type";
-    static final String EXTRA_ID = "android:screenshot_id";
-    static final String ACTION_TYPE_DELETE = "Delete";
-    static final String ACTION_TYPE_SHARE = "Share";
-    static final String ACTION_TYPE_EDIT = "Edit";
-    static final String EXTRA_SMART_ACTIONS_ENABLED = "android:smart_actions_enabled";
-    static final String EXTRA_ACTION_INTENT = "android:screenshot_action_intent";
-
-    static final String SCREENSHOT_URI_ID = "android:screenshot_uri_id";
-    static final String EXTRA_CANCEL_NOTIFICATION = "android:screenshot_cancel_notification";
-    static final String EXTRA_DISALLOW_ENTER_PIP = "android:screenshot_disallow_enter_pip";
-
-    // From WizardManagerHelper.java
-    private static final String SETTINGS_SECURE_USER_SETUP_COMPLETE = "user_setup_complete";
-
-    private static final String TAG = "GlobalScreenshot";
-
-    private static final long SCREENSHOT_FLASH_IN_DURATION_MS = 133;
-    private static final long SCREENSHOT_FLASH_OUT_DURATION_MS = 217;
-    // delay before starting to fade in dismiss button
-    private static final long SCREENSHOT_TO_CORNER_DISMISS_DELAY_MS = 200;
-    private static final long SCREENSHOT_TO_CORNER_X_DURATION_MS = 234;
-    private static final long SCREENSHOT_TO_CORNER_Y_DURATION_MS = 500;
-    private static final long SCREENSHOT_TO_CORNER_SCALE_DURATION_MS = 234;
-    private static final long SCREENSHOT_ACTIONS_EXPANSION_DURATION_MS = 400;
-    private static final long SCREENSHOT_ACTIONS_ALPHA_DURATION_MS = 100;
-    private static final long SCREENSHOT_DISMISS_Y_DURATION_MS = 350;
-    private static final long SCREENSHOT_DISMISS_ALPHA_DURATION_MS = 183;
-    private static final long SCREENSHOT_DISMISS_ALPHA_OFFSET_MS = 50; // delay before starting fade
-    private static final float SCREENSHOT_ACTIONS_START_SCALE_X = .7f;
-    private static final float ROUNDED_CORNER_RADIUS = .05f;
-    private static final int SCREENSHOT_CORNER_DEFAULT_TIMEOUT_MILLIS = 6000;
-    private static final int MESSAGE_CORNER_TIMEOUT = 2;
-
-    private final Interpolator mAccelerateInterpolator = new AccelerateInterpolator();
-
-    private final ScreenshotNotificationsController mNotificationsController;
-    private final UiEventLogger mUiEventLogger;
-
-    private final Context mContext;
-    private final ScreenshotSmartActions mScreenshotSmartActions;
-    private final WindowManager mWindowManager;
-    private final WindowManager.LayoutParams mWindowLayoutParams;
-    private final Display mDisplay;
-    private final DisplayMetrics mDisplayMetrics;
-
-    private View mScreenshotLayout;
-    private ScreenshotSelectorView mScreenshotSelectorView;
-    private ImageView mScreenshotAnimatedView;
-    private ImageView mScreenshotPreview;
-    private ImageView mScreenshotFlash;
-    private ImageView mActionsContainerBackground;
-    private HorizontalScrollView mActionsContainer;
-    private LinearLayout mActionsView;
-    private ImageView mBackgroundProtection;
-    private FrameLayout mDismissButton;
-
-    private Bitmap mScreenBitmap;
-    private SaveImageInBackgroundTask mSaveInBgTask;
-    private Animator mScreenshotAnimation;
-    private Runnable mOnCompleteRunnable;
-    private Animator mDismissAnimation;
-    private boolean mInDarkMode;
-    private boolean mDirectionLTR;
-    private boolean mOrientationPortrait;
-
-    private float mCornerSizeX;
-    private float mDismissDeltaY;
-
-    private MediaActionSound mCameraSound;
-
-    private int mNavMode;
-    private int mLeftInset;
-    private int mRightInset;
-
-    // standard material ease
-    private final Interpolator mFastOutSlowIn;
-
-    private final Handler mScreenshotHandler = new Handler(Looper.getMainLooper()) {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MESSAGE_CORNER_TIMEOUT:
-                    mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_INTERACTION_TIMEOUT);
-                    GlobalScreenshot.this.dismissScreenshot("timeout", false);
-                    mOnCompleteRunnable.run();
-                    break;
-                default:
-                    break;
-            }
-        }
-    };
-
-    @Inject
-    public GlobalScreenshot(
-            Context context, @Main Resources resources,
-            ScreenshotSmartActions screenshotSmartActions,
-            ScreenshotNotificationsController screenshotNotificationsController,
-            UiEventLogger uiEventLogger) {
-        mContext = context;
-        mScreenshotSmartActions = screenshotSmartActions;
-        mNotificationsController = screenshotNotificationsController;
-        mUiEventLogger = uiEventLogger;
-
-        reloadAssets();
-        Configuration config = mContext.getResources().getConfiguration();
-        mInDarkMode = config.isNightModeActive();
-        mDirectionLTR = config.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR;
-        mOrientationPortrait = config.orientation == ORIENTATION_PORTRAIT;
-
-        // Setup the window that we are going to use
-        mWindowLayoutParams = new WindowManager.LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 0, 0,
-                WindowManager.LayoutParams.TYPE_SCREENSHOT,
-                WindowManager.LayoutParams.FLAG_FULLSCREEN
-                        | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
-                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
-                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
-                        | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
-                        | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
-                PixelFormat.TRANSLUCENT);
-        mWindowLayoutParams.setTitle("ScreenshotAnimation");
-        mWindowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-        mWindowLayoutParams.setFitInsetsTypes(0 /* types */);
-        mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
-        mDisplay = mWindowManager.getDefaultDisplay();
-        mDisplayMetrics = new DisplayMetrics();
-        mDisplay.getRealMetrics(mDisplayMetrics);
-
-        mCornerSizeX = resources.getDimensionPixelSize(R.dimen.global_screenshot_x_scale);
-        mDismissDeltaY = resources.getDimensionPixelSize(R.dimen.screenshot_dismissal_height_delta);
-
-        mFastOutSlowIn =
-                AnimationUtils.loadInterpolator(mContext, android.R.interpolator.fast_out_slow_in);
-
-        // Setup the Camera shutter sound
-        mCameraSound = new MediaActionSound();
-        mCameraSound.load(MediaActionSound.SHUTTER_CLICK);
-    }
-
-    @Override // ViewTreeObserver.OnComputeInternalInsetsListener
-    public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
-        inoutInfo.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
-        Region touchRegion = new Region();
-
-        Rect screenshotRect = new Rect();
-        mScreenshotPreview.getBoundsOnScreen(screenshotRect);
-        touchRegion.op(screenshotRect, Region.Op.UNION);
-        Rect actionsRect = new Rect();
-        mActionsContainer.getBoundsOnScreen(actionsRect);
-        touchRegion.op(actionsRect, Region.Op.UNION);
-        Rect dismissRect = new Rect();
-        mDismissButton.getBoundsOnScreen(dismissRect);
-        touchRegion.op(dismissRect, Region.Op.UNION);
-
-        if (QuickStepContract.isGesturalMode(mNavMode)) {
-            // Receive touches in gesture insets such that they don't cause TOUCH_OUTSIDE
-            Rect inset = new Rect(0, 0, mLeftInset, mDisplayMetrics.heightPixels);
-            touchRegion.op(inset, Region.Op.UNION);
-            inset.set(mDisplayMetrics.widthPixels - mRightInset, 0, mDisplayMetrics.widthPixels,
-                    mDisplayMetrics.heightPixels);
-            touchRegion.op(inset, Region.Op.UNION);
-        }
-
-        inoutInfo.touchableRegion.set(touchRegion);
-    }
-
-    void takeScreenshotFullscreen(Consumer<Uri> finisher, Runnable onComplete) {
-        mOnCompleteRunnable = onComplete;
-
-        mDisplay.getRealMetrics(mDisplayMetrics);
-        takeScreenshotInternal(
-                finisher,
-                new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
-    }
-
-    void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds,
-            Insets visibleInsets, int taskId, int userId, ComponentName topComponent,
-            Consumer<Uri> finisher, Runnable onComplete) {
-        // TODO: use task Id, userId, topComponent for smart handler
-
-        mOnCompleteRunnable = onComplete;
-        if (aspectRatiosMatch(screenshot, visibleInsets, screenshotScreenBounds)) {
-            saveScreenshot(screenshot, finisher, screenshotScreenBounds, visibleInsets, false);
-        } else {
-            saveScreenshot(screenshot, finisher,
-                    new Rect(0, 0, screenshot.getWidth(), screenshot.getHeight()), Insets.NONE,
-                    true);
-        }
-    }
-
-    /**
-     * Displays a screenshot selector
-     */
-    @SuppressLint("ClickableViewAccessibility")
-    void takeScreenshotPartial(final Consumer<Uri> finisher, Runnable onComplete) {
-        dismissScreenshot("new screenshot requested", true);
-        mOnCompleteRunnable = onComplete;
-
-        mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
-        mScreenshotSelectorView.setOnTouchListener((v, event) -> {
-            ScreenshotSelectorView view = (ScreenshotSelectorView) v;
-            switch (event.getAction()) {
-                case MotionEvent.ACTION_DOWN:
-                    view.startSelection((int) event.getX(), (int) event.getY());
-                    return true;
-                case MotionEvent.ACTION_MOVE:
-                    view.updateSelection((int) event.getX(), (int) event.getY());
-                    return true;
-                case MotionEvent.ACTION_UP:
-                    view.setVisibility(View.GONE);
-                    mWindowManager.removeView(mScreenshotLayout);
-                    final Rect rect = view.getSelectionRect();
-                    if (rect != null) {
-                        if (rect.width() != 0 && rect.height() != 0) {
-                            // Need mScreenshotLayout to handle it after the view disappears
-                            mScreenshotLayout.post(() -> takeScreenshotInternal(finisher, rect));
-                        }
-                    }
-
-                    view.stopSelection();
-                    return true;
-            }
-
-            return false;
-        });
-        mScreenshotLayout.post(() -> {
-            mScreenshotSelectorView.setVisibility(View.VISIBLE);
-            mScreenshotSelectorView.requestFocus();
-        });
-    }
-
-    /**
-     * Cancels screenshot request
-     */
-    void stopScreenshot() {
-        // If the selector layer still presents on screen, we remove it and resets its state.
-        if (mScreenshotSelectorView.getSelectionRect() != null) {
-            mWindowManager.removeView(mScreenshotLayout);
-            mScreenshotSelectorView.stopSelection();
-        }
-    }
-
-    /**
-     * Clears current screenshot
-     */
-    void dismissScreenshot(String reason, boolean immediate) {
-        Log.v(TAG, "clearing screenshot: " + reason);
-        mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
-        mScreenshotLayout.getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
-        if (!immediate) {
-            mDismissAnimation = createScreenshotDismissAnimation();
-            mDismissAnimation.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    super.onAnimationEnd(animation);
-                    clearScreenshot();
-                }
-            });
-            mDismissAnimation.start();
-        } else {
-            clearScreenshot();
-        }
-    }
-
-    private void onConfigChanged(Configuration newConfig) {
-        boolean needsUpdate = false;
-        // dark mode
-        if (newConfig.isNightModeActive()) {
-            // Night mode is active, we're using dark theme
-            if (!mInDarkMode) {
-                mInDarkMode = true;
-                needsUpdate = true;
-            }
-        } else {
-            // Night mode is not active, we're using the light theme
-            if (mInDarkMode) {
-                mInDarkMode = false;
-                needsUpdate = true;
-            }
-        }
-
-        // RTL configuration
-        switch (newConfig.getLayoutDirection()) {
-            case View.LAYOUT_DIRECTION_LTR:
-                if (!mDirectionLTR) {
-                    mDirectionLTR = true;
-                    needsUpdate = true;
-                }
-                break;
-            case View.LAYOUT_DIRECTION_RTL:
-                if (mDirectionLTR) {
-                    mDirectionLTR = false;
-                    needsUpdate = true;
-                }
-                break;
-        }
-
-        // portrait/landscape orientation
-        switch (newConfig.orientation) {
-            case ORIENTATION_PORTRAIT:
-                if (!mOrientationPortrait) {
-                    mOrientationPortrait = true;
-                    needsUpdate = true;
-                }
-                break;
-            case ORIENTATION_LANDSCAPE:
-                if (mOrientationPortrait) {
-                    mOrientationPortrait = false;
-                    needsUpdate = true;
-                }
-                break;
-        }
-
-        if (needsUpdate) {
-            reloadAssets();
-        }
-
-        mNavMode = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_navBarInteractionMode);
-    }
-
-    /**
-     * Update assets (called when the dark theme status changes). We only need to update the dismiss
-     * button and the actions container background, since the buttons are re-inflated on demand.
-     */
-    private void reloadAssets() {
-        boolean wasAttached = mScreenshotLayout != null && mScreenshotLayout.isAttachedToWindow();
-        if (wasAttached) {
-            mWindowManager.removeView(mScreenshotLayout);
-        }
-
-        // Inflate the screenshot layout
-        mScreenshotLayout = LayoutInflater.from(mContext).inflate(R.layout.global_screenshot, null);
-        // TODO(159460485): Remove this when focus is handled properly in the system
-        mScreenshotLayout.setOnTouchListener((v, event) -> {
-            if (event.getActionMasked() == MotionEvent.ACTION_OUTSIDE) {
-                // Once the user touches outside, stop listening for input
-                setWindowFocusable(false);
-            }
-            return false;
-        });
-        mScreenshotLayout.setOnApplyWindowInsetsListener((v, insets) -> {
-            if (QuickStepContract.isGesturalMode(mNavMode)) {
-                Insets gestureInsets = insets.getInsets(
-                        WindowInsets.Type.systemGestures());
-                mLeftInset = gestureInsets.left;
-                mRightInset = gestureInsets.right;
-            } else {
-                mLeftInset = mRightInset = 0;
-            }
-            return mScreenshotLayout.onApplyWindowInsets(insets);
-        });
-        mScreenshotLayout.setOnKeyListener((v, keyCode, event) -> {
-            if (keyCode == KeyEvent.KEYCODE_BACK) {
-                dismissScreenshot("back pressed", false);
-                return true;
-            }
-            return false;
-        });
-        // Get focus so that the key events go to the layout.
-        mScreenshotLayout.setFocusableInTouchMode(true);
-        mScreenshotLayout.requestFocus();
-
-        mScreenshotAnimatedView =
-                mScreenshotLayout.findViewById(R.id.global_screenshot_animated_view);
-        mScreenshotAnimatedView.setClipToOutline(true);
-        mScreenshotAnimatedView.setOutlineProvider(new ViewOutlineProvider() {
-            @Override
-            public void getOutline(View view, Outline outline) {
-                outline.setRoundRect(new Rect(0, 0, view.getWidth(), view.getHeight()),
-                        ROUNDED_CORNER_RADIUS * view.getWidth());
-            }
-        });
-        mScreenshotPreview = mScreenshotLayout.findViewById(R.id.global_screenshot_preview);
-        mScreenshotPreview.setClipToOutline(true);
-        mScreenshotPreview.setOutlineProvider(new ViewOutlineProvider() {
-            @Override
-            public void getOutline(View view, Outline outline) {
-                outline.setRoundRect(new Rect(0, 0, view.getWidth(), view.getHeight()),
-                        ROUNDED_CORNER_RADIUS * view.getWidth());
-            }
-        });
-
-        mActionsContainerBackground = mScreenshotLayout.findViewById(
-                R.id.global_screenshot_actions_container_background);
-        mActionsContainer = mScreenshotLayout.findViewById(
-                R.id.global_screenshot_actions_container);
-        mActionsView = mScreenshotLayout.findViewById(R.id.global_screenshot_actions);
-        mBackgroundProtection = mScreenshotLayout.findViewById(
-                R.id.global_screenshot_actions_background);
-        mDismissButton = mScreenshotLayout.findViewById(R.id.global_screenshot_dismiss_button);
-        mDismissButton.setOnClickListener(view -> {
-            mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_EXPLICIT_DISMISSAL);
-            dismissScreenshot("dismiss_button", false);
-            mOnCompleteRunnable.run();
-        });
-
-        mScreenshotFlash = mScreenshotLayout.findViewById(R.id.global_screenshot_flash);
-        mScreenshotSelectorView = mScreenshotLayout.findViewById(R.id.global_screenshot_selector);
-        mScreenshotLayout.setFocusable(true);
-        mScreenshotSelectorView.setFocusable(true);
-        mScreenshotSelectorView.setFocusableInTouchMode(true);
-        mScreenshotAnimatedView.setPivotX(0);
-        mScreenshotAnimatedView.setPivotY(0);
-        mActionsContainer.setScrollX(0);
-
-        if (wasAttached) {
-            mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
-        }
-    }
-
-    /**
-     * Takes a screenshot of the current display and shows an animation.
-     */
-    private void takeScreenshotInternal(Consumer<Uri> finisher, Rect crop) {
-        // copy the input Rect, since SurfaceControl.screenshot can mutate it
-        Rect screenRect = new Rect(crop);
-        int width = crop.width();
-        int height = crop.height();
-        final IBinder displayToken = SurfaceControl.getInternalDisplayToken();
-        final SurfaceControl.DisplayCaptureArgs captureArgs =
-                new SurfaceControl.DisplayCaptureArgs.Builder(displayToken)
-                        .setSourceCrop(crop)
-                        .setSize(width, height)
-                        .build();
-        final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
-                SurfaceControl.captureDisplay(captureArgs);
-        final Bitmap screenshot = screenshotBuffer == null ? null : screenshotBuffer.asBitmap();
-        saveScreenshot(screenshot, finisher, screenRect, Insets.NONE, true);
-    }
-
-    private void saveScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect,
-            Insets screenInsets, boolean showFlash) {
-        if (mScreenshotLayout.isAttachedToWindow()) {
-            // if we didn't already dismiss for another reason
-            if (mDismissAnimation == null || !mDismissAnimation.isRunning()) {
-                mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_REENTERED);
-            }
-            dismissScreenshot("new screenshot requested", true);
-        }
-
-        mScreenBitmap = screenshot;
-
-        if (mScreenBitmap == null) {
-            mNotificationsController.notifyScreenshotError(
-                    R.string.screenshot_failed_to_capture_text);
-            finisher.accept(null);
-            mOnCompleteRunnable.run();
-            return;
-        }
-
-        if (!isUserSetupComplete()) {
-            // User setup isn't complete, so we don't want to show any UI beyond a toast, as editing
-            // and sharing shouldn't be exposed to the user.
-            saveScreenshotAndToast(finisher);
-            return;
-        }
-
-        // Optimizations
-        mScreenBitmap.setHasAlpha(false);
-        mScreenBitmap.prepareToDraw();
-
-        onConfigChanged(mContext.getResources().getConfiguration());
-
-        if (mDismissAnimation != null && mDismissAnimation.isRunning()) {
-            mDismissAnimation.cancel();
-        }
-
-        // The window is focusable by default
-        setWindowFocusable(true);
-
-        // Start the post-screenshot animation
-        startAnimation(finisher, screenRect, screenInsets, showFlash);
-    }
-
-    /**
-     * Save the bitmap but don't show the normal screenshot UI.. just a toast (or notification on
-     * failure).
-     */
-    private void saveScreenshotAndToast(Consumer<Uri> finisher) {
-        // Play the shutter sound to notify that we've taken a screenshot
-        mScreenshotHandler.post(() -> {
-            mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
-        });
-
-        saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() {
-            @Override
-            void onActionsReady(SavedImageData imageData) {
-                finisher.accept(imageData.uri);
-                if (imageData.uri == null) {
-                    mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED);
-                    mNotificationsController.notifyScreenshotError(
-                            R.string.screenshot_failed_to_capture_text);
-                } else {
-                    mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED);
-
-                    mScreenshotHandler.post(() -> {
-                        Toast.makeText(mContext, R.string.screenshot_saved_title,
-                                Toast.LENGTH_SHORT).show();
-                    });
-                }
-            }
-        });
-    }
-
-    /**
-     * Starts the animation after taking the screenshot
-     */
-    private void startAnimation(final Consumer<Uri> finisher, Rect screenRect, Insets screenInsets,
-            boolean showFlash) {
-        mScreenshotHandler.post(() -> {
-            if (!mScreenshotLayout.isAttachedToWindow()) {
-                mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
-            }
-            mScreenshotAnimatedView.setImageDrawable(
-                    createScreenDrawable(mScreenBitmap, screenInsets));
-            setAnimatedViewSize(screenRect.width(), screenRect.height());
-            // Show when the animation starts
-            mScreenshotAnimatedView.setVisibility(View.GONE);
-
-            mScreenshotPreview.setImageDrawable(createScreenDrawable(mScreenBitmap, screenInsets));
-            // make static preview invisible (from gone) so we can query its location on screen
-            mScreenshotPreview.setVisibility(View.INVISIBLE);
-
-            mScreenshotHandler.post(() -> {
-                mScreenshotLayout.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
-
-                mScreenshotAnimation =
-                        createScreenshotDropInAnimation(screenRect, showFlash);
-
-                saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() {
-                    @Override
-                    void onActionsReady(SavedImageData imageData) {
-                        showUiOnActionsReady(imageData);
-                    }
-                });
-
-                // Play the shutter sound to notify that we've taken a screenshot
-                mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
-
-                mScreenshotPreview.setLayerType(View.LAYER_TYPE_HARDWARE, null);
-                mScreenshotPreview.buildLayer();
-                mScreenshotAnimation.start();
-            });
-        });
-    }
-
-    /**
-     * Creates a new worker thread and saves the screenshot to the media store.
-     */
-    private void saveScreenshotInWorkerThread(
-            Consumer<Uri> finisher, @Nullable ActionsReadyListener actionsReadyListener) {
-        SaveImageInBackgroundData data = new SaveImageInBackgroundData();
-        data.image = mScreenBitmap;
-        data.finisher = finisher;
-        data.mActionsReadyListener = actionsReadyListener;
-
-        if (mSaveInBgTask != null) {
-            // just log success/failure for the pre-existing screenshot
-            mSaveInBgTask.setActionsReadyListener(new ActionsReadyListener() {
-                @Override
-                void onActionsReady(SavedImageData imageData) {
-                    logSuccessOnActionsReady(imageData);
-                }
-            });
-        }
-
-        mSaveInBgTask = new SaveImageInBackgroundTask(mContext, mScreenshotSmartActions, data);
-        mSaveInBgTask.execute();
-    }
-
-    /**
-     * Sets up the action shade and its entrance animation, once we get the screenshot URI.
-     */
-    private void showUiOnActionsReady(SavedImageData imageData) {
-        logSuccessOnActionsReady(imageData);
-
-        AccessibilityManager accessibilityManager = (AccessibilityManager)
-                mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
-        long timeoutMs = accessibilityManager.getRecommendedTimeoutMillis(
-                SCREENSHOT_CORNER_DEFAULT_TIMEOUT_MILLIS,
-                AccessibilityManager.FLAG_CONTENT_CONTROLS);
-
-        mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
-        mScreenshotHandler.sendMessageDelayed(
-                mScreenshotHandler.obtainMessage(MESSAGE_CORNER_TIMEOUT),
-                timeoutMs);
-
-        if (imageData.uri != null) {
-            mScreenshotHandler.post(() -> {
-                if (mScreenshotAnimation != null && mScreenshotAnimation.isRunning()) {
-                    mScreenshotAnimation.addListener(new AnimatorListenerAdapter() {
-                        @Override
-                        public void onAnimationEnd(Animator animation) {
-                            super.onAnimationEnd(animation);
-                            createScreenshotActionsShadeAnimation(imageData).start();
-                        }
-                    });
-                } else {
-                    createScreenshotActionsShadeAnimation(imageData).start();
-                }
-            });
-        }
-    }
-
-    /**
-     * Logs success/failure of the screenshot saving task, and shows an error if it failed.
-     */
-    private void logSuccessOnActionsReady(SavedImageData imageData) {
-        if (imageData.uri == null) {
-            mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED);
-            mNotificationsController.notifyScreenshotError(
-                    R.string.screenshot_failed_to_capture_text);
-        } else {
-            mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED);
-        }
-    }
-
-    private AnimatorSet createScreenshotDropInAnimation(Rect bounds, boolean showFlash) {
-        Rect previewBounds = new Rect();
-        mScreenshotPreview.getBoundsOnScreen(previewBounds);
-
-        float cornerScale =
-                mCornerSizeX / (mOrientationPortrait ? bounds.width() : bounds.height());
-        final float currentScale = 1f;
-
-        mScreenshotAnimatedView.setScaleX(currentScale);
-        mScreenshotAnimatedView.setScaleY(currentScale);
-
-        mDismissButton.setAlpha(0);
-        mDismissButton.setVisibility(View.VISIBLE);
-
-        AnimatorSet dropInAnimation = new AnimatorSet();
-        ValueAnimator flashInAnimator = ValueAnimator.ofFloat(0, 1);
-        flashInAnimator.setDuration(SCREENSHOT_FLASH_IN_DURATION_MS);
-        flashInAnimator.setInterpolator(mFastOutSlowIn);
-        flashInAnimator.addUpdateListener(animation ->
-                mScreenshotFlash.setAlpha((float) animation.getAnimatedValue()));
-
-        ValueAnimator flashOutAnimator = ValueAnimator.ofFloat(1, 0);
-        flashOutAnimator.setDuration(SCREENSHOT_FLASH_OUT_DURATION_MS);
-        flashOutAnimator.setInterpolator(mFastOutSlowIn);
-        flashOutAnimator.addUpdateListener(animation ->
-                mScreenshotFlash.setAlpha((float) animation.getAnimatedValue()));
-
-        // animate from the current location, to the static preview location
-        final PointF startPos = new PointF(bounds.centerX(), bounds.centerY());
-        final PointF finalPos = new PointF(previewBounds.centerX(), previewBounds.centerY());
-
-        ValueAnimator toCorner = ValueAnimator.ofFloat(0, 1);
-        toCorner.setDuration(SCREENSHOT_TO_CORNER_Y_DURATION_MS);
-        float xPositionPct =
-                SCREENSHOT_TO_CORNER_X_DURATION_MS / (float) SCREENSHOT_TO_CORNER_Y_DURATION_MS;
-        float dismissPct =
-                SCREENSHOT_TO_CORNER_DISMISS_DELAY_MS / (float) SCREENSHOT_TO_CORNER_Y_DURATION_MS;
-        float scalePct =
-                SCREENSHOT_TO_CORNER_SCALE_DURATION_MS / (float) SCREENSHOT_TO_CORNER_Y_DURATION_MS;
-        toCorner.addUpdateListener(animation -> {
-            float t = animation.getAnimatedFraction();
-            if (t < scalePct) {
-                float scale = MathUtils.lerp(
-                        currentScale, cornerScale, mFastOutSlowIn.getInterpolation(t / scalePct));
-                mScreenshotAnimatedView.setScaleX(scale);
-                mScreenshotAnimatedView.setScaleY(scale);
-            } else {
-                mScreenshotAnimatedView.setScaleX(cornerScale);
-                mScreenshotAnimatedView.setScaleY(cornerScale);
-            }
-
-            float currentScaleX = mScreenshotAnimatedView.getScaleX();
-            float currentScaleY = mScreenshotAnimatedView.getScaleY();
-
-            if (t < xPositionPct) {
-                float xCenter = MathUtils.lerp(startPos.x, finalPos.x,
-                        mFastOutSlowIn.getInterpolation(t / xPositionPct));
-                mScreenshotAnimatedView.setX(xCenter - bounds.width() * currentScaleX / 2f);
-            } else {
-                mScreenshotAnimatedView.setX(finalPos.x - bounds.width() * currentScaleX / 2f);
-            }
-            float yCenter = MathUtils.lerp(
-                    startPos.y, finalPos.y, mFastOutSlowIn.getInterpolation(t));
-            mScreenshotAnimatedView.setY(yCenter - bounds.height() * currentScaleY / 2f);
-
-            if (t >= dismissPct) {
-                mDismissButton.setAlpha((t - dismissPct) / (1 - dismissPct));
-                float currentX = mScreenshotAnimatedView.getX();
-                float currentY = mScreenshotAnimatedView.getY();
-                mDismissButton.setY(currentY - mDismissButton.getHeight() / 2f);
-                if (mDirectionLTR) {
-                    mDismissButton.setX(currentX
-                            + bounds.width() * currentScaleX - mDismissButton.getWidth() / 2f);
-                } else {
-                    mDismissButton.setX(currentX - mDismissButton.getWidth() / 2f);
-                }
-            }
-        });
-
-        toCorner.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationStart(Animator animation) {
-                super.onAnimationStart(animation);
-                mScreenshotAnimatedView.setVisibility(View.VISIBLE);
-            }
-        });
-
-        mScreenshotFlash.setAlpha(0f);
-        mScreenshotFlash.setVisibility(View.VISIBLE);
-
-        if (showFlash) {
-            dropInAnimation.play(flashOutAnimator).after(flashInAnimator);
-            dropInAnimation.play(flashOutAnimator).with(toCorner);
-        } else {
-            dropInAnimation.play(toCorner);
-        }
-
-        dropInAnimation.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                super.onAnimationEnd(animation);
-                mDismissButton.setAlpha(1);
-                float dismissOffset = mDismissButton.getWidth() / 2f;
-                float finalDismissX = mDirectionLTR
-                        ? finalPos.x - dismissOffset + bounds.width() * cornerScale / 2f
-                        : finalPos.x - dismissOffset - bounds.width() * cornerScale / 2f;
-                mDismissButton.setX(finalDismissX);
-                mDismissButton.setY(
-                        finalPos.y - dismissOffset - bounds.height() * cornerScale / 2f);
-                mScreenshotAnimatedView.setScaleX(1);
-                mScreenshotAnimatedView.setScaleY(1);
-                mScreenshotAnimatedView.setX(finalPos.x - bounds.width() * cornerScale / 2f);
-                mScreenshotAnimatedView.setY(finalPos.y - bounds.height() * cornerScale / 2f);
-                mScreenshotAnimatedView.setVisibility(View.GONE);
-                mScreenshotPreview.setVisibility(View.VISIBLE);
-                mScreenshotLayout.forceLayout();
-            }
-        });
-
-        return dropInAnimation;
-    }
-
-    private ValueAnimator createScreenshotActionsShadeAnimation(SavedImageData imageData) {
-        LayoutInflater inflater = LayoutInflater.from(mContext);
-        mActionsView.removeAllViews();
-        mScreenshotLayout.invalidate();
-        mScreenshotLayout.requestLayout();
-        mScreenshotLayout.getViewTreeObserver().dispatchOnGlobalLayout();
-
-        // By default the activities won't be able to start immediately; override this to keep
-        // the same behavior as if started from a notification
-        try {
-            ActivityManager.getService().resumeAppSwitches();
-        } catch (RemoteException e) {
-        }
-
-        ArrayList<ScreenshotActionChip> chips = new ArrayList<>();
-
-        for (Notification.Action smartAction : imageData.smartActions) {
-            ScreenshotActionChip actionChip = (ScreenshotActionChip) inflater.inflate(
-                    R.layout.global_screenshot_action_chip, mActionsView, false);
-            actionChip.setText(smartAction.title);
-            actionChip.setIcon(smartAction.getIcon(), false);
-            actionChip.setPendingIntent(smartAction.actionIntent,
-                    () -> {
-                        mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SMART_ACTION_TAPPED);
-                        dismissScreenshot("chip tapped", false);
-                        mOnCompleteRunnable.run();
-                    });
-            mActionsView.addView(actionChip);
-            chips.add(actionChip);
-        }
-
-        ScreenshotActionChip shareChip = (ScreenshotActionChip) inflater.inflate(
-                R.layout.global_screenshot_action_chip, mActionsView, false);
-        shareChip.setText(imageData.shareAction.title);
-        shareChip.setIcon(imageData.shareAction.getIcon(), true);
-        shareChip.setPendingIntent(imageData.shareAction.actionIntent, () -> {
-            mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SHARE_TAPPED);
-            dismissScreenshot("chip tapped", false);
-            mOnCompleteRunnable.run();
-        });
-        mActionsView.addView(shareChip);
-        chips.add(shareChip);
-
-        ScreenshotActionChip editChip = (ScreenshotActionChip) inflater.inflate(
-                R.layout.global_screenshot_action_chip, mActionsView, false);
-        editChip.setText(imageData.editAction.title);
-        editChip.setIcon(imageData.editAction.getIcon(), true);
-        editChip.setPendingIntent(imageData.editAction.actionIntent, () -> {
-            mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_EDIT_TAPPED);
-            dismissScreenshot("chip tapped", false);
-            mOnCompleteRunnable.run();
-        });
-        mActionsView.addView(editChip);
-        chips.add(editChip);
-
-        mScreenshotPreview.setOnClickListener(v -> {
-            try {
-                imageData.editAction.actionIntent.send();
-                mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_PREVIEW_TAPPED);
-                dismissScreenshot("screenshot preview tapped", false);
-                mOnCompleteRunnable.run();
-            } catch (PendingIntent.CanceledException e) {
-                Log.e(TAG, "Intent cancelled", e);
-            }
-        });
-        mScreenshotPreview.setContentDescription(imageData.editAction.title);
-
-        // remove the margin from the last chip so that it's correctly aligned with the end
-        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams)
-                mActionsView.getChildAt(mActionsView.getChildCount() - 1).getLayoutParams();
-        params.setMarginEnd(0);
-
-        ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
-        animator.setDuration(SCREENSHOT_ACTIONS_EXPANSION_DURATION_MS);
-        float alphaFraction = (float) SCREENSHOT_ACTIONS_ALPHA_DURATION_MS
-                / SCREENSHOT_ACTIONS_EXPANSION_DURATION_MS;
-        mActionsContainer.setAlpha(0f);
-        mActionsContainerBackground.setAlpha(0f);
-        mActionsContainer.setVisibility(View.VISIBLE);
-        mActionsContainerBackground.setVisibility(View.VISIBLE);
-
-        animator.addUpdateListener(animation -> {
-            float t = animation.getAnimatedFraction();
-            mBackgroundProtection.setAlpha(t);
-            float containerAlpha = t < alphaFraction ? t / alphaFraction : 1;
-            mActionsContainer.setAlpha(containerAlpha);
-            mActionsContainerBackground.setAlpha(containerAlpha);
-            float containerScale = SCREENSHOT_ACTIONS_START_SCALE_X
-                    + (t * (1 - SCREENSHOT_ACTIONS_START_SCALE_X));
-            mActionsContainer.setScaleX(containerScale);
-            mActionsContainerBackground.setScaleX(containerScale);
-            for (ScreenshotActionChip chip : chips) {
-                chip.setAlpha(t);
-                chip.setScaleX(1 / containerScale); // invert to keep size of children constant
-            }
-            mActionsContainer.setScrollX(mDirectionLTR ? 0 : mActionsContainer.getWidth());
-            mActionsContainer.setPivotX(mDirectionLTR ? 0 : mActionsContainer.getWidth());
-            mActionsContainerBackground.setPivotX(
-                    mDirectionLTR ? 0 : mActionsContainerBackground.getWidth());
-        });
-        return animator;
-    }
-
-    private AnimatorSet createScreenshotDismissAnimation() {
-        ValueAnimator alphaAnim = ValueAnimator.ofFloat(0, 1);
-        alphaAnim.setStartDelay(SCREENSHOT_DISMISS_ALPHA_OFFSET_MS);
-        alphaAnim.setDuration(SCREENSHOT_DISMISS_ALPHA_DURATION_MS);
-        alphaAnim.addUpdateListener(animation -> {
-            mScreenshotLayout.setAlpha(1 - animation.getAnimatedFraction());
-        });
-
-        ValueAnimator yAnim = ValueAnimator.ofFloat(0, 1);
-        yAnim.setInterpolator(mAccelerateInterpolator);
-        yAnim.setDuration(SCREENSHOT_DISMISS_Y_DURATION_MS);
-        float screenshotStartY = mScreenshotPreview.getTranslationY();
-        float dismissStartY = mDismissButton.getTranslationY();
-        yAnim.addUpdateListener(animation -> {
-            float yDelta = MathUtils.lerp(0, mDismissDeltaY, animation.getAnimatedFraction());
-            mScreenshotPreview.setTranslationY(screenshotStartY + yDelta);
-            mDismissButton.setTranslationY(dismissStartY + yDelta);
-            mActionsContainer.setTranslationY(yDelta);
-            mActionsContainerBackground.setTranslationY(yDelta);
-        });
-
-        AnimatorSet animSet = new AnimatorSet();
-        animSet.play(yAnim).with(alphaAnim);
-
-        return animSet;
-    }
-
-    private void clearScreenshot() {
-        if (mScreenshotLayout.isAttachedToWindow()) {
-            mWindowManager.removeView(mScreenshotLayout);
-        }
-
-        // Clear any references to the bitmap
-        mScreenshotPreview.setImageDrawable(null);
-        mScreenshotAnimatedView.setImageDrawable(null);
-        mScreenshotAnimatedView.setVisibility(View.GONE);
-        mActionsContainerBackground.setVisibility(View.GONE);
-        mActionsContainer.setVisibility(View.GONE);
-        mBackgroundProtection.setAlpha(0f);
-        mDismissButton.setVisibility(View.GONE);
-        mScreenshotPreview.setVisibility(View.GONE);
-        mScreenshotPreview.setLayerType(View.LAYER_TYPE_NONE, null);
-        mScreenshotPreview.setContentDescription(
-                mContext.getResources().getString(R.string.screenshot_preview_description));
-        mScreenshotPreview.setOnClickListener(null);
-        mScreenshotLayout.setAlpha(1);
-        mDismissButton.setTranslationY(0);
-        mActionsContainer.setTranslationY(0);
-        mActionsContainerBackground.setTranslationY(0);
-        mScreenshotPreview.setTranslationY(0);
-    }
-
-    private void setAnimatedViewSize(int width, int height) {
-        ViewGroup.LayoutParams layoutParams = mScreenshotAnimatedView.getLayoutParams();
-        layoutParams.width = width;
-        layoutParams.height = height;
-        mScreenshotAnimatedView.setLayoutParams(layoutParams);
-    }
-
-    /**
-     * Updates the window focusability.  If the window is already showing, then it updates the
-     * window immediately, otherwise the layout params will be applied when the window is next
-     * shown.
-     */
-    private void setWindowFocusable(boolean focusable) {
-        if (focusable) {
-            mWindowLayoutParams.flags &= ~FLAG_NOT_FOCUSABLE;
-        } else {
-            mWindowLayoutParams.flags |= FLAG_NOT_FOCUSABLE;
-        }
-        if (mScreenshotLayout.isAttachedToWindow()) {
-            mWindowManager.updateViewLayout(mScreenshotLayout, mWindowLayoutParams);
-        }
-    }
-
-    private boolean isUserSetupComplete() {
-        return Settings.Secure.getInt(mContext.getContentResolver(),
-                SETTINGS_SECURE_USER_SETUP_COMPLETE, 0) == 1;
-    }
-
-    /** Does the aspect ratio of the bitmap with insets removed match the bounds. */
-    private boolean aspectRatiosMatch(Bitmap bitmap, Insets bitmapInsets, Rect screenBounds) {
-        int insettedWidth = bitmap.getWidth() - bitmapInsets.left - bitmapInsets.right;
-        int insettedHeight = bitmap.getHeight() - bitmapInsets.top - bitmapInsets.bottom;
-
-        if (insettedHeight == 0 || insettedWidth == 0 || bitmap.getWidth() == 0
-                || bitmap.getHeight() == 0) {
-            Log.e(TAG, String.format(
-                    "Provided bitmap and insets create degenerate region: %dx%d %s",
-                    bitmap.getWidth(), bitmap.getHeight(), bitmapInsets));
-            return false;
-        }
-
-        float insettedBitmapAspect = ((float) insettedWidth) / insettedHeight;
-        float boundsAspect = ((float) screenBounds.width()) / screenBounds.height();
-
-        boolean matchWithinTolerance = Math.abs(insettedBitmapAspect - boundsAspect) < 0.1f;
-        if (!matchWithinTolerance) {
-            Log.d(TAG, String.format("aspectRatiosMatch: don't match bitmap: %f, bounds: %f",
-                    insettedBitmapAspect, boundsAspect));
-        }
-
-        return matchWithinTolerance;
-    }
-
-    /**
-     * Create a drawable using the size of the bitmap and insets as the fractional inset parameters.
-     */
-    private Drawable createScreenDrawable(Bitmap bitmap, Insets insets) {
-        int insettedWidth = bitmap.getWidth() - insets.left - insets.right;
-        int insettedHeight = bitmap.getHeight() - insets.top - insets.bottom;
-
-        BitmapDrawable bitmapDrawable = new BitmapDrawable(mContext.getResources(), bitmap);
-        if (insettedHeight == 0 || insettedWidth == 0 || bitmap.getWidth() == 0
-                || bitmap.getHeight() == 0) {
-            Log.e(TAG, String.format(
-                    "Can't create insetted drawable, using 0 insets "
-                            + "bitmap and insets create degenerate region: %dx%d %s",
-                    bitmap.getWidth(), bitmap.getHeight(), insets));
-            return bitmapDrawable;
-        }
-
-        InsetDrawable insetDrawable = new InsetDrawable(bitmapDrawable,
-                -1f * insets.left / insettedWidth,
-                -1f * insets.top / insettedHeight,
-                -1f * insets.right / insettedWidth,
-                -1f * insets.bottom / insettedHeight);
-
-        if (insets.left < 0 || insets.top < 0 || insets.right < 0 || insets.bottom < 0) {
-            // Are any of the insets negative, meaning the bitmap is smaller than the bounds so need
-            // to fill in the background of the drawable.
-            return new LayerDrawable(new Drawable[]{
-                    new ColorDrawable(Color.BLACK), insetDrawable});
-        } else {
-            return insetDrawable;
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index df1d789..c0061ad 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -82,8 +82,8 @@
 
     private final Context mContext;
     private final ScreenshotSmartActions mScreenshotSmartActions;
-    private final GlobalScreenshot.SaveImageInBackgroundData mParams;
-    private final GlobalScreenshot.SavedImageData mImageData;
+    private final ScreenshotController.SaveImageInBackgroundData mParams;
+    private final ScreenshotController.SavedImageData mImageData;
     private final String mImageFileName;
     private final long mImageTime;
     private final ScreenshotNotificationSmartActionsProvider mSmartActionsProvider;
@@ -92,10 +92,10 @@
     private final Random mRandom = new Random();
 
     SaveImageInBackgroundTask(Context context, ScreenshotSmartActions screenshotSmartActions,
-            GlobalScreenshot.SaveImageInBackgroundData data) {
+            ScreenshotController.SaveImageInBackgroundData data) {
         mContext = context;
         mScreenshotSmartActions = screenshotSmartActions;
-        mImageData = new GlobalScreenshot.SavedImageData();
+        mImageData = new ScreenshotController.SavedImageData();
 
         // Prepare all the output metadata
         mParams = data;
@@ -217,13 +217,11 @@
             mParams.mActionsReadyListener.onActionsReady(mImageData);
             mParams.finisher.accept(mImageData.uri);
             mParams.image = null;
-            mParams.errorMsgResId = 0;
         } catch (Exception e) {
             // IOException/UnsupportedOperationException may be thrown if external storage is
             // not mounted
             Slog.e(TAG, "unable to save screenshot", e);
             mParams.clearImage();
-            mParams.errorMsgResId = R.string.screenshot_failed_to_save_text;
             mImageData.reset();
             mParams.mActionsReadyListener.onActionsReady(mImageData);
             mParams.finisher.accept(null);
@@ -236,7 +234,7 @@
      * Update the listener run when the saving task completes. Used to avoid showing UI for the
      * first screenshot when a second one is taken.
      */
-    void setActionsReadyListener(GlobalScreenshot.ActionsReadyListener listener) {
+    void setActionsReadyListener(ScreenshotController.ActionsReadyListener listener) {
         mParams.mActionsReadyListener = listener;
     }
 
@@ -289,10 +287,10 @@
         // Create a share action for the notification
         PendingIntent shareAction = PendingIntent.getBroadcastAsUser(context, requestCode,
                 new Intent(context, ActionProxyReceiver.class)
-                        .putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, pendingIntent)
-                        .putExtra(GlobalScreenshot.EXTRA_DISALLOW_ENTER_PIP, true)
-                        .putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId)
-                        .putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED,
+                        .putExtra(ScreenshotController.EXTRA_ACTION_INTENT, pendingIntent)
+                        .putExtra(ScreenshotController.EXTRA_DISALLOW_ENTER_PIP, true)
+                        .putExtra(ScreenshotController.EXTRA_ID, mScreenshotId)
+                        .putExtra(ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED,
                                 mSmartActionsEnabled)
                         .setAction(Intent.ACTION_SEND)
                         .addFlags(Intent.FLAG_RECEIVER_FOREGROUND),
@@ -334,9 +332,9 @@
         // Create a edit action
         PendingIntent editAction = PendingIntent.getBroadcastAsUser(context, requestCode,
                 new Intent(context, ActionProxyReceiver.class)
-                        .putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, pendingIntent)
-                        .putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId)
-                        .putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED,
+                        .putExtra(ScreenshotController.EXTRA_ACTION_INTENT, pendingIntent)
+                        .putExtra(ScreenshotController.EXTRA_ID, mScreenshotId)
+                        .putExtra(ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED,
                                 mSmartActionsEnabled)
                         .setAction(Intent.ACTION_EDIT)
                         .addFlags(Intent.FLAG_RECEIVER_FOREGROUND),
@@ -357,9 +355,9 @@
         // Create a delete action for the notification
         PendingIntent deleteAction = PendingIntent.getBroadcast(context, requestCode,
                 new Intent(context, DeleteScreenshotReceiver.class)
-                        .putExtra(GlobalScreenshot.SCREENSHOT_URI_ID, uri.toString())
-                        .putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId)
-                        .putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED,
+                        .putExtra(ScreenshotController.SCREENSHOT_URI_ID, uri.toString())
+                        .putExtra(ScreenshotController.EXTRA_ID, mScreenshotId)
+                        .putExtra(ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED,
                                 mSmartActionsEnabled)
                         .addFlags(Intent.FLAG_RECEIVER_FOREGROUND),
                 PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
@@ -397,7 +395,7 @@
                     ScreenshotNotificationSmartActionsProvider.ACTION_TYPE,
                     ScreenshotNotificationSmartActionsProvider.DEFAULT_ACTION_TYPE);
             Intent intent = new Intent(context, SmartActionsReceiver.class)
-                    .putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, action.actionIntent)
+                    .putExtra(ScreenshotController.EXTRA_ACTION_INTENT, action.actionIntent)
                     .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
             addIntentExtras(mScreenshotId, intent, actionType, mSmartActionsEnabled);
             PendingIntent broadcastIntent = PendingIntent.getBroadcast(context,
@@ -413,9 +411,9 @@
     private static void addIntentExtras(String screenshotId, Intent intent, String actionType,
             boolean smartActionsEnabled) {
         intent
-                .putExtra(GlobalScreenshot.EXTRA_ACTION_TYPE, actionType)
-                .putExtra(GlobalScreenshot.EXTRA_ID, screenshotId)
-                .putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED, smartActionsEnabled);
+                .putExtra(ScreenshotController.EXTRA_ACTION_TYPE, actionType)
+                .putExtra(ScreenshotController.EXTRA_ID, screenshotId)
+                .putExtra(ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED, smartActionsEnabled);
     }
 
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java
index a488702..3370946 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.screenshot;
 
-import android.annotation.ColorInt;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.graphics.drawable.Icon;
@@ -35,9 +34,9 @@
 
     private static final String TAG = "ScreenshotActionChip";
 
-    private ImageView mIcon;
-    private TextView mText;
-    private @ColorInt int mIconColor;
+    private ImageView mIconView;
+    private TextView mTextView;
+    private boolean mIsPending = false;
 
     public ScreenshotActionChip(Context context) {
         this(context, null);
@@ -54,25 +53,29 @@
     public ScreenshotActionChip(
             Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
-
-        mIconColor = context.getColor(R.color.global_screenshot_button_icon);
     }
 
     @Override
     protected void onFinishInflate() {
-        mIcon = findViewById(R.id.screenshot_action_chip_icon);
-        mText = findViewById(R.id.screenshot_action_chip_text);
+        mIconView = findViewById(R.id.screenshot_action_chip_icon);
+        mTextView = findViewById(R.id.screenshot_action_chip_text);
+    }
+
+    @Override
+    public void setPressed(boolean pressed) {
+        // override pressed state to true if there is an action pending
+        super.setPressed(mIsPending || pressed);
     }
 
     void setIcon(Icon icon, boolean tint) {
-        mIcon.setImageIcon(icon);
+        mIconView.setImageIcon(icon);
         if (!tint) {
-            mIcon.setImageTintList(null);
+            mIconView.setImageTintList(null);
         }
     }
 
     void setText(CharSequence text) {
-        mText.setText(text);
+        mTextView.setText(text);
     }
 
     void setPendingIntent(PendingIntent intent, Runnable finisher) {
@@ -85,4 +88,9 @@
             }
         });
     }
+
+    void setIsPending(boolean isPending) {
+        mIsPending = isPending;
+        setPressed(mIsPending);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
new file mode 100644
index 0000000..5d8f70c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -0,0 +1,665 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.screenshot;
+
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static java.util.Objects.requireNonNull;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.app.Notification;
+import android.app.WindowContext;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Bitmap;
+import android.graphics.Insets;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
+import android.media.MediaActionSound;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.provider.Settings;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.Display;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.SurfaceControl;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.widget.Toast;
+
+import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.R;
+
+import java.util.List;
+import java.util.function.Consumer;
+
+import javax.inject.Inject;
+
+/**
+ * Controls the state and flow for screenshots.
+ */
+public class ScreenshotController {
+    /**
+     * POD used in the AsyncTask which saves an image in the background.
+     */
+    static class SaveImageInBackgroundData {
+        public Bitmap image;
+        public Consumer<Uri> finisher;
+        public ScreenshotController.ActionsReadyListener mActionsReadyListener;
+
+        void clearImage() {
+            image = null;
+        }
+    }
+
+    /**
+     * Structure returned by the SaveImageInBackgroundTask
+     */
+    static class SavedImageData {
+        public Uri uri;
+        public Notification.Action shareAction;
+        public Notification.Action editAction;
+        public Notification.Action deleteAction;
+        public List<Notification.Action> smartActions;
+
+        /**
+         * Used to reset the return data on error
+         */
+        public void reset() {
+            uri = null;
+            shareAction = null;
+            editAction = null;
+            deleteAction = null;
+            smartActions = null;
+        }
+    }
+
+    abstract static class ActionsReadyListener {
+        abstract void onActionsReady(ScreenshotController.SavedImageData imageData);
+    }
+
+    private static final String TAG = "GlobalScreenshotController";
+
+    // These strings are used for communicating the action invoked to
+    // ScreenshotNotificationSmartActionsProvider.
+    static final String EXTRA_ACTION_TYPE = "android:screenshot_action_type";
+    static final String EXTRA_ID = "android:screenshot_id";
+    static final String ACTION_TYPE_DELETE = "Delete";
+    static final String ACTION_TYPE_SHARE = "Share";
+    static final String ACTION_TYPE_EDIT = "Edit";
+    static final String EXTRA_SMART_ACTIONS_ENABLED = "android:smart_actions_enabled";
+    static final String EXTRA_ACTION_INTENT = "android:screenshot_action_intent";
+
+    static final String SCREENSHOT_URI_ID = "android:screenshot_uri_id";
+    static final String EXTRA_CANCEL_NOTIFICATION = "android:screenshot_cancel_notification";
+    static final String EXTRA_DISALLOW_ENTER_PIP = "android:screenshot_disallow_enter_pip";
+
+
+    private static final int MESSAGE_CORNER_TIMEOUT = 2;
+    private static final int SCREENSHOT_CORNER_DEFAULT_TIMEOUT_MILLIS = 6000;
+
+    // From WizardManagerHelper.java
+    private static final String SETTINGS_SECURE_USER_SETUP_COMPLETE = "user_setup_complete";
+
+    private final Context mContext;
+    private final ScreenshotNotificationsController mNotificationsController;
+    private final ScreenshotSmartActions mScreenshotSmartActions;
+    private final UiEventLogger mUiEventLogger;
+
+    private final WindowManager mWindowManager;
+    private final WindowManager.LayoutParams mWindowLayoutParams;
+    private final Display mDisplay;
+    private final DisplayMetrics mDisplayMetrics;
+    private final AccessibilityManager mAccessibilityManager;
+    private final MediaActionSound mCameraSound;
+
+    private ScreenshotView mScreenshotView;
+    private Bitmap mScreenBitmap;
+    private SaveImageInBackgroundTask mSaveInBgTask;
+
+    private Animator mScreenshotAnimation;
+    private Animator mDismissAnimation;
+
+    private Runnable mOnCompleteRunnable;
+    private boolean mInDarkMode;
+    private boolean mDirectionLTR;
+    private boolean mOrientationPortrait;
+
+    private final Handler mScreenshotHandler = new Handler(Looper.getMainLooper()) {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MESSAGE_CORNER_TIMEOUT:
+                    mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_INTERACTION_TIMEOUT);
+                    ScreenshotController.this.dismissScreenshot(false);
+                    mOnCompleteRunnable.run();
+                    break;
+                default:
+                    break;
+            }
+        }
+    };
+
+    @Inject
+    ScreenshotController(Context context, ScreenshotSmartActions screenshotSmartActions,
+            ScreenshotNotificationsController screenshotNotificationsController,
+            UiEventLogger uiEventLogger) {
+        mScreenshotSmartActions = screenshotSmartActions;
+        mNotificationsController = screenshotNotificationsController;
+        mUiEventLogger = uiEventLogger;
+
+        // Create a visual (Window) context
+        // After this, our windowToken is available from mContext.getActivityToken()
+        final DisplayManager dm = requireNonNull(context.getSystemService(DisplayManager.class));
+        mDisplay = dm.getDisplay(DEFAULT_DISPLAY);
+        Context displayContext = context.createDisplayContext(mDisplay);
+        mContext = new WindowContext(displayContext, WindowManager.LayoutParams.TYPE_SCREENSHOT,
+                null);
+        mWindowManager = mContext.getSystemService(WindowManager.class);
+
+        mAccessibilityManager = AccessibilityManager.getInstance(mContext);
+
+        reloadAssets();
+        Configuration config = mContext.getResources().getConfiguration();
+        mInDarkMode = config.isNightModeActive();
+        mDirectionLTR = config.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR;
+        mOrientationPortrait = config.orientation == ORIENTATION_PORTRAIT;
+
+        // Setup the window that we are going to use
+        mWindowLayoutParams = new WindowManager.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 0, 0,
+                WindowManager.LayoutParams.TYPE_SCREENSHOT,
+                WindowManager.LayoutParams.FLAG_FULLSCREEN
+                        | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+                        | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+                        | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
+                PixelFormat.TRANSLUCENT);
+        mWindowLayoutParams.setTitle("ScreenshotAnimation");
+        mWindowLayoutParams.layoutInDisplayCutoutMode =
+                WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+        mWindowLayoutParams.setFitInsetsTypes(0 /* types */);
+
+        mDisplayMetrics = new DisplayMetrics();
+        mDisplay.getRealMetrics(mDisplayMetrics);
+
+        // Setup the Camera shutter sound
+        mCameraSound = new MediaActionSound();
+        mCameraSound.load(MediaActionSound.SHUTTER_CLICK);
+    }
+
+    void takeScreenshotFullscreen(Consumer<Uri> finisher, Runnable onComplete) {
+        mOnCompleteRunnable = onComplete;
+
+        mDisplay.getRealMetrics(mDisplayMetrics);
+        takeScreenshotInternal(
+                finisher,
+                new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
+    }
+
+    void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds,
+            Insets visibleInsets, int taskId, int userId, ComponentName topComponent,
+            Consumer<Uri> finisher, Runnable onComplete) {
+        // TODO: use task Id, userId, topComponent for smart handler
+        mOnCompleteRunnable = onComplete;
+
+        if (screenshot == null) {
+            Log.e(TAG, "Got null bitmap from screenshot message");
+            mNotificationsController.notifyScreenshotError(
+                    R.string.screenshot_failed_to_capture_text);
+            finisher.accept(null);
+            mOnCompleteRunnable.run();
+            return;
+        }
+
+        if (aspectRatiosMatch(screenshot, visibleInsets, screenshotScreenBounds)) {
+            saveScreenshot(screenshot, finisher, screenshotScreenBounds, visibleInsets, false);
+        } else {
+            saveScreenshot(screenshot, finisher,
+                    new Rect(0, 0, screenshot.getWidth(), screenshot.getHeight()), Insets.NONE,
+                    true);
+        }
+    }
+
+    /**
+     * Displays a screenshot selector
+     */
+    @SuppressLint("ClickableViewAccessibility")
+    void takeScreenshotPartial(final Consumer<Uri> finisher, Runnable onComplete) {
+        dismissScreenshot(true);
+        mOnCompleteRunnable = onComplete;
+
+        mWindowManager.addView(mScreenshotView, mWindowLayoutParams);
+
+        mScreenshotView.takePartialScreenshot(
+                rect -> takeScreenshotInternal(finisher, rect));
+    }
+
+    boolean isDismissing() {
+        return (mDismissAnimation != null && mDismissAnimation.isRunning());
+    }
+
+    /**
+     * Clears current screenshot
+     */
+    void dismissScreenshot(boolean immediate) {
+        Log.v(TAG, "clearing screenshot");
+        mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
+        mScreenshotView.getViewTreeObserver().removeOnComputeInternalInsetsListener(
+                mScreenshotView);
+        if (!immediate) {
+            mDismissAnimation = mScreenshotView.createScreenshotDismissAnimation();
+            mDismissAnimation.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    super.onAnimationEnd(animation);
+                    clearScreenshot();
+                }
+            });
+            mDismissAnimation.start();
+        } else {
+            clearScreenshot();
+        }
+    }
+
+    private void onConfigChanged(Configuration newConfig) {
+        boolean needsUpdate = false;
+        // dark mode
+        if (newConfig.isNightModeActive()) {
+            // Night mode is active, we're using dark theme
+            if (!mInDarkMode) {
+                mInDarkMode = true;
+                needsUpdate = true;
+            }
+        } else {
+            // Night mode is not active, we're using the light theme
+            if (mInDarkMode) {
+                mInDarkMode = false;
+                needsUpdate = true;
+            }
+        }
+
+        // RTL configuration
+        switch (newConfig.getLayoutDirection()) {
+            case View.LAYOUT_DIRECTION_LTR:
+                if (!mDirectionLTR) {
+                    mDirectionLTR = true;
+                    needsUpdate = true;
+                }
+                break;
+            case View.LAYOUT_DIRECTION_RTL:
+                if (mDirectionLTR) {
+                    mDirectionLTR = false;
+                    needsUpdate = true;
+                }
+                break;
+        }
+
+        // portrait/landscape orientation
+        switch (newConfig.orientation) {
+            case ORIENTATION_PORTRAIT:
+                if (!mOrientationPortrait) {
+                    mOrientationPortrait = true;
+                    needsUpdate = true;
+                }
+                break;
+            case ORIENTATION_LANDSCAPE:
+                if (mOrientationPortrait) {
+                    mOrientationPortrait = false;
+                    needsUpdate = true;
+                }
+                break;
+        }
+
+        if (needsUpdate) {
+            reloadAssets();
+        }
+    }
+
+    /**
+     * Update assets (called when the dark theme status changes). We only need to update the dismiss
+     * button and the actions container background, since the buttons are re-inflated on demand.
+     */
+    private void reloadAssets() {
+        boolean wasAttached = mScreenshotView != null && mScreenshotView.isAttachedToWindow();
+        if (wasAttached) {
+            mWindowManager.removeView(mScreenshotView);
+        }
+
+        // Inflate the screenshot layout
+        mScreenshotView = (ScreenshotView)
+                LayoutInflater.from(mContext).inflate(R.layout.global_screenshot, null);
+
+        // TODO(159460485): Remove this when focus is handled properly in the system
+        mScreenshotView.setOnTouchListener((v, event) -> {
+            if (event.getActionMasked() == MotionEvent.ACTION_OUTSIDE) {
+                // Once the user touches outside, stop listening for input
+                setWindowFocusable(false);
+            }
+            return false;
+        });
+
+        mScreenshotView.setOnKeyListener((v, keyCode, event) -> {
+            if (keyCode == KeyEvent.KEYCODE_BACK) {
+                dismissScreenshot(false);
+                return true;
+            }
+            return false;
+        });
+
+        if (wasAttached) {
+            mWindowManager.addView(mScreenshotView, mWindowLayoutParams);
+        }
+    }
+
+    /**
+     * Takes a screenshot of the current display and shows an animation.
+     */
+    private void takeScreenshotInternal(Consumer<Uri> finisher, Rect crop) {
+        // copy the input Rect, since SurfaceControl.screenshot can mutate it
+        Rect screenRect = new Rect(crop);
+        int width = crop.width();
+        int height = crop.height();
+        final IBinder displayToken = SurfaceControl.getInternalDisplayToken();
+        final SurfaceControl.DisplayCaptureArgs captureArgs =
+                new SurfaceControl.DisplayCaptureArgs.Builder(displayToken)
+                        .setSourceCrop(crop)
+                        .setSize(width, height)
+                        .build();
+        final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
+                SurfaceControl.captureDisplay(captureArgs);
+        Bitmap screenshot = screenshotBuffer == null ? null : screenshotBuffer.asBitmap();
+
+        if (screenshot == null) {
+            Log.e(TAG, "Screenshot bitmap was null");
+            mNotificationsController.notifyScreenshotError(
+                    R.string.screenshot_failed_to_capture_text);
+            finisher.accept(null);
+            mOnCompleteRunnable.run();
+            return;
+        }
+
+        saveScreenshot(screenshot, finisher, screenRect, Insets.NONE, true);
+    }
+
+    private void saveScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect,
+            Insets screenInsets, boolean showFlash) {
+        if (mAccessibilityManager.isEnabled()) {
+            AccessibilityEvent event =
+                    new AccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+            event.setContentDescription(
+                    mContext.getResources().getString(R.string.screenshot_saving_title));
+            mAccessibilityManager.sendAccessibilityEvent(event);
+        }
+
+        if (mScreenshotView.isAttachedToWindow()) {
+            // if we didn't already dismiss for another reason
+            if (mDismissAnimation == null || !mDismissAnimation.isRunning()) {
+                mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_REENTERED);
+            }
+            dismissScreenshot(true);
+        }
+
+        mScreenBitmap = screenshot;
+
+        if (!isUserSetupComplete()) {
+            // User setup isn't complete, so we don't want to show any UI beyond a toast, as editing
+            // and sharing shouldn't be exposed to the user.
+            saveScreenshotAndToast(finisher);
+            return;
+        }
+
+        // Optimizations
+        mScreenBitmap.setHasAlpha(false);
+        mScreenBitmap.prepareToDraw();
+
+        onConfigChanged(mContext.getResources().getConfiguration());
+
+        if (mDismissAnimation != null && mDismissAnimation.isRunning()) {
+            mDismissAnimation.cancel();
+        }
+
+        // The window is focusable by default
+        setWindowFocusable(true);
+
+        // Start the post-screenshot animation
+        startAnimation(finisher, screenRect, screenInsets, showFlash);
+    }
+
+    /**
+     * Save the bitmap but don't show the normal screenshot UI.. just a toast (or notification on
+     * failure).
+     */
+    private void saveScreenshotAndToast(Consumer<Uri> finisher) {
+        // Play the shutter sound to notify that we've taken a screenshot
+        mScreenshotHandler.post(() -> {
+            mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
+        });
+
+        saveScreenshotInWorkerThread(finisher,
+                new ScreenshotController.ActionsReadyListener() {
+                    @Override
+                    void onActionsReady(ScreenshotController.SavedImageData imageData) {
+                        finisher.accept(imageData.uri);
+                        if (imageData.uri == null) {
+                            mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED);
+                            mNotificationsController.notifyScreenshotError(
+                                    R.string.screenshot_failed_to_save_text);
+                        } else {
+                            mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED);
+
+                            mScreenshotHandler.post(() -> {
+                                Toast.makeText(mContext, R.string.screenshot_saved_title,
+                                        Toast.LENGTH_SHORT).show();
+                            });
+                        }
+                    }
+                });
+    }
+
+    /**
+     * Starts the animation after taking the screenshot
+     */
+    private void startAnimation(final Consumer<Uri> finisher, Rect screenRect, Insets screenInsets,
+            boolean showFlash) {
+        mScreenshotHandler.post(() -> {
+            if (!mScreenshotView.isAttachedToWindow()) {
+                mWindowManager.addView(mScreenshotView, mWindowLayoutParams);
+            }
+
+            mScreenshotView.prepareForAnimation(mScreenBitmap, screenRect, screenInsets);
+
+            mScreenshotHandler.post(() -> {
+                mScreenshotView.getViewTreeObserver().addOnComputeInternalInsetsListener(
+                        mScreenshotView);
+
+                mScreenshotAnimation =
+                        mScreenshotView.createScreenshotDropInAnimation(screenRect, showFlash,
+                                this::onElementTapped);
+
+                saveScreenshotInWorkerThread(finisher,
+                        new ScreenshotController.ActionsReadyListener() {
+                            @Override
+                            void onActionsReady(
+                                    ScreenshotController.SavedImageData imageData) {
+                                showUiOnActionsReady(imageData);
+                            }
+                        });
+
+                // Play the shutter sound to notify that we've taken a screenshot
+                mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
+
+                mScreenshotAnimation.start();
+            });
+        });
+    }
+
+    /**
+     * Creates a new worker thread and saves the screenshot to the media store.
+     */
+    private void saveScreenshotInWorkerThread(
+            Consumer<Uri> finisher,
+            @Nullable ScreenshotController.ActionsReadyListener actionsReadyListener) {
+        ScreenshotController.SaveImageInBackgroundData
+                data = new ScreenshotController.SaveImageInBackgroundData();
+        data.image = mScreenBitmap;
+        data.finisher = finisher;
+        data.mActionsReadyListener = actionsReadyListener;
+
+        if (mSaveInBgTask != null) {
+            // just log success/failure for the pre-existing screenshot
+            mSaveInBgTask.setActionsReadyListener(
+                    new ScreenshotController.ActionsReadyListener() {
+                        @Override
+                        void onActionsReady(ScreenshotController.SavedImageData imageData) {
+                            logSuccessOnActionsReady(imageData);
+                        }
+                    });
+        }
+
+        mSaveInBgTask = new SaveImageInBackgroundTask(mContext, mScreenshotSmartActions, data);
+        mSaveInBgTask.execute();
+    }
+
+    /**
+     * Sets up the action shade and its entrance animation, once we get the screenshot URI.
+     */
+    private void showUiOnActionsReady(ScreenshotController.SavedImageData imageData) {
+        logSuccessOnActionsReady(imageData);
+
+        AccessibilityManager accessibilityManager = (AccessibilityManager)
+                mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
+        long timeoutMs = accessibilityManager.getRecommendedTimeoutMillis(
+                SCREENSHOT_CORNER_DEFAULT_TIMEOUT_MILLIS,
+                AccessibilityManager.FLAG_CONTENT_CONTROLS);
+
+        mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
+        mScreenshotHandler.sendMessageDelayed(
+                mScreenshotHandler.obtainMessage(MESSAGE_CORNER_TIMEOUT),
+                timeoutMs);
+
+        if (imageData.uri != null) {
+            mScreenshotHandler.post(() -> {
+                if (mScreenshotAnimation != null && mScreenshotAnimation.isRunning()) {
+                    mScreenshotAnimation.addListener(new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationEnd(Animator animation) {
+                            super.onAnimationEnd(animation);
+                            mScreenshotView.setChipIntents(
+                                    imageData, event -> onElementTapped(event));
+                        }
+                    });
+                } else {
+                    mScreenshotView.setChipIntents(
+                            imageData, this::onElementTapped);
+                }
+            });
+        }
+    }
+
+    private void onElementTapped(ScreenshotEvent event) {
+        mUiEventLogger.log(event);
+        dismissScreenshot(false);
+        mOnCompleteRunnable.run();
+    }
+
+    /**
+     * Logs success/failure of the screenshot saving task, and shows an error if it failed.
+     */
+    private void logSuccessOnActionsReady(ScreenshotController.SavedImageData imageData) {
+        if (imageData.uri == null) {
+            mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED);
+            mNotificationsController.notifyScreenshotError(
+                    R.string.screenshot_failed_to_save_text);
+        } else {
+            mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED);
+        }
+    }
+
+    private void clearScreenshot() {
+        if (mScreenshotView.isAttachedToWindow()) {
+            mWindowManager.removeView(mScreenshotView);
+        }
+
+        mScreenshotView.reset();
+    }
+
+    private boolean isUserSetupComplete() {
+        return Settings.Secure.getInt(mContext.getContentResolver(),
+                SETTINGS_SECURE_USER_SETUP_COMPLETE, 0) == 1;
+    }
+
+
+    /**
+     * Updates the window focusability.  If the window is already showing, then it updates the
+     * window immediately, otherwise the layout params will be applied when the window is next
+     * shown.
+     */
+    private void setWindowFocusable(boolean focusable) {
+        if (focusable) {
+            mWindowLayoutParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+        } else {
+            mWindowLayoutParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+        }
+        if (mScreenshotView.isAttachedToWindow()) {
+            mWindowManager.updateViewLayout(mScreenshotView, mWindowLayoutParams);
+        }
+    }
+
+    /** Does the aspect ratio of the bitmap with insets removed match the bounds. */
+    private static boolean aspectRatiosMatch(Bitmap bitmap, Insets bitmapInsets,
+            Rect screenBounds) {
+        int insettedWidth = bitmap.getWidth() - bitmapInsets.left - bitmapInsets.right;
+        int insettedHeight = bitmap.getHeight() - bitmapInsets.top - bitmapInsets.bottom;
+
+        if (insettedHeight == 0 || insettedWidth == 0 || bitmap.getWidth() == 0
+                || bitmap.getHeight() == 0) {
+            Log.e(TAG, String.format(
+                    "Provided bitmap and insets create degenerate region: %dx%d %s",
+                    bitmap.getWidth(), bitmap.getHeight(), bitmapInsets));
+            return false;
+        }
+
+        float insettedBitmapAspect = ((float) insettedWidth) / insettedHeight;
+        float boundsAspect = ((float) screenBounds.width()) / screenBounds.height();
+
+        boolean matchWithinTolerance = Math.abs(insettedBitmapAspect - boundsAspect) < 0.1f;
+        if (!matchWithinTolerance) {
+            Log.d(TAG, String.format("aspectRatiosMatch: don't match bitmap: %f, bounds: %f",
+                    insettedBitmapAspect, boundsAspect));
+        }
+
+        return matchWithinTolerance;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
index 6d1299b..db5a494 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
@@ -25,13 +25,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.ColorMatrix;
-import android.graphics.ColorMatrixColorFilter;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Picture;
 import android.os.UserHandle;
 import android.util.DisplayMetrics;
 import android.view.WindowManager;
@@ -52,179 +45,16 @@
     private final Context mContext;
     private final Resources mResources;
     private final NotificationManager mNotificationManager;
-    private final Notification.BigPictureStyle mNotificationStyle;
-
-    private int mIconSize;
-    private int mPreviewWidth, mPreviewHeight;
-    private Notification.Builder mNotificationBuilder, mPublicNotificationBuilder;
 
     @Inject
     ScreenshotNotificationsController(Context context, WindowManager windowManager) {
         mContext = context;
         mResources = context.getResources();
-
         mNotificationManager =
                 (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
 
-        mIconSize = mResources.getDimensionPixelSize(
-                android.R.dimen.notification_large_icon_height);
-
         DisplayMetrics displayMetrics = new DisplayMetrics();
         windowManager.getDefaultDisplay().getRealMetrics(displayMetrics);
-
-
-        // determine the optimal preview size
-        int panelWidth = 0;
-        try {
-            panelWidth = mResources.getDimensionPixelSize(R.dimen.notification_panel_width);
-        } catch (Resources.NotFoundException e) {
-        }
-        if (panelWidth <= 0) {
-            // includes notification_panel_width==match_parent (-1)
-            panelWidth = displayMetrics.widthPixels;
-        }
-        mPreviewWidth = panelWidth;
-        mPreviewHeight = mResources.getDimensionPixelSize(R.dimen.notification_max_height);
-
-        // Setup the notification
-        mNotificationStyle = new Notification.BigPictureStyle();
-    }
-
-    /**
-     * Resets the notification builders.
-     */
-    public void reset() {
-        // The public notification will show similar info but with the actual screenshot omitted
-        mPublicNotificationBuilder =
-                new Notification.Builder(mContext, NotificationChannels.SCREENSHOTS_HEADSUP);
-        mNotificationBuilder =
-                new Notification.Builder(mContext, NotificationChannels.SCREENSHOTS_HEADSUP);
-    }
-
-    /**
-     * Sets the current screenshot bitmap.
-     *
-     * @param image the bitmap of the current screenshot (used for preview)
-     */
-    public void setImage(Bitmap image) {
-        // Create the large notification icon
-        int imageWidth = image.getWidth();
-        int imageHeight = image.getHeight();
-
-        Paint paint = new Paint();
-        ColorMatrix desat = new ColorMatrix();
-        desat.setSaturation(0.25f);
-        paint.setColorFilter(new ColorMatrixColorFilter(desat));
-        Matrix matrix = new Matrix();
-        int overlayColor = 0x40FFFFFF;
-
-        matrix.setTranslate((mPreviewWidth - imageWidth) / 2f, (mPreviewHeight - imageHeight) / 2f);
-
-        Bitmap picture = generateAdjustedHwBitmap(
-                image, mPreviewWidth, mPreviewHeight, matrix, paint, overlayColor);
-
-        mNotificationStyle.bigPicture(picture.asShared());
-
-        // Note, we can't use the preview for the small icon, since it is non-square
-        float scale = (float) mIconSize / Math.min(imageWidth, imageHeight);
-        matrix.setScale(scale, scale);
-        matrix.postTranslate(
-                (mIconSize - (scale * imageWidth)) / 2,
-                (mIconSize - (scale * imageHeight)) / 2);
-        Bitmap icon =
-                generateAdjustedHwBitmap(image, mIconSize, mIconSize, matrix, paint, overlayColor);
-
-        /**
-         * NOTE: The following code prepares the notification builder for updating the
-         * notification after the screenshot has been written to disk.
-         */
-
-        // On the tablet, the large icon makes the notification appear as if it is clickable
-        // (and on small devices, the large icon is not shown) so defer showing the large icon
-        // until we compose the final post-save notification below.
-        mNotificationBuilder.setLargeIcon(icon.asShared());
-        // But we still don't set it for the expanded view, allowing the smallIcon to show here.
-        mNotificationStyle.bigLargeIcon((Bitmap) null);
-    }
-
-    /**
-     * Shows a notification to inform the user that a screenshot is currently being saved.
-     */
-    public void showSavingScreenshotNotification() {
-        final long now = System.currentTimeMillis();
-
-        mPublicNotificationBuilder
-                .setContentTitle(mResources.getString(R.string.screenshot_saving_title))
-                .setSmallIcon(R.drawable.stat_notify_image)
-                .setCategory(Notification.CATEGORY_PROGRESS)
-                .setWhen(now)
-                .setShowWhen(true)
-                .setColor(mResources.getColor(
-                        com.android.internal.R.color.system_notification_accent_color));
-        SystemUI.overrideNotificationAppName(mContext, mPublicNotificationBuilder, true);
-
-        mNotificationBuilder
-                .setContentTitle(mResources.getString(R.string.screenshot_saving_title))
-                .setSmallIcon(R.drawable.stat_notify_image)
-                .setWhen(now)
-                .setShowWhen(true)
-                .setColor(mResources.getColor(
-                        com.android.internal.R.color.system_notification_accent_color))
-                .setStyle(mNotificationStyle)
-                .setPublicVersion(mPublicNotificationBuilder.build());
-        mNotificationBuilder.setFlag(Notification.FLAG_NO_CLEAR, true);
-        SystemUI.overrideNotificationAppName(mContext, mNotificationBuilder, true);
-
-        mNotificationManager.notify(SystemMessageProto.SystemMessage.NOTE_GLOBAL_SCREENSHOT,
-                mNotificationBuilder.build());
-    }
-
-    /**
-     * Shows a notification with the saved screenshot and actions that can be taken with it.
-     *
-     * @param actionData SavedImageData struct with image URI and actions
-     */
-    public void showScreenshotActionsNotification(
-            GlobalScreenshot.SavedImageData actionData) {
-        mNotificationBuilder.addAction(actionData.shareAction);
-        mNotificationBuilder.addAction(actionData.editAction);
-        mNotificationBuilder.addAction(actionData.deleteAction);
-        for (Notification.Action smartAction : actionData.smartActions) {
-            mNotificationBuilder.addAction(smartAction);
-        }
-
-        // Create the intent to show the screenshot in gallery
-        Intent launchIntent = new Intent(Intent.ACTION_VIEW);
-        launchIntent.setDataAndType(actionData.uri, "image/png");
-        launchIntent.setFlags(
-                Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION);
-
-        final long now = System.currentTimeMillis();
-
-        // Update the text and the icon for the existing notification
-        mPublicNotificationBuilder
-                .setContentTitle(mResources.getString(R.string.screenshot_saved_title))
-                .setContentText(mResources.getString(R.string.screenshot_saved_text))
-                .setContentIntent(PendingIntent
-                        .getActivity(mContext, 0, launchIntent, PendingIntent.FLAG_IMMUTABLE))
-                .setWhen(now)
-                .setAutoCancel(true)
-                .setColor(mContext.getColor(
-                        com.android.internal.R.color.system_notification_accent_color));
-        mNotificationBuilder
-                .setContentTitle(mResources.getString(R.string.screenshot_saved_title))
-                .setContentText(mResources.getString(R.string.screenshot_saved_text))
-                .setContentIntent(PendingIntent
-                        .getActivity(mContext, 0, launchIntent, PendingIntent.FLAG_IMMUTABLE))
-                .setWhen(now)
-                .setAutoCancel(true)
-                .setColor(mContext.getColor(
-                        com.android.internal.R.color.system_notification_accent_color))
-                .setPublicVersion(mPublicNotificationBuilder.build())
-                .setFlag(Notification.FLAG_NO_CLEAR, false);
-
-        mNotificationManager.notify(SystemMessageProto.SystemMessage.NOTE_GLOBAL_SCREENSHOT,
-                mNotificationBuilder.build());
     }
 
     /**
@@ -263,31 +93,4 @@
                 .build();
         mNotificationManager.notify(SystemMessageProto.SystemMessage.NOTE_GLOBAL_SCREENSHOT, n);
     }
-
-    /**
-     * Cancels the current screenshot notification.
-     */
-    public void cancelNotification() {
-        mNotificationManager.cancel(SystemMessageProto.SystemMessage.NOTE_GLOBAL_SCREENSHOT);
-    }
-
-    /**
-     * Generates a new hardware bitmap with specified values, copying the content from the
-     * passed in bitmap.
-     */
-    private Bitmap generateAdjustedHwBitmap(Bitmap bitmap, int width, int height, Matrix matrix,
-            Paint paint, int color) {
-        Picture picture = new Picture();
-        Canvas canvas = picture.beginRecording(width, height);
-        canvas.drawColor(color);
-        canvas.drawBitmap(bitmap, matrix, paint);
-        picture.endRecording();
-        return Bitmap.createBitmap(picture);
-    }
-
-    static void cancelScreenshotNotification(Context context) {
-        final NotificationManager nm =
-                (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
-        nm.cancel(SystemMessageProto.SystemMessage.NOTE_GLOBAL_SCREENSHOT);
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSelectorView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSelectorView.java
index 07a9246..c793b5b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSelectorView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSelectorView.java
@@ -26,8 +26,11 @@
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
 import android.util.AttributeSet;
+import android.view.MotionEvent;
 import android.view.View;
 
+import java.util.function.Consumer;
+
 /**
  * Draws a selection rectangle while taking screenshot
  */
@@ -36,6 +39,8 @@
     private Rect mSelectionRect;
     private final Paint mPaintSelection, mPaintBackground;
 
+    private Consumer<Rect> mOnScreenshotSelected;
+
     public ScreenshotSelectorView(Context context) {
         this(context, null);
     }
@@ -46,14 +51,54 @@
         mPaintBackground.setAlpha(160);
         mPaintSelection = new Paint(Color.TRANSPARENT);
         mPaintSelection.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
+
+        setOnTouchListener((v, event) -> {
+            switch (event.getAction()) {
+                case MotionEvent.ACTION_DOWN:
+                    startSelection((int) event.getX(), (int) event.getY());
+                    return true;
+                case MotionEvent.ACTION_MOVE:
+                    updateSelection((int) event.getX(), (int) event.getY());
+                    return true;
+                case MotionEvent.ACTION_UP:
+                    setVisibility(View.GONE);
+                    final Rect rect = getSelectionRect();
+                    if (mOnScreenshotSelected != null
+                            && rect != null
+                            && rect.width() != 0 && rect.height() != 0) {
+                        mOnScreenshotSelected.accept(rect);
+                    }
+                    stopSelection();
+                    return true;
+            }
+            return false;
+        });
     }
 
-    public void startSelection(int x, int y) {
+    @Override
+    public void draw(Canvas canvas) {
+        canvas.drawRect(mLeft, mTop, mRight, mBottom, mPaintBackground);
+        if (mSelectionRect != null) {
+            canvas.drawRect(mSelectionRect, mPaintSelection);
+        }
+    }
+
+    void setOnScreenshotSelected(Consumer<Rect> onScreenshotSelected) {
+        mOnScreenshotSelected = onScreenshotSelected;
+    }
+
+    void stop() {
+        if (getSelectionRect() != null) {
+            stopSelection();
+        }
+    }
+
+    private void startSelection(int x, int y) {
         mStartPoint = new Point(x, y);
         mSelectionRect = new Rect(x, y, x, y);
     }
 
-    public void updateSelection(int x, int y) {
+    private void updateSelection(int x, int y) {
         if (mSelectionRect != null) {
             mSelectionRect.left = Math.min(mStartPoint.x, x);
             mSelectionRect.right = Math.max(mStartPoint.x, x);
@@ -63,20 +108,12 @@
         }
     }
 
-    public Rect getSelectionRect() {
+    private Rect getSelectionRect() {
         return mSelectionRect;
     }
 
-    public void stopSelection() {
+    private void stopSelection() {
         mStartPoint = null;
         mSelectionRect = null;
     }
-
-    @Override
-    public void draw(Canvas canvas) {
-        canvas.drawRect(mLeft, mTop, mRight, mBottom, mPaintBackground);
-        if (mSelectionRect != null) {
-            canvas.drawRect(mSelectionRect, mPaintSelection);
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
new file mode 100644
index 0000000..03fe292
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -0,0 +1,611 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.screenshot;
+
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+
+import static java.util.Objects.requireNonNull;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ValueAnimator;
+import android.app.ActivityManager;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.Insets;
+import android.graphics.Outline;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.drawable.BitmapDrawable;
+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.os.RemoteException;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.MathUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
+import android.view.ViewTreeObserver;
+import android.view.WindowInsets;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.widget.FrameLayout;
+import android.widget.HorizontalScrollView;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+
+import com.android.systemui.R;
+import com.android.systemui.shared.system.QuickStepContract;
+
+import java.util.ArrayList;
+import java.util.function.Consumer;
+
+/**
+ * Handles the visual elements and animations for the screenshot flow.
+ */
+public class ScreenshotView extends FrameLayout implements
+        ViewTreeObserver.OnComputeInternalInsetsListener {
+
+    private static final String TAG = "GlobalScreenshotView";
+
+    private static final long SCREENSHOT_FLASH_IN_DURATION_MS = 133;
+    private static final long SCREENSHOT_FLASH_OUT_DURATION_MS = 217;
+    // delay before starting to fade in dismiss button
+    private static final long SCREENSHOT_TO_CORNER_DISMISS_DELAY_MS = 200;
+    private static final long SCREENSHOT_TO_CORNER_X_DURATION_MS = 234;
+    private static final long SCREENSHOT_TO_CORNER_Y_DURATION_MS = 500;
+    private static final long SCREENSHOT_TO_CORNER_SCALE_DURATION_MS = 234;
+    private static final long SCREENSHOT_ACTIONS_EXPANSION_DURATION_MS = 400;
+    private static final long SCREENSHOT_ACTIONS_ALPHA_DURATION_MS = 100;
+    private static final long SCREENSHOT_DISMISS_Y_DURATION_MS = 350;
+    private static final long SCREENSHOT_DISMISS_ALPHA_DURATION_MS = 183;
+    private static final long SCREENSHOT_DISMISS_ALPHA_OFFSET_MS = 50; // delay before starting fade
+    private static final float SCREENSHOT_ACTIONS_START_SCALE_X = .7f;
+    private static final float ROUNDED_CORNER_RADIUS = .05f;
+
+    private final Interpolator mAccelerateInterpolator = new AccelerateInterpolator();
+
+    private final Resources mResources;
+    private final Interpolator mFastOutSlowIn;
+    private final DisplayMetrics mDisplayMetrics;
+    private final float mCornerSizeX;
+    private final float mDismissDeltaY;
+
+    private int mNavMode;
+    private int mLeftInset;
+    private int mRightInset;
+    private boolean mOrientationPortrait;
+    private boolean mDirectionLTR;
+
+    private ScreenshotSelectorView mScreenshotSelectorView;
+    private ImageView mScreenshotAnimatedView;
+    private ImageView mScreenshotPreview;
+    private ImageView mScreenshotFlash;
+    private ImageView mActionsContainerBackground;
+    private HorizontalScrollView mActionsContainer;
+    private LinearLayout mActionsView;
+    private ImageView mBackgroundProtection;
+    private FrameLayout mDismissButton;
+    private ScreenshotActionChip mShareChip;
+    private ScreenshotActionChip mEditChip;
+
+    private ArrayList<ScreenshotActionChip> mSmartChips = new ArrayList<>();
+    private PendingInteraction mPendingInteraction;
+
+    private enum PendingInteraction {
+        PREVIEW,
+        EDIT,
+        SHARE
+    }
+
+    public ScreenshotView(Context context) {
+        this(context, null);
+    }
+
+    public ScreenshotView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public ScreenshotView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public ScreenshotView(
+            Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        mResources = mContext.getResources();
+
+        mCornerSizeX = mResources.getDimensionPixelSize(R.dimen.global_screenshot_x_scale);
+        mDismissDeltaY = mResources.getDimensionPixelSize(
+                R.dimen.screenshot_dismissal_height_delta);
+
+        // standard material ease
+        mFastOutSlowIn =
+                AnimationUtils.loadInterpolator(mContext, android.R.interpolator.fast_out_slow_in);
+
+        mDisplayMetrics = new DisplayMetrics();
+        mContext.getDisplay().getRealMetrics(mDisplayMetrics);
+    }
+
+    @Override // ViewTreeObserver.OnComputeInternalInsetsListener
+    public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
+        inoutInfo.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+        Region touchRegion = new Region();
+
+        Rect screenshotRect = new Rect();
+        mScreenshotPreview.getBoundsOnScreen(screenshotRect);
+        touchRegion.op(screenshotRect, Region.Op.UNION);
+        Rect actionsRect = new Rect();
+        mActionsContainer.getBoundsOnScreen(actionsRect);
+        touchRegion.op(actionsRect, Region.Op.UNION);
+        Rect dismissRect = new Rect();
+        mDismissButton.getBoundsOnScreen(dismissRect);
+        touchRegion.op(dismissRect, Region.Op.UNION);
+
+        if (QuickStepContract.isGesturalMode(mNavMode)) {
+            // Receive touches in gesture insets such that they don't cause TOUCH_OUTSIDE
+            Rect inset = new Rect(0, 0, mLeftInset, mDisplayMetrics.heightPixels);
+            touchRegion.op(inset, Region.Op.UNION);
+            inset.set(mDisplayMetrics.widthPixels - mRightInset, 0, mDisplayMetrics.widthPixels,
+                    mDisplayMetrics.heightPixels);
+            touchRegion.op(inset, Region.Op.UNION);
+        }
+
+        inoutInfo.touchableRegion.set(touchRegion);
+    }
+
+    @Override // View
+    protected void onFinishInflate() {
+        mScreenshotAnimatedView = requireNonNull(
+                findViewById(R.id.global_screenshot_animated_view));
+        mScreenshotPreview = requireNonNull(findViewById(R.id.global_screenshot_preview));
+        mActionsContainerBackground = requireNonNull(findViewById(
+                R.id.global_screenshot_actions_container_background));
+        mActionsContainer = requireNonNull(findViewById(R.id.global_screenshot_actions_container));
+        mActionsView = requireNonNull(findViewById(R.id.global_screenshot_actions));
+        mBackgroundProtection = requireNonNull(
+                findViewById(R.id.global_screenshot_actions_background));
+        mDismissButton = requireNonNull(findViewById(R.id.global_screenshot_dismiss_button));
+        mScreenshotFlash = requireNonNull(findViewById(R.id.global_screenshot_flash));
+        mScreenshotSelectorView = requireNonNull(findViewById(R.id.global_screenshot_selector));
+        mShareChip = requireNonNull(mActionsContainer.findViewById(R.id.screenshot_share_chip));
+        mEditChip = requireNonNull(mActionsContainer.findViewById(R.id.screenshot_edit_chip));
+
+        mScreenshotAnimatedView.setClipToOutline(true);
+        mScreenshotAnimatedView.setOutlineProvider(new ViewOutlineProvider() {
+            @Override
+            public void getOutline(View view, Outline outline) {
+                outline.setRoundRect(new Rect(0, 0, view.getWidth(), view.getHeight()),
+                        ROUNDED_CORNER_RADIUS * view.getWidth());
+            }
+        });
+        mScreenshotPreview.setClipToOutline(true);
+        mScreenshotPreview.setOutlineProvider(new ViewOutlineProvider() {
+            @Override
+            public void getOutline(View view, Outline outline) {
+                outline.setRoundRect(new Rect(0, 0, view.getWidth(), view.getHeight()),
+                        ROUNDED_CORNER_RADIUS * view.getWidth());
+            }
+        });
+
+        setFocusable(true);
+        mScreenshotSelectorView.setFocusable(true);
+        mScreenshotSelectorView.setFocusableInTouchMode(true);
+        mScreenshotAnimatedView.setPivotX(0);
+        mScreenshotAnimatedView.setPivotY(0);
+        mActionsContainer.setScrollX(0);
+
+        mNavMode = getResources().getInteger(
+                com.android.internal.R.integer.config_navBarInteractionMode);
+        mOrientationPortrait =
+                getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT;
+        mDirectionLTR =
+                getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_LTR;
+
+        setOnApplyWindowInsetsListener((v, insets) -> {
+            if (QuickStepContract.isGesturalMode(mNavMode)) {
+                Insets gestureInsets = insets.getInsets(
+                        WindowInsets.Type.systemGestures());
+                mLeftInset = gestureInsets.left;
+                mRightInset = gestureInsets.right;
+            } else {
+                mLeftInset = mRightInset = 0;
+            }
+            return ScreenshotView.this.onApplyWindowInsets(insets);
+        });
+
+        // Get focus so that the key events go to the layout.
+        setFocusableInTouchMode(true);
+        requestFocus();
+    }
+
+    void takePartialScreenshot(Consumer<Rect> onPartialScreenshotSelected) {
+        mScreenshotSelectorView.setOnScreenshotSelected(onPartialScreenshotSelected);
+        mScreenshotSelectorView.setVisibility(View.VISIBLE);
+        mScreenshotSelectorView.requestFocus();
+    }
+
+    void prepareForAnimation(Bitmap bitmap, Rect screenRect, Insets screenInsets) {
+        mScreenshotAnimatedView.setImageDrawable(
+                createScreenDrawable(mResources, bitmap, screenInsets));
+        setAnimatedViewSize(screenRect.width(), screenRect.height());
+
+        // will show when the animation starts
+        mScreenshotAnimatedView.setVisibility(View.GONE);
+
+        mScreenshotPreview.setImageDrawable(createScreenDrawable(mResources, bitmap, screenInsets));
+        // make static preview invisible (from gone) so we can query its location on screen
+        mScreenshotPreview.setVisibility(View.INVISIBLE);
+    }
+
+    AnimatorSet createScreenshotDropInAnimation(Rect bounds, boolean showFlash,
+            Consumer<ScreenshotEvent> onElementTapped) {
+        mScreenshotPreview.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+        mScreenshotPreview.buildLayer();
+
+        Rect previewBounds = new Rect();
+        mScreenshotPreview.getBoundsOnScreen(previewBounds);
+
+        float cornerScale =
+                mCornerSizeX / (mOrientationPortrait ? bounds.width() : bounds.height());
+        final float currentScale = 1f;
+
+        mScreenshotAnimatedView.setScaleX(currentScale);
+        mScreenshotAnimatedView.setScaleY(currentScale);
+
+        mDismissButton.setAlpha(0);
+        mDismissButton.setVisibility(View.VISIBLE);
+
+        AnimatorSet dropInAnimation = new AnimatorSet();
+        ValueAnimator flashInAnimator = ValueAnimator.ofFloat(0, 1);
+        flashInAnimator.setDuration(SCREENSHOT_FLASH_IN_DURATION_MS);
+        flashInAnimator.setInterpolator(mFastOutSlowIn);
+        flashInAnimator.addUpdateListener(animation ->
+                mScreenshotFlash.setAlpha((float) animation.getAnimatedValue()));
+
+        ValueAnimator flashOutAnimator = ValueAnimator.ofFloat(1, 0);
+        flashOutAnimator.setDuration(SCREENSHOT_FLASH_OUT_DURATION_MS);
+        flashOutAnimator.setInterpolator(mFastOutSlowIn);
+        flashOutAnimator.addUpdateListener(animation ->
+                mScreenshotFlash.setAlpha((float) animation.getAnimatedValue()));
+
+        // animate from the current location, to the static preview location
+        final PointF startPos = new PointF(bounds.centerX(), bounds.centerY());
+        final PointF finalPos = new PointF(previewBounds.centerX(), previewBounds.centerY());
+
+        ValueAnimator toCorner = ValueAnimator.ofFloat(0, 1);
+        toCorner.setDuration(SCREENSHOT_TO_CORNER_Y_DURATION_MS);
+        float xPositionPct =
+                SCREENSHOT_TO_CORNER_X_DURATION_MS / (float) SCREENSHOT_TO_CORNER_Y_DURATION_MS;
+        float dismissPct =
+                SCREENSHOT_TO_CORNER_DISMISS_DELAY_MS / (float) SCREENSHOT_TO_CORNER_Y_DURATION_MS;
+        float scalePct =
+                SCREENSHOT_TO_CORNER_SCALE_DURATION_MS / (float) SCREENSHOT_TO_CORNER_Y_DURATION_MS;
+        toCorner.addUpdateListener(animation -> {
+            float t = animation.getAnimatedFraction();
+            if (t < scalePct) {
+                float scale = MathUtils.lerp(
+                        currentScale, cornerScale, mFastOutSlowIn.getInterpolation(t / scalePct));
+                mScreenshotAnimatedView.setScaleX(scale);
+                mScreenshotAnimatedView.setScaleY(scale);
+            } else {
+                mScreenshotAnimatedView.setScaleX(cornerScale);
+                mScreenshotAnimatedView.setScaleY(cornerScale);
+            }
+
+            float currentScaleX = mScreenshotAnimatedView.getScaleX();
+            float currentScaleY = mScreenshotAnimatedView.getScaleY();
+
+            if (t < xPositionPct) {
+                float xCenter = MathUtils.lerp(startPos.x, finalPos.x,
+                        mFastOutSlowIn.getInterpolation(t / xPositionPct));
+                mScreenshotAnimatedView.setX(xCenter - bounds.width() * currentScaleX / 2f);
+            } else {
+                mScreenshotAnimatedView.setX(finalPos.x - bounds.width() * currentScaleX / 2f);
+            }
+            float yCenter = MathUtils.lerp(
+                    startPos.y, finalPos.y, mFastOutSlowIn.getInterpolation(t));
+            mScreenshotAnimatedView.setY(yCenter - bounds.height() * currentScaleY / 2f);
+
+            if (t >= dismissPct) {
+                mDismissButton.setAlpha((t - dismissPct) / (1 - dismissPct));
+                float currentX = mScreenshotAnimatedView.getX();
+                float currentY = mScreenshotAnimatedView.getY();
+                mDismissButton.setY(currentY - mDismissButton.getHeight() / 2f);
+                if (mDirectionLTR) {
+                    mDismissButton.setX(currentX
+                            + bounds.width() * currentScaleX - mDismissButton.getWidth() / 2f);
+                } else {
+                    mDismissButton.setX(currentX - mDismissButton.getWidth() / 2f);
+                }
+            }
+        });
+
+        toCorner.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                super.onAnimationStart(animation);
+                mScreenshotAnimatedView.setVisibility(View.VISIBLE);
+            }
+        });
+
+        mScreenshotFlash.setAlpha(0f);
+        mScreenshotFlash.setVisibility(View.VISIBLE);
+
+        if (showFlash) {
+            dropInAnimation.play(flashOutAnimator).after(flashInAnimator);
+            dropInAnimation.play(flashOutAnimator).with(toCorner);
+        } else {
+            dropInAnimation.play(toCorner);
+        }
+
+        dropInAnimation.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                mDismissButton.setOnClickListener(view ->
+                        onElementTapped.accept(ScreenshotEvent.SCREENSHOT_EXPLICIT_DISMISSAL));
+                mDismissButton.setAlpha(1);
+                float dismissOffset = mDismissButton.getWidth() / 2f;
+                float finalDismissX = mDirectionLTR
+                        ? finalPos.x - dismissOffset + bounds.width() * cornerScale / 2f
+                        : finalPos.x - dismissOffset - bounds.width() * cornerScale / 2f;
+                mDismissButton.setX(finalDismissX);
+                mDismissButton.setY(
+                        finalPos.y - dismissOffset - bounds.height() * cornerScale / 2f);
+                mScreenshotAnimatedView.setScaleX(1);
+                mScreenshotAnimatedView.setScaleY(1);
+                mScreenshotAnimatedView.setX(finalPos.x - bounds.width() * cornerScale / 2f);
+                mScreenshotAnimatedView.setY(finalPos.y - bounds.height() * cornerScale / 2f);
+                mScreenshotAnimatedView.setVisibility(View.GONE);
+                mScreenshotPreview.setVisibility(View.VISIBLE);
+                forceLayout();
+                createScreenshotActionsShadeAnimation().start();
+            }
+        });
+
+        return dropInAnimation;
+    }
+
+    ValueAnimator createScreenshotActionsShadeAnimation() {
+        // By default the activities won't be able to start immediately; override this to keep
+        // the same behavior as if started from a notification
+        try {
+            ActivityManager.getService().resumeAppSwitches();
+        } catch (RemoteException e) {
+        }
+
+        ArrayList<ScreenshotActionChip> chips = new ArrayList<>();
+
+        mShareChip.setText(mContext.getString(com.android.internal.R.string.share));
+        mShareChip.setIcon(Icon.createWithResource(mContext, R.drawable.ic_screenshot_share), true);
+        mShareChip.setOnClickListener(v -> {
+            mShareChip.setIsPending(true);
+            mEditChip.setIsPending(false);
+            mPendingInteraction = PendingInteraction.SHARE;
+        });
+        chips.add(mShareChip);
+
+        mEditChip.setText(mContext.getString(com.android.internal.R.string.screenshot_edit));
+        mEditChip.setIcon(Icon.createWithResource(mContext, R.drawable.ic_screenshot_edit), true);
+        mEditChip.setOnClickListener(v -> {
+            mEditChip.setIsPending(true);
+            mShareChip.setIsPending(false);
+            mPendingInteraction = PendingInteraction.EDIT;
+        });
+        chips.add(mEditChip);
+
+        mScreenshotPreview.setOnClickListener(v -> {
+            mShareChip.setIsPending(false);
+            mEditChip.setIsPending(false);
+            mPendingInteraction = PendingInteraction.PREVIEW;
+        });
+
+        // remove the margin from the last chip so that it's correctly aligned with the end
+        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams)
+                mActionsView.getChildAt(0).getLayoutParams();
+        params.setMarginEnd(0);
+        mActionsView.getChildAt(0).setLayoutParams(params);
+
+        ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
+        animator.setDuration(SCREENSHOT_ACTIONS_EXPANSION_DURATION_MS);
+        float alphaFraction = (float) SCREENSHOT_ACTIONS_ALPHA_DURATION_MS
+                / SCREENSHOT_ACTIONS_EXPANSION_DURATION_MS;
+        mActionsContainer.setAlpha(0f);
+        mActionsContainerBackground.setAlpha(0f);
+        mActionsContainer.setVisibility(View.VISIBLE);
+        mActionsContainerBackground.setVisibility(View.VISIBLE);
+
+        animator.addUpdateListener(animation -> {
+            float t = animation.getAnimatedFraction();
+            mBackgroundProtection.setAlpha(t);
+            float containerAlpha = t < alphaFraction ? t / alphaFraction : 1;
+            mActionsContainer.setAlpha(containerAlpha);
+            mActionsContainerBackground.setAlpha(containerAlpha);
+            float containerScale = SCREENSHOT_ACTIONS_START_SCALE_X
+                    + (t * (1 - SCREENSHOT_ACTIONS_START_SCALE_X));
+            mActionsContainer.setScaleX(containerScale);
+            mActionsContainerBackground.setScaleX(containerScale);
+            for (ScreenshotActionChip chip : chips) {
+                chip.setAlpha(t);
+                chip.setScaleX(1 / containerScale); // invert to keep size of children constant
+            }
+            mActionsContainer.setScrollX(mDirectionLTR ? 0 : mActionsContainer.getWidth());
+            mActionsContainer.setPivotX(mDirectionLTR ? 0 : mActionsContainer.getWidth());
+            mActionsContainerBackground.setPivotX(
+                    mDirectionLTR ? 0 : mActionsContainerBackground.getWidth());
+        });
+        return animator;
+    }
+
+    void setChipIntents(ScreenshotController.SavedImageData imageData,
+            Consumer<ScreenshotEvent> onElementTapped) {
+        mShareChip.setPendingIntent(imageData.shareAction.actionIntent,
+                () -> onElementTapped.accept(ScreenshotEvent.SCREENSHOT_SHARE_TAPPED));
+        mEditChip.setPendingIntent(imageData.editAction.actionIntent,
+                () -> onElementTapped.accept(ScreenshotEvent.SCREENSHOT_EDIT_TAPPED));
+        mScreenshotPreview.setOnClickListener(v -> {
+            try {
+                imageData.editAction.actionIntent.send();
+            } catch (PendingIntent.CanceledException e) {
+                Log.e(TAG, "Intent cancelled", e);
+            }
+            onElementTapped.accept(ScreenshotEvent.SCREENSHOT_PREVIEW_TAPPED);
+        });
+
+        if (mPendingInteraction != null) {
+            switch (mPendingInteraction) {
+                case PREVIEW:
+                    mScreenshotPreview.callOnClick();
+                    break;
+                case SHARE:
+                    mShareChip.callOnClick();
+                    break;
+                case EDIT:
+                    mEditChip.callOnClick();
+                    break;
+            }
+        } else {
+            LayoutInflater inflater = LayoutInflater.from(mContext);
+
+            for (Notification.Action smartAction : imageData.smartActions) {
+                ScreenshotActionChip actionChip = (ScreenshotActionChip) inflater.inflate(
+                        R.layout.global_screenshot_action_chip, mActionsView, false);
+                actionChip.setText(smartAction.title);
+                actionChip.setIcon(smartAction.getIcon(), false);
+                actionChip.setPendingIntent(smartAction.actionIntent,
+                        () -> onElementTapped.accept(
+                                ScreenshotEvent.SCREENSHOT_SMART_ACTION_TAPPED));
+                mActionsView.addView(actionChip);
+                mSmartChips.add(actionChip);
+            }
+        }
+    }
+
+
+    AnimatorSet createScreenshotDismissAnimation() {
+        ValueAnimator alphaAnim = ValueAnimator.ofFloat(0, 1);
+        alphaAnim.setStartDelay(SCREENSHOT_DISMISS_ALPHA_OFFSET_MS);
+        alphaAnim.setDuration(SCREENSHOT_DISMISS_ALPHA_DURATION_MS);
+        alphaAnim.addUpdateListener(animation -> {
+            setAlpha(1 - animation.getAnimatedFraction());
+        });
+
+        ValueAnimator yAnim = ValueAnimator.ofFloat(0, 1);
+        yAnim.setInterpolator(mAccelerateInterpolator);
+        yAnim.setDuration(SCREENSHOT_DISMISS_Y_DURATION_MS);
+        float screenshotStartY = mScreenshotPreview.getTranslationY();
+        float dismissStartY = mDismissButton.getTranslationY();
+        yAnim.addUpdateListener(animation -> {
+            float yDelta = MathUtils.lerp(0, mDismissDeltaY, animation.getAnimatedFraction());
+            mScreenshotPreview.setTranslationY(screenshotStartY + yDelta);
+            mDismissButton.setTranslationY(dismissStartY + yDelta);
+            mActionsContainer.setTranslationY(yDelta);
+            mActionsContainerBackground.setTranslationY(yDelta);
+        });
+
+        AnimatorSet animSet = new AnimatorSet();
+        animSet.play(yAnim).with(alphaAnim);
+
+        return animSet;
+    }
+
+    void reset() {
+        // Clear any references to the bitmap
+        mScreenshotPreview.setImageDrawable(null);
+        mScreenshotAnimatedView.setImageDrawable(null);
+        mScreenshotAnimatedView.setVisibility(View.GONE);
+        mActionsContainerBackground.setVisibility(View.GONE);
+        mActionsContainer.setVisibility(View.GONE);
+        mBackgroundProtection.setAlpha(0f);
+        mDismissButton.setVisibility(View.GONE);
+        mScreenshotPreview.setVisibility(View.GONE);
+        mScreenshotPreview.setLayerType(View.LAYER_TYPE_NONE, null);
+        mScreenshotPreview.setContentDescription(
+                mContext.getResources().getString(R.string.screenshot_preview_description));
+        mScreenshotPreview.setOnClickListener(null);
+        mShareChip.setOnClickListener(null);
+        mEditChip.setOnClickListener(null);
+        mShareChip.setIsPending(false);
+        mEditChip.setIsPending(false);
+        mPendingInteraction = null;
+        for (ScreenshotActionChip chip : mSmartChips) {
+            mActionsView.removeView(chip);
+        }
+        mSmartChips.clear();
+        setAlpha(1);
+        mDismissButton.setTranslationY(0);
+        mActionsContainer.setTranslationY(0);
+        mActionsContainerBackground.setTranslationY(0);
+        mScreenshotPreview.setTranslationY(0);
+        mScreenshotSelectorView.stop();
+    }
+
+    private void setAnimatedViewSize(int width, int height) {
+        ViewGroup.LayoutParams layoutParams = mScreenshotAnimatedView.getLayoutParams();
+        layoutParams.width = width;
+        layoutParams.height = height;
+        mScreenshotAnimatedView.setLayoutParams(layoutParams);
+    }
+
+    /**
+     * Create a drawable using the size of the bitmap and insets as the fractional inset parameters.
+     */
+    private static Drawable createScreenDrawable(Resources res, Bitmap bitmap, Insets insets) {
+        int insettedWidth = bitmap.getWidth() - insets.left - insets.right;
+        int insettedHeight = bitmap.getHeight() - insets.top - insets.bottom;
+
+        BitmapDrawable bitmapDrawable = new BitmapDrawable(res, bitmap);
+        if (insettedHeight == 0 || insettedWidth == 0 || bitmap.getWidth() == 0
+                || bitmap.getHeight() == 0) {
+            Log.e(TAG, String.format(
+                    "Can't create insetted drawable, using 0 insets "
+                            + "bitmap and insets create degenerate region: %dx%d %s",
+                    bitmap.getWidth(), bitmap.getHeight(), insets));
+            return bitmapDrawable;
+        }
+
+        InsetDrawable insetDrawable = new InsetDrawable(bitmapDrawable,
+                -1f * insets.left / insettedWidth,
+                -1f * insets.top / insettedHeight,
+                -1f * insets.right / insettedWidth,
+                -1f * insets.bottom / insettedHeight);
+
+        if (insets.left < 0 || insets.top < 0 || insets.right < 0 || insets.bottom < 0) {
+            // Are any of the insets negative, meaning the bitmap is smaller than the bounds so need
+            // to fill in the background of the drawable.
+            return new LayerDrawable(new Drawable[]{
+                    new ColorDrawable(Color.BLACK), insetDrawable});
+        } else {
+            return insetDrawable;
+        }
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java
index 217235b..f32529f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java
@@ -16,9 +16,9 @@
 
 package com.android.systemui.screenshot;
 
-import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ACTION_INTENT;
-import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ACTION_TYPE;
-import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
+import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ACTION_INTENT;
+import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ACTION_TYPE;
+import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ID;
 
 import android.app.ActivityOptions;
 import android.app.PendingIntent;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index a043f0f..4e22833 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -52,7 +52,7 @@
 public class TakeScreenshotService extends Service {
     private static final String TAG = "TakeScreenshotService";
 
-    private final GlobalScreenshot mScreenshot;
+    private final ScreenshotController mScreenshot;
     private final UserManager mUserManager;
     private final UiEventLogger mUiEventLogger;
 
@@ -61,7 +61,7 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             if (ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction()) && mScreenshot != null) {
-                mScreenshot.dismissScreenshot("close system dialogs", false);
+                mScreenshot.dismissScreenshot(false);
             }
         }
     };
@@ -125,7 +125,7 @@
     };
 
     @Inject
-    public TakeScreenshotService(GlobalScreenshot globalScreenshot, UserManager userManager,
+    public TakeScreenshotService(ScreenshotController globalScreenshot, UserManager userManager,
             UiEventLogger uiEventLogger) {
         mScreenshot = globalScreenshot;
         mUserManager = userManager;
@@ -144,7 +144,9 @@
 
     @Override
     public boolean onUnbind(Intent intent) {
-        if (mScreenshot != null) mScreenshot.stopScreenshot();
+        if (mScreenshot != null && !mScreenshot.isDismissing()) {
+            mScreenshot.dismissScreenshot(true);
+        }
         unregisterReceiver(mBroadcastReceiver);
         return true;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
index 1bea72a..72034f8 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
@@ -50,6 +50,8 @@
 
 import java.util.ArrayList;
 
+import javax.inject.Inject;
+
 public class BrightnessController implements ToggleSlider.Listener {
     private static final String TAG = "StatusBar.BrightnessController";
     private static final int SLIDER_ANIMATION_DURATION = 3000;
@@ -475,4 +477,20 @@
         mSliderAnimator.start();
     }
 
+    /** Factory for creating a {@link BrightnessController}. */
+    public static class Factory {
+        private final Context mContext;
+        private final BroadcastDispatcher mBroadcastDispatcher;
+
+        @Inject
+        public Factory(Context context, BroadcastDispatcher broadcastDispatcher) {
+            mContext = context;
+            mBroadcastDispatcher = broadcastDispatcher;
+        }
+
+        /** Create a {@link BrightnessController} */
+        public BrightnessController create(ToggleSlider toggleSlider) {
+            return new BrightnessController(mContext, toggleSlider, mBroadcastDispatcher);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 0184fa7..dcee9fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -140,6 +140,8 @@
     private static final int MSG_SUPPRESS_AMBIENT_DISPLAY          = 56 << MSG_SHIFT;
     private static final int MSG_REQUEST_WINDOW_MAGNIFICATION_CONNECTION = 57 << MSG_SHIFT;
     private static final int MSG_HANDLE_WINDOW_MANAGER_LOGGING_COMMAND = 58 << MSG_SHIFT;
+    //TODO(b/169175022) Update name and when feature name is locked.
+    private static final int MSG_EMERGENCY_ACTION_LAUNCH_GESTURE      = 59 << MSG_SHIFT;
 
     public static final int FLAG_EXCLUDE_NONE = 0;
     public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -258,6 +260,11 @@
         default void showAssistDisclosure() { }
         default void startAssist(Bundle args) { }
         default void onCameraLaunchGestureDetected(int source) { }
+
+        /**
+         * Notifies SysUI that the emergency action gesture was detected.
+         */
+        default void onEmergencyActionLaunchGestureDetected() { }
         default void showPictureInPictureMenu() { }
         default void setTopAppHidesStatusBar(boolean topAppHidesStatusBar) { }
 
@@ -730,6 +737,14 @@
     }
 
     @Override
+    public void onEmergencyActionLaunchGestureDetected() {
+        synchronized (mLock) {
+            mHandler.removeMessages(MSG_EMERGENCY_ACTION_LAUNCH_GESTURE);
+            mHandler.obtainMessage(MSG_EMERGENCY_ACTION_LAUNCH_GESTURE).sendToTarget();
+        }
+    }
+
+    @Override
     public void addQsTile(ComponentName tile) {
         synchronized (mLock) {
             mHandler.obtainMessage(MSG_ADD_QS_TILE, tile).sendToTarget();
@@ -1186,6 +1201,10 @@
                         mCallbacks.get(i).onCameraLaunchGestureDetected(msg.arg1);
                     }
                     break;
+                case MSG_EMERGENCY_ACTION_LAUNCH_GESTURE:
+                    for (int i = 0; i < mCallbacks.size(); i++) {
+                        mCallbacks.get(i).onEmergencyActionLaunchGestureDetected();
+                    }
                 case MSG_SHOW_PICTURE_IN_PICTURE_MENU:
                     for (int i = 0; i < mCallbacks.size(); i++) {
                         mCallbacks.get(i).showPictureInPictureMenu();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/MediaTransferManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/MediaTransferManager.java
deleted file mode 100644
index ac3523b..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/MediaTransferManager.java
+++ /dev/null
@@ -1,219 +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.systemui.statusbar;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.ColorStateList;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.RippleDrawable;
-import android.service.notification.StatusBarNotification;
-import android.util.FeatureFlagUtils;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.settingslib.bluetooth.LocalBluetoothManager;
-import com.android.settingslib.media.InfoMediaManager;
-import com.android.settingslib.media.LocalMediaManager;
-import com.android.settingslib.media.MediaDevice;
-import com.android.settingslib.media.MediaOutputSliceConstants;
-import com.android.settingslib.widget.AdaptiveIcon;
-import com.android.systemui.Dependency;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Class for handling MediaTransfer state over a set of notifications.
- */
-public class MediaTransferManager {
-    private final Context mContext;
-    private final ActivityStarter mActivityStarter;
-    private MediaDevice mDevice;
-    private List<View> mViews = new ArrayList<>();
-    private LocalMediaManager mLocalMediaManager;
-
-    private static final String TAG = "MediaTransferManager";
-
-    private final View.OnClickListener mOnClickHandler = new View.OnClickListener() {
-        @Override
-        public void onClick(View view) {
-            if (handleMediaTransfer(view)) {
-                return;
-            }
-        }
-
-        private boolean handleMediaTransfer(View view) {
-            if (view.findViewById(com.android.internal.R.id.media_seamless) == null) {
-                return false;
-            }
-
-            ViewParent parent = view.getParent();
-            StatusBarNotification statusBarNotification =
-                    getRowForParent(parent).getEntry().getSbn();
-            final Intent intent = new Intent()
-                    .setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT)
-                    .putExtra(MediaOutputSliceConstants.EXTRA_PACKAGE_NAME,
-                            statusBarNotification.getPackageName());
-            mActivityStarter.startActivity(intent, false, true /* dismissShade */,
-                    Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
-            return true;
-        }
-    };
-
-    private final LocalMediaManager.DeviceCallback mMediaDeviceCallback =
-            new LocalMediaManager.DeviceCallback() {
-        @Override
-        public void onDeviceListUpdate(List<MediaDevice> devices) {
-            MediaDevice currentDevice = mLocalMediaManager.getCurrentConnectedDevice();
-            // Check because this can be called several times while changing devices
-            if (mDevice == null || !mDevice.equals(currentDevice)) {
-                mDevice = currentDevice;
-                updateAllChips();
-            }
-        }
-
-        @Override
-        public void onSelectedDeviceStateChanged(MediaDevice device, int state) {
-            if (mDevice == null || !mDevice.equals(device)) {
-                mDevice = device;
-                updateAllChips();
-            }
-        }
-    };
-
-    public MediaTransferManager(Context context) {
-        mContext = context;
-        mActivityStarter = Dependency.get(ActivityStarter.class);
-        LocalBluetoothManager lbm = Dependency.get(LocalBluetoothManager.class);
-        InfoMediaManager imm = new InfoMediaManager(mContext, null, null, lbm);
-        mLocalMediaManager = new LocalMediaManager(mContext, lbm, imm, null);
-    }
-
-    /**
-     * Mark a view as removed. If no views remain the media device listener will be unregistered.
-     * @param root
-     */
-    public void setRemoved(View root) {
-        if (!FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SEAMLESS_TRANSFER)
-                || mLocalMediaManager == null || root == null) {
-            return;
-        }
-        View view = root.findViewById(com.android.internal.R.id.media_seamless);
-        if (mViews.remove(view)) {
-            if (mViews.size() == 0) {
-                mLocalMediaManager.unregisterCallback(mMediaDeviceCallback);
-            }
-        } else {
-            Log.e(TAG, "Tried to remove unknown view " + view);
-        }
-    }
-
-    private ExpandableNotificationRow getRowForParent(ViewParent parent) {
-        while (parent != null) {
-            if (parent instanceof ExpandableNotificationRow) {
-                return ((ExpandableNotificationRow) parent);
-            }
-            parent = parent.getParent();
-        }
-        return null;
-    }
-
-    /**
-     * apply the action button for MediaTransfer
-     *
-     * @param root  The parent container of the view.
-     * @param entry The entry of MediaTransfer action button.
-     */
-    public void applyMediaTransferView(ViewGroup root, NotificationEntry entry) {
-        if (!FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SEAMLESS_TRANSFER)
-                || mLocalMediaManager == null || root == null) {
-            return;
-        }
-
-        View view = root.findViewById(com.android.internal.R.id.media_seamless);
-        if (view == null) {
-            return;
-        }
-
-        view.setVisibility(View.VISIBLE);
-        view.setOnClickListener(mOnClickHandler);
-        if (!mViews.contains(view)) {
-            mViews.add(view);
-            if (mViews.size() == 1) {
-                mLocalMediaManager.registerCallback(mMediaDeviceCallback);
-            }
-        }
-
-        // Initial update
-        mLocalMediaManager.startScan();
-        mDevice = mLocalMediaManager.getCurrentConnectedDevice();
-        updateChip(view);
-    }
-
-    private void updateAllChips() {
-        for (View view : mViews) {
-            updateChip(view);
-        }
-    }
-
-    private void updateChip(View view) {
-        ExpandableNotificationRow enr = getRowForParent(view.getParent());
-        int fgColor = enr.getNotificationHeader().getOriginalIconColor();
-        ColorStateList fgTintList = ColorStateList.valueOf(fgColor);
-        int bgColor = enr.getCurrentBackgroundTint();
-
-        // Update outline color
-        LinearLayout viewLayout = (LinearLayout) view;
-        RippleDrawable bkgDrawable = (RippleDrawable) viewLayout.getBackground();
-        GradientDrawable rect = (GradientDrawable) bkgDrawable.getDrawable(0);
-        rect.setStroke(2, fgColor);
-        rect.setColor(bgColor);
-
-        ImageView iconView = view.findViewById(com.android.internal.R.id.media_seamless_image);
-        TextView deviceName = view.findViewById(com.android.internal.R.id.media_seamless_text);
-        deviceName.setTextColor(fgTintList);
-
-        if (mDevice != null) {
-            Drawable icon = mDevice.getIcon();
-            iconView.setVisibility(View.VISIBLE);
-            iconView.setImageTintList(fgTintList);
-
-            if (icon instanceof AdaptiveIcon) {
-                AdaptiveIcon aIcon = (AdaptiveIcon) icon;
-                aIcon.setBackgroundColor(bgColor);
-                iconView.setImageDrawable(aIcon);
-            } else {
-                iconView.setImageDrawable(icon);
-            }
-            deviceName.setText(mDevice.getName());
-        } else {
-            // Reset to default
-            iconView.setVisibility(View.GONE);
-            deviceName.setText(com.android.internal.R.string.ext_media_seamless_action);
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
index 670a65f..5976582 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
@@ -17,18 +17,16 @@
 package com.android.systemui.statusbar;
 
 import android.app.Notification;
-import android.content.res.Configuration;
-import android.graphics.PorterDuff;
 import android.graphics.drawable.Icon;
 import android.text.TextUtils;
-import android.view.NotificationHeaderView;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageView;
 import android.widget.TextView;
 
-import com.android.internal.util.ContrastColorUtil;
+import com.android.internal.widget.CachingIconView;
 import com.android.internal.widget.ConversationLayout;
+import com.android.internal.widget.NotificationExpandButton;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationContentView;
 
@@ -67,32 +65,14 @@
     private final static ResultApplicator mGreyApplicator = new ResultApplicator() {
         @Override
         public void apply(View parent, View view, boolean apply, boolean reset) {
-            NotificationHeaderView header = (NotificationHeaderView) view;
-            ImageView icon = (ImageView) view.findViewById(
-                    com.android.internal.R.id.icon);
-            ImageView expand = (ImageView) view.findViewById(
-                    com.android.internal.R.id.expand_button);
-            applyToChild(icon, apply, header.getOriginalIconColor());
-            applyToChild(expand, apply, header.getOriginalNotificationColor());
-        }
-
-        private void applyToChild(View view, boolean shouldApply, int originalColor) {
-            if (originalColor != NotificationHeaderView.NO_COLOR) {
-                ImageView imageView = (ImageView) view;
-                imageView.getDrawable().mutate();
-                if (shouldApply) {
-                    // lets gray it out
-                    Configuration config = view.getContext().getResources().getConfiguration();
-                    boolean inNightMode = (config.uiMode & Configuration.UI_MODE_NIGHT_MASK)
-                            == Configuration.UI_MODE_NIGHT_YES;
-                    int grey = ContrastColorUtil.resolveColor(view.getContext(),
-                            Notification.COLOR_DEFAULT, inNightMode);
-                    imageView.getDrawable().setColorFilter(grey, PorterDuff.Mode.SRC_ATOP);
-                } else {
-                    // lets reset it
-                    imageView.getDrawable().setColorFilter(originalColor,
-                            PorterDuff.Mode.SRC_ATOP);
-                }
+            CachingIconView icon = view.findViewById(com.android.internal.R.id.icon);
+            if (icon != null) {
+                icon.setGrayedOut(apply);
+            }
+            NotificationExpandButton expand =
+                    view.findViewById(com.android.internal.R.id.expand_button);
+            if (expand != null) {
+                expand.setGrayedOut(apply);
             }
         }
     };
@@ -178,7 +158,7 @@
 
     private void sanitizeHeaderViews(ExpandableNotificationRow row) {
         if (row.isSummaryWithChildren()) {
-            sanitizeHeader(row.getNotificationHeader());
+            sanitizeHeader(row.getNotificationViewWrapper().getNotificationHeader());
             return;
         }
         final NotificationContentView layout = row.getPrivateLayout();
@@ -275,7 +255,8 @@
         }
 
         public void init() {
-            mParentView = mParentRow.getNotificationHeader().findViewById(mId);
+            mParentView = mParentRow.getNotificationViewWrapper().getNotificationHeader()
+                    .findViewById(mId);
             mParentData = mExtractor == null ? null : mExtractor.extractData(mParentRow);
             mApply = !mComparator.isEmpty(mParentView);
         }
@@ -305,7 +286,7 @@
         public void apply(ExpandableNotificationRow row, boolean reset) {
             boolean apply = mApply && !reset;
             if (row.isSummaryWithChildren()) {
-                applyToView(apply, reset, row.getNotificationHeader());
+                applyToView(apply, reset, row.getNotificationViewWrapper().getNotificationHeader());
                 return;
             }
             applyToView(apply, reset, row.getPrivateLayout().getContractedChild());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
index 83e51cd..7ecdc81 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
@@ -106,12 +106,8 @@
             mViewTransformationAnimation.cancel();
         }
         mViewTransformationAnimation = ValueAnimator.ofFloat(0.0f, 1.0f);
-        mViewTransformationAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                transformTo(notification, animation.getAnimatedFraction());
-            }
-        });
+        mViewTransformationAnimation.addUpdateListener(
+                animation -> transformTo(notification, animation.getAnimatedFraction()));
         mViewTransformationAnimation.setInterpolator(Interpolators.LINEAR);
         mViewTransformationAnimation.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
         mViewTransformationAnimation.addListener(new AnimatorListenerAdapter() {
@@ -167,12 +163,8 @@
             mViewTransformationAnimation.cancel();
         }
         mViewTransformationAnimation = ValueAnimator.ofFloat(0.0f, 1.0f);
-        mViewTransformationAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                transformFrom(notification, animation.getAnimatedFraction());
-            }
-        });
+        mViewTransformationAnimation.addUpdateListener(
+                animation -> transformFrom(notification, animation.getAnimatedFraction()));
         mViewTransformationAnimation.addListener(new AnimatorListenerAdapter() {
             public boolean mCancelled;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
index 433c8b0..ddfa18e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
@@ -27,10 +27,10 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.row.NotificationContentView
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
 import java.util.concurrent.ConcurrentHashMap
 import javax.inject.Inject
 
@@ -85,38 +85,43 @@
                 for (entry in activeConversationEntries) {
                     if (rankingMap.getRanking(entry.sbn.key, ranking) && ranking.isConversation) {
                         val important = ranking.channel.isImportantConversation
-                        val layouts = entry.row?.layouts?.asSequence()
+                        var changed = false
+                        entry.row?.layouts?.asSequence()
                                 ?.flatMap(::getLayouts)
                                 ?.mapNotNull { it as? ConversationLayout }
-                                ?: emptySequence()
-                        var changed = false
-                        for (layout in layouts) {
-                            if (important == layout.isImportantConversation) {
-                                continue
-                            }
-                            changed = true
-                            if (important && entry.isMarkedForUserTriggeredMovement) {
-                                // delay this so that it doesn't animate in until after
-                                // the notif has been moved in the shade
-                                mainHandler.postDelayed({
-                                    layout.setIsImportantConversation(
-                                            important, true /* animate */)
-                                }, IMPORTANCE_ANIMATION_DELAY.toLong())
-                            } else {
-                                layout.setIsImportantConversation(important)
-                            }
-                        }
+                                ?.filterNot { it.isImportantConversation == important }
+                                ?.forEach { layout ->
+                                    changed = true
+                                    if (important && entry.isMarkedForUserTriggeredMovement) {
+                                        // delay this so that it doesn't animate in until after
+                                        // the notif has been moved in the shade
+                                        mainHandler.postDelayed(
+                                                {
+                                                    layout.setIsImportantConversation(
+                                                            important,
+                                                            true)
+                                                },
+                                                IMPORTANCE_ANIMATION_DELAY.toLong())
+                                    } else {
+                                        layout.setIsImportantConversation(important, false)
+                                    }
+                                }
                         if (changed) {
                             notificationGroupManager.updateIsolation(entry)
+                            // ensure that the conversation icon isn't hidden
+                            // (ex: if it was showing in the shelf)
+                            entry.row?.updateIconVisibilities()
                         }
                     }
                 }
             }
 
             override fun onEntryInflated(entry: NotificationEntry) {
-                if (!entry.ranking.isConversation) return
+                if (!entry.ranking.isConversation) {
+                    return
+                }
                 fun updateCount(isExpanded: Boolean) {
-                    if (isExpanded && (!notifPanelCollapsed || entry.isPinnedAndExpanded())) {
+                    if (isExpanded && (!notifPanelCollapsed || entry.isPinnedAndExpanded)) {
                         resetCount(entry.key)
                         entry.row?.let(::resetBadgeUi)
                     }
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 e1e77b0..7c3b791 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -618,6 +618,7 @@
         NotificationEntry entry = mPendingNotifications.get(key);
         if (entry != null) {
             entry.setSbn(notification);
+            entry.setRanking(ranking);
         } else {
             entry = new NotificationEntry(
                     notification,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
index 133ddfe..6da4d8b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
@@ -41,8 +41,6 @@
  */
 @SysUISingleton
 public class RankingCoordinator implements Coordinator {
-    private static final String TAG = "RankingNotificationCoordinator";
-
     private final StatusBarStateController mStatusBarStateController;
     private final HighPriorityProvider mHighPriorityProvider;
     private final NodeController mSilentHeaderController;
@@ -65,7 +63,7 @@
         mStatusBarStateController.addCallback(mStatusBarStateCallback);
 
         pipeline.addPreGroupFilter(mSuspendedFilter);
-        pipeline.addPreGroupFilter(mDozingFilter);
+        pipeline.addPreGroupFilter(mDndVisualEffectsFilter);
     }
 
     public NotifSectioner getAlertingSectioner() {
@@ -114,10 +112,10 @@
         }
     };
 
-    private final NotifFilter mDozingFilter = new NotifFilter("IsDozingFilter") {
+    private final NotifFilter mDndVisualEffectsFilter = new NotifFilter(
+            "DndSuppressingVisualEffects") {
         @Override
         public boolean shouldFilterOut(NotificationEntry entry, long now) {
-            // Dozing + DND Settings from Ranking object
             if (mStatusBarStateController.isDozing() && entry.shouldSuppressAmbient()) {
                 return true;
             }
@@ -130,7 +128,7 @@
             new StatusBarStateController.StateListener() {
                 @Override
                 public void onDozingChanged(boolean isDozing) {
-                    mDozingFilter.invalidateList();
+                    mDndVisualEffectsFilter.invalidateList();
                 }
             };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index f1727ec..094e866 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -124,6 +124,7 @@
     private float mAppearAnimationTranslation;
     private int mNormalColor;
     private boolean mIsBelowSpeedBump;
+    private long mLastActionUpTime;
 
     private float mNormalBackgroundVisibilityAmount;
     private float mDimmedBackgroundFadeInAmount = -1;
@@ -225,6 +226,22 @@
         return super.onInterceptTouchEvent(ev);
     }
 
+    /** Sets the last action up time this view was touched. */
+    void setLastActionUpTime(long eventTime) {
+        mLastActionUpTime = eventTime;
+    }
+
+    /**
+     * Returns the last action up time. The last time will also be cleared because the source of
+     * action is not only from touch event. That prevents the caller from utilizing the time with
+     * unrelated event. The time can be 0 if the event is unavailable.
+     */
+    public long getAndResetLastActionUpTime() {
+        long lastActionUpTime = mLastActionUpTime;
+        mLastActionUpTime = 0;
+        return lastActionUpTime;
+    }
+
     protected boolean disallowSingleClick(MotionEvent ev) {
         return false;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
index dd30c89..41ce51c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.notification.row;
 
+import android.os.SystemClock;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.accessibility.AccessibilityManager;
@@ -92,6 +93,9 @@
                 mBlockNextTouch = false;
                 return true;
             }
+            if (ev.getAction() == MotionEvent.ACTION_UP) {
+                mView.setLastActionUpTime(SystemClock.uptimeMillis());
+            }
             if (mNeedsDimming && !mAccessibilityManager.isTouchExplorationEnabled()
                     && mView.isInteractive()) {
                 if (mNeedsDimming && !mView.isDimmed()) {
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 113c115..3f13306 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
@@ -304,7 +304,6 @@
             }
         }
     };
-    private boolean mForceUnlocked;
     private boolean mKeepInParent;
     private boolean mRemoved;
     private static final Property<ExpandableNotificationRow, Float> TRANSLATE_CONTENT =
@@ -410,8 +409,14 @@
             setIconAnimationRunning(running, l);
         }
         if (mIsSummaryWithChildren) {
-            setIconAnimationRunningForChild(running, mChildrenContainer.getHeaderView());
-            setIconAnimationRunningForChild(running, mChildrenContainer.getLowPriorityHeaderView());
+            NotificationViewWrapper viewWrapper = mChildrenContainer.getNotificationViewWrapper();
+            if (viewWrapper != null) {
+                setIconAnimationRunningForChild(running, viewWrapper.getIcon());
+            }
+            NotificationViewWrapper lowPriWrapper = mChildrenContainer.getLowPriorityViewWrapper();
+            if (lowPriWrapper != null) {
+                setIconAnimationRunningForChild(running, lowPriWrapper.getIcon());
+            }
             List<ExpandableNotificationRow> notificationChildren =
                     mChildrenContainer.getAttachedChildren();
             for (int i = 0; i < notificationChildren.size(); i++) {
@@ -435,10 +440,9 @@
 
     private void setIconAnimationRunningForChild(boolean running, View child) {
         if (child != null) {
-            ImageView icon = (ImageView) child.findViewById(com.android.internal.R.id.icon);
+            ImageView icon = child.findViewById(com.android.internal.R.id.icon);
             setIconRunning(icon, running);
-            ImageView rightIcon = (ImageView) child.findViewById(
-                    com.android.internal.R.id.right_icon);
+            ImageView rightIcon = child.findViewById(com.android.internal.R.id.right_icon);
             setIconRunning(rightIcon, running);
         }
     }
@@ -497,7 +501,7 @@
 
     /**
      * Returns whether this row is considered non-blockable (i.e. it's a non-blockable system notif
-     * or is in a whitelist).
+     * or is in an allowList).
      */
     public boolean getIsNonblockable() {
         // If the SystemNotifAsyncTask hasn't finished running or retrieved a value, we'll try once
@@ -594,7 +598,7 @@
 
     public int getOriginalIconColor() {
         if (mIsSummaryWithChildren && !shouldShowPublic()) {
-            return mChildrenContainer.getVisibleHeader().getOriginalIconColor();
+            return mChildrenContainer.getVisibleWrapper().getOriginalIconColor();
         }
         int color = getShowingLayout().getOriginalIconColor();
         if (color != Notification.COLOR_INVALID) {
@@ -1040,22 +1044,25 @@
         }
     }
 
-    public NotificationHeaderView getNotificationHeader() {
+    /**
+     * @return the main notification view wrapper.
+     */
+    public NotificationViewWrapper getNotificationViewWrapper() {
         if (mIsSummaryWithChildren) {
-            return mChildrenContainer.getHeaderView();
+            return mChildrenContainer.getNotificationViewWrapper();
         }
-        return mPrivateLayout.getNotificationHeader();
+        return mPrivateLayout.getNotificationViewWrapper();
     }
 
     /**
-     * @return the currently visible notification header. This can be different from
-     * {@link #getNotificationHeader()} in case it is a low-priority group.
+     * @return the currently visible notification view wrapper. This can be different from
+     * {@link #getNotificationViewWrapper()} in case it is a low-priority group.
      */
-    public NotificationHeaderView getVisibleNotificationHeader() {
+    public NotificationViewWrapper getVisibleNotificationViewWrapper() {
         if (mIsSummaryWithChildren && !shouldShowPublic()) {
-            return mChildrenContainer.getVisibleHeader();
+            return mChildrenContainer.getVisibleWrapper();
         }
-        return getShowingLayout().getVisibleNotificationHeader();
+        return getShowingLayout().getVisibleWrapper();
     }
 
     public void setLongPressListener(LongPressListener longPressListener) {
@@ -1294,16 +1301,6 @@
         onAttachedChildrenCountChanged();
     }
 
-    public void setForceUnlocked(boolean forceUnlocked) {
-        mForceUnlocked = forceUnlocked;
-        if (mIsSummaryWithChildren) {
-            List<ExpandableNotificationRow> notificationChildren = getAttachedChildren();
-            for (ExpandableNotificationRow child : notificationChildren) {
-                child.setForceUnlocked(forceUnlocked);
-            }
-        }
-    }
-
     @Override
     public void dismiss(boolean refocusOnDismiss) {
         super.dismiss(refocusOnDismiss);
@@ -1422,7 +1419,7 @@
     @Override
     public View getShelfTransformationTarget() {
         if (mIsSummaryWithChildren && !shouldShowPublic()) {
-            return mChildrenContainer.getVisibleHeader().getIcon();
+            return mChildrenContainer.getVisibleWrapper().getShelfTransformationTarget();
         }
         return getShowingLayout().getShelfTransformationTarget();
     }
@@ -1478,8 +1475,9 @@
         }
     }
 
-    private void updateIconVisibilities() {
-        // The shelficon is never hidden for children in groups
+    /** Refreshes the visibility of notification icons */
+    public void updateIconVisibilities() {
+        // The shelf icon is never hidden for children in groups
         boolean visible = !isChildInGroup() && mShelfIconVisible;
         for (NotificationContentView l : mLayouts) {
             l.setShelfIconVisible(visible);
@@ -1692,37 +1690,30 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mPublicLayout = (NotificationContentView) findViewById(R.id.expandedPublic);
-        mPrivateLayout = (NotificationContentView) findViewById(R.id.expanded);
+        mPublicLayout = findViewById(R.id.expandedPublic);
+        mPrivateLayout = findViewById(R.id.expanded);
         mLayouts = new NotificationContentView[] {mPrivateLayout, mPublicLayout};
 
         for (NotificationContentView l : mLayouts) {
             l.setExpandClickListener(mExpandClickListener);
             l.setContainingNotification(this);
         }
-        mGutsStub = (ViewStub) findViewById(R.id.notification_guts_stub);
-        mGutsStub.setOnInflateListener(new ViewStub.OnInflateListener() {
-            @Override
-            public void onInflate(ViewStub stub, View inflated) {
-                mGuts = (NotificationGuts) inflated;
-                mGuts.setClipTopAmount(getClipTopAmount());
-                mGuts.setActualHeight(getActualHeight());
-                mGutsStub = null;
-            }
+        mGutsStub = findViewById(R.id.notification_guts_stub);
+        mGutsStub.setOnInflateListener((stub, inflated) -> {
+            mGuts = (NotificationGuts) inflated;
+            mGuts.setClipTopAmount(getClipTopAmount());
+            mGuts.setActualHeight(getActualHeight());
+            mGutsStub = null;
         });
-        mChildrenContainerStub = (ViewStub) findViewById(R.id.child_container_stub);
-        mChildrenContainerStub.setOnInflateListener(new ViewStub.OnInflateListener() {
+        mChildrenContainerStub = findViewById(R.id.child_container_stub);
+        mChildrenContainerStub.setOnInflateListener((stub, inflated) -> {
+            mChildrenContainer = (NotificationChildrenContainer) inflated;
+            mChildrenContainer.setIsLowPriority(mIsLowPriority);
+            mChildrenContainer.setContainingNotification(ExpandableNotificationRow.this);
+            mChildrenContainer.onNotificationUpdated();
 
-            @Override
-            public void onInflate(ViewStub stub, View inflated) {
-                mChildrenContainer = (NotificationChildrenContainer) inflated;
-                mChildrenContainer.setIsLowPriority(mIsLowPriority);
-                mChildrenContainer.setContainingNotification(ExpandableNotificationRow.this);
-                mChildrenContainer.onNotificationUpdated();
-
-                if (mShouldTranslateContents) {
-                    mTranslateableViews.add(mChildrenContainer);
-                }
+            if (mShouldTranslateContents) {
+                mTranslateableViews.add(mChildrenContainer);
             }
         });
 
@@ -2182,7 +2173,7 @@
     }
 
     public boolean isUserLocked() {
-        return mUserLocked && !mForceUnlocked;
+        return mUserLocked;
     }
 
     public void setUserLocked(boolean userLocked) {
@@ -2302,8 +2293,12 @@
     private void onAttachedChildrenCountChanged() {
         mIsSummaryWithChildren = mChildrenContainer != null
                 && mChildrenContainer.getNotificationChildCount() > 0;
-        if (mIsSummaryWithChildren && mChildrenContainer.getHeaderView() == null) {
-            mChildrenContainer.recreateNotificationHeader(mExpandClickListener, isConversation());
+        if (mIsSummaryWithChildren) {
+            NotificationViewWrapper wrapper = mChildrenContainer.getNotificationViewWrapper();
+            if (wrapper == null || wrapper.getNotificationHeader() == null) {
+                mChildrenContainer.recreateNotificationHeader(mExpandClickListener,
+                        isConversation());
+            }
         }
         getShowingLayout().updateBackgroundColor(false /* animate */);
         mPrivateLayout.updateExpandButtons(isExpandable());
@@ -2413,9 +2408,9 @@
      * the top.
      */
     private void updateContentShiftHeight() {
-        NotificationHeaderView notificationHeader = getVisibleNotificationHeader();
-        if (notificationHeader != null) {
-            CachingIconView icon = notificationHeader.getIcon();
+        NotificationViewWrapper wrapper = getVisibleNotificationViewWrapper();
+        CachingIconView icon = wrapper == null ? null : wrapper.getIcon();
+        if (icon != null) {
             mIconTransformContentShift = getRelativeTopPadding(icon) + icon.getHeight();
         } else {
             mIconTransformContentShift = mContentShift;
@@ -2503,12 +2498,7 @@
                     .alpha(0f)
                     .setStartDelay(delay)
                     .setDuration(duration)
-                    .withEndAction(new Runnable() {
-                        @Override
-                        public void run() {
-                            hiddenView.setVisibility(View.INVISIBLE);
-                        }
-                    });
+                    .withEndAction(() -> hiddenView.setVisibility(View.INVISIBLE));
         }
         for (View showView : shownChildren) {
             showView.setVisibility(View.VISIBLE);
@@ -2866,7 +2856,8 @@
         }
         float x = event.getX();
         float y = event.getY();
-        NotificationHeaderView header = getVisibleNotificationHeader();
+        NotificationViewWrapper wrapper = getVisibleNotificationViewWrapper();
+        NotificationHeaderView header = wrapper == null ? null : wrapper.getNotificationHeader();
         if (header != null && header.isInTouchRect(x - getTranslation(), y)) {
             return true;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index 1d72557..c995e32 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -81,21 +81,27 @@
     private final PeopleNotificationIdentifier mPeopleNotificationIdentifier;
 
     @Inject
-    public ExpandableNotificationRowController(ExpandableNotificationRow view,
+    public ExpandableNotificationRowController(
+            ExpandableNotificationRow view,
             NotificationListContainer listContainer,
             ActivatableNotificationViewController activatableNotificationViewController,
-            NotificationMediaManager mediaManager, PluginManager pluginManager,
-            SystemClock clock, @AppName String appName, @NotificationKey String notificationKey,
+            NotificationMediaManager mediaManager,
+            PluginManager pluginManager,
+            SystemClock clock,
+            @AppName String appName,
+            @NotificationKey String notificationKey,
             KeyguardBypassController keyguardBypassController,
             GroupMembershipManager groupMembershipManager,
             GroupExpansionManager groupExpansionManager,
             RowContentBindStage rowContentBindStage,
-            NotificationLogger notificationLogger, HeadsUpManager headsUpManager,
+            NotificationLogger notificationLogger,
+            HeadsUpManager headsUpManager,
             ExpandableNotificationRow.OnExpandClickListener onExpandClickListener,
             StatusBarStateController statusBarStateController,
             NotificationGutsManager notificationGutsManager,
             @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress,
-            OnUserInteractionCallback onUserInteractionCallback, FalsingManager falsingManager,
+            OnUserInteractionCallback onUserInteractionCallback,
+            FalsingManager falsingManager,
             PeopleNotificationIdentifier peopleNotificationIdentifier) {
         mView = view;
         mListContainer = listContainer;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 9bcac11..c2c4590 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -42,17 +42,15 @@
 import com.android.systemui.media.MediaFeatureFlag;
 import com.android.systemui.statusbar.InflationTask;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.SmartReplyController;
 import com.android.systemui.statusbar.notification.ConversationNotificationProcessor;
 import com.android.systemui.statusbar.notification.InflationException;
 import com.android.systemui.statusbar.notification.MediaNotificationProcessor;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
 import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.InflatedSmartReplies;
 import com.android.systemui.statusbar.policy.InflatedSmartReplies.SmartRepliesAndActions;
-import com.android.systemui.statusbar.policy.SmartReplyConstants;
+import com.android.systemui.statusbar.policy.SmartRepliesAndActionsInflater;
 import com.android.systemui.util.Assert;
 
 import java.util.HashMap;
@@ -60,8 +58,6 @@
 
 import javax.inject.Inject;
 
-import dagger.Lazy;
-
 /**
  * {@link NotificationContentInflater} binds content to a {@link ExpandableNotificationRow} by
  * asynchronously building the content's {@link RemoteViews} and applying it to the row.
@@ -76,27 +72,24 @@
     private final boolean mIsMediaInQS;
     private final NotificationRemoteInputManager mRemoteInputManager;
     private final NotifRemoteViewCache mRemoteViewCache;
-    private final Lazy<SmartReplyConstants> mSmartReplyConstants;
-    private final Lazy<SmartReplyController> mSmartReplyController;
     private final ConversationNotificationProcessor mConversationProcessor;
     private final Executor mBgExecutor;
+    private final SmartRepliesAndActionsInflater mSmartRepliesAndActionsInflater;
 
     @Inject
     NotificationContentInflater(
             NotifRemoteViewCache remoteViewCache,
             NotificationRemoteInputManager remoteInputManager,
-            Lazy<SmartReplyConstants> smartReplyConstants,
-            Lazy<SmartReplyController> smartReplyController,
             ConversationNotificationProcessor conversationProcessor,
             MediaFeatureFlag mediaFeatureFlag,
-            @Background Executor bgExecutor) {
+            @Background Executor bgExecutor,
+            SmartRepliesAndActionsInflater smartRepliesInflater) {
         mRemoteViewCache = remoteViewCache;
         mRemoteInputManager = remoteInputManager;
-        mSmartReplyConstants = smartReplyConstants;
-        mSmartReplyController = smartReplyController;
         mConversationProcessor = conversationProcessor;
         mIsMediaInQS = mediaFeatureFlag.getEnabled();
         mBgExecutor = bgExecutor;
+        mSmartRepliesAndActionsInflater = smartRepliesInflater;
     }
 
     @Override
@@ -132,8 +125,6 @@
                 contentToBind,
                 mRemoteViewCache,
                 entry,
-                mSmartReplyConstants.get(),
-                mSmartReplyController.get(),
                 mConversationProcessor,
                 row,
                 bindParams.isLowPriority,
@@ -141,7 +132,8 @@
                 bindParams.usesIncreasedHeadsUpHeight,
                 callback,
                 mRemoteInputManager.getRemoteViewsOnClickHandler(),
-                mIsMediaInQS);
+                mIsMediaInQS,
+                mSmartRepliesAndActionsInflater);
         if (mInflateSynchronously) {
             task.onPostExecute(task.doInBackground());
         } else {
@@ -157,17 +149,19 @@
             boolean inflateSynchronously,
             @InflationFlag int reInflateFlags,
             Notification.Builder builder,
-            Context packageContext) {
+            Context packageContext,
+            SmartRepliesAndActionsInflater smartRepliesInflater) {
         InflationProgress result = createRemoteViews(reInflateFlags,
                 builder,
                 bindParams.isLowPriority,
                 bindParams.usesIncreasedHeight,
                 bindParams.usesIncreasedHeadsUpHeight,
                 packageContext);
+
         result = inflateSmartReplyViews(result, reInflateFlags, entry,
-                row.getContext(), packageContext, row.getHeadsUpManager(),
-                mSmartReplyConstants.get(), mSmartReplyController.get(),
-                row.getExistingSmartRepliesAndActions());
+                row.getContext(), packageContext,
+                row.getExistingSmartRepliesAndActions(),
+                smartRepliesInflater);
 
         apply(
                 mBgExecutor,
@@ -268,22 +262,21 @@
         }
     }
 
-    private static InflationProgress inflateSmartReplyViews(InflationProgress result,
-            @InflationFlag int reInflateFlags, NotificationEntry entry, Context context,
-            Context packageContext, HeadsUpManager headsUpManager,
-            SmartReplyConstants smartReplyConstants, SmartReplyController smartReplyController,
-            SmartRepliesAndActions previousSmartRepliesAndActions) {
+    private static InflationProgress inflateSmartReplyViews(
+            InflationProgress result,
+            @InflationFlag int reInflateFlags,
+            NotificationEntry entry,
+            Context context,
+            Context packageContext,
+            SmartRepliesAndActions previousSmartRepliesAndActions,
+            SmartRepliesAndActionsInflater inflater) {
         if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0 && result.newExpandedView != null) {
-            result.expandedInflatedSmartReplies =
-                    InflatedSmartReplies.inflate(
-                            context, packageContext, entry, smartReplyConstants,
-                            smartReplyController, headsUpManager, previousSmartRepliesAndActions);
+            result.expandedInflatedSmartReplies = inflater.inflateSmartReplies(
+                    context, packageContext, entry, previousSmartRepliesAndActions);
         }
         if ((reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0 && result.newHeadsUpView != null) {
-            result.headsUpInflatedSmartReplies =
-                    InflatedSmartReplies.inflate(
-                            context, packageContext, entry, smartReplyConstants,
-                            smartReplyController, headsUpManager, previousSmartRepliesAndActions);
+            result.headsUpInflatedSmartReplies = inflater.inflateSmartReplies(
+                    context, packageContext, entry, previousSmartRepliesAndActions);
         }
         return result;
     }
@@ -709,8 +702,6 @@
         private final boolean mUsesIncreasedHeadsUpHeight;
         private final @InflationFlag int mReInflateFlags;
         private final NotifRemoteViewCache mRemoteViewCache;
-        private final SmartReplyConstants mSmartReplyConstants;
-        private final SmartReplyController mSmartReplyController;
         private final Executor mBgExecutor;
         private ExpandableNotificationRow mRow;
         private Exception mError;
@@ -718,6 +709,7 @@
         private CancellationSignal mCancellationSignal;
         private final ConversationNotificationProcessor mConversationProcessor;
         private final boolean mIsMediaInQS;
+        private final SmartRepliesAndActionsInflater mSmartRepliesInflater;
 
         private AsyncInflationTask(
                 Executor bgExecutor,
@@ -725,8 +717,6 @@
                 @InflationFlag int reInflateFlags,
                 NotifRemoteViewCache cache,
                 NotificationEntry entry,
-                SmartReplyConstants smartReplyConstants,
-                SmartReplyController smartReplyController,
                 ConversationNotificationProcessor conversationProcessor,
                 ExpandableNotificationRow row,
                 boolean isLowPriority,
@@ -734,15 +724,15 @@
                 boolean usesIncreasedHeadsUpHeight,
                 InflationCallback callback,
                 RemoteViews.OnClickHandler remoteViewClickHandler,
-                boolean isMediaFlagEnabled) {
+                boolean isMediaFlagEnabled,
+                SmartRepliesAndActionsInflater smartRepliesInflater) {
             mEntry = entry;
             mRow = row;
-            mSmartReplyConstants = smartReplyConstants;
-            mSmartReplyController = smartReplyController;
             mBgExecutor = bgExecutor;
             mInflateSynchronously = inflateSynchronously;
             mReInflateFlags = reInflateFlags;
             mRemoteViewCache = cache;
+            mSmartRepliesInflater = smartRepliesInflater;
             mContext = mRow.getContext();
             mIsLowPriority = isLowPriority;
             mUsesIncreasedHeight = usesIncreasedHeight;
@@ -786,10 +776,16 @@
                 InflationProgress inflationProgress = createRemoteViews(mReInflateFlags,
                         recoveredBuilder, mIsLowPriority, mUsesIncreasedHeight,
                         mUsesIncreasedHeadsUpHeight, packageContext);
-                return inflateSmartReplyViews(inflationProgress, mReInflateFlags, mEntry,
-                        mRow.getContext(), packageContext, mRow.getHeadsUpManager(),
-                        mSmartReplyConstants, mSmartReplyController,
-                        mRow.getExistingSmartRepliesAndActions());
+                SmartRepliesAndActions repliesAndActions =
+                        mRow.getExistingSmartRepliesAndActions();
+                return inflateSmartReplyViews(
+                        inflationProgress,
+                        mReInflateFlags,
+                        mEntry,
+                        mContext,
+                        packageContext,
+                        repliesAndActions,
+                        mSmartRepliesInflater);
             } catch (Exception e) {
                 mError = e;
                 return null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 1de9308..160b6f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -44,7 +44,6 @@
 import com.android.internal.util.ContrastColorUtil;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.MediaTransferManager;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.SmartReplyController;
 import com.android.systemui.statusbar.TransformableView;
@@ -57,6 +56,7 @@
 import com.android.systemui.statusbar.policy.InflatedSmartReplies;
 import com.android.systemui.statusbar.policy.InflatedSmartReplies.SmartRepliesAndActions;
 import com.android.systemui.statusbar.policy.RemoteInputView;
+import com.android.systemui.statusbar.policy.SmartRepliesAndActionsInflaterKt;
 import com.android.systemui.statusbar.policy.SmartReplyConstants;
 import com.android.systemui.statusbar.policy.SmartReplyView;
 
@@ -172,12 +172,10 @@
     private boolean mIsContentExpandable;
     private boolean mRemoteInputVisible;
     private int mUnrestrictedContentHeight;
-    private MediaTransferManager mMediaTransferManager;
 
     public NotificationContentView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mHybridGroupManager = new HybridGroupManager(getContext());
-        mMediaTransferManager = new MediaTransferManager(getContext());
         mSmartReplyConstants = Dependency.get(SmartReplyConstants.class);
         mSmartReplyController = Dependency.get(SmartReplyController.class);
         initView();
@@ -1020,6 +1018,10 @@
                 mSingleLineView };
     }
 
+    public NotificationViewWrapper getVisibleWrapper() {
+        return getVisibleWrapper(mVisibleType);
+    }
+
     public NotificationViewWrapper getVisibleWrapper(int visibleType) {
         switch (visibleType) {
             case VISIBLE_TYPE_EXPANDED:
@@ -1156,7 +1158,6 @@
             mHeadsUpWrapper.onContentUpdated(row);
         }
         applyRemoteInputAndSmartReply(entry);
-        applyMediaTransfer(entry);
         updateLegacy();
         mForceSelectNextLayout = true;
         mPreviousExpandedRemoteInputIntent = null;
@@ -1184,22 +1185,14 @@
         }
     }
 
-    private void applyMediaTransfer(final NotificationEntry entry) {
-        if (!entry.isMediaNotification()) {
-            return;
-        }
-
-        View bigContentView = mExpandedChild;
-        if (bigContentView != null && (bigContentView instanceof ViewGroup)) {
-            mMediaTransferManager.applyMediaTransferView((ViewGroup) bigContentView,
-                    entry);
-        }
-
-        View smallContentView = mContractedChild;
-        if (smallContentView != null && (smallContentView instanceof ViewGroup)) {
-            mMediaTransferManager.applyMediaTransferView((ViewGroup) smallContentView,
-                    entry);
-        }
+    /**
+     * Returns whether the {@link Notification} represented by entry has a free-form remote input.
+     * Such an input can be used e.g. to implement smart reply buttons - by passing the replies
+     * through the remote input.
+     */
+    public static boolean hasFreeformRemoteInput(NotificationEntry entry) {
+        Notification notification = entry.getSbn().getNotification();
+        return null != notification.findRemoteInputActionPair(true /* freeform */);
     }
 
     private void applyRemoteInputAndSmartReply(final NotificationEntry entry) {
@@ -1207,7 +1200,7 @@
             return;
         }
 
-        applyRemoteInput(entry, InflatedSmartReplies.hasFreeformRemoteInput(entry));
+        applyRemoteInput(entry, hasFreeformRemoteInput(entry));
 
         if (mExpandedInflatedSmartReplies == null && mHeadsUpInflatedSmartReplies == null) {
             if (DEBUG) {
@@ -1438,7 +1431,8 @@
         }
 
         LinearLayout smartReplyContainer = (LinearLayout) smartReplyContainerCandidate;
-        if (!InflatedSmartReplies.shouldShowSmartReplyView(entry, smartRepliesAndActions)) {
+        if (!SmartRepliesAndActionsInflaterKt
+                .shouldShowSmartReplyView(entry, smartRepliesAndActions)) {
             smartReplyContainer.setVisibility(View.GONE);
             return null;
         }
@@ -1551,18 +1545,20 @@
         mIsContentExpandable = expandable;
     }
 
-    public NotificationHeaderView getNotificationHeader() {
-        NotificationHeaderView header = null;
-        if (mContractedChild != null) {
-            header = mContractedWrapper.getNotificationHeader();
+    /**
+     * @return a view wrapper for one of the inflated states of the notification.
+     */
+    public NotificationViewWrapper getNotificationViewWrapper() {
+        if (mContractedChild != null && mContractedWrapper != null) {
+            return mContractedWrapper;
         }
-        if (header == null && mExpandedChild != null) {
-            header = mExpandedWrapper.getNotificationHeader();
+        if (mExpandedChild != null && mExpandedWrapper != null) {
+            return mExpandedWrapper;
         }
-        if (header == null && mHeadsUpChild != null) {
-            header = mHeadsUpWrapper.getNotificationHeader();
+        if (mHeadsUpChild != null && mHeadsUpWrapper != null) {
+            return mHeadsUpWrapper;
         }
-        return header;
+        return null;
     }
 
     public void showFeedbackIcon(boolean show) {
@@ -1590,11 +1586,6 @@
         }
     }
 
-    public NotificationHeaderView getVisibleNotificationHeader() {
-        NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
-        return wrapper == null ? null : wrapper.getNotificationHeader();
-    }
-
     public void setContainingNotification(ExpandableNotificationRow containingNotification) {
         mContainingNotification = containingNotification;
     }
@@ -1652,11 +1643,9 @@
         }
         if (mExpandedWrapper != null) {
             mExpandedWrapper.setRemoved();
-            mMediaTransferManager.setRemoved(mExpandedChild);
         }
         if (mContractedWrapper != null) {
             mContractedWrapper.setRemoved();
-            mMediaTransferManager.setRemoved(mContractedChild);
         }
         if (mHeadsUpWrapper != null) {
             mHeadsUpWrapper.setRemoved();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java
index 41f93cc..d58c183 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java
@@ -37,7 +37,7 @@
     }
 
     private void resolveViews(StatusBarNotification notification) {
-        mBigtext = (ImageFloatingTextView) mView.findViewById(com.android.internal.R.id.big_text);
+        mBigtext = mView.findViewById(com.android.internal.R.id.big_text);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
index fe70c81..17f326b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
@@ -147,6 +147,8 @@
                 // hiding the conversationIcon will already do that via its listener.
                 return
             }
+        } else {
+            conversationIconView.isForceHidden = false
         }
         super.setShelfIconVisible(visible)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index 3f58674..8c82756 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -55,8 +55,6 @@
 
     protected final ViewTransformationHelper mTransformationHelper;
 
-    protected int mColor;
-
     private CachingIconView mIcon;
     private NotificationExpandButton mExpandButton;
     protected NotificationHeaderView mNotificationHeader;
@@ -119,7 +117,6 @@
         mFeedbackIcon = mView.findViewById(com.android.internal.R.id.feedback);
         if (mNotificationHeader != null) {
             mNotificationHeader.setShowExpandButtonAtEnd(mShowExpandButtonAtEnd);
-            mColor = mNotificationHeader.getOriginalIconColor();
         }
     }
 
@@ -279,6 +276,11 @@
     }
 
     @Override
+    public void setExpanded(boolean expanded) {
+        mExpandButton.setExpanded(expanded);
+    }
+
+    @Override
     public void setRecentlyAudiblyAlerted(boolean audiblyAlerted) {
         if (mAudiblyAlertedIcon != null) {
             mAudiblyAlertedIcon.setVisibility(audiblyAlerted ? View.VISIBLE : View.GONE);
@@ -296,6 +298,11 @@
     }
 
     @Override
+    public CachingIconView getIcon() {
+        return mIcon;
+    }
+
+    @Override
     public int getOriginalIconColor() {
         return mIcon.getOriginalIconColor();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
index 33c93905..2535e5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
@@ -370,7 +370,7 @@
             return;
         }
 
-        int tintColor = getNotificationHeader().getOriginalIconColor();
+        int tintColor = getOriginalIconColor();
         mSeekBarElapsedTime.setTextColor(tintColor);
         mSeekBarTotalTime.setTextColor(tintColor);
         mSeekBarTotalTime.setShadowLayer(1.5f, 1.5f, 1.5f, mBackgroundColor);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
index 14aab9d..76ec59e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
@@ -140,13 +140,13 @@
     }
 
     private void resolveTemplateViews(StatusBarNotification notification) {
-        mPicture = (ImageView) mView.findViewById(com.android.internal.R.id.right_icon);
+        mPicture = mView.findViewById(com.android.internal.R.id.right_icon);
         if (mPicture != null) {
             mPicture.setTag(ImageTransformState.ICON_TAG,
                     notification.getNotification().getLargeIcon());
         }
-        mTitle = (TextView) mView.findViewById(com.android.internal.R.id.title);
-        mText = (TextView) mView.findViewById(com.android.internal.R.id.text);
+        mTitle = mView.findViewById(com.android.internal.R.id.title);
+        mText = mView.findViewById(com.android.internal.R.id.text);
         final View progress = mView.findViewById(com.android.internal.R.id.progress);
         if (progress instanceof ProgressBar) {
             mProgressBar = (ProgressBar) progress;
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 42f5e38..6920e3f 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
@@ -29,7 +29,6 @@
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
-import android.util.ArraySet;
 import android.view.NotificationHeaderView;
 import android.view.View;
 import android.view.ViewGroup;
@@ -38,7 +37,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.graphics.ColorUtils;
 import com.android.internal.util.ContrastColorUtil;
-import com.android.internal.widget.ConversationLayout;
+import com.android.internal.widget.CachingIconView;
 import com.android.systemui.statusbar.CrossFadeHelper;
 import com.android.systemui.statusbar.TransformableView;
 import com.android.systemui.statusbar.notification.TransformState;
@@ -67,8 +66,7 @@
             } else if ("messaging".equals(v.getTag())) {
                 return new NotificationMessagingTemplateViewWrapper(ctx, v, row);
             } else if ("conversation".equals(v.getTag())) {
-                return new NotificationConversationTemplateViewWrapper(ctx, (ConversationLayout) v,
-                        row);
+                return new NotificationConversationTemplateViewWrapper(ctx, v, row);
             }
             Class<? extends Notification.Style> style =
                     row.getEntry().getSbn().getNotification().getNotificationStyle();
@@ -231,6 +229,9 @@
      */
     public void updateExpandability(boolean expandable, View.OnClickListener onClickListener) {}
 
+    /** Set the expanded state on the view wrapper */
+    public void setExpanded(boolean expanded) {}
+
     /**
      * @return the notification header if it exists
      */
@@ -241,7 +242,16 @@
     /**
      * @return the expand button if it exists
      */
-    public @Nullable View getExpandButton() {
+    @Nullable
+    public View getExpandButton() {
+        return null;
+    }
+
+    /**
+     * @return the icon if it exists
+     */
+    @Nullable
+    public CachingIconView getIcon() {
         return null;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index a396305..00bccfc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -31,6 +31,7 @@
 import android.widget.TextView;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.widget.CachingIconView;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.CrossFadeHelper;
 import com.android.systemui.statusbar.NotificationHeaderUtil;
@@ -318,9 +319,8 @@
         RemoteViews header = builder.makeNotificationHeader();
         if (mNotificationHeader == null) {
             mNotificationHeader = (NotificationHeaderView) header.apply(getContext(), this);
-            final View expandButton = mNotificationHeader.findViewById(
-                    com.android.internal.R.id.expand_button);
-            expandButton.setVisibility(VISIBLE);
+            mNotificationHeader.findViewById(com.android.internal.R.id.expand_button)
+                    .setVisibility(VISIBLE);
             mNotificationHeader.setOnClickListener(mHeaderClickListener);
             mNotificationHeaderWrapper = NotificationViewWrapper.wrap(getContext(),
                     mNotificationHeader, mContainingNotification);
@@ -361,9 +361,8 @@
             if (mNotificationHeaderLowPriority == null) {
                 mNotificationHeaderLowPriority = (NotificationHeaderView) header.apply(getContext(),
                         this);
-                final View expandButton = mNotificationHeaderLowPriority.findViewById(
-                        com.android.internal.R.id.expand_button);
-                expandButton.setVisibility(VISIBLE);
+                mNotificationHeaderLowPriority.findViewById(com.android.internal.R.id.expand_button)
+                        .setVisibility(VISIBLE);
                 mNotificationHeaderLowPriority.setOnClickListener(mHeaderClickListener);
                 mNotificationHeaderWrapperLowPriority = NotificationViewWrapper.wrap(getContext(),
                         mNotificationHeaderLowPriority, mContainingNotification);
@@ -849,8 +848,8 @@
     public void setChildrenExpanded(boolean childrenExpanded) {
         mChildrenExpanded = childrenExpanded;
         updateExpansionStates();
-        if (mNotificationHeader != null) {
-            mNotificationHeader.setExpanded(childrenExpanded);
+        if (mNotificationHeaderWrapper != null) {
+            mNotificationHeaderWrapper.setExpanded(childrenExpanded);
         }
         final int count = mAttachedChildren.size();
         for (int childIdx = 0; childIdx < count; childIdx++) {
@@ -869,12 +868,12 @@
         return mContainingNotification;
     }
 
-    public NotificationHeaderView getHeaderView() {
-        return mNotificationHeader;
+    public NotificationViewWrapper getNotificationViewWrapper() {
+        return mNotificationHeaderWrapper;
     }
 
-    public NotificationHeaderView getLowPriorityHeaderView() {
-        return mNotificationHeaderLowPriority;
+    public NotificationViewWrapper getLowPriorityViewWrapper() {
+        return mNotificationHeaderWrapperLowPriority;
     }
 
     @VisibleForTesting
@@ -1224,16 +1223,15 @@
 
     public void setShelfIconVisible(boolean iconVisible) {
         if (mNotificationHeaderWrapper != null) {
-            NotificationHeaderView header = mNotificationHeaderWrapper.getNotificationHeader();
-            if (header != null) {
-                header.getIcon().setForceHidden(iconVisible);
+            CachingIconView icon = mNotificationHeaderWrapper.getIcon();
+            if (icon != null) {
+                icon.setForceHidden(iconVisible);
             }
         }
         if (mNotificationHeaderWrapperLowPriority != null) {
-            NotificationHeaderView header
-                    = mNotificationHeaderWrapperLowPriority.getNotificationHeader();
-            if (header != null) {
-                header.getIcon().setForceHidden(iconVisible);
+            CachingIconView icon = mNotificationHeaderWrapperLowPriority.getIcon();
+            if (icon != null) {
+                icon.setForceHidden(iconVisible);
             }
         }
     }
@@ -1254,12 +1252,14 @@
         }
     }
 
-    public NotificationHeaderView getVisibleHeader() {
-        NotificationHeaderView header = mNotificationHeader;
+    /**
+     * @return the view wrapper for the currently showing priority.
+     */
+    public NotificationViewWrapper getVisibleWrapper() {
         if (showingAsLowPriority()) {
-            header = mNotificationHeaderLowPriority;
+            return mNotificationHeaderWrapperLowPriority;
         }
-        return header;
+        return mNotificationHeaderWrapper;
     }
 
     public void onExpansionChanged() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index efb2469..a5667bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -456,6 +456,7 @@
             return;
         }
         mSuppressed = suppressed;
+        mDozeLog.traceDozingSuppressed(mSuppressed);
         for (Callback callback : mCallbacks) {
             callback.onDozeSuppressedChanged(suppressed);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index af6ac22..2767b7e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -38,9 +38,9 @@
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.keyguard.dagger.KeyguardBouncerComponent;
-import com.android.keyguard.dagger.RootView;
 import com.android.systemui.DejankUtils;
 import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.dagger.qualifiers.RootView;
 import com.android.systemui.keyguard.DismissCallbackRegistry;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.shared.system.SysUiStatsLog;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index a3f14ba..ef4108b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -23,6 +23,7 @@
 import android.util.MathUtils;
 
 import com.android.keyguard.KeyguardStatusView;
+import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 
@@ -122,6 +123,8 @@
      */
     private int mUnlockedStackScrollerPadding;
 
+    private int mLockScreenMode;
+
     /**
      * Refreshes the dimension values.
      */
@@ -171,6 +174,13 @@
         result.clockX = (int) interpolate(0, burnInPreventionOffsetX(), mDarkAmount);
     }
 
+    /**
+     * Update lock screen mode for testing different layouts
+     */
+    public void onLockScreenModeChanged(int mode) {
+        mLockScreenMode = mode;
+    }
+
     public float getMinStackScrollerPadding() {
         return mBypassEnabled ? mUnlockedStackScrollerPadding
                 : mMinTopMargin + mKeyguardStatusHeight + mClockNotificationsMargin;
@@ -185,6 +195,9 @@
     }
 
     private int getExpandedPreferredClockY() {
+        if (mLockScreenMode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL) {
+            return mMinTopMargin;
+        }
         return (mHasCustomClock && (!mHasVisibleNotifs || mBypassEnabled)) ? getPreferredClockY()
                 : getExpandedClockPosition();
     }
@@ -228,6 +241,13 @@
         clockYDark = MathUtils.lerp(clockYBouncer, clockYDark, shadeExpansion);
 
         float darkAmount = mBypassEnabled && !mHasCustomClock ? 1.0f : mDarkAmount;
+
+        // TODO(b/12836565) - prototyping only adjustment
+        if (mLockScreenMode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL) {
+            // This will keep the clock at the top for AOD
+            darkAmount = 0f;
+        }
+
         return (int) (MathUtils.lerp(clockY, clockYDark, darkAmount) + mEmptyDragAmount);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 3f636ff..47b16c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -70,6 +70,7 @@
 import com.android.internal.util.LatencyTracker;
 import com.android.keyguard.KeyguardClockSwitchController;
 import com.android.keyguard.KeyguardStatusView;
+import com.android.keyguard.KeyguardStatusViewController;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.keyguard.dagger.KeyguardStatusViewComponent;
@@ -202,9 +203,6 @@
     private static final Rect M_DUMMY_DIRTY_RECT = new Rect(0, 0, 1, 1);
     private static final Rect EMPTY_RECT = new Rect();
 
-    private static final AnimationProperties
-            CLOCK_ANIMATION_PROPERTIES =
-            new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
     private final AnimatableProperty KEYGUARD_HEADS_UP_SHOWING_AMOUNT = AnimatableProperty.from(
             "KEYGUARD_HEADS_UP_SHOWING_AMOUNT",
             (notificationPanelView, aFloat) -> setKeyguardHeadsUpShowingAmount(aFloat),
@@ -221,6 +219,11 @@
             new KeyguardUpdateMonitorCallback() {
 
                 @Override
+                public void onLockScreenModeChanged(int mode) {
+                    mClockPositionAlgorithm.onLockScreenModeChanged(mode);
+                }
+
+                @Override
                 public void onBiometricAuthenticated(int userId,
                         BiometricSourceType biometricSourceType,
                         boolean isStrongBiometric) {
@@ -275,7 +278,7 @@
     private ViewGroup mBigClockContainer;
     private QS mQs;
     private FrameLayout mQsFrame;
-    private KeyguardStatusView mKeyguardStatusView;
+    private KeyguardStatusViewController mKeyguardStatusViewController;
     private View mQsNavbarScrim;
     private NotificationsQuickSettingsContainer mNotificationContainerParent;
     private boolean mAnimateNextPositionUpdate;
@@ -339,7 +342,6 @@
     private boolean mIsLaunchTransitionRunning;
     private Runnable mLaunchAnimationEndRunnable;
     private boolean mOnlyAffordanceInThisMotion;
-    private boolean mKeyguardStatusViewAnimating;
     private ValueAnimator mQsSizeChangeAnimator;
 
     private boolean mQsScrimEnabled = true;
@@ -601,16 +603,8 @@
     private void onFinishInflate() {
         loadDimens();
         mKeyguardStatusBar = mView.findViewById(R.id.keyguard_header);
-        mKeyguardStatusView = mView.findViewById(R.id.keyguard_status_view);
-
-        KeyguardClockSwitchController keyguardClockSwitchController =
-                mKeyguardStatusViewComponentFactory
-                        .build(mKeyguardStatusView)
-                        .getKeyguardClockSwitchController();
-        keyguardClockSwitchController.init();
         mBigClockContainer = mView.findViewById(R.id.big_clock_container);
-        keyguardClockSwitchController.setBigClockContainer(mBigClockContainer);
-
+        updateViewControllers(mView.findViewById(R.id.keyguard_status_view));
         mNotificationContainerParent = mView.findViewById(R.id.notification_container_parent);
         NotificationStackScrollLayout stackScrollLayout = mView.findViewById(
                 R.id.notification_stack_scroller);
@@ -684,11 +678,24 @@
                 R.dimen.heads_up_status_bar_padding);
     }
 
+    private void updateViewControllers(KeyguardStatusView keyguardStatusView) {
+        // Re-associate the KeyguardStatusViewController
+        KeyguardStatusViewComponent statusViewComponent =
+                mKeyguardStatusViewComponentFactory.build(keyguardStatusView);
+        mKeyguardStatusViewController = statusViewComponent.getKeyguardStatusViewController();
+        mKeyguardStatusViewController.init();
+
+        // Re-associate the clock container with the keyguard clock switch.
+        KeyguardClockSwitchController keyguardClockSwitchController =
+                statusViewComponent.getKeyguardClockSwitchController();
+        keyguardClockSwitchController.setBigClockContainer(mBigClockContainer);
+    }
+
     /**
      * Returns if there's a custom clock being presented.
      */
     public boolean hasCustomClock() {
-        return mKeyguardStatusView.hasCustomClock();
+        return mKeyguardStatusViewController.hasCustomClock();
     }
 
     private void setStatusBar(StatusBar bar) {
@@ -725,21 +732,16 @@
 
     private void reInflateViews() {
         // Re-inflate the status view group.
-        int index = mView.indexOfChild(mKeyguardStatusView);
-        mView.removeView(mKeyguardStatusView);
-        mKeyguardStatusView = (KeyguardStatusView) mInjectionInflationController.injectable(
+        KeyguardStatusView keyguardStatusView = mView.findViewById(R.id.keyguard_status_view);
+        int index = mView.indexOfChild(keyguardStatusView);
+        mView.removeView(keyguardStatusView);
+        keyguardStatusView = (KeyguardStatusView) mInjectionInflationController.injectable(
                 LayoutInflater.from(mView.getContext())).inflate(
                 R.layout.keyguard_status_view, mView, false);
-        mView.addView(mKeyguardStatusView, index);
+        mView.addView(keyguardStatusView, index);
 
-        // Re-associate the clock container with the keyguard clock switch.
         mBigClockContainer.removeAllViews();
-        KeyguardClockSwitchController keyguardClockSwitchController =
-                mKeyguardStatusViewComponentFactory
-                        .build(mKeyguardStatusView)
-                        .getKeyguardClockSwitchController();
-        keyguardClockSwitchController.init();
-        keyguardClockSwitchController.setBigClockContainer(mBigClockContainer);
+        updateViewControllers(keyguardStatusView);
 
         // Update keyguard bottom area
         index = mView.indexOfChild(mKeyguardBottomArea);
@@ -759,7 +761,11 @@
             mKeyguardStatusBar.onThemeChanged();
         }
 
-        setKeyguardStatusViewVisibility(mBarState, false, false);
+        mKeyguardStatusViewController.setKeyguardStatusViewVisibility(
+                mBarState,
+                false,
+                false,
+                mBarState);
         setKeyguardBottomAreaVisibility(mBarState, false);
         if (mOnReinflationListener != null) {
             mOnReinflationListener.run();
@@ -853,23 +859,23 @@
         } else {
             int totalHeight = mView.getHeight();
             int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding);
-            int clockPreferredY = mKeyguardStatusView.getClockPreferredY(totalHeight);
+            int clockPreferredY = mKeyguardStatusViewController.getClockPreferredY(totalHeight);
             boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled();
             final boolean hasVisibleNotifications = !bypassEnabled
                     && mNotificationStackScrollLayoutController.getVisibleNotificationCount() != 0;
-            mKeyguardStatusView.setHasVisibleNotifications(hasVisibleNotifications);
+            mKeyguardStatusViewController.setHasVisibleNotifications(hasVisibleNotifications);
             mClockPositionAlgorithm.setup(mStatusBarMinHeight, totalHeight - bottomPadding,
                     mNotificationStackScrollLayoutController.getIntrinsicContentHeight(),
                     getExpandedFraction(),
-                    totalHeight, (int) (mKeyguardStatusView.getHeight() - mShelfHeight / 2.0f
-                            - mDarkIconSize / 2.0f), clockPreferredY, hasCustomClock(),
+                    totalHeight,
+                    (int) (mKeyguardStatusViewController.getHeight()
+                            - mShelfHeight / 2.0f - mDarkIconSize / 2.0f),
+                    clockPreferredY, hasCustomClock(),
                     hasVisibleNotifications, mInterpolatedDarkAmount, mEmptyDragAmount,
                     bypassEnabled, getUnlockedStackScrollerPadding());
             mClockPositionAlgorithm.run(mClockPositionResult);
-            PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.X,
-                    mClockPositionResult.clockX, CLOCK_ANIMATION_PROPERTIES, animateClock);
-            PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.Y,
-                    mClockPositionResult.clockY, CLOCK_ANIMATION_PROPERTIES, animateClock);
+            mKeyguardStatusViewController.updatePosition(
+                    mClockPositionResult.clockX, mClockPositionResult.clockY, animateClock);
             updateNotificationTranslucency();
             updateClock();
             stackScrollerPadding = mClockPositionResult.stackScrollerPaddingExpanded;
@@ -905,7 +911,7 @@
         float availableSpace =
                 mNotificationStackScrollLayoutController.getHeight() - minPadding - shelfSize
                         - Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding)
-                        - mKeyguardStatusView.getLogoutButtonHeight();
+                        - mKeyguardStatusViewController.getLogoutButtonHeight();
         int count = 0;
         ExpandableView previousView = null;
         for (int i = 0; i < mNotificationStackScrollLayoutController.getChildCount(); i++) {
@@ -1000,9 +1006,7 @@
     }
 
     private void updateClock() {
-        if (!mKeyguardStatusViewAnimating) {
-            mKeyguardStatusView.setAlpha(mClockPositionResult.clockAlpha);
-        }
+        mKeyguardStatusViewController.setAlpha(mClockPositionResult.clockAlpha);
     }
 
     public void animateToFullShade(long delay) {
@@ -1600,29 +1604,6 @@
         }
     }
 
-    private final Runnable mAnimateKeyguardStatusViewInvisibleEndRunnable = new Runnable() {
-        @Override
-        public void run() {
-            mKeyguardStatusViewAnimating = false;
-            mKeyguardStatusView.setVisibility(View.INVISIBLE);
-        }
-    };
-
-    private final Runnable mAnimateKeyguardStatusViewGoneEndRunnable = new Runnable() {
-        @Override
-        public void run() {
-            mKeyguardStatusViewAnimating = false;
-            mKeyguardStatusView.setVisibility(View.GONE);
-        }
-    };
-
-    private final Runnable mAnimateKeyguardStatusViewVisibleEndRunnable = new Runnable() {
-        @Override
-        public void run() {
-            mKeyguardStatusViewAnimating = false;
-        }
-    };
-
     private final Runnable mAnimateKeyguardStatusBarInvisibleEndRunnable = new Runnable() {
         @Override
         public void run() {
@@ -1700,46 +1681,6 @@
         }
     }
 
-    private void setKeyguardStatusViewVisibility(int statusBarState, boolean keyguardFadingAway,
-            boolean goingToFullShade) {
-        mKeyguardStatusView.animate().cancel();
-        mKeyguardStatusViewAnimating = false;
-        if ((!keyguardFadingAway && mBarState == KEYGUARD
-                && statusBarState != KEYGUARD) || goingToFullShade) {
-            mKeyguardStatusViewAnimating = true;
-            mKeyguardStatusView.animate().alpha(0f).setStartDelay(0).setDuration(
-                    160).setInterpolator(Interpolators.ALPHA_OUT).withEndAction(
-                    mAnimateKeyguardStatusViewGoneEndRunnable);
-            if (keyguardFadingAway) {
-                mKeyguardStatusView.animate().setStartDelay(
-                        mKeyguardStateController.getKeyguardFadingAwayDelay()).setDuration(
-                        mKeyguardStateController.getShortenedFadingAwayDuration()).start();
-            }
-        } else if (mBarState == StatusBarState.SHADE_LOCKED
-                && statusBarState == KEYGUARD) {
-            mKeyguardStatusView.setVisibility(View.VISIBLE);
-            mKeyguardStatusViewAnimating = true;
-            mKeyguardStatusView.setAlpha(0f);
-            mKeyguardStatusView.animate().alpha(1f).setStartDelay(0).setDuration(
-                    320).setInterpolator(Interpolators.ALPHA_IN).withEndAction(
-                    mAnimateKeyguardStatusViewVisibleEndRunnable);
-        } else if (statusBarState == KEYGUARD) {
-            if (keyguardFadingAway) {
-                mKeyguardStatusViewAnimating = true;
-                mKeyguardStatusView.animate().alpha(0).translationYBy(
-                        -getHeight() * 0.05f).setInterpolator(
-                        Interpolators.FAST_OUT_LINEAR_IN).setDuration(125).setStartDelay(
-                        0).withEndAction(mAnimateKeyguardStatusViewInvisibleEndRunnable).start();
-            } else {
-                mKeyguardStatusView.setVisibility(View.VISIBLE);
-                mKeyguardStatusView.setAlpha(1f);
-            }
-        } else {
-            mKeyguardStatusView.setVisibility(View.GONE);
-            mKeyguardStatusView.setAlpha(1f);
-        }
-    }
-
     private void updateQsState() {
         mNotificationStackScrollLayoutController.setQsExpanded(mQsExpanded);
         mNotificationStackScrollLayoutController.setScrollingEnabled(
@@ -2070,7 +2011,7 @@
     private int getMaxPanelHeightBypass() {
         int position =
                 mClockPositionAlgorithm.getExpandedClockPosition()
-                        + mKeyguardStatusView.getHeight();
+                        + mKeyguardStatusViewController.getHeight();
         if (mNotificationStackScrollLayoutController.getVisibleNotificationCount() != 0) {
             position += mShelfHeight / 2.0f + mDarkIconSize / 2.0f;
         }
@@ -2151,7 +2092,7 @@
             int
                     minKeyguardPanelBottom =
                     mClockPositionAlgorithm.getExpandedClockPosition()
-                            + mKeyguardStatusView.getHeight()
+                            + mKeyguardStatusViewController.getHeight()
                             + mNotificationStackScrollLayoutController.getIntrinsicContentHeight();
             return Math.max(maxHeight, minKeyguardPanelBottom);
         } else {
@@ -2599,7 +2540,7 @@
     }
 
     public void onScreenTurningOn() {
-        mKeyguardStatusView.dozeTimeTick();
+        mKeyguardStatusViewController.dozeTimeTick();
     }
 
     @Override
@@ -2984,7 +2925,6 @@
             mAnimateNextPositionUpdate = false;
         }
         mNotificationStackScrollLayoutController.setPulsing(pulsing, animatePulse);
-        mKeyguardStatusView.setPulsing(pulsing);
     }
 
     public void setAmbientIndicationBottomPadding(int ambientIndicationBottomPadding) {
@@ -2996,14 +2936,14 @@
 
     public void dozeTimeTick() {
         mKeyguardBottomArea.dozeTimeTick();
-        mKeyguardStatusView.dozeTimeTick();
+        mKeyguardStatusViewController.dozeTimeTick();
         if (mInterpolatedDarkAmount > 0) {
             positionClockAndNotifications();
         }
     }
 
     public void setStatusAccessibilityImportance(int mode) {
-        mKeyguardStatusView.setImportantForAccessibility(mode);
+        mKeyguardStatusViewController.setStatusAccessibilityImportance(mode);
     }
 
     /**
@@ -3063,8 +3003,11 @@
      * security view of the bouncer.
      */
     public void onBouncerPreHideAnimation() {
-        setKeyguardStatusViewVisibility(mBarState, true /* keyguardFadingAway */,
-                false /* goingToFullShade */);
+        mKeyguardStatusViewController.setKeyguardStatusViewVisibility(
+                mBarState,
+                true /* keyguardFadingAway */,
+                false /* goingToFullShade */,
+                mBarState);
     }
 
     /**
@@ -3634,7 +3577,11 @@
             int oldState = mBarState;
             boolean keyguardShowing = statusBarState == KEYGUARD;
 
-            setKeyguardStatusViewVisibility(statusBarState, keyguardFadingAway, goingToFullShade);
+            mKeyguardStatusViewController.setKeyguardStatusViewVisibility(
+                    statusBarState,
+                    keyguardFadingAway,
+                    goingToFullShade,
+                    mBarState);
             setKeyguardBottomAreaVisibility(statusBarState, goingToFullShade);
 
             mBarState = statusBarState;
@@ -3685,7 +3632,7 @@
         public void onDozeAmountChanged(float linearAmount, float amount) {
             mInterpolatedDarkAmount = amount;
             mLinearDarkAmount = linearAmount;
-            mKeyguardStatusView.setDarkAmount(mInterpolatedDarkAmount);
+            mKeyguardStatusViewController.setDarkAmount(mInterpolatedDarkAmount);
             mKeyguardBottomArea.setDarkAmount(mInterpolatedDarkAmount);
             positionClockAndNotifications();
         }
@@ -3731,9 +3678,10 @@
             setIsFullWidth(mNotificationStackScrollLayoutController.getWidth() == mView.getWidth());
 
             // Update Clock Pivot
-            mKeyguardStatusView.setPivotX(mView.getWidth() / 2);
-            mKeyguardStatusView.setPivotY(
-                    (FONT_HEIGHT - CAP_HEIGHT) / 2048f * mKeyguardStatusView.getClockTextSize());
+            mKeyguardStatusViewController.setPivotX(mView.getWidth() / 2);
+            mKeyguardStatusViewController.setPivotY(
+                    (FONT_HEIGHT - CAP_HEIGHT) / 2048f
+                            * mKeyguardStatusViewController.getClockTextSize());
 
             // Calculate quick setting heights.
             int oldMaxHeight = mQsMaxExpansionHeight;
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 e42c3dc..6ed092f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -671,14 +671,18 @@
 
         mIconController.setIconVisibility(mSlotCamera, showCamera);
         mIconController.setIconVisibility(mSlotMicrophone, showMicrophone);
-        if (mPrivacyItemController.getAllIndicatorsAvailable()) {
+        if (mPrivacyItemController.getAllIndicatorsAvailable()
+                || mPrivacyItemController.getLocationAvailable()) {
             mIconController.setIconVisibility(mSlotLocation, showLocation);
         }
     }
 
     @Override
     public void onLocationActiveChanged(boolean active) {
-        if (!mPrivacyItemController.getAllIndicatorsAvailable()) updateLocationFromController();
+        if (!mPrivacyItemController.getAllIndicatorsAvailable()
+                && !mPrivacyItemController.getLocationAvailable()) {
+            updateLocationFromController();
+        }
     }
 
     // Updates the status view based on the current state of location requests.
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 e7c29b6..c7932bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -69,6 +69,7 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
 import android.graphics.Point;
 import android.graphics.PointF;
@@ -152,6 +153,7 @@
 import com.android.systemui.demomode.DemoMode;
 import com.android.systemui.demomode.DemoModeCommandReceiver;
 import com.android.systemui.demomode.DemoModeController;
+import com.android.systemui.emergency.EmergencyGesture;
 import com.android.systemui.fragments.ExtensionFragmentListener;
 import com.android.systemui.fragments.FragmentHostManager;
 import com.android.systemui.keyguard.DismissCallbackRegistry;
@@ -170,7 +172,7 @@
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSFragment;
-import com.android.systemui.qs.QSPanel;
+import com.android.systemui.qs.QSPanelController;
 import com.android.systemui.recents.ScreenPinningRequest;
 import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.shared.system.WindowManagerWrapper;
@@ -406,7 +408,7 @@
     protected NotificationPanelViewController mNotificationPanelViewController;
 
     // settings
-    private QSPanel mQSPanel;
+    private QSPanelController mQSPanelController;
 
     KeyguardIndicationController mKeyguardIndicationController;
 
@@ -819,7 +821,7 @@
                     updateScrimController();
                 };
 
-
+        mActivityIntentHelper = new ActivityIntentHelper(mContext);
         DateTimeView.setReceiverHandler(timeTickHandler);
     }
 
@@ -833,8 +835,6 @@
             mBubblesOptional.get().setExpandListener(mBubbleExpandListener);
         }
 
-        mActivityIntentHelper = new ActivityIntentHelper(mContext);
-
         mColorExtractor.addOnColorsChangedListener(this);
         mStatusBarStateController.addCallback(this,
                 SysuiStatusBarStateController.RANK_STATUS_BAR);
@@ -1200,8 +1200,8 @@
             fragmentHostManager.addTagListener(QS.TAG, (tag, f) -> {
                 QS qs = (QS) f;
                 if (qs instanceof QSFragment) {
-                    mQSPanel = ((QSFragment) qs).getQsPanel();
-                    mQSPanel.setBrightnessMirror(mBrightnessMirrorController);
+                    mQSPanelController = ((QSFragment) qs).getQSPanelController();
+                    mQSPanelController.setBrightnessMirror(mBrightnessMirrorController);
                 }
             });
         }
@@ -1593,19 +1593,19 @@
     }
 
     public void addQsTile(ComponentName tile) {
-        if (mQSPanel != null && mQSPanel.getHost() != null) {
-            mQSPanel.getHost().addTile(tile);
+        if (mQSPanelController != null && mQSPanelController.getHost() != null) {
+            mQSPanelController.getHost().addTile(tile);
         }
     }
 
     public void remQsTile(ComponentName tile) {
-        if (mQSPanel != null && mQSPanel.getHost() != null) {
-            mQSPanel.getHost().removeTile(tile);
+        if (mQSPanelController != null && mQSPanelController.getHost() != null) {
+            mQSPanelController.getHost().removeTile(tile);
         }
     }
 
     public void clickTile(ComponentName tile) {
-        mQSPanel.clickTile(tile);
+        mQSPanelController.clickTile(tile);
     }
 
     /**
@@ -2197,7 +2197,7 @@
         if (!mUserSetup) return;
 
         if (subPanel != null) {
-            mQSPanel.openDetails(subPanel);
+            mQSPanelController.openDetails(subPanel);
         }
         mNotificationPanelViewController.expandWithQs();
 
@@ -2845,7 +2845,7 @@
                 resetUserExpandedStates();
             }
             else if (DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG.equals(action)) {
-                mQSPanel.showDeviceMonitoringDialog();
+                mQSPanelController.showDeviceMonitoringDialog();
             }
             Trace.endSection();
         }
@@ -2936,8 +2936,8 @@
      */
     void updateResources() {
         // Update the quick setting tiles
-        if (mQSPanel != null) {
-            mQSPanel.updateResources();
+        if (mQSPanelController != null) {
+            mQSPanelController.updateResources();
         }
 
         if (mStatusBarWindowController != null) {
@@ -3402,8 +3402,8 @@
 
         // Keyguard state has changed, but QS is not listening anymore. Make sure to update the tile
         // visibilities so next time we open the panel we know the correct height already.
-        if (mQSPanel != null) {
-            mQSPanel.refreshAllTiles();
+        if (mQSPanelController != null) {
+            mQSPanelController.refreshAllTiles();
         }
         mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
         releaseGestureWakeLock();
@@ -3539,8 +3539,6 @@
         if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED) {
             if (mNotificationPanelViewController.canPanelBeCollapsed()) {
                 mShadeController.animateCollapsePanels();
-            } else if (mBubblesOptional.isPresent()) {
-                mBubblesOptional.get().performBackPressIfNeeded();
             }
             return true;
         }
@@ -3982,6 +3980,28 @@
         }
     }
 
+    @Override
+    public void onEmergencyActionLaunchGestureDetected() {
+        // TODO (b/169793384) Polish the panic gesture to be just like its older brother, camera.
+        Intent emergencyIntent = new Intent(EmergencyGesture.ACTION_LAUNCH_EMERGENCY);
+        PackageManager pm = mContext.getPackageManager();
+        ResolveInfo resolveInfo = pm.resolveActivity(emergencyIntent, /*flags=*/0);
+        if (resolveInfo == null) {
+            // TODO(b/171084088) Upgrade log to wtf when we have default app in main branch.
+            Log.d(TAG, "Couldn't find an app to process the emergency intent.");
+            return;
+        }
+
+        if (mVibrator != null && mVibrator.hasVibrator()) {
+            mVibrator.vibrate(500L);
+        }
+
+        emergencyIntent.setComponent(new ComponentName(resolveInfo.activityInfo.packageName,
+                resolveInfo.activityInfo.name));
+        emergencyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        startActivity(emergencyIntent, /*dismissShade=*/true);
+    }
+
     boolean isCameraAllowedByAdmin() {
         if (mDevicePolicyManager.getCameraDisabled(null,
                 mLockscreenUserManager.getCurrentUserId())) {
@@ -4284,6 +4304,19 @@
     }
 
     public static Bundle getActivityOptions(@Nullable RemoteAnimationAdapter animationAdapter) {
+        return getDefaultActivityOptions(animationAdapter).toBundle();
+    }
+
+    public static Bundle getActivityOptions(@Nullable RemoteAnimationAdapter animationAdapter,
+            boolean isKeyguardShowing, long eventTime) {
+        ActivityOptions options = getDefaultActivityOptions(animationAdapter);
+        options.setSourceInfo(isKeyguardShowing ? ActivityOptions.SourceInfo.TYPE_LOCKSCREEN
+                : ActivityOptions.SourceInfo.TYPE_NOTIFICATION, eventTime);
+        return options.toBundle();
+    }
+
+    public static ActivityOptions getDefaultActivityOptions(
+            @Nullable RemoteAnimationAdapter animationAdapter) {
         ActivityOptions options;
         if (animationAdapter != null) {
             options = ActivityOptions.makeRemoteAnimation(animationAdapter);
@@ -4293,7 +4326,7 @@
         // Anything launched from the notification shade should always go into the secondary
         // split-screen windowing mode.
         options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
-        return options.toBundle();
+        return options;
     }
 
     void visibilityChanged(boolean visible) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index aa01642..256ee20 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -30,6 +30,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.AsyncTask;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.RemoteException;
@@ -40,7 +41,6 @@
 import android.text.TextUtils;
 import android.util.EventLog;
 import android.view.RemoteAnimationAdapter;
-import android.view.View;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.statusbar.NotificationVisibility;
@@ -402,7 +402,7 @@
             PendingIntent intent,
             Intent fillInIntent,
             NotificationEntry entry,
-            View row,
+            ExpandableNotificationRow row,
             boolean wasOccluded,
             boolean isActivityIntent) {
         RemoteAnimationAdapter adapter = mActivityLaunchAnimator.getLaunchAnimation(row,
@@ -414,8 +414,11 @@
                         .registerRemoteAnimationForNextActivityStart(
                                 intent.getCreatorPackage(), adapter);
             }
+            long eventTime = row.getAndResetLastActionUpTime();
+            Bundle options = eventTime > 0 ? getActivityOptions(adapter,
+                    mKeyguardStateController.isShowing(), eventTime) : getActivityOptions(adapter);
             int launchResult = intent.sendAndReturnResult(mContext, 0, fillInIntent, null,
-                    null, null, getActivityOptions(adapter));
+                    null, null, options);
             mMainThreadHandler.post(() -> {
                 mActivityLaunchAnimator.setLaunchResult(launchResult, isActivityIntent);
             });
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java
index c6ae669..cbc8405 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java
@@ -19,27 +19,8 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Notification;
-import android.app.RemoteInput;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ResolveInfo;
-import android.os.Build;
-import android.util.Log;
-import android.util.Pair;
 import android.widget.Button;
 
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
-import com.android.systemui.Dependency;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.DevicePolicyManagerWrapper;
-import com.android.systemui.shared.system.PackageManagerWrapper;
-import com.android.systemui.statusbar.NotificationUiAdjustment;
-import com.android.systemui.statusbar.SmartReplyController;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-
-import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
@@ -48,13 +29,11 @@
  * thread, to later be accessed and modified on the (performance critical) UI thread.
  */
 public class InflatedSmartReplies {
-    private static final String TAG = "InflatedSmartReplies";
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     @Nullable private final SmartReplyView mSmartReplyView;
     @Nullable private final List<Button> mSmartSuggestionButtons;
     @NonNull private final SmartRepliesAndActions mSmartRepliesAndActions;
 
-    private InflatedSmartReplies(
+    public InflatedSmartReplies(
             @Nullable SmartReplyView smartReplyView,
             @Nullable List<Button> smartSuggestionButtons,
             @NonNull SmartRepliesAndActions smartRepliesAndActions) {
@@ -76,206 +55,6 @@
     }
 
     /**
-     * Inflate a SmartReplyView and its smart suggestions.
-     */
-    public static InflatedSmartReplies inflate(
-            Context context,
-            Context packageContext,
-            NotificationEntry entry,
-            SmartReplyConstants smartReplyConstants,
-            SmartReplyController smartReplyController,
-            HeadsUpManager headsUpManager,
-            SmartRepliesAndActions existingSmartRepliesAndActions) {
-        SmartRepliesAndActions newSmartRepliesAndActions =
-                chooseSmartRepliesAndActions(smartReplyConstants, entry);
-        if (!shouldShowSmartReplyView(entry, newSmartRepliesAndActions)) {
-            return new InflatedSmartReplies(null /* smartReplyView */,
-                    null /* smartSuggestionButtons */, newSmartRepliesAndActions);
-        }
-
-        // Only block clicks if the smart buttons are different from the previous set - to avoid
-        // scenarios where a user incorrectly cannot click smart buttons because the notification is
-        // updated.
-        boolean delayOnClickListener =
-                !areSuggestionsSimilar(existingSmartRepliesAndActions, newSmartRepliesAndActions);
-
-        SmartReplyView smartReplyView = SmartReplyView.inflate(context);
-
-        List<Button> suggestionButtons = new ArrayList<>();
-        if (newSmartRepliesAndActions.smartReplies != null) {
-            suggestionButtons.addAll(smartReplyView.inflateRepliesFromRemoteInput(
-                    newSmartRepliesAndActions.smartReplies, smartReplyController, entry,
-                    delayOnClickListener));
-        }
-        if (newSmartRepliesAndActions.smartActions != null) {
-            suggestionButtons.addAll(
-                    smartReplyView.inflateSmartActions(packageContext,
-                            newSmartRepliesAndActions.smartActions, smartReplyController, entry,
-                            headsUpManager, delayOnClickListener));
-        }
-
-        return new InflatedSmartReplies(smartReplyView, suggestionButtons,
-                newSmartRepliesAndActions);
-    }
-
-    @VisibleForTesting
-    static boolean areSuggestionsSimilar(
-            SmartRepliesAndActions left, SmartRepliesAndActions right) {
-        if (left == right) return true;
-        if (left == null || right == null) return false;
-
-        if (!left.getSmartReplies().equals(right.getSmartReplies())) {
-            return false;
-        }
-
-        return !NotificationUiAdjustment.areDifferent(
-                left.getSmartActions(), right.getSmartActions());
-    }
-
-    /**
-     * Returns whether we should show the smart reply view and its smart suggestions.
-     */
-    public static boolean shouldShowSmartReplyView(
-            NotificationEntry entry,
-            SmartRepliesAndActions smartRepliesAndActions) {
-        if (smartRepliesAndActions.smartReplies == null
-                && smartRepliesAndActions.smartActions == null) {
-            // There are no smart replies and no smart actions.
-            return false;
-        }
-        // If we are showing the spinner we don't want to add the buttons.
-        boolean showingSpinner = entry.getSbn().getNotification()
-                .extras.getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false);
-        if (showingSpinner) {
-            return false;
-        }
-        // If we are keeping the notification around while sending we don't want to add the buttons.
-        boolean hideSmartReplies = entry.getSbn().getNotification()
-                .extras.getBoolean(Notification.EXTRA_HIDE_SMART_REPLIES, false);
-        if (hideSmartReplies) {
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * Chose what smart replies and smart actions to display. App generated suggestions take
-     * precedence. So if the app provides any smart replies, we don't show any
-     * replies or actions generated by the NotificationAssistantService (NAS), and if the app
-     * provides any smart actions we also don't show any NAS-generated replies or actions.
-     */
-    @NonNull
-    public static SmartRepliesAndActions chooseSmartRepliesAndActions(
-            SmartReplyConstants smartReplyConstants,
-            final NotificationEntry entry) {
-        Notification notification = entry.getSbn().getNotification();
-        Pair<RemoteInput, Notification.Action> remoteInputActionPair =
-                notification.findRemoteInputActionPair(false /* freeform */);
-        Pair<RemoteInput, Notification.Action> freeformRemoteInputActionPair =
-                notification.findRemoteInputActionPair(true /* freeform */);
-
-        if (!smartReplyConstants.isEnabled()) {
-            if (DEBUG) {
-                Log.d(TAG, "Smart suggestions not enabled, not adding suggestions for "
-                        + entry.getSbn().getKey());
-            }
-            return new SmartRepliesAndActions(null, null);
-        }
-        // Only use smart replies from the app if they target P or above. We have this check because
-        // the smart reply API has been used for other things (Wearables) in the past. The API to
-        // add smart actions is new in Q so it doesn't require a target-sdk check.
-        boolean enableAppGeneratedSmartReplies = (!smartReplyConstants.requiresTargetingP()
-                || entry.targetSdk >= Build.VERSION_CODES.P);
-
-        boolean appGeneratedSmartRepliesExist =
-                enableAppGeneratedSmartReplies
-                        && remoteInputActionPair != null
-                        && !ArrayUtils.isEmpty(remoteInputActionPair.first.getChoices())
-                        && remoteInputActionPair.second.actionIntent != null;
-
-        List<Notification.Action> appGeneratedSmartActions = notification.getContextualActions();
-        boolean appGeneratedSmartActionsExist = !appGeneratedSmartActions.isEmpty();
-
-        SmartReplyView.SmartReplies smartReplies = null;
-        SmartReplyView.SmartActions smartActions = null;
-        if (appGeneratedSmartRepliesExist) {
-            smartReplies = new SmartReplyView.SmartReplies(
-                    Arrays.asList(remoteInputActionPair.first.getChoices()),
-                    remoteInputActionPair.first,
-                    remoteInputActionPair.second.actionIntent,
-                    false /* fromAssistant */);
-        }
-        if (appGeneratedSmartActionsExist) {
-            smartActions = new SmartReplyView.SmartActions(appGeneratedSmartActions,
-                    false /* fromAssistant */);
-        }
-        // Apps didn't provide any smart replies / actions, use those from NAS (if any).
-        if (!appGeneratedSmartRepliesExist && !appGeneratedSmartActionsExist) {
-            boolean useGeneratedReplies = !ArrayUtils.isEmpty(entry.getSmartReplies())
-                    && freeformRemoteInputActionPair != null
-                    && freeformRemoteInputActionPair.second.getAllowGeneratedReplies()
-                    && freeformRemoteInputActionPair.second.actionIntent != null;
-            if (useGeneratedReplies) {
-                smartReplies = new SmartReplyView.SmartReplies(
-                        entry.getSmartReplies(),
-                        freeformRemoteInputActionPair.first,
-                        freeformRemoteInputActionPair.second.actionIntent,
-                        true /* fromAssistant */);
-            }
-            boolean useSmartActions = !ArrayUtils.isEmpty(entry.getSmartActions())
-                    && notification.getAllowSystemGeneratedContextualActions();
-            if (useSmartActions) {
-                List<Notification.Action> systemGeneratedActions =
-                        entry.getSmartActions();
-                // Filter actions if we're in kiosk-mode - we don't care about screen pinning mode,
-                // since notifications aren't shown there anyway.
-                ActivityManagerWrapper activityManagerWrapper =
-                        Dependency.get(ActivityManagerWrapper.class);
-                if (activityManagerWrapper.isLockTaskKioskModeActive()) {
-                    systemGeneratedActions = filterWhiteListedLockTaskApps(systemGeneratedActions);
-                }
-                smartActions = new SmartReplyView.SmartActions(
-                        systemGeneratedActions, true /* fromAssistant */);
-            }
-        }
-        return new SmartRepliesAndActions(smartReplies, smartActions);
-    }
-
-    /**
-     * Filter actions so that only actions pointing to whitelisted apps are allowed.
-     * This filtering is only meaningful when in lock-task mode.
-     */
-    private static List<Notification.Action> filterWhiteListedLockTaskApps(
-            List<Notification.Action> actions) {
-        PackageManagerWrapper packageManagerWrapper = Dependency.get(PackageManagerWrapper.class);
-        DevicePolicyManagerWrapper devicePolicyManagerWrapper =
-                Dependency.get(DevicePolicyManagerWrapper.class);
-        List<Notification.Action> filteredActions = new ArrayList<>();
-        for (Notification.Action action : actions) {
-            if (action.actionIntent == null) continue;
-            Intent intent = action.actionIntent.getIntent();
-            //  Only allow actions that are explicit (implicit intents are not handled in lock-task
-            //  mode), and link to whitelisted apps.
-            ResolveInfo resolveInfo = packageManagerWrapper.resolveActivity(intent, 0 /* flags */);
-            if (resolveInfo != null && devicePolicyManagerWrapper.isLockTaskPermitted(
-                    resolveInfo.activityInfo.packageName)) {
-                filteredActions.add(action);
-            }
-        }
-        return filteredActions;
-    }
-
-    /**
-     * Returns whether the {@link Notification} represented by entry has a free-form remote input.
-     * Such an input can be used e.g. to implement smart reply buttons - by passing the replies
-     * through the remote input.
-     */
-    public static boolean hasFreeformRemoteInput(NotificationEntry entry) {
-        Notification notification = entry.getSbn().getNotification();
-        return null != notification.findRemoteInputActionPair(true /* freeform */);
-    }
-
-    /**
      * A storage for smart replies and smart action.
      */
     public static class SmartRepliesAndActions {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
index f52a6e0..f45178c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
@@ -342,7 +342,7 @@
                     }
                     v.setActivated(true);
                 }
-                switchTo(user);
+                onUserListItemClicked(user);
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index eb2d9bc..6b991f8e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -23,7 +23,6 @@
 import android.os.Looper;
 import android.provider.Settings.Global;
 import android.telephony.Annotation;
-import android.telephony.CdmaEriInformation;
 import android.telephony.CellSignalStrength;
 import android.telephony.CellSignalStrengthCdma;
 import android.telephony.PhoneStateListener;
@@ -427,11 +426,8 @@
         if (isCarrierNetworkChangeActive()) {
             return false;
         }
-        if (isCdma() && mServiceState != null) {
-            final int iconMode = mPhone.getCdmaEriInformation().getEriIconMode();
-            return mPhone.getCdmaEriInformation().getEriIconIndex() != CdmaEriInformation.ERI_OFF
-                    && (iconMode == CdmaEriInformation.ERI_ICON_MODE_NORMAL
-                    || iconMode == CdmaEriInformation.ERI_ICON_MODE_FLASH);
+        if (isCdma()) {
+            return mPhone.getCdmaEnhancedRoamingIndicatorIconIndex() != TelephonyManager.ERI_OFF;
         } else {
             return mServiceState != null && mServiceState.getRoaming();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 9c3395f..c84589a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -18,11 +18,13 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.app.RemoteInput;
+import android.content.ClipData;
 import android.content.ClipDescription;
 import android.content.Context;
 import android.content.Intent;
@@ -43,6 +45,7 @@
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
+import android.view.OnReceiveContentCallback;
 import android.view.View;
 import android.view.ViewAnimationUtils;
 import android.view.ViewGroup;
@@ -57,9 +60,6 @@
 import android.widget.ProgressBar;
 import android.widget.TextView;
 
-import androidx.core.view.inputmethod.InputConnectionCompat;
-import androidx.core.view.inputmethod.InputContentInfoCompat;
-
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.internal.statusbar.IStatusBarService;
@@ -73,7 +73,9 @@
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 import com.android.systemui.statusbar.phone.LightBarController;
 
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.Set;
 import java.util.function.Consumer;
 
 /**
@@ -313,6 +315,7 @@
         mRemoteInputs = remoteInputs;
         mRemoteInput = remoteInput;
         mEditText.setHint(mRemoteInput.getLabel());
+        mEditText.mSupportedMimeTypes = remoteInput.getAllowedDataTypes();
 
         mEntry.editedSuggestionInfo = editedSuggestionInfo;
         if (editedSuggestionInfo != null) {
@@ -571,6 +574,7 @@
         boolean mShowImeOnInputConnection;
         private LightBarController mLightBarController;
         UserHandle mUser;
+        private Set<String> mSupportedMimeTypes;
 
         public RemoteEditText(Context context, AttributeSet attrs) {
             super(context, attrs);
@@ -578,6 +582,40 @@
             mLightBarController = Dependency.get(LightBarController.class);
         }
 
+        @Override
+        protected void onFinishInflate() {
+            super.onFinishInflate();
+            setOnReceiveContentCallback(new OnReceiveContentCallback<View>() {
+                @Override
+                public boolean onReceiveContent(@NonNull View view, @NonNull Payload payload) {
+                    ClipData clip = payload.getClip();
+                    if (clip.getItemCount() == 0) {
+                        return false;
+                    }
+                    Uri contentUri = clip.getItemAt(0).getUri();
+                    ClipDescription description = clip.getDescription();
+                    String mimeType = null;
+                    if (description.getMimeTypeCount() > 0) {
+                        mimeType = description.getMimeType(0);
+                    }
+                    if (mimeType != null) {
+                        Intent dataIntent = mRemoteInputView
+                                .prepareRemoteInputFromData(mimeType, contentUri);
+                        mRemoteInputView.sendRemoteInput(dataIntent);
+                    }
+                    return true;
+                }
+
+                @NonNull
+                @Override
+                public Set<String> getSupportedMimeTypes(@NonNull View view) {
+                    return mSupportedMimeTypes != null
+                            ? mSupportedMimeTypes
+                            : Collections.emptySet();
+                }
+            });
+        }
+
         private void defocusIfNeeded(boolean animate) {
             if (mRemoteInputView != null && mRemoteInputView.mEntry.getRow().isChangingPosition()
                     || isTemporarilyDetached()) {
@@ -670,36 +708,7 @@
 
         @Override
         public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
-            // TODO: Pass RemoteInput data types to allow image insertion.
-            // String[] allowedDataTypes = mRemoteInputView.mRemoteInput.getAllowedDataTypes()
-            //     .toArray(new String[0]);
-            // EditorInfoCompat.setContentMimeTypes(outAttrs, allowedDataTypes);
-            final InputConnection inputConnection = super.onCreateInputConnection(outAttrs);
-
-            final InputConnectionCompat.OnCommitContentListener callback =
-                    new InputConnectionCompat.OnCommitContentListener() {
-                        @Override
-                        public boolean onCommitContent(
-                                InputContentInfoCompat inputContentInfoCompat, int i,
-                                Bundle bundle) {
-                            Uri contentUri = inputContentInfoCompat.getContentUri();
-                            ClipDescription description = inputContentInfoCompat.getDescription();
-                            String mimeType = null;
-                            if (description != null && description.getMimeTypeCount() > 0) {
-                                mimeType = description.getMimeType(0);
-                            }
-                            if (mimeType != null) {
-                                Intent dataIntent = mRemoteInputView.prepareRemoteInputFromData(
-                                        mimeType, contentUri);
-                                mRemoteInputView.sendRemoteInput(dataIntent);
-                            }
-                            return true;
-                        }
-                    };
-
-            InputConnection ic = inputConnection == null ? null :
-                    InputConnectionCompat.createWrapper(inputConnection, outAttrs, callback);
-
+            final InputConnection ic = super.onCreateInputConnection(outAttrs);
             Context userContext = null;
             try {
                 userContext = mContext.createPackageContextAsUser(
@@ -747,7 +756,6 @@
             } else {
                 setBackground(null);
             }
-
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartRepliesAndActionsInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartRepliesAndActionsInflater.kt
new file mode 100644
index 0000000..6a3a69c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartRepliesAndActionsInflater.kt
@@ -0,0 +1,464 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.policy
+
+import android.app.Notification
+import android.app.PendingIntent
+import android.app.RemoteInput
+import android.content.Context
+import android.content.Intent
+import android.os.Build
+import android.os.Bundle
+import android.os.SystemClock
+import android.util.Log
+import android.view.ContextThemeWrapper
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.view.accessibility.AccessibilityNodeInfo
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction
+import android.widget.Button
+import com.android.systemui.R
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.shared.system.ActivityManagerWrapper
+import com.android.systemui.shared.system.DevicePolicyManagerWrapper
+import com.android.systemui.shared.system.PackageManagerWrapper
+import com.android.systemui.statusbar.NotificationRemoteInputManager
+import com.android.systemui.statusbar.NotificationUiAdjustment
+import com.android.systemui.statusbar.SmartReplyController
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.logging.NotificationLogger
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil
+import com.android.systemui.statusbar.policy.InflatedSmartReplies.SmartRepliesAndActions
+import com.android.systemui.statusbar.policy.SmartReplyView.SmartActions
+import com.android.systemui.statusbar.policy.SmartReplyView.SmartButtonType
+import com.android.systemui.statusbar.policy.SmartReplyView.SmartReplies
+import javax.inject.Inject
+
+/** Returns whether we should show the smart reply view and its smart suggestions. */
+fun shouldShowSmartReplyView(
+    entry: NotificationEntry,
+    smartRepliesAndActions: SmartRepliesAndActions
+): Boolean {
+    if (smartRepliesAndActions.smartReplies == null
+            && smartRepliesAndActions.smartActions == null) {
+        // There are no smart replies and no smart actions.
+        return false
+    }
+    // If we are showing the spinner we don't want to add the buttons.
+    val showingSpinner = entry.sbn.notification.extras
+            .getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false)
+    if (showingSpinner) {
+        return false
+    }
+    // If we are keeping the notification around while sending we don't want to add the buttons.
+    return !entry.sbn.notification.extras
+            .getBoolean(Notification.EXTRA_HIDE_SMART_REPLIES, false)
+}
+
+/** Determines if two [SmartRepliesAndActions] are visually similar. */
+fun areSuggestionsSimilar(
+    left: SmartRepliesAndActions?,
+    right: SmartRepliesAndActions?
+): Boolean = when {
+    left === right -> true
+    left == null || right == null -> false
+    left.getSmartReplies() != right.getSmartReplies() -> false
+    else -> !NotificationUiAdjustment.areDifferent(left.getSmartActions(), right.getSmartActions())
+}
+
+interface SmartRepliesAndActionsInflater {
+    fun inflateSmartReplies(
+        sysuiContext: Context,
+        notifPackageContext: Context,
+        entry: NotificationEntry,
+        existingRepliesAndAction: SmartRepliesAndActions?
+    ): InflatedSmartReplies
+}
+
+/*internal*/ class SmartRepliesAndActionsInflaterImpl @Inject constructor(
+    private val constants: SmartReplyConstants,
+    private val activityManagerWrapper: ActivityManagerWrapper,
+    private val packageManagerWrapper: PackageManagerWrapper,
+    private val devicePolicyManagerWrapper: DevicePolicyManagerWrapper,
+    private val smartRepliesInflater: SmartReplyInflater,
+    private val smartActionsInflater: SmartActionInflater
+) : SmartRepliesAndActionsInflater {
+
+    override fun inflateSmartReplies(
+        sysuiContext: Context,
+        notifPackageContext: Context,
+        entry: NotificationEntry,
+        existingRepliesAndAction: SmartRepliesAndActions?
+    ): InflatedSmartReplies {
+        val newRepliesAndActions = chooseSmartRepliesAndActions(entry)
+        if (!shouldShowSmartReplyView(entry, newRepliesAndActions)) {
+            return InflatedSmartReplies(
+                    null /* smartReplyView */,
+                    null /* smartSuggestionButtons */,
+                    newRepliesAndActions)
+        }
+
+        // Only block clicks if the smart buttons are different from the previous set - to avoid
+        // scenarios where a user incorrectly cannot click smart buttons because the
+        // notification is updated.
+        val delayOnClickListener =
+                !areSuggestionsSimilar(existingRepliesAndAction, newRepliesAndActions)
+
+        val smartReplyView = SmartReplyView.inflate(sysuiContext, constants)
+
+        val smartReplies = newRepliesAndActions.smartReplies
+        smartReplyView.setSmartRepliesGeneratedByAssistant(smartReplies?.fromAssistant ?: false)
+        val smartReplyButtons = smartReplies?.let {
+            smartReplies.choices.asSequence().mapIndexed { index, choice ->
+                smartRepliesInflater.inflateReplyButton(
+                        smartReplyView,
+                        entry,
+                        smartReplies,
+                        index,
+                        choice,
+                        delayOnClickListener)
+            }
+        } ?: emptySequence()
+
+        val smartActionButtons = newRepliesAndActions.smartActions?.let { smartActions ->
+            val themedPackageContext =
+                    ContextThemeWrapper(notifPackageContext, sysuiContext.theme)
+            smartActions.actions.asSequence()
+                    .filter { it.actionIntent != null }
+                    .mapIndexed { index, action ->
+                        smartActionsInflater.inflateActionButton(
+                                smartReplyView,
+                                entry,
+                                smartActions,
+                                index,
+                                action,
+                                delayOnClickListener,
+                                themedPackageContext)
+                    }
+        } ?: emptySequence()
+
+        return InflatedSmartReplies(
+                smartReplyView,
+                (smartReplyButtons + smartActionButtons).toList(),
+                newRepliesAndActions)
+    }
+
+    /**
+     * Chose what smart replies and smart actions to display. App generated suggestions take
+     * precedence. So if the app provides any smart replies, we don't show any
+     * replies or actions generated by the NotificationAssistantService (NAS), and if the app
+     * provides any smart actions we also don't show any NAS-generated replies or actions.
+     */
+    fun chooseSmartRepliesAndActions(entry: NotificationEntry): SmartRepliesAndActions {
+        val notification = entry.sbn.notification
+        val remoteInputActionPair = notification.findRemoteInputActionPair(false /* freeform */)
+        val freeformRemoteInputActionPair =
+                notification.findRemoteInputActionPair(true /* freeform */)
+        if (!constants.isEnabled) {
+            if (DEBUG) {
+                Log.d(TAG, "Smart suggestions not enabled, not adding suggestions for "
+                        + entry.sbn.key)
+            }
+            return SmartRepliesAndActions(null, null)
+        }
+        // Only use smart replies from the app if they target P or above. We have this check because
+        // the smart reply API has been used for other things (Wearables) in the past. The API to
+        // add smart actions is new in Q so it doesn't require a target-sdk check.
+        val enableAppGeneratedSmartReplies = (!constants.requiresTargetingP()
+                || entry.targetSdk >= Build.VERSION_CODES.P)
+        val appGeneratedSmartActions = notification.contextualActions
+
+        var smartReplies: SmartReplies? = when {
+            enableAppGeneratedSmartReplies -> remoteInputActionPair?.let { pair ->
+                pair.second.actionIntent?.let { actionIntent ->
+                    if (pair.first.choices?.isNotEmpty() == true)
+                        SmartReplies(
+                                pair.first.choices.asList(),
+                                pair.first,
+                                actionIntent,
+                                false /* fromAssistant */)
+                    else null
+                }
+            }
+            else -> null
+        }
+        var smartActions: SmartActions? = when {
+            appGeneratedSmartActions.isNotEmpty() ->
+                SmartActions(appGeneratedSmartActions, false /* fromAssistant */)
+            else -> null
+        }
+        // Apps didn't provide any smart replies / actions, use those from NAS (if any).
+        if (smartReplies == null && smartActions == null) {
+            val entryReplies = entry.smartReplies
+            val entryActions = entry.smartActions
+            if (entryReplies.isNotEmpty()
+                    && freeformRemoteInputActionPair != null
+                    && freeformRemoteInputActionPair.second.allowGeneratedReplies
+                    && freeformRemoteInputActionPair.second.actionIntent != null) {
+                smartReplies = SmartReplies(
+                        entryReplies,
+                        freeformRemoteInputActionPair.first,
+                        freeformRemoteInputActionPair.second.actionIntent,
+                        true /* fromAssistant */)
+            }
+            if (entryActions.isNotEmpty()
+                    && notification.allowSystemGeneratedContextualActions) {
+                val systemGeneratedActions: List<Notification.Action> = when {
+                    activityManagerWrapper.isLockTaskKioskModeActive ->
+                        // Filter actions if we're in kiosk-mode - we don't care about screen
+                        // pinning mode, since notifications aren't shown there anyway.
+                        filterAllowlistedLockTaskApps(entryActions)
+                    else -> entryActions
+                }
+                smartActions = SmartActions(systemGeneratedActions, true /* fromAssistant */)
+            }
+        }
+        return SmartRepliesAndActions(smartReplies, smartActions)
+    }
+
+    /**
+     * Filter actions so that only actions pointing to allowlisted apps are permitted.
+     * This filtering is only meaningful when in lock-task mode.
+     */
+    private fun filterAllowlistedLockTaskApps(
+        actions: List<Notification.Action>
+    ): List<Notification.Action> = actions.filter { action ->
+        //  Only allow actions that are explicit (implicit intents are not handled in lock-task
+        //  mode), and link to allowlisted apps.
+        action.actionIntent?.intent?.let { intent ->
+            packageManagerWrapper.resolveActivity(intent, 0 /* flags */)
+        }?.let { resolveInfo ->
+            devicePolicyManagerWrapper.isLockTaskPermitted(resolveInfo.activityInfo.packageName)
+        } ?: false
+    }
+}
+
+interface SmartActionInflater {
+    fun inflateActionButton(
+        parent: ViewGroup,
+        entry: NotificationEntry,
+        smartActions: SmartActions,
+        actionIndex: Int,
+        action: Notification.Action,
+        delayOnClickListener: Boolean,
+        packageContext: Context
+    ): Button
+}
+
+/* internal */ class SmartActionInflaterImpl @Inject constructor(
+    private val constants: SmartReplyConstants,
+    private val activityStarter: ActivityStarter,
+    private val smartReplyController: SmartReplyController,
+    private val headsUpManager: HeadsUpManager
+) : SmartActionInflater {
+
+    override fun inflateActionButton(
+        parent: ViewGroup,
+        entry: NotificationEntry,
+        smartActions: SmartActions,
+        actionIndex: Int,
+        action: Notification.Action,
+        delayOnClickListener: Boolean,
+        packageContext: Context
+    ): Button =
+            (LayoutInflater.from(parent.context)
+                    .inflate(R.layout.smart_action_button, parent, false) as Button
+            ).apply {
+                text = action.title
+
+                // We received the Icon from the application - so use the Context of the application to
+                // reference icon resources.
+                val iconDrawable = action.getIcon().loadDrawable(packageContext)
+                        .apply {
+                            val newIconSize: Int = context.resources.getDimensionPixelSize(
+                                    R.dimen.smart_action_button_icon_size)
+                            setBounds(0, 0, newIconSize, newIconSize)
+                        }
+                // Add the action icon to the Smart Action button.
+                setCompoundDrawables(iconDrawable, null, null, null)
+
+                val onClickListener = View.OnClickListener {
+                    onSmartActionClick(entry, smartActions, actionIndex, action)
+                }
+                setOnClickListener(
+                        if (delayOnClickListener)
+                            DelayedOnClickListener(onClickListener, constants.onClickInitDelay)
+                        else onClickListener)
+
+                // Mark this as an Action button
+                (layoutParams as SmartReplyView.LayoutParams).mButtonType = SmartButtonType.ACTION
+            }
+
+    private fun onSmartActionClick(
+        entry: NotificationEntry,
+        smartActions: SmartActions,
+        actionIndex: Int,
+        action: Notification.Action
+    ) =
+            activityStarter.startPendingIntentDismissingKeyguard(action.actionIntent, entry.row) {
+                smartReplyController
+                        .smartActionClicked(entry, actionIndex, action, smartActions.fromAssistant)
+                headsUpManager.removeNotification(entry.key, true /* releaseImmediately */)
+            }
+}
+
+interface SmartReplyInflater {
+    fun inflateReplyButton(
+        parent: SmartReplyView,
+        entry: NotificationEntry,
+        smartReplies: SmartReplies,
+        replyIndex: Int,
+        choice: CharSequence,
+        delayOnClickListener: Boolean
+    ): Button
+}
+
+class SmartReplyInflaterImpl @Inject constructor(
+    private val constants: SmartReplyConstants,
+    private val keyguardDismissUtil: KeyguardDismissUtil,
+    private val remoteInputManager: NotificationRemoteInputManager,
+    private val smartReplyController: SmartReplyController,
+    private val context: Context
+) : SmartReplyInflater {
+
+    override fun inflateReplyButton(
+        parent: SmartReplyView,
+        entry: NotificationEntry,
+        smartReplies: SmartReplies,
+        replyIndex: Int,
+        choice: CharSequence,
+        delayOnClickListener: Boolean
+    ): Button =
+            (LayoutInflater.from(parent.context)
+                    .inflate(R.layout.smart_reply_button, parent, false) as Button
+            ).apply {
+                text = choice
+                val onClickListener = View.OnClickListener {
+                    onSmartReplyClick(
+                            entry,
+                            smartReplies,
+                            replyIndex,
+                            parent,
+                            this,
+                            choice)
+                }
+                setOnClickListener(
+                        if (delayOnClickListener)
+                            DelayedOnClickListener(onClickListener, constants.onClickInitDelay)
+                        else onClickListener)
+                accessibilityDelegate = object : View.AccessibilityDelegate() {
+                    override fun onInitializeAccessibilityNodeInfo(
+                        host: View,
+                        info: AccessibilityNodeInfo
+                    ) {
+                        super.onInitializeAccessibilityNodeInfo(host, info)
+                        val label = parent.resources
+                                .getString(R.string.accessibility_send_smart_reply)
+                        val action = AccessibilityAction(AccessibilityNodeInfo.ACTION_CLICK, label)
+                        info.addAction(action)
+                    }
+                }
+                // TODO: probably shouldn't do this here, bad API
+                // Mark this as a Reply button
+                (layoutParams as SmartReplyView.LayoutParams).mButtonType = SmartButtonType.REPLY
+            }
+
+    private fun onSmartReplyClick(
+        entry: NotificationEntry,
+        smartReplies: SmartReplies,
+        replyIndex: Int,
+        smartReplyView: SmartReplyView,
+        button: Button,
+        choice: CharSequence
+    ) = keyguardDismissUtil.executeWhenUnlocked(!entry.isRowPinned) {
+        val canEditBeforeSend = constants.getEffectiveEditChoicesBeforeSending(
+                smartReplies.remoteInput.editChoicesBeforeSending)
+        if (canEditBeforeSend) {
+            remoteInputManager.activateRemoteInput(
+                    button,
+                    arrayOf(smartReplies.remoteInput),
+                    smartReplies.remoteInput,
+                    smartReplies.pendingIntent,
+                    NotificationEntry.EditedSuggestionInfo(choice, replyIndex))
+        } else {
+            smartReplyController.smartReplySent(
+                    entry,
+                    replyIndex,
+                    button.text,
+                    NotificationLogger.getNotificationLocation(entry).toMetricsEventEnum(),
+                    false /* modifiedBeforeSending */)
+            entry.setHasSentReply()
+            try {
+                val intent = createRemoteInputIntent(smartReplies, choice)
+                smartReplies.pendingIntent.send(context, 0, intent)
+            } catch (e: PendingIntent.CanceledException) {
+                Log.w(TAG, "Unable to send smart reply", e)
+            }
+            smartReplyView.hideSmartSuggestions()
+        }
+        false // do not defer
+    }
+
+    private fun createRemoteInputIntent(smartReplies: SmartReplies, choice: CharSequence): Intent {
+        val results = Bundle()
+        results.putString(smartReplies.remoteInput.resultKey, choice.toString())
+        val intent = Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+        RemoteInput.addResultsToIntent(arrayOf(smartReplies.remoteInput), intent, results)
+        RemoteInput.setResultsSource(intent, RemoteInput.SOURCE_CHOICE)
+        return intent
+    }
+}
+
+/**
+ * An OnClickListener wrapper that blocks the underlying OnClickListener for a given amount of
+ * time.
+ */
+private class DelayedOnClickListener(
+    private val mActualListener: View.OnClickListener,
+    private val mInitDelayMs: Long
+) : View.OnClickListener {
+
+    private val mInitTimeMs = SystemClock.elapsedRealtime()
+
+    override fun onClick(v: View) {
+        if (hasFinishedInitialization()) {
+            mActualListener.onClick(v)
+        } else {
+            Log.i(TAG, "Accidental Smart Suggestion click registered, delay: $mInitDelayMs")
+        }
+    }
+
+    private fun hasFinishedInitialization(): Boolean =
+            SystemClock.elapsedRealtime() >= mInitTimeMs + mInitDelayMs
+}
+
+private const val TAG = "SmartReplyViewInflater"
+private val DEBUG = Log.isLoggable(TAG, Log.DEBUG)
+
+// convenience function that swaps parameter order so that lambda can be placed at the end
+private fun KeyguardDismissUtil.executeWhenUnlocked(
+    requiresShadeOpen: Boolean,
+    onDismissAction: () -> Boolean
+) = executeWhenUnlocked(onDismissAction, requiresShadeOpen)
+
+// convenience function that swaps parameter order so that lambda can be placed at the end
+private fun ActivityStarter.startPendingIntentDismissingKeyguard(
+    intent: PendingIntent,
+    associatedView: View?,
+    runnable: () -> Unit
+) = startPendingIntentDismissingKeyguard(intent, runnable::invoke, associatedView)
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
index 949ac4d..e7f84a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -6,7 +6,6 @@
 import android.app.PendingIntent;
 import android.app.RemoteInput;
 import android.content.Context;
-import android.content.Intent;
 import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
@@ -15,34 +14,20 @@
 import android.graphics.drawable.GradientDrawable;
 import android.graphics.drawable.InsetDrawable;
 import android.graphics.drawable.RippleDrawable;
-import android.os.Bundle;
-import android.os.SystemClock;
 import android.text.Layout;
 import android.text.TextPaint;
 import android.text.method.TransformationMethod;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 import android.widget.Button;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ContrastColorUtil;
-import com.android.systemui.Dependency;
 import com.android.systemui.R;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.SmartReplyController;
 import com.android.systemui.statusbar.notification.NotificationUtils;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry.EditedSuggestionInfo;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
-import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 
 import java.text.BreakIterator;
 import java.util.ArrayList;
@@ -64,10 +49,6 @@
 
     private static final int SQUEEZE_FAILED = -1;
 
-    private final SmartReplyConstants mConstants;
-    private final KeyguardDismissUtil mKeyguardDismissUtil;
-    private final NotificationRemoteInputManager mRemoteInputManager;
-
     /**
      * The upper bound for the height of this view in pixels. Notifications are automatically
      * recreated on density or font size changes so caching this should be fine.
@@ -98,30 +79,25 @@
      */
     private boolean mSmartRepliesGeneratedByAssistant = false;
 
-    @ColorInt
-    private int mCurrentBackgroundColor;
-    @ColorInt
-    private final int mDefaultBackgroundColor;
-    @ColorInt
-    private final int mDefaultStrokeColor;
-    @ColorInt
-    private final int mDefaultTextColor;
-    @ColorInt
-    private final int mDefaultTextColorDarkBg;
-    @ColorInt
-    private final int mRippleColorDarkBg;
-    @ColorInt
-    private final int mRippleColor;
+    @ColorInt private int mCurrentBackgroundColor;
+    @ColorInt private final int mDefaultBackgroundColor;
+    @ColorInt private final int mDefaultStrokeColor;
+    @ColorInt private final int mDefaultTextColor;
+    @ColorInt private final int mDefaultTextColorDarkBg;
+    @ColorInt private final int mRippleColorDarkBg;
+    @ColorInt private final int mRippleColor;
     private final int mStrokeWidth;
     private final double mMinStrokeContrast;
 
-    private ActivityStarter mActivityStarter;
+    @ColorInt private int mCurrentStrokeColor;
+    @ColorInt private int mCurrentTextColor;
+    @ColorInt private int mCurrentRippleColor;
+    private int mMaxSqueezeRemeasureAttempts;
+    private int mMaxNumActions;
+    private int mMinNumSystemGeneratedReplies;
 
     public SmartReplyView(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mConstants = Dependency.get(SmartReplyConstants.class);
-        mKeyguardDismissUtil = Dependency.get(KeyguardDismissUtil.class);
-        mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class);
 
         mHeightUpperLimit = NotificationUtils.getFontScaledHeight(mContext,
             R.dimen.smart_reply_button_max_height);
@@ -172,6 +148,18 @@
     }
 
     /**
+     * Inflate an instance of this class.
+     */
+    public static SmartReplyView inflate(Context context, SmartReplyConstants constants) {
+        SmartReplyView view = (SmartReplyView) LayoutInflater.from(context).inflate(
+                R.layout.smart_reply_view, null /* root */);
+        view.setMaxNumActions(constants.getMaxNumActions());
+        view.setMaxSqueezeRemeasureAttempts(constants.getMaxSqueezeRemeasureAttempts());
+        view.setMinNumSystemGeneratedReplies(constants.getMinNumSystemGeneratedReplies());
+        return view;
+    }
+
+    /**
      * Returns an upper bound for the height of this view in pixels. This method is intended to be
      * invoked before onMeasure, so it doesn't do any analysis on the contents of the buttons.
      */
@@ -197,174 +185,25 @@
         mCurrentBackgroundColor = mDefaultBackgroundColor;
     }
 
-    /**
-     * Add buttons to the {@link SmartReplyView} - these buttons must have been preinflated using
-     * one of the methods in this class.
-     */
+    /** Add buttons to the {@link SmartReplyView} */
     public void addPreInflatedButtons(List<Button> smartSuggestionButtons) {
         for (Button button : smartSuggestionButtons) {
             addView(button);
+            setButtonColors(button);
         }
         reallocateCandidateButtonQueueForSqueezing();
     }
 
-    /**
-     * Add smart replies to this view, using the provided {@link RemoteInput} and
-     * {@link PendingIntent} to respond when the user taps a smart reply. Only the replies that fit
-     * into the notification are shown.
-     */
-    public List<Button> inflateRepliesFromRemoteInput(
-            @NonNull SmartReplies smartReplies,
-            SmartReplyController smartReplyController, NotificationEntry entry,
-            boolean delayOnClickListener) {
-        List<Button> buttons = new ArrayList<>();
-
-        if (smartReplies.remoteInput != null && smartReplies.pendingIntent != null) {
-            if (smartReplies.choices != null) {
-                for (int i = 0; i < smartReplies.choices.size(); ++i) {
-                    buttons.add(inflateReplyButton(
-                            this, getContext(), i, smartReplies, smartReplyController, entry,
-                            delayOnClickListener));
-                }
-                this.mSmartRepliesGeneratedByAssistant = smartReplies.fromAssistant;
-            }
-        }
-        return buttons;
+    public void setMaxNumActions(int maxNumActions) {
+        mMaxNumActions = maxNumActions;
     }
 
-    /**
-     * Add smart actions to be shown next to smart replies. Only the actions that fit into the
-     * notification are shown.
-     */
-    public List<Button> inflateSmartActions(Context packageContext,
-            @NonNull SmartActions smartActions, SmartReplyController smartReplyController,
-            NotificationEntry entry, HeadsUpManager headsUpManager, boolean delayOnClickListener) {
-        Context themedPackageContext = new ContextThemeWrapper(packageContext, mContext.getTheme());
-        List<Button> buttons = new ArrayList<>();
-        int numSmartActions = smartActions.actions.size();
-        for (int n = 0; n < numSmartActions; n++) {
-            Notification.Action action = smartActions.actions.get(n);
-            if (action.actionIntent != null) {
-                buttons.add(inflateActionButton(
-                        this, getContext(), themedPackageContext, n, smartActions,
-                        smartReplyController,
-                        entry, headsUpManager, delayOnClickListener));
-            }
-        }
-        return buttons;
+    public void setMinNumSystemGeneratedReplies(int minNumSystemGeneratedReplies) {
+        mMinNumSystemGeneratedReplies = minNumSystemGeneratedReplies;
     }
 
-    /**
-     * Inflate an instance of this class.
-     */
-    public static SmartReplyView inflate(Context context) {
-        return (SmartReplyView) LayoutInflater.from(context).inflate(
-                R.layout.smart_reply_view, null /* root */);
-    }
-
-    @VisibleForTesting
-    static Button inflateReplyButton(SmartReplyView smartReplyView, Context context,
-            int replyIndex, SmartReplies smartReplies, SmartReplyController smartReplyController,
-            NotificationEntry entry, boolean useDelayedOnClickListener) {
-        Button b = (Button) LayoutInflater.from(context).inflate(
-                R.layout.smart_reply_button, smartReplyView, false);
-        CharSequence choice = smartReplies.choices.get(replyIndex);
-        b.setText(choice);
-
-        OnDismissAction action = () -> {
-            if (smartReplyView.mConstants.getEffectiveEditChoicesBeforeSending(
-                    smartReplies.remoteInput.getEditChoicesBeforeSending())) {
-                EditedSuggestionInfo editedSuggestionInfo =
-                        new EditedSuggestionInfo(choice, replyIndex);
-                smartReplyView.mRemoteInputManager.activateRemoteInput(b,
-                        new RemoteInput[] { smartReplies.remoteInput }, smartReplies.remoteInput,
-                        smartReplies.pendingIntent, editedSuggestionInfo);
-                return false;
-            }
-
-            smartReplyController.smartReplySent(entry, replyIndex, b.getText(),
-                    NotificationLogger.getNotificationLocation(entry).toMetricsEventEnum(),
-                    false /* modifiedBeforeSending */);
-            Bundle results = new Bundle();
-            results.putString(smartReplies.remoteInput.getResultKey(), choice.toString());
-            Intent intent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-            RemoteInput.addResultsToIntent(new RemoteInput[] { smartReplies.remoteInput }, intent,
-                    results);
-            RemoteInput.setResultsSource(intent, RemoteInput.SOURCE_CHOICE);
-            entry.setHasSentReply();
-            try {
-                smartReplies.pendingIntent.send(context, 0, intent);
-            } catch (PendingIntent.CanceledException e) {
-                Log.w(TAG, "Unable to send smart reply", e);
-            }
-            // Note that as inflateReplyButton is called mSmartReplyContainer is null, but when the
-            // reply Button is added to the SmartReplyView mSmartReplyContainer will be set. So, it
-            // will not be possible for a user to trigger this on-click-listener without
-            // mSmartReplyContainer being set.
-            smartReplyView.mSmartReplyContainer.setVisibility(View.GONE);
-            return false; // do not defer
-        };
-
-        OnClickListener onClickListener = view ->
-            smartReplyView.mKeyguardDismissUtil.executeWhenUnlocked(action, !entry.isRowPinned());
-        if (useDelayedOnClickListener) {
-            onClickListener = new DelayedOnClickListener(onClickListener,
-                    smartReplyView.mConstants.getOnClickInitDelay());
-        }
-        b.setOnClickListener(onClickListener);
-
-        b.setAccessibilityDelegate(new AccessibilityDelegate() {
-            public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
-                super.onInitializeAccessibilityNodeInfo(host, info);
-                String label = smartReplyView.getResources().getString(
-                        R.string.accessibility_send_smart_reply);
-                info.addAction(new AccessibilityAction(AccessibilityNodeInfo.ACTION_CLICK, label));
-            }
-        });
-
-        SmartReplyView.setButtonColors(b, smartReplyView.mCurrentBackgroundColor,
-                smartReplyView.mDefaultStrokeColor, smartReplyView.mDefaultTextColor,
-                smartReplyView.mRippleColor, smartReplyView.mStrokeWidth);
-        return b;
-    }
-
-    @VisibleForTesting
-    static Button inflateActionButton(SmartReplyView smartReplyView, Context context,
-            Context packageContext, int actionIndex, SmartActions smartActions,
-            SmartReplyController smartReplyController, NotificationEntry entry,
-            HeadsUpManager headsUpManager, boolean useDelayedOnClickListener) {
-        Notification.Action action = smartActions.actions.get(actionIndex);
-        Button button = (Button) LayoutInflater.from(context).inflate(
-                R.layout.smart_action_button, smartReplyView, false);
-        button.setText(action.title);
-
-        // We received the Icon from the application - so use the Context of the application to
-        // reference icon resources.
-        Drawable iconDrawable = action.getIcon().loadDrawable(packageContext);
-        // Add the action icon to the Smart Action button.
-        int newIconSize = context.getResources().getDimensionPixelSize(
-                R.dimen.smart_action_button_icon_size);
-        iconDrawable.setBounds(0, 0, newIconSize, newIconSize);
-        button.setCompoundDrawables(iconDrawable, null, null, null);
-
-        OnClickListener onClickListener = view ->
-                smartReplyView.getActivityStarter().startPendingIntentDismissingKeyguard(
-                        action.actionIntent,
-                        () -> {
-                            smartReplyController.smartActionClicked(
-                                    entry, actionIndex, action, smartActions.fromAssistant);
-                            headsUpManager.removeNotification(entry.getKey(), true);
-                        }, entry.getRow());
-        if (useDelayedOnClickListener) {
-            onClickListener = new DelayedOnClickListener(onClickListener,
-                    smartReplyView.mConstants.getOnClickInitDelay());
-        }
-        button.setOnClickListener(onClickListener);
-
-        // Mark this as an Action button
-        final LayoutParams lp = (LayoutParams) button.getLayoutParams();
-        lp.buttonType = SmartButtonType.ACTION;
-        return button;
+    public void setMaxSqueezeRemeasureAttempts(int maxSqueezeRemeasureAttempts) {
+        mMaxSqueezeRemeasureAttempts = maxSqueezeRemeasureAttempts;
     }
 
     @Override
@@ -416,13 +255,13 @@
         // reply button is added.
         SmartSuggestionMeasures actionsMeasures = null;
 
-        final int maxNumActions = mConstants.getMaxNumActions();
+        final int maxNumActions = mMaxNumActions;
         int numShownActions = 0;
 
         for (View child : smartSuggestions) {
             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
             if (maxNumActions != -1 // -1 means 'no limit'
-                    && lp.buttonType == SmartButtonType.ACTION
+                    && lp.mButtonType == SmartButtonType.ACTION
                     && numShownActions >= maxNumActions) {
                 // We've reached the maximum number of actions, don't add another one!
                 continue;
@@ -446,7 +285,7 @@
 
             // Remember the current measurements in case the current button doesn't fit in.
             SmartSuggestionMeasures originalMeasures = accumulatedMeasures.clone();
-            if (actionsMeasures == null && lp.buttonType == SmartButtonType.REPLY) {
+            if (actionsMeasures == null && lp.mButtonType == SmartButtonType.REPLY) {
                 // We've added all actions (we go through actions first), now add their
                 // measurements.
                 actionsMeasures = accumulatedMeasures.clone();
@@ -510,7 +349,7 @@
 
             lp.show = true;
             displayedChildCount++;
-            if (lp.buttonType == SmartButtonType.ACTION) {
+            if (lp.mButtonType == SmartButtonType.ACTION) {
                 numShownActions++;
             }
         }
@@ -551,6 +390,19 @@
                 resolveSize(buttonHeight, heightMeasureSpec));
     }
 
+    // TODO: this should be replaced, and instead, setMinSystemGenerated... should be invoked
+    //  with MAX_VALUE if mSmartRepliesGeneratedByAssistant would be false (essentially, this is a
+    //  ViewModel decision, as opposed to a View decision)
+    void setSmartRepliesGeneratedByAssistant(boolean fromAssistant) {
+        mSmartRepliesGeneratedByAssistant = fromAssistant;
+    }
+
+    void hideSmartSuggestions() {
+        if (mSmartReplyContainer != null) {
+            mSmartReplyContainer.setVisibility(View.GONE);
+        }
+    }
+
     /**
      * Fields we keep track of inside onMeasure() to correctly measure the SmartReplyView depending
      * on which suggestions are added.
@@ -577,6 +429,7 @@
      * Returns whether our notification contains at least N smart replies (or 0) where N is
      * determined by {@link SmartReplyConstants}.
      */
+    // TODO: we probably sholdn't make this deliberation in the View
     private boolean gotEnoughSmartReplies(List<View> smartReplies) {
         int numShownReplies = 0;
         for (View smartReplyButton : smartReplies) {
@@ -585,8 +438,7 @@
                 numShownReplies++;
             }
         }
-        if (numShownReplies == 0
-                || numShownReplies >= mConstants.getMinNumSystemGeneratedReplies()) {
+        if (numShownReplies == 0 || numShownReplies >= mMinNumSystemGeneratedReplies) {
             // We have enough replies, yay!
             return true;
         }
@@ -602,7 +454,7 @@
             if (child.getVisibility() != View.VISIBLE || !(child instanceof Button)) {
                 continue;
             }
-            if (lp.buttonType == buttonType) {
+            if (lp.mButtonType == buttonType) {
                 actions.add(child);
             }
         }
@@ -656,7 +508,7 @@
             // See if there's a better line-break point (leading to a more narrow button) in
             // either left or right direction.
             final boolean moveLeft = initialLeftTextWidth > initialRightTextWidth;
-            final int maxSqueezeRemeasureAttempts = mConstants.getMaxSqueezeRemeasureAttempts();
+            final int maxSqueezeRemeasureAttempts = mMaxSqueezeRemeasureAttempts;
             for (int i = 0; i < maxSqueezeRemeasureAttempts; i++) {
                 final int newPosition =
                         moveLeft ? mBreakIterator.previous() : mBreakIterator.next();
@@ -833,41 +685,38 @@
 
         final boolean dark = !ContrastColorUtil.isColorLight(backgroundColor);
 
-        int textColor = ContrastColorUtil.ensureTextContrast(
+        mCurrentTextColor = ContrastColorUtil.ensureTextContrast(
                 dark ? mDefaultTextColorDarkBg : mDefaultTextColor,
                 backgroundColor | 0xff000000, dark);
-        int strokeColor = ContrastColorUtil.ensureContrast(
+        mCurrentStrokeColor = ContrastColorUtil.ensureContrast(
                 mDefaultStrokeColor, backgroundColor | 0xff000000, dark, mMinStrokeContrast);
-        int rippleColor = dark ? mRippleColorDarkBg : mRippleColor;
+        mCurrentRippleColor = dark ? mRippleColorDarkBg : mRippleColor;
 
         int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
-            final Button child = (Button) getChildAt(i);
-            setButtonColors(child, backgroundColor, strokeColor, textColor, rippleColor,
-                    mStrokeWidth);
+            setButtonColors((Button) getChildAt(i));
         }
     }
 
-    private static void setButtonColors(Button button, int backgroundColor, int strokeColor,
-            int textColor, int rippleColor, int strokeWidth) {
+    private void setButtonColors(Button button) {
         Drawable drawable = button.getBackground();
         if (drawable instanceof RippleDrawable) {
             // Mutate in case other notifications are using this drawable.
             drawable = drawable.mutate();
             RippleDrawable ripple = (RippleDrawable) drawable;
-            ripple.setColor(ColorStateList.valueOf(rippleColor));
+            ripple.setColor(ColorStateList.valueOf(mCurrentRippleColor));
             Drawable inset = ripple.getDrawable(0);
             if (inset instanceof InsetDrawable) {
                 Drawable background = ((InsetDrawable) inset).getDrawable();
                 if (background instanceof GradientDrawable) {
                     GradientDrawable gradientDrawable = (GradientDrawable) background;
-                    gradientDrawable.setColor(backgroundColor);
-                    gradientDrawable.setStroke(strokeWidth, strokeColor);
+                    gradientDrawable.setColor(mCurrentBackgroundColor);
+                    gradientDrawable.setStroke(mStrokeWidth, mCurrentStrokeColor);
                 }
             }
             button.setBackground(drawable);
         }
-        button.setTextColor(textColor);
+        button.setTextColor(mCurrentTextColor);
     }
 
     private void setCornerRadius(Button button, float radius) {
@@ -887,14 +736,7 @@
         }
     }
 
-    private ActivityStarter getActivityStarter() {
-        if (mActivityStarter == null) {
-            mActivityStarter = Dependency.get(ActivityStarter.class);
-        }
-        return mActivityStarter;
-    }
-
-    private enum SmartButtonType {
+    enum SmartButtonType {
         REPLY,
         ACTION
     }
@@ -924,7 +766,7 @@
 
         private boolean show = false;
         private int squeezeStatus = SQUEEZE_STATUS_NONE;
-        private SmartButtonType buttonType = SmartButtonType.REPLY;
+        SmartButtonType mButtonType = SmartButtonType.REPLY;
 
         private LayoutParams(Context c, AttributeSet attrs) {
             super(c, attrs);
@@ -975,32 +817,4 @@
             this.fromAssistant = fromAssistant;
         }
     }
-
-    /**
-     * An OnClickListener wrapper that blocks the underlying OnClickListener for a given amount of
-     * time.
-     */
-    private static class DelayedOnClickListener implements OnClickListener {
-        private final OnClickListener mActualListener;
-        private final long mInitDelayMs;
-        private final long mInitTimeMs;
-
-        DelayedOnClickListener(OnClickListener actualOnClickListener, long initDelayMs) {
-            mActualListener = actualOnClickListener;
-            mInitDelayMs = initDelayMs;
-            mInitTimeMs = SystemClock.elapsedRealtime();
-        }
-
-        public void onClick(View v) {
-            if (hasFinishedInitialization()) {
-                mActualListener.onClick(v);
-            } else {
-                Log.i(TAG, "Accidental Smart Suggestion click registered, delay: " + mInitDelayMs);
-            }
-        }
-
-        private boolean hasFinishedInitialization() {
-            return SystemClock.elapsedRealtime() >= mInitTimeMs + mInitDelayMs;
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 17fcb1d..72e8e38 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -23,6 +23,7 @@
 
 import android.app.ActivityManager;
 import android.app.Dialog;
+import android.app.IActivityTaskManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.DialogInterface;
@@ -53,7 +54,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.util.UserIcons;
 import com.android.settingslib.RestrictedLockUtilsInternal;
 import com.android.systemui.Dumpable;
 import com.android.systemui.GuestResumeSessionReceiver;
@@ -69,6 +69,7 @@
 import com.android.systemui.qs.QSUserSwitcherEvent;
 import com.android.systemui.qs.tiles.UserDetailView;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.user.CreateUserActivity;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -104,6 +105,7 @@
     protected final Handler mHandler;
     private final ActivityStarter mActivityStarter;
     private final BroadcastDispatcher mBroadcastDispatcher;
+    private final IActivityTaskManager mActivityTaskManager;
 
     private ArrayList<UserRecord> mUsers = new ArrayList<>();
     private Dialog mExitGuestDialog;
@@ -121,9 +123,11 @@
     @Inject
     public UserSwitcherController(Context context, KeyguardStateController keyguardStateController,
             @Main Handler handler, ActivityStarter activityStarter,
-            BroadcastDispatcher broadcastDispatcher, UiEventLogger uiEventLogger) {
+            BroadcastDispatcher broadcastDispatcher, UiEventLogger uiEventLogger,
+            IActivityTaskManager activityTaskManager) {
         mContext = context;
         mBroadcastDispatcher = broadcastDispatcher;
+        mActivityTaskManager = activityTaskManager;
         mUiEventLogger = uiEventLogger;
         if (!UserManager.isGuestUserEphemeral()) {
             mGuestResumeSessionReceiver.register(mBroadcastDispatcher);
@@ -363,7 +367,7 @@
         }
     }
 
-    public void switchTo(UserRecord record) {
+    private void onUserListItemClicked(UserRecord record) {
         int id;
         if (record.isGuest && record.info == null) {
             // No guest user. Create one.
@@ -408,19 +412,6 @@
         switchToUserId(id);
     }
 
-    public void switchTo(int userId) {
-        final int count = mUsers.size();
-        for (int i = 0; i < count; ++i) {
-            UserRecord record = mUsers.get(i);
-            if (record.info != null && record.info.id == userId) {
-                switchTo(record);
-                return;
-            }
-        }
-
-        Log.e(TAG, "Couldn't switch to user, id=" + userId);
-    }
-
     protected void switchToUserId(int id) {
         try {
             pauseRefreshUsers();
@@ -666,8 +657,11 @@
             return position;
         }
 
-        public void switchTo(UserRecord record) {
-            mController.switchTo(record);
+        /**
+         * It handles click events on user list items.
+         */
+        public void onUserListItemClicked(UserRecord record) {
+            mController.onUserListItemClicked(record);
         }
 
         public String getName(Context context, UserRecord item) {
@@ -924,18 +918,33 @@
                 if (ActivityManager.isUserAMonkey()) {
                     return;
                 }
-                UserInfo user = mUserManager.createUser(
-                        mContext.getString(R.string.user_new_user_name), 0 /* flags */);
-                if (user == null) {
-                    // Couldn't create user, most likely because there are too many, but we haven't
-                    // been able to reload the list yet.
-                    return;
+                Intent intent = CreateUserActivity.createIntentForStart(getContext());
+
+                // There are some differences between ActivityStarter and ActivityTaskManager in
+                // terms of how they start an activity. ActivityStarter hides the notification bar
+                // before starting the activity to make sure nothing is in front of the new
+                // activity. ActivityStarter also tries to unlock the device if it's locked.
+                // When locked with PIN/pattern/password then it shows the prompt, if there are no
+                // security steps then it dismisses the keyguard and then starts the activity.
+                // ActivityTaskManager doesn't hide the notification bar or unlocks the device, but
+                // it can start an activity on top of the locked screen.
+                if (!mKeyguardStateController.isUnlocked()
+                        && !mKeyguardStateController.canDismissLockScreen()) {
+                    // Device is locked and can't be unlocked without a PIN/pattern/password so we
+                    // need to use ActivityTaskManager to start the activity on top of the locked
+                    // screen.
+                    try {
+                        mActivityTaskManager.startActivity(null,
+                                mContext.getBasePackageName(), mContext.getAttributionTag(), intent,
+                                intent.resolveTypeIfNeeded(mContext.getContentResolver()), null,
+                                null, 0, 0, null, null);
+                    } catch (RemoteException e) {
+                        e.printStackTrace();
+                        Log.e(TAG, "Couldn't start create user activity", e);
+                    }
+                } else {
+                    mActivityStarter.startActivity(intent, true);
                 }
-                int id = user.id;
-                Bitmap icon = UserIcons.convertToBitmap(UserIcons.getDefaultUserIcon(
-                        mContext.getResources(), id, /* light= */ false));
-                mUserManager.setUserIcon(id, icon);
-                switchToUserId(id);
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/SmartRepliesInflationModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/SmartRepliesInflationModule.kt
new file mode 100644
index 0000000..803d26e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/SmartRepliesInflationModule.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.policy.dagger
+
+import com.android.systemui.statusbar.policy.SmartActionInflater
+import com.android.systemui.statusbar.policy.SmartActionInflaterImpl
+import com.android.systemui.statusbar.policy.SmartRepliesAndActionsInflater
+import com.android.systemui.statusbar.policy.SmartRepliesAndActionsInflaterImpl
+import com.android.systemui.statusbar.policy.SmartReplyInflater
+import com.android.systemui.statusbar.policy.SmartReplyInflaterImpl
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface SmartRepliesInflationModule {
+    @Binds fun bindSmartActionsInflater(impl: SmartActionInflaterImpl): SmartActionInflater
+    @Binds fun bindSmartReplyInflater(impl: SmartReplyInflaterImpl): SmartReplyInflater
+    @Binds fun bindsInflatedSmartRepliesProvider(
+        impl: SmartRepliesAndActionsInflaterImpl
+    ): SmartRepliesAndActionsInflater
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
new file mode 100644
index 0000000..e9fcf1a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.toast;
+
+import android.animation.Animator;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.view.View;
+import android.widget.ToastPresenter;
+
+import com.android.internal.R;
+import com.android.systemui.plugins.ToastPlugin;
+
+/**
+ * SystemUI TextToast that can be customized by ToastPlugins. Should never instantiate this class
+ * directly. Instead, use {@link ToastFactory#createToast}.
+ */
+public class SystemUIToast implements ToastPlugin.Toast {
+    final Context mContext;
+    final CharSequence mText;
+    final ToastPlugin.Toast mPluginToast;
+
+    final int mDefaultGravity;
+    final int mDefaultY;
+    final int mDefaultX = 0;
+    final int mDefaultHorizontalMargin = 0;
+    final int mDefaultVerticalMargin = 0;
+
+    SystemUIToast(Context context, CharSequence text) {
+        this(context, text, null);
+    }
+
+    SystemUIToast(Context context, CharSequence text, ToastPlugin.Toast pluginToast) {
+        mContext = context;
+        mText = text;
+        mPluginToast = pluginToast;
+
+        mDefaultGravity = context.getResources().getInteger(R.integer.config_toastDefaultGravity);
+        mDefaultY = context.getResources().getDimensionPixelSize(R.dimen.toast_y_offset);
+    }
+
+    @Override
+    @NonNull
+    public Integer getGravity() {
+        if (isPluginToast() && mPluginToast.getGravity() != null) {
+            return mPluginToast.getGravity();
+        }
+        return mDefaultGravity;
+    }
+
+    @Override
+    @NonNull
+    public Integer getXOffset() {
+        if (isPluginToast() && mPluginToast.getXOffset() != null) {
+            return mPluginToast.getXOffset();
+        }
+        return mDefaultX;
+    }
+
+    @Override
+    @NonNull
+    public Integer getYOffset() {
+        if (isPluginToast() && mPluginToast.getYOffset() != null) {
+            return mPluginToast.getYOffset();
+        }
+        return mDefaultY;
+    }
+
+    @Override
+    @NonNull
+    public Integer getHorizontalMargin() {
+        if (isPluginToast() && mPluginToast.getHorizontalMargin() != null) {
+            return mPluginToast.getHorizontalMargin();
+        }
+        return mDefaultHorizontalMargin;
+    }
+
+    @Override
+    @NonNull
+    public Integer getVerticalMargin() {
+        if (isPluginToast() && mPluginToast.getVerticalMargin() != null) {
+            return mPluginToast.getVerticalMargin();
+        }
+        return mDefaultVerticalMargin;
+    }
+
+    @Override
+    @NonNull
+    public View getView() {
+        if (isPluginToast() && mPluginToast.getView() != null) {
+            return mPluginToast.getView();
+        }
+        return ToastPresenter.getTextToastView(mContext, mText);
+    }
+
+    @Override
+    @Nullable
+    public Animator getInAnimation() {
+        if (isPluginToast() && mPluginToast.getInAnimation() != null) {
+            return mPluginToast.getInAnimation();
+        }
+        return null;
+    }
+
+    @Override
+    @Nullable
+    public Animator getOutAnimation() {
+        if (isPluginToast() && mPluginToast.getOutAnimation() != null) {
+            return mPluginToast.getOutAnimation();
+        }
+        return null;
+    }
+
+    /**
+     * Whether this toast has a custom animation.
+     */
+    public boolean hasCustomAnimation() {
+        return getInAnimation() != null || getOutAnimation() != null;
+    }
+
+    private boolean isPluginToast() {
+        return mPluginToast != null;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java b/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java
new file mode 100644
index 0000000..d8cb61c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.toast;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.Dumpable;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.ToastPlugin;
+import com.android.systemui.shared.plugins.PluginManager;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+import javax.inject.Inject;
+
+/**
+ * Factory for creating toasts to be shown by ToastUI.
+ * These toasts can be customized by {@link ToastPlugin}.
+ */
+@SysUISingleton
+public class ToastFactory implements Dumpable {
+    // only one ToastPlugin can be connected at a time.
+    private ToastPlugin mPlugin;
+
+    @Inject
+    public ToastFactory(PluginManager pluginManager, DumpManager dumpManager) {
+        dumpManager.registerDumpable("ToastFactory", this);
+        pluginManager.addPluginListener(
+                new PluginListener<ToastPlugin>() {
+                    @Override
+                    public void onPluginConnected(ToastPlugin plugin, Context pluginContext) {
+                        mPlugin = plugin;
+                    }
+
+                    @Override
+                    public void onPluginDisconnected(ToastPlugin plugin) {
+                        if (plugin.equals(mPlugin)) {
+                            mPlugin = null;
+                        }
+                    }
+                }, ToastPlugin.class, false /* Allow multiple plugins */);
+    }
+
+    /**
+     * Create a toast to be shown by ToastUI.
+     */
+    public SystemUIToast createToast(Context context, CharSequence text, String packageName,
+            int userId) {
+        if (isPluginAvailable()) {
+            return new SystemUIToast(context, text, mPlugin.createToast(text, packageName, userId));
+        }
+        return new SystemUIToast(context, text);
+    }
+
+    private boolean isPluginAvailable() {
+        return mPlugin != null;
+    }
+
+    @Override
+    public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+        pw.println("ToastFactory:");
+        pw.println("    mAttachedPlugin=" + mPlugin);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt b/packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt
new file mode 100644
index 0000000..78173cf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.toast
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
+import com.android.systemui.log.LogLevel.DEBUG
+import com.android.systemui.log.LogMessage
+import com.android.systemui.log.dagger.ToastLog
+import javax.inject.Inject
+
+private const val TAG = "ToastLog"
+
+class ToastLogger @Inject constructor(
+    @ToastLog private val buffer: LogBuffer
+) {
+
+    fun logOnShowToast(uid: Int, packageName: String, text: String, token: String) {
+        log(DEBUG, {
+            int1 = uid
+            str1 = packageName
+            str2 = text
+            str3 = token
+        }, {
+            "[$str3] Show toast for ($str1, $int1). msg=\'$str2\'"
+        })
+    }
+
+    fun logOnHideToast(packageName: String, token: String) {
+        log(DEBUG, {
+            str1 = packageName
+            str2 = token
+        }, {
+            "[$str2] Hide toast for [$str1]"
+        })
+    }
+
+    private inline fun log(
+        logLevel: LogLevel,
+        initializer: LogMessage.() -> Unit,
+        noinline printer: LogMessage.() -> String
+    ) {
+        buffer.log(TAG, logLevel, initializer, printer)
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
index a220373..1c682e3 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
+++ b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
@@ -16,25 +16,27 @@
 
 package com.android.systemui.toast;
 
+import android.animation.Animator;
 import android.annotation.MainThread;
 import android.annotation.Nullable;
 import android.app.INotificationManager;
 import android.app.ITransientNotificationCallback;
 import android.content.Context;
-import android.content.res.Resources;
 import android.os.IBinder;
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.util.Log;
-import android.view.View;
+import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.IAccessibilityManager;
+import android.widget.Toast;
 import android.widget.ToastPresenter;
 
-import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.SystemUI;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.util.concurrency.DelayableExecutor;
 
 import java.util.Objects;
 
@@ -45,35 +47,53 @@
  */
 @SysUISingleton
 public class ToastUI extends SystemUI implements CommandQueue.Callbacks {
+    // values from NotificationManagerService#LONG_DELAY and NotificationManagerService#SHORT_DELAY
+    private static final int TOAST_LONG_TIME = 3500; // 3.5 seconds
+    private static final int TOAST_SHORT_TIME = 2000; // 2 seconds
+
     private static final String TAG = "ToastUI";
 
     private final CommandQueue mCommandQueue;
     private final INotificationManager mNotificationManager;
-    private final IAccessibilityManager mAccessibilityManager;
-    private final int mGravity;
-    private final int mY;
+    private final IAccessibilityManager mIAccessibilityManager;
+    private final AccessibilityManager mAccessibilityManager;
+    private final ToastFactory mToastFactory;
+    private final DelayableExecutor mMainExecutor;
+    private final ToastLogger mToastLogger;
+    private SystemUIToast mToast;
     @Nullable private ToastPresenter mPresenter;
     @Nullable private ITransientNotificationCallback mCallback;
 
     @Inject
-    public ToastUI(Context context, CommandQueue commandQueue) {
+    public ToastUI(
+            Context context,
+            CommandQueue commandQueue,
+            ToastFactory toastFactory,
+            @Main DelayableExecutor mainExecutor,
+            ToastLogger toastLogger) {
         this(context, commandQueue,
                 INotificationManager.Stub.asInterface(
                         ServiceManager.getService(Context.NOTIFICATION_SERVICE)),
                 IAccessibilityManager.Stub.asInterface(
-                        ServiceManager.getService(Context.ACCESSIBILITY_SERVICE)));
+                        ServiceManager.getService(Context.ACCESSIBILITY_SERVICE)),
+                toastFactory,
+                mainExecutor,
+                toastLogger);
     }
 
     @VisibleForTesting
     ToastUI(Context context, CommandQueue commandQueue, INotificationManager notificationManager,
-            @Nullable IAccessibilityManager accessibilityManager) {
+            @Nullable IAccessibilityManager accessibilityManager,
+            ToastFactory toastFactory, DelayableExecutor mainExecutor, ToastLogger toastLogger
+    ) {
         super(context);
         mCommandQueue = commandQueue;
         mNotificationManager = notificationManager;
-        mAccessibilityManager = accessibilityManager;
-        Resources resources = mContext.getResources();
-        mGravity = resources.getInteger(R.integer.config_toastDefaultGravity);
-        mY = resources.getDimensionPixelSize(R.dimen.toast_y_offset);
+        mIAccessibilityManager = accessibilityManager;
+        mToastFactory = toastFactory;
+        mMainExecutor = mainExecutor;
+        mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
+        mToastLogger = toastLogger;
     }
 
     @Override
@@ -88,12 +108,31 @@
         if (mPresenter != null) {
             hideCurrentToast();
         }
-        Context context = mContext.createContextAsUser(UserHandle.getUserHandleForUid(uid), 0);
-        View view = ToastPresenter.getTextToastView(context, text);
+        UserHandle userHandle = UserHandle.getUserHandleForUid(uid);
+        Context context = mContext.createContextAsUser(userHandle, 0);
+        mToast = mToastFactory.createToast(context, text, packageName, userHandle.getIdentifier());
+
+        if (mToast.hasCustomAnimation()) {
+            if (mToast.getInAnimation() != null) {
+                mToast.getInAnimation().start();
+            }
+            final Animator hideAnimator = mToast.getOutAnimation();
+            if (hideAnimator != null) {
+                final long durationMillis = duration == Toast.LENGTH_LONG
+                        ? TOAST_LONG_TIME : TOAST_SHORT_TIME;
+                final long updatedDuration = mAccessibilityManager.getRecommendedTimeoutMillis(
+                        (int) durationMillis, AccessibilityManager.FLAG_CONTENT_TEXT);
+                mMainExecutor.executeDelayed(() -> hideAnimator.start(),
+                        updatedDuration - hideAnimator.getTotalDuration());
+            }
+        }
         mCallback = callback;
-        mPresenter = new ToastPresenter(context, mAccessibilityManager, mNotificationManager,
+        mPresenter = new ToastPresenter(context, mIAccessibilityManager, mNotificationManager,
                 packageName);
-        mPresenter.show(view, token, windowToken, duration, mGravity, 0, mY, 0, 0, mCallback);
+        mToastLogger.logOnShowToast(uid, packageName, text.toString(), token.toString());
+        mPresenter.show(mToast.getView(), token, windowToken, duration, mToast.getGravity(),
+                mToast.getXOffset(), mToast.getYOffset(), mToast.getHorizontalMargin(),
+                mToast.getVerticalMargin(), mCallback, mToast.hasCustomAnimation());
     }
 
     @Override
@@ -104,6 +143,7 @@
             Log.w(TAG, "Attempt to hide non-current toast from package " + packageName);
             return;
         }
+        mToastLogger.logOnHideToast(packageName, token.toString());
         hideCurrentToast();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
index 70bba26..b67574d 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
@@ -32,6 +32,7 @@
 public abstract class TunerService {
 
     public static final String ACTION_CLEAR = "com.android.systemui.action.CLEAR_TUNER";
+    private final Context mContext;
 
     public abstract void clearAll();
     public abstract void destroy();
@@ -50,6 +51,10 @@
         void onTuningChanged(String key, String newValue);
     }
 
+    public TunerService(Context context) {
+        mContext = context;
+    }
+
     private static Context userContext(Context context, UserHandle user) {
         try {
             return context.createPackageContextAsUser(context.getPackageName(), 0, user);
@@ -58,6 +63,11 @@
         }
     }
 
+    /** Enables or disables the tuner for the supplied user. */
+    public void setTunerEnabled(UserHandle user, boolean enabled) {
+        setTunerEnabled(mContext, user, enabled);
+    }
+
     public static final void setTunerEnabled(Context context, UserHandle user, boolean enabled) {
         userContext(context, user).getPackageManager().setComponentEnabledSetting(
                 new ComponentName(context, TunerActivity.class),
@@ -66,6 +76,11 @@
                 PackageManager.DONT_KILL_APP);
     }
 
+    /** Returns true if the tuner is enabled for the supplied user. */
+    public boolean isTunerEnabled(UserHandle user) {
+        return isTunerEnabled(mContext, user);
+    }
+
     public static final boolean isTunerEnabled(Context context, UserHandle user) {
         return userContext(context, user).getPackageManager().getComponentEnabledSetting(
                 new ComponentName(context, TunerActivity.class))
@@ -81,6 +96,11 @@
         }
     }
 
+    /** */
+    public void showResetRequest(UserHandle user, final Runnable onDisabled) {
+        showResetRequest(mContext, user, onDisabled);
+    }
+
     public static final void showResetRequest(final Context context, UserHandle user,
             final Runnable onDisabled) {
         SystemUIDialog dialog = new SystemUIDialog(context);
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
index 22f03e07..027c282b 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
@@ -94,6 +94,7 @@
             DemoModeController demoModeController,
             BroadcastDispatcher broadcastDispatcher,
             UserTracker userTracker) {
+        super(context);
         mContext = context;
         mContentResolver = mContext.getContentResolver();
         mLeakDetector = leakDetector;
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvGlobalRootComponent.java b/packages/SystemUI/src/com/android/systemui/tv/TvGlobalRootComponent.java
index df741a0..89ab23b 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvGlobalRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvGlobalRootComponent.java
@@ -42,6 +42,12 @@
         TvGlobalRootComponent build();
     }
 
+    /**
+     * Builder for a WMComponent.
+     */
+    @Override
+    TvWMComponent.Builder getWMComponentBuilder();
+
     @Override
     TvSysUIComponent.Builder getSysUIComponent();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponentModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponentModule.java
index 334bb01..9621e5f 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponentModule.java
@@ -19,7 +19,7 @@
 import dagger.Module;
 
 /**
- * Dagger module for including the WMComponent.
+ * Dagger module for including the SysUIComponent.
  */
 @Module(subcomponents = {TvSysUIComponent.class})
 public abstract class TvSysUIComponentModule {
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
index bde88b1..2c3ea4f 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
@@ -22,7 +22,7 @@
 import dagger.Binds;
 import dagger.Module;
 
-@Module(includes = TvPipModule.class)
+@Module
 interface TvSystemUIBinder {
     @Binds
     GlobalRootComponent bindGlobalRootComponent(TvGlobalRootComponent globalRootComponent);
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
index c5bb9c1..8ffc7cf 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
@@ -62,7 +62,6 @@
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.wmshell.TvWMShellModule;
 
 import javax.inject.Named;
 
@@ -75,8 +74,7 @@
  * overridden by the System UI implementation.
  */
 @Module(includes = {
-            QSModule.class,
-            TvWMShellModule.class,
+            QSModule.class
         },
         subcomponents = {
         })
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvWMComponent.java b/packages/SystemUI/src/com/android/systemui/tv/TvWMComponent.java
new file mode 100644
index 0000000..f678513
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvWMComponent.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.systemui.tv;
+
+import com.android.systemui.dagger.WMComponent;
+import com.android.systemui.dagger.WMSingleton;
+import com.android.systemui.wmshell.TvWMShellModule;
+
+import dagger.Subcomponent;
+
+
+/**
+ * Dagger Subcomponent for WindowManager.
+ */
+@WMSingleton
+@Subcomponent(modules = {TvWMShellModule.class})
+public interface TvWMComponent extends WMComponent {
+
+    /**
+     * Builder for a SysUIComponent.
+     */
+    @Subcomponent.Builder
+    interface Builder extends WMComponent.Builder {
+        TvWMComponent build();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java b/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java
new file mode 100644
index 0000000..890ee5f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.user;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.app.IActivityManager;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.settingslib.users.EditUserInfoController;
+import com.android.systemui.R;
+
+import javax.inject.Inject;
+
+/**
+ * This screen shows a Dialog for choosing nickname and photo for a new user, and then delegates the
+ * user creation to a UserCreator.
+ */
+public class CreateUserActivity extends Activity {
+
+    /**
+     * Creates an intent to start this activity.
+     */
+    public static Intent createIntentForStart(Context context) {
+        return new Intent(context, CreateUserActivity.class);
+    }
+
+    private static final String TAG = "CreateUserActivity";
+    private static final String DIALOG_STATE_KEY = "create_user_dialog_state";
+
+    private final UserCreator mUserCreator;
+    private final EditUserInfoController mEditUserInfoController;
+    private final IActivityManager mActivityManager;
+
+    private Dialog mSetupUserDialog;
+
+    @Inject
+    public CreateUserActivity(UserCreator userCreator,
+            EditUserInfoController editUserInfoController, IActivityManager activityManager) {
+        mUserCreator = userCreator;
+        mEditUserInfoController = editUserInfoController;
+        mActivityManager = activityManager;
+    }
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setShowWhenLocked(true);
+        setContentView(R.layout.activity_create_new_user);
+
+        if (savedInstanceState != null) {
+            mEditUserInfoController.onRestoreInstanceState(savedInstanceState);
+        }
+
+        mSetupUserDialog = createDialog();
+        mSetupUserDialog.show();
+    }
+
+    @Override
+    protected void onSaveInstanceState(@NonNull Bundle outState) {
+        if (mSetupUserDialog != null && mSetupUserDialog.isShowing()) {
+            outState.putBundle(DIALOG_STATE_KEY, mSetupUserDialog.onSaveInstanceState());
+        }
+
+        mEditUserInfoController.onSaveInstanceState(outState);
+        super.onSaveInstanceState(outState);
+    }
+
+    @Override
+    protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
+        super.onRestoreInstanceState(savedInstanceState);
+        Bundle savedDialogState = savedInstanceState.getBundle(DIALOG_STATE_KEY);
+        if (savedDialogState != null && mSetupUserDialog != null) {
+            mSetupUserDialog.onRestoreInstanceState(savedDialogState);
+        }
+    }
+
+    private Dialog createDialog() {
+        String defaultUserName = getString(com.android.settingslib.R.string.user_new_user_name);
+
+        return mEditUserInfoController.createDialog(
+                this,
+                (intent, requestCode) -> {
+                    mEditUserInfoController.startingActivityForResult();
+                    startActivityForResult(intent, requestCode);
+                },
+                null,
+                defaultUserName,
+                getString(R.string.user_add_user),
+                this::addUserNow,
+                this::finish
+        );
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        mEditUserInfoController.onActivityResult(requestCode, resultCode, data);
+    }
+
+    @Override
+    public void onBackPressed() {
+        super.onBackPressed();
+        if (mSetupUserDialog != null) {
+            mSetupUserDialog.dismiss();
+        }
+    }
+
+    private void addUserNow(String userName, Drawable userIcon) {
+        mSetupUserDialog.dismiss();
+
+        userName = (userName == null || userName.trim().isEmpty())
+                ? getString(R.string.user_new_user_name)
+                : userName;
+
+        mUserCreator.createUser(userName, userIcon,
+                userInfo -> {
+                    switchToUser(userInfo.id);
+                    finishIfNeeded();
+                }, () -> {
+                    Log.e(TAG, "Unable to create user");
+                    finishIfNeeded();
+                });
+    }
+
+    private void finishIfNeeded() {
+        if (!isFinishing() && !isDestroyed()) {
+            finish();
+        }
+    }
+
+    private void switchToUser(int userId) {
+        try {
+            mActivityManager.switchUser(userId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Couldn't switch user.", e);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/user/UserCreator.java b/packages/SystemUI/src/com/android/systemui/user/UserCreator.java
new file mode 100644
index 0000000..3a270bb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/UserCreator.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.user;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.graphics.drawable.Drawable;
+import android.os.UserManager;
+
+import com.android.internal.util.UserIcons;
+import com.android.settingslib.users.UserCreatingDialog;
+import com.android.settingslib.utils.ThreadUtils;
+
+import java.util.function.Consumer;
+
+import javax.inject.Inject;
+
+/**
+ * A class to do the user creation process. It shows a progress dialog, and manages the user
+ * creation
+ */
+public class UserCreator {
+
+    private final Context mContext;
+    private final UserManager mUserManager;
+
+    @Inject
+    public UserCreator(Context context, UserManager userManager) {
+        mContext = context;
+        mUserManager = userManager;
+    }
+
+    /**
+     * Shows a progress dialog then starts the user creation process on the main thread.
+     *
+     * @param successCallback is called when the user creation is successful.
+     * @param errorCallback   is called when userManager.createUser returns null.
+     *                        (Exceptions are not handled by this class)
+     */
+    public void createUser(String userName, Drawable userIcon, Consumer<UserInfo> successCallback,
+            Runnable errorCallback) {
+
+        Dialog userCreationProgressDialog = new UserCreatingDialog(mContext);
+        userCreationProgressDialog.show();
+
+        // userManager.createUser will block the thread so post is needed for the dialog to show
+        ThreadUtils.postOnMainThread(() -> {
+            UserInfo user =
+                    mUserManager.createUser(userName, UserManager.USER_TYPE_FULL_SECONDARY, 0);
+            if (user == null) {
+                // Couldn't create user for some reason
+                userCreationProgressDialog.dismiss();
+                errorCallback.run();
+                return;
+            }
+
+            Drawable newUserIcon = userIcon;
+            if (newUserIcon == null) {
+                newUserIcon = UserIcons.getDefaultUserIcon(mContext.getResources(), user.id, false);
+            }
+            mUserManager.setUserIcon(user.id, UserIcons.convertToBitmap(newUserIcon));
+
+            userCreationProgressDialog.dismiss();
+            successCallback.accept(user);
+        });
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/user/UserModule.java b/packages/SystemUI/src/com/android/systemui/user/UserModule.java
new file mode 100644
index 0000000..0ad0984
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/UserModule.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.user;
+
+import com.android.settingslib.users.EditUserInfoController;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Dagger module for User related classes.
+ */
+@Module
+public class UserModule {
+
+    private static final String FILE_PROVIDER_AUTHORITY = "com.android.systemui.fileprovider";
+
+    @Provides
+    EditUserInfoController provideEditUserInfoController() {
+        return new EditUserInfoController(FILE_PROVIDER_AUTHORITY);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/DeviceConfigProxy.java b/packages/SystemUI/src/com/android/systemui/util/DeviceConfigProxy.java
index 66f8f74..6b5556b 100644
--- a/packages/SystemUI/src/com/android/systemui/util/DeviceConfigProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/util/DeviceConfigProxy.java
@@ -23,13 +23,19 @@
 import android.provider.DeviceConfig;
 import android.provider.Settings;
 
+import com.android.systemui.dagger.SysUISingleton;
+
 import java.util.concurrent.Executor;
 
+import javax.inject.Inject;
+
 /**
  * Wrapper around DeviceConfig useful for testing.
  */
+@SysUISingleton
 public class DeviceConfigProxy {
 
+    @Inject
     public DeviceConfigProxy() {
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
index 344f0d2..e79d432 100644
--- a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
+++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
@@ -24,7 +24,6 @@
 import android.view.View;
 
 import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.qs.QSFooterImpl;
 import com.android.systemui.qs.QSPanel;
 import com.android.systemui.qs.QuickQSPanel;
 import com.android.systemui.qs.customize.QSCustomizer;
@@ -92,11 +91,6 @@
         }
 
         /**
-         * Creates the QSFooterImpl.
-         */
-        QSFooterImpl createQsFooter();
-
-        /**
          * Creates the NotificationStackScrollLayout.
          */
         NotificationStackScrollLayout createNotificationStackScrollLayout();
diff --git a/packages/SystemUI/src/com/android/systemui/util/ViewController.java b/packages/SystemUI/src/com/android/systemui/util/ViewController.java
index c7aa780fc..e8b837e 100644
--- a/packages/SystemUI/src/com/android/systemui/util/ViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/util/ViewController.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.util;
 
+import android.content.Context;
+import android.content.res.Resources;
 import android.view.View;
 import android.view.View.OnAttachStateChangeListener;
 
@@ -24,8 +26,8 @@
  *
  * Implementations should handle setup and teardown related activities inside of
  * {@link #onViewAttached()} and {@link  #onViewDetached()}. Be sure to call {@link #init()} on
- * any child controllers that this uses. This can be done in {@link init()} if the controllers
- * are injected, or right after creation time of the child controller.
+ * any child controllers that this uses. This can be done in {@link #initInternal()} if the
+ * controllers are injected, or right after creation time of the child controller.
  *
  * Tip: View "attachment" happens top down - parents are notified that they are attached before
  * any children. That means that if you call a method on a child controller in
@@ -60,11 +62,18 @@
         mView = view;
     }
 
-    /** Call immediately after constructing Controller in order to handle view lifecycle events. */
+    /**
+     * Call immediately after constructing Controller in order to handle view lifecycle events.
+     *
+     * Generally speaking, you don't want to override this method. Instead, override
+     * {@link #initInternal()} as a way to have an run-once idempotent method that you can use for
+     * setup of your ViewController.
+     */
     public void init() {
         if (mInited) {
             return;
         }
+        initInternal();
         mInited = true;
 
         if (mView != null) {
@@ -76,6 +85,22 @@
     }
 
     /**
+     * Run once when {@link #init()} is called.
+     *
+     * Override this to perform idempotent, one-time setup that your controller needs. It will
+     * be called before {@link #onViewAttached()}.
+     */
+    protected void initInternal() {}
+
+    protected Context getContext() {
+        return mView.getContext();
+    }
+
+    protected Resources getResources() {
+        return mView.getResources();
+    }
+
+    /**
      * Called when the view is attached and a call to {@link #init()} has been made in either order.
      */
     protected abstract void onViewAttached();
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 2081cfe..735338f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -90,6 +90,7 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
+import com.android.systemui.media.dialog.MediaOutputDialogFactory;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.VolumeDialog;
 import com.android.systemui.plugins.VolumeDialogController;
@@ -519,6 +520,7 @@
                 Events.writeEvent(Events.EVENT_SETTINGS_CLICK);
                 Intent intent = new Intent(Settings.Panel.ACTION_VOLUME);
                 dismissH(DISMISS_REASON_SETTINGS_CLICKED);
+                Dependency.get(MediaOutputDialogFactory.class).dismiss();
                 Dependency.get(ActivityStarter.class).startActivity(intent,
                         true /* dismissShade */);
             });
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
index 247baf8..5310b3f 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
@@ -20,12 +20,13 @@
 import android.os.Handler;
 import android.view.LayoutInflater;
 
-import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.WMSingleton;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.WindowManagerShellWrapper;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.pip.Pip;
 import com.android.wm.shell.pip.PipBoundsHandler;
+import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
 import com.android.wm.shell.pip.PipTaskOrganizer;
 import com.android.wm.shell.pip.PipUiEventLogger;
@@ -45,53 +46,65 @@
  */
 @Module
 public abstract class TvPipModule {
-
-    @SysUISingleton
+    @WMSingleton
     @Provides
-    static Pip providePipController(Context context,
+    static Optional<Pip> providePip(
+            Context context,
             PipBoundsHandler pipBoundsHandler,
             PipTaskOrganizer pipTaskOrganizer,
             WindowManagerShellWrapper windowManagerShellWrapper) {
-        return new PipController(context, pipBoundsHandler, pipTaskOrganizer,
-                windowManagerShellWrapper);
+        return Optional.of(
+                new PipController(
+                        context,
+                        pipBoundsHandler,
+                        pipTaskOrganizer,
+                        windowManagerShellWrapper));
     }
 
-    @SysUISingleton
+    @WMSingleton
     @Provides
-    static PipControlsViewController providePipControlsViewContrller(
+    static PipControlsViewController providePipControlsViewController(
             PipControlsView pipControlsView, PipController pipController,
             LayoutInflater layoutInflater, Handler handler) {
         return new PipControlsViewController(pipControlsView, pipController, layoutInflater,
                 handler);
     }
 
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static PipControlsView providePipControlsView(Context context) {
         return new PipControlsView(context, null);
     }
 
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static PipNotification providePipNotification(Context context,
             PipController pipController) {
         return new PipNotification(context, pipController);
     }
 
-    @SysUISingleton
+    @WMSingleton
     @Provides
-    static PipBoundsHandler providePipBoundsHandler(Context context) {
-        return new PipBoundsHandler(context);
+    static PipBoundsHandler providePipBoundsHandler(Context context,
+            PipBoundsState pipBoundsState) {
+        return new PipBoundsHandler(context, pipBoundsState);
     }
 
-    @SysUISingleton
+    @WMSingleton
+    @Provides
+    static PipBoundsState providePipBoundsState() {
+        return new PipBoundsState();
+    }
+
+    @WMSingleton
     @Provides
     static PipTaskOrganizer providePipTaskOrganizer(Context context,
+            PipBoundsState pipBoundsState,
             PipBoundsHandler pipBoundsHandler,
             PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
             Optional<SplitScreen> splitScreenOptional, DisplayController displayController,
             PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer) {
-        return new PipTaskOrganizer(context, pipBoundsHandler,
+        return new PipTaskOrganizer(context, pipBoundsState, pipBoundsHandler,
                 pipSurfaceTransactionHelper, splitScreenOptional, displayController,
                 pipUiEventLogger, shellTaskOrganizer);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
index 56efffc..294c749a 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
@@ -20,7 +20,7 @@
 import android.os.Handler;
 import android.view.IWindowManager;
 
-import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.WMSingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
@@ -40,10 +40,9 @@
  * Provides dependencies from {@link com.android.wm.shell} which could be customized among different
  * branches of SystemUI.
  */
-// TODO(b/162923491): Move most of these dependencies into WMSingleton scope.
 @Module(includes = {WMShellBaseModule.class, TvPipModule.class})
 public class TvWMShellModule {
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static DisplayImeController provideDisplayImeController(IWindowManager wmService,
             DisplayController displayController, @Main Executor mainExecutor,
@@ -52,6 +51,8 @@
                 transactionPool);
     }
 
+    @WMSingleton
+    @Provides
     static SplitScreen provideSplitScreen(Context context,
             DisplayController displayController, SystemWindows systemWindows,
             DisplayImeController displayImeController, @Main Handler handler,
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index b828535..89ea9e2 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -55,9 +55,9 @@
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.navigationbar.NavigationModeController;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.InputConsumerController;
 import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.systemui.shared.tracing.ProtoTraceable;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -65,7 +65,6 @@
 import com.android.systemui.tracing.ProtoTracer;
 import com.android.systemui.tracing.nano.SystemUiTraceProto;
 import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.DisplayImeController;
 import com.android.wm.shell.nano.WmShellTraceProto;
 import com.android.wm.shell.onehanded.OneHanded;
 import com.android.wm.shell.onehanded.OneHandedEvents;
@@ -101,19 +100,18 @@
 
     private final CommandQueue mCommandQueue;
     private final ConfigurationController mConfigurationController;
-    private final DisplayImeController mDisplayImeController;
     private final InputConsumerController mInputConsumerController;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-    private final ActivityManagerWrapper mActivityManagerWrapper;
+    private final TaskStackChangeListeners mTaskStackChangeListeners;
     private final NavigationModeController mNavigationModeController;
     private final ScreenLifecycle mScreenLifecycle;
     private final SysUiState mSysUiState;
+    // TODO: This is only here because we need to dump state. Remove and replace with a dumper
+    //  interface.
+    private final ShellTaskOrganizer mShellTaskOrganizer;
     private final Optional<Pip> mPipOptional;
     private final Optional<SplitScreen> mSplitScreenOptional;
     private final Optional<OneHanded> mOneHandedOptional;
-    // Inject the organizer directly in case the optionals aren't loaded to depend on it. There
-    // are non-optional windowing features like FULLSCREEN.
-    private final ShellTaskOrganizer mShellTaskOrganizer;
     private final ProtoTracer mProtoTracer;
     private boolean mIsSysUiStateValid;
     private KeyguardUpdateMonitorCallback mSplitScreenKeyguardCallback;
@@ -125,8 +123,7 @@
             ConfigurationController configurationController,
             InputConsumerController inputConsumerController,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
-            ActivityManagerWrapper activityManagerWrapper,
-            DisplayImeController displayImeController,
+            TaskStackChangeListeners taskStackChangeListeners,
             NavigationModeController navigationModeController,
             ScreenLifecycle screenLifecycle,
             SysUiState sysUiState,
@@ -140,8 +137,7 @@
         mConfigurationController = configurationController;
         mInputConsumerController = inputConsumerController;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
-        mActivityManagerWrapper = activityManagerWrapper;
-        mDisplayImeController = displayImeController;
+        mTaskStackChangeListeners = taskStackChangeListeners;
         mNavigationModeController = navigationModeController;
         mScreenLifecycle = screenLifecycle;
         mSysUiState = sysUiState;
@@ -156,10 +152,6 @@
     @Override
     public void start() {
         mCommandQueue.addCallback(this);
-        // This is to prevent circular init problem by separating registration step out of its
-        // constructor. And make sure the initialization of DisplayImeController won't depend on
-        // specific feature anymore.
-        mDisplayImeController.startMonitorDisplays();
         mPipOptional.ifPresent(this::initPip);
         mSplitScreenOptional.ifPresent(this::initSplitScreen);
         mOneHandedOptional.ifPresent(this::initOneHanded);
@@ -167,9 +159,6 @@
 
     @VisibleForTesting
     void initPip(Pip pip) {
-        if (!PipUtils.hasSystemFeature(mContext)) {
-            return;
-        }
         mCommandQueue.addCallback(new CommandQueue.Callbacks() {
             @Override
             public void showPictureInPictureMenu() {
@@ -204,8 +193,9 @@
             }
         });
 
+        // TODO: Move this into the shell
         // Handle for system task stack changes.
-        mActivityManagerWrapper.registerTaskStackListener(
+        mTaskStackChangeListeners.registerTaskStackListener(
                 new TaskStackChangeListener() {
                     @Override
                     public void onTaskStackChanged() {
@@ -276,7 +266,7 @@
         };
         mKeyguardUpdateMonitor.registerCallback(mSplitScreenKeyguardCallback);
 
-        mActivityManagerWrapper.registerTaskStackListener(
+        mTaskStackChangeListeners.registerTaskStackListener(
                 new TaskStackChangeListener() {
                     @Override
                     public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
@@ -312,10 +302,6 @@
 
     @VisibleForTesting
     void initOneHanded(OneHanded oneHanded) {
-        if (!oneHanded.hasOneHandedFeature()) {
-            return;
-        }
-
         int currentMode = mNavigationModeController.addListener(mode ->
                 oneHanded.setThreeButtonModeEnabled(mode == NAV_BAR_MODE_3BUTTON));
         oneHanded.setThreeButtonModeEnabled(currentMode == NAV_BAR_MODE_3BUTTON);
@@ -392,7 +378,7 @@
             }
         });
 
-        mActivityManagerWrapper.registerTaskStackListener(
+        mTaskStackChangeListeners.registerTaskStackListener(
                 new TaskStackChangeListener() {
                     @Override
                     public void onTaskCreated(int taskId, ComponentName componentName) {
@@ -423,8 +409,13 @@
         if (handleLoggingCommand(args, pw)) {
             return;
         }
-
         // Dump WMShell stuff here if no commands were handled
+        mShellTaskOrganizer.dump(pw, "");
+        pw.println();
+        pw.println();
+        mPipOptional.ifPresent(pip -> pip.dump(pw));
+        mSplitScreenOptional.ifPresent(splitScreen -> splitScreen.dump(pw));
+        mOneHandedOptional.ifPresent(oneHanded -> oneHanded.dump(pw));
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index 970d500..b333f8b 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -20,27 +20,22 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.Handler;
-import android.util.DisplayMetrics;
 import android.view.IWindowManager;
 
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.bubbles.Bubbles;
-import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.WMSingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.shared.system.InputConsumerController;
-import com.android.systemui.util.DeviceConfigProxy;
 import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.WindowManagerShellWrapper;
-import com.android.wm.shell.animation.FlingAnimationUtils;
 import com.android.wm.shell.common.AnimationThread;
 import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.common.FloatingContentCoordinator;
 import com.android.wm.shell.common.HandlerExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.SystemWindows;
 import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.onehanded.OneHanded;
-import com.android.wm.shell.pip.Pip;
+import com.android.wm.shell.onehanded.OneHandedController;
 import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
 import com.android.wm.shell.pip.PipUiEventLogger;
 import com.android.wm.shell.pip.phone.PipAppOpsListener;
@@ -48,6 +43,8 @@
 import com.android.wm.shell.pip.phone.PipTouchHandler;
 import com.android.wm.shell.splitscreen.SplitScreen;
 
+import java.util.Optional;
+
 import dagger.BindsOptionalOf;
 import dagger.Module;
 import dagger.Provides;
@@ -56,41 +53,28 @@
  * Provides basic dependencies from {@link com.android.wm.shell}, the dependencies declared here
  * should be shared among different branches of SystemUI.
  */
-// TODO(b/162923491): Move most of these dependencies into WMSingleton scope.
 @Module
 public abstract class WMShellBaseModule {
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static TransactionPool provideTransactionPool() {
         return new TransactionPool();
     }
 
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static DisplayController provideDisplayController(Context context, @Main Handler handler,
             IWindowManager wmService) {
         return new DisplayController(context, handler, wmService);
     }
 
-    @SysUISingleton
-    @Provides
-    static DeviceConfigProxy provideDeviceConfigProxy() {
-        return new DeviceConfigProxy();
-    }
-
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static InputConsumerController provideInputConsumerController() {
         return InputConsumerController.getPipInputConsumer();
     }
 
-    @SysUISingleton
-    @Provides
-    static FloatingContentCoordinator provideFloatingContentCoordinator() {
-        return new FloatingContentCoordinator();
-    }
-
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static PipAppOpsListener providePipAppOpsListener(Context context,
             IActivityManager activityManager,
@@ -98,72 +82,58 @@
         return new PipAppOpsListener(context, activityManager, pipTouchHandler.getMotionHelper());
     }
 
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static PipMediaController providePipMediaController(Context context,
             IActivityManager activityManager) {
         return new PipMediaController(context, activityManager);
     }
 
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static PipUiEventLogger providePipUiEventLogger(UiEventLogger uiEventLogger,
             PackageManager packageManager) {
         return new PipUiEventLogger(uiEventLogger, packageManager);
     }
 
-    @SysUISingleton
+    @WMSingleton
     @Provides
-    static PipSurfaceTransactionHelper providesPipSurfaceTransactionHelper(Context context) {
+    static PipSurfaceTransactionHelper providePipSurfaceTransactionHelper(Context context) {
         return new PipSurfaceTransactionHelper(context);
     }
 
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static SystemWindows provideSystemWindows(DisplayController displayController,
             IWindowManager wmService) {
         return new SystemWindows(displayController, wmService);
     }
 
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static SyncTransactionQueue provideSyncTransactionQueue(@Main Handler handler,
             TransactionPool pool) {
         return new SyncTransactionQueue(pool, handler);
     }
 
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static ShellTaskOrganizer provideShellTaskOrganizer(SyncTransactionQueue syncQueue,
             @Main Handler handler, TransactionPool transactionPool) {
-        ShellTaskOrganizer organizer = new ShellTaskOrganizer(syncQueue, transactionPool,
+        return new ShellTaskOrganizer(syncQueue, transactionPool,
                 new HandlerExecutor(handler), AnimationThread.instance().getExecutor());
-        organizer.registerOrganizer();
-        return organizer;
     }
 
-    @SysUISingleton
-    @Provides
-    static WindowManagerShellWrapper provideWindowManagerShellWrapper() {
-        return new WindowManagerShellWrapper();
-    }
-
-    @SysUISingleton
-    @Provides
-    static FlingAnimationUtils.Builder provideFlingAnimationUtilsBuilder(
-            DisplayMetrics displayMetrics) {
-        return new FlingAnimationUtils.Builder(displayMetrics);
-    }
-
-    @BindsOptionalOf
-    abstract Pip optionalPip();
-
     @BindsOptionalOf
     abstract SplitScreen optionalSplitScreen();
 
     @BindsOptionalOf
     abstract Bubbles optionalBubbles();
 
-    @BindsOptionalOf
-    abstract OneHanded optionalOneHanded();
+    @WMSingleton
+    @Provides
+    static Optional<OneHanded> provideOneHandedController(Context context,
+            DisplayController displayController) {
+        return Optional.ofNullable(OneHandedController.create(context, displayController));
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
index 61c3f9c..a6fe728 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
@@ -20,7 +20,7 @@
 import android.os.Handler;
 import android.view.IWindowManager;
 
-import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.WMSingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.WindowManagerShellWrapper;
@@ -30,10 +30,9 @@
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.SystemWindows;
 import com.android.wm.shell.common.TransactionPool;
-import com.android.wm.shell.onehanded.OneHanded;
-import com.android.wm.shell.onehanded.OneHandedController;
 import com.android.wm.shell.pip.Pip;
 import com.android.wm.shell.pip.PipBoundsHandler;
+import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
 import com.android.wm.shell.pip.PipTaskOrganizer;
 import com.android.wm.shell.pip.PipUiEventLogger;
@@ -55,10 +54,9 @@
  * Provides dependencies from {@link com.android.wm.shell} which could be customized among different
  * branches of SystemUI.
  */
-// TODO(b/162923491): Move most of these dependencies into WMSingleton scope.
 @Module(includes = WMShellBaseModule.class)
 public class WMShellModule {
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static DisplayImeController provideDisplayImeController(IWindowManager wmService,
             DisplayController displayController, @Main Executor mainExecutor,
@@ -67,23 +65,7 @@
                 transactionPool);
     }
 
-    @SysUISingleton
-    @Provides
-    static Pip providePipController(Context context,
-            DisplayController displayController,
-            PipAppOpsListener pipAppOpsListener,
-            PipBoundsHandler pipBoundsHandler,
-            PipMediaController pipMediaController,
-            PipMenuActivityController pipMenuActivityController,
-            PipTaskOrganizer pipTaskOrganizer,
-            PipTouchHandler pipTouchHandler,
-            WindowManagerShellWrapper windowManagerShellWrapper) {
-        return new PipController(context, displayController,
-                pipAppOpsListener, pipBoundsHandler, pipMediaController, pipMenuActivityController,
-                pipTaskOrganizer, pipTouchHandler, windowManagerShellWrapper);
-    }
-
-    @SysUISingleton
+    @WMSingleton
     @Provides
     static SplitScreen provideSplitScreen(Context context,
             DisplayController displayController, SystemWindows systemWindows,
@@ -94,46 +76,61 @@
                 displayImeController, handler, transactionPool, shellTaskOrganizer, syncQueue);
     }
 
-    @SysUISingleton
+    @WMSingleton
     @Provides
-    static PipBoundsHandler providesPipBoundsHandler(Context context) {
-        return new PipBoundsHandler(context);
+    static Optional<Pip> providePip(Context context, DisplayController displayController,
+            PipAppOpsListener pipAppOpsListener, PipBoundsHandler pipBoundsHandler,
+            PipBoundsState pipBoundsState, PipMediaController pipMediaController,
+            PipMenuActivityController pipMenuActivityController, PipTaskOrganizer pipTaskOrganizer,
+            PipTouchHandler pipTouchHandler, WindowManagerShellWrapper windowManagerShellWrapper) {
+        return Optional.ofNullable(PipController.create(context, displayController,
+                pipAppOpsListener, pipBoundsHandler, pipBoundsState, pipMediaController,
+                pipMenuActivityController, pipTaskOrganizer, pipTouchHandler,
+                windowManagerShellWrapper));
     }
 
-    @SysUISingleton
+    @WMSingleton
     @Provides
-    static PipMenuActivityController providesPipMenuActivityController(Context context,
+    static PipBoundsState providePipBoundsState() {
+        return new PipBoundsState();
+    }
+
+    @WMSingleton
+    @Provides
+    static PipBoundsHandler providesPipBoundsHandler(Context context,
+            PipBoundsState pipBoundsState) {
+        return new PipBoundsHandler(context, pipBoundsState);
+    }
+
+    @WMSingleton
+    @Provides
+    static PipMenuActivityController providePipMenuActivityController(Context context,
             PipMediaController pipMediaController, PipTaskOrganizer pipTaskOrganizer) {
         return new PipMenuActivityController(context, pipMediaController, pipTaskOrganizer);
     }
 
-    @SysUISingleton
+    @WMSingleton
     @Provides
-    static PipTouchHandler providesPipTouchHandler(Context context,
+    static PipTouchHandler providePipTouchHandler(Context context,
             PipMenuActivityController menuActivityController, PipBoundsHandler pipBoundsHandler,
+            PipBoundsState pipBoundsState,
             PipTaskOrganizer pipTaskOrganizer,
             FloatingContentCoordinator floatingContentCoordinator,
             PipUiEventLogger pipUiEventLogger) {
         return new PipTouchHandler(context, menuActivityController, pipBoundsHandler,
-                pipTaskOrganizer, floatingContentCoordinator, pipUiEventLogger);
+                pipBoundsState, pipTaskOrganizer, floatingContentCoordinator, pipUiEventLogger);
     }
 
-    @SysUISingleton
+    @WMSingleton
     @Provides
-    static PipTaskOrganizer providesPipTaskOrganizer(Context context,
+    static PipTaskOrganizer providePipTaskOrganizer(Context context,
+            PipBoundsState pipBoundsState,
             PipBoundsHandler pipBoundsHandler,
             PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
             Optional<SplitScreen> splitScreenOptional, DisplayController displayController,
             PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer) {
-        return new PipTaskOrganizer(context, pipBoundsHandler,
+        return new PipTaskOrganizer(context, pipBoundsState, pipBoundsHandler,
                 pipSurfaceTransactionHelper, splitScreenOptional, displayController,
                 pipUiEventLogger, shellTaskOrganizer);
     }
-
-    @SysUISingleton
-    @Provides
-    static OneHanded provideOneHandedController(Context context,
-            DisplayController displayController) {
-        return OneHandedController.create(context, displayController);
-    }
 }
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index db87845..d541c8f 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -70,6 +70,13 @@
             android:exported="false"
             android:resizeableActivity="true" />
 
+        <activity android:name="com.android.systemui.emergency.EmergencyActivityTest"
+                  android:exported="true">
+            <intent-filter>
+                <action android:name="com.android.systemui.action.LAUNCH_EMERGENCY"/>
+            </intent-filter>
+        </activity>
+
         <activity
             android:name="com.android.systemui.globalactions.GlobalActionsImeTest$TestActivity"
             android:excludeFromRecents="true"
@@ -82,6 +89,10 @@
 
         <activity android:name="com.android.systemui.screenshot.ScrollViewActivity"
                   android:exported="false" />
+
+        <activity android:name="com.android.systemui.screenshot.RecyclerViewActivity"
+                  android:exported="false" />
+
         <provider
             android:name="androidx.lifecycle.ProcessLifecycleOwnerInitializer"
             tools:replace="android:authorities"
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index 3aa6ec0..094230d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -24,6 +24,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.content.res.Resources;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.view.View;
@@ -64,6 +65,8 @@
     ColorExtractor.GradientColors mGradientColors;
     @Mock
     KeyguardSliceViewController mKeyguardSliceViewController;
+    @Mock
+    Resources mResources;
 
     private KeyguardClockSwitchController mController;
 
@@ -72,9 +75,13 @@
         MockitoAnnotations.initMocks(this);
 
         when(mView.isAttachedToWindow()).thenReturn(true);
-
+        when(mResources.getString(anyInt())).thenReturn("h:mm");
         mController = new KeyguardClockSwitchController(
-                mView, mStatusBarStateController, mColorExtractor, mClockManager,
+                mView,
+                mResources,
+                mStatusBarStateController,
+                mColorExtractor,
+                mClockManager,
                 mKeyguardSliceViewController);
 
         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
index ae159c7..62906f3 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
@@ -20,9 +20,11 @@
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.hardware.display.DisplayManager;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.util.AttributeSet;
+import android.view.Display;
 import android.view.LayoutInflater;
 import android.view.View;
 
@@ -104,9 +106,10 @@
 
     @Test
     public void testInflation_doesntCrash() {
-        KeyguardPresentation keyguardPresentation = new KeyguardPresentation(mContext,
-                mContext.getDisplayNoVerify(), mKeyguardStatusViewComponentFactory,
-                mLayoutInflater);
+        final Display display = mContext.getSystemService(DisplayManager.class).getDisplay(
+                Display.DEFAULT_DISPLAY);
+        KeyguardPresentation keyguardPresentation = new KeyguardPresentation(mContext, display,
+                mKeyguardStatusViewComponentFactory);
         keyguardPresentation.onCreate(null /*savedInstanceState */);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java
index b7bcaa3..9f8f6c1 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java
@@ -44,9 +44,7 @@
 @RunWithLooper(setAsMainLooper = true)
 public class KeyguardSliceViewControllerTest extends SysuiTestCase {
     @Mock
-    private KeyguardSliceView mView;;
-    @Mock
-    private KeyguardStatusView mKeyguardStatusView;
+    private KeyguardSliceView mView;
     @Mock
     private TunerService mTunerService;
     @Mock
@@ -63,7 +61,7 @@
         when(mView.isAttachedToWindow()).thenReturn(true);
         when(mView.getContext()).thenReturn(mContext);
         mController = new KeyguardSliceViewController(
-                mView, mKeyguardStatusView, mActivityStarter, mConfigurationController,
+                mView, mActivityStarter, mConfigurationController,
                 mTunerService, mDumpManager);
         mController.setupUri(KeyguardSliceProvider.KEYGUARD_SLICE_URI);
     }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
new file mode 100644
index 0000000..752a744
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
+
+import static org.mockito.Mockito.verify;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+
+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)
+public class KeyguardStatusViewControllerTest extends SysuiTestCase {
+
+    @Mock
+    private KeyguardStatusView mKeyguardStatusView;
+    @Mock
+    private KeyguardSliceViewController mKeyguardSliceViewController;
+    @Mock
+    private KeyguardClockSwitchController mKeyguardClockSwitchController;
+    @Mock
+    private KeyguardStateController mKeyguardStateController;
+    @Mock
+    private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    @Mock
+    ConfigurationController mConfigurationController;
+
+    private KeyguardStatusViewController mController;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+
+        mController = new KeyguardStatusViewController(
+                mKeyguardStatusView,
+                mKeyguardSliceViewController,
+                mKeyguardClockSwitchController,
+                mKeyguardStateController,
+                mKeyguardUpdateMonitor,
+                mConfigurationController);
+    }
+
+    @Test
+    public void dozeTimeTick_updatesSlice() {
+        mController.dozeTimeTick();
+        verify(mKeyguardSliceViewController).refresh();
+    }
+
+    @Test
+    public void dozeTimeTick_updatesClock() {
+        mController.dozeTimeTick();
+        verify(mKeyguardClockSwitchController).refresh();
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java
deleted file mode 100644
index 79ec4f2..0000000
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java
+++ /dev/null
@@ -1,69 +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.keyguard;
-
-import static org.mockito.Mockito.verify;
-
-import android.test.suitebuilder.annotation.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper.RunWithLooper;
-import android.view.LayoutInflater;
-
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-
-@SmallTest
-@RunWithLooper
-@RunWith(AndroidTestingRunner.class)
-public class KeyguardStatusViewTest extends SysuiTestCase {
-
-    @Mock
-    KeyguardSliceViewController mKeyguardSliceViewController;
-
-    @Mock
-    KeyguardClockSwitch mClockView;
-    @InjectMocks
-    KeyguardStatusView mKeyguardStatusView;
-
-    @Before
-    public void setUp() {
-        allowTestableLooperAsMainThread();
-        LayoutInflater layoutInflater = LayoutInflater.from(getContext());
-        mKeyguardStatusView =
-                (KeyguardStatusView) layoutInflater.inflate(R.layout.keyguard_status_view, null);
-        org.mockito.MockitoAnnotations.initMocks(this);
-    }
-
-    @Test
-    public void dozeTimeTick_updatesSlice() {
-        mKeyguardStatusView.dozeTimeTick();
-        verify(mKeyguardSliceViewController).refresh();
-    }
-
-    @Test
-    public void dozeTimeTick_updatesClock() {
-        mKeyguardStatusView.dozeTimeTick();
-        verify(mClockView).refresh();
-    }
-
-}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
index 353fe62..35fe1ba 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
@@ -168,7 +168,7 @@
         verify(mMockListener2).onClockChanged(captor2.capture());
         assertThat(captor1.getValue()).isInstanceOf(BUBBLE_CLOCK_CLASS);
         assertThat(captor2.getValue()).isInstanceOf(BUBBLE_CLOCK_CLASS);
-        assertThat(captor1.getValue()).isNotSameAs(captor2.getValue());
+        assertThat(captor1.getValue()).isNotSameInstanceAs(captor2.getValue());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index ab805f4..0c87f59 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -621,6 +621,44 @@
         assertEquals(mScreenDecorations.mRoundedDefault, new Point(5, 5));
     }
 
+    @Test
+    public void testOnlyRoundedCornerRadiusTop() {
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.dimen.rounded_corner_radius, 0);
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.dimen.rounded_corner_radius_top, 10);
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.dimen.rounded_corner_radius_bottom, 0);
+        mContext.getOrCreateTestableResources()
+                .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
+
+        mScreenDecorations.start();
+        assertEquals(new Point(0, 0), mScreenDecorations.mRoundedDefault);
+        assertEquals(new Point(10, 10), mScreenDecorations.mRoundedDefaultTop);
+        assertEquals(new Point(0, 0), mScreenDecorations.mRoundedDefaultBottom);
+    }
+
+    @Test
+    public void testOnlyRoundedCornerRadiusBottom() {
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.dimen.rounded_corner_radius, 0);
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.dimen.rounded_corner_radius_top, 0);
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.dimen.rounded_corner_radius_bottom, 20);
+        mContext.getOrCreateTestableResources()
+                .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
+
+        mScreenDecorations.start();
+        assertEquals(new Point(0, 0), mScreenDecorations.mRoundedDefault);
+        assertEquals(new Point(0, 0), mScreenDecorations.mRoundedDefaultTop);
+        assertEquals(new Point(20, 20), mScreenDecorations.mRoundedDefaultBottom);
+    }
+
 
     @Test
     public void testBoundingRectsToRegion() throws Exception {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SizeCompatModeActivityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/SizeCompatModeActivityControllerTest.java
index 1638ea1..71a0434 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SizeCompatModeActivityControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SizeCompatModeActivityControllerTest.java
@@ -29,8 +29,8 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SizeCompatModeActivityController.RestartActivityButton;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.systemui.statusbar.CommandQueue;
 
 import org.junit.Before;
@@ -50,7 +50,7 @@
 
     private SizeCompatModeActivityController mController;
     private TaskStackChangeListener mTaskStackListener;
-    private @Mock ActivityManagerWrapper mMockAm;
+    private @Mock TaskStackChangeListeners mMockTaskListeners;
     private @Mock RestartActivityButton mMockButton;
     private @Mock IBinder mMockActivityToken;
 
@@ -59,7 +59,7 @@
         MockitoAnnotations.initMocks(this);
         doReturn(true).when(mMockButton).show();
 
-        mController = new SizeCompatModeActivityController(mContext, mMockAm,
+        mController = new SizeCompatModeActivityController(mContext, mMockTaskListeners,
                 new CommandQueue(mContext)) {
             @Override
             RestartActivityButton createRestartButton(Context context) {
@@ -69,7 +69,7 @@
 
         ArgumentCaptor<TaskStackChangeListener> listenerCaptor =
                 ArgumentCaptor.forClass(TaskStackChangeListener.class);
-        verify(mMockAm).registerTaskStackListener(listenerCaptor.capture());
+        verify(mMockTaskListeners).registerTaskStackListener(listenerCaptor.capture());
         mTaskStackListener = listenerCaptor.getValue();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index 86b1e61..4f3266d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -30,6 +30,7 @@
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.uiautomator.UiDevice;
 
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
@@ -72,7 +73,7 @@
 
     @Before
     public void SysuiSetup() throws Exception {
-        SystemUIFactory.createFromConfig(mContext);
+        SystemUIFactory.createFromConfig(mContext, true);
         mDependency = new TestableDependency(
                 SystemUIFactory.getInstance().getSysUIComponent().createDependency());
         Dependency.setInstance(mDependency);
@@ -149,6 +150,10 @@
         return mContext;
     }
 
+    protected UiDevice getUiDevice() {
+        return UiDevice.getInstance(mRealInstrumentation);
+    }
+
     protected void runShellCommand(String command) throws IOException {
         ParcelFileDescriptor pfd = mRealInstrumentation.getUiAutomation()
                 .executeShellCommand(command);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
index 64e0673..3da1f29 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
@@ -22,17 +22,22 @@
 import static android.view.MotionEvent.ACTION_DOWN;
 import static android.view.MotionEvent.ACTION_MOVE;
 import static android.view.MotionEvent.ACTION_UP;
+import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK;
 
+import static com.android.systemui.accessibility.MagnificationModeSwitch.DEFAULT_FADE_OUT_ANIMATION_DELAY_MS;
+import static com.android.systemui.accessibility.MagnificationModeSwitch.FADING_ANIMATION_DURATION_MS;
 import static com.android.systemui.accessibility.MagnificationModeSwitch.getIconResId;
 
 import static junit.framework.Assert.assertEquals;
 
+import static org.hamcrest.CoreMatchers.hasItems;
+import static org.hamcrest.MatcherAssert.assertThat;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -45,10 +50,13 @@
 import android.view.ViewConfiguration;
 import android.view.ViewPropertyAnimator;
 import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.ImageView;
 
 import androidx.test.filters.SmallTest;
 
+import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 
 import org.junit.Before;
@@ -64,8 +72,13 @@
 @RunWith(AndroidTestingRunner.class)
 public class MagnificationModeSwitchTest extends SysuiTestCase {
 
+    private static final float FADE_IN_ALPHA = 1f;
+    private static final float FADE_OUT_ALPHA = 0f;
+
     private ImageView mSpyImageView;
     @Mock
+    private AccessibilityManager mAccessibilityManager;
+    @Mock
     private WindowManager mWindowManager;
     @Mock
     private ViewPropertyAnimator mViewPropertyAnimator;
@@ -81,50 +94,70 @@
                 wm.getMaximumWindowMetrics()
         ).when(mWindowManager).getMaximumWindowMetrics();
         mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
+        mContext.addMockSystemService(Context.ACCESSIBILITY_SERVICE, mAccessibilityManager);
         mSpyImageView = Mockito.spy(new ImageView(mContext));
-        doAnswer(invocation -> null).when(mSpyImageView).setOnTouchListener(
-                mTouchListenerCaptor.capture());
-        initMockImageViewAndAnimator();
+        resetMockImageViewAndAnimator();
 
         mMagnificationModeSwitch = new MagnificationModeSwitch(mContext, mSpyImageView);
     }
 
     @Test
-    public void removeButton_removeView() {
+    public void removeButton_buttonIsShowing_removeView() {
         mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
 
         mMagnificationModeSwitch.removeButton();
 
         verify(mWindowManager).removeView(mSpyImageView);
-        // First invocation is in showButton.
-        verify(mViewPropertyAnimator, times(2)).cancel();
+        verify(mViewPropertyAnimator).cancel();
     }
 
     @Test
     public void showWindowModeButton_fullscreenMode_addViewAndSetImageResource() {
         mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
 
-        verify(mSpyImageView).setAlpha(1.0f);
         verify(mSpyImageView).setImageResource(
                 getIconResId(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW));
-        assertShowButtonAnimation();
-        ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
-        verify(mViewPropertyAnimator).withEndAction(captor.capture());
         verify(mWindowManager).addView(eq(mSpyImageView), any(WindowManager.LayoutParams.class));
+        assertShowFadingAnimation(FADE_IN_ALPHA);
+        assertShowFadingAnimation(FADE_OUT_ALPHA);
+    }
 
-        captor.getValue().run();
+    @Test
+    public void showMagnificationButton_a11yTimeout_autoFadeOut() {
+        final int a11yTimeout = 12345;
+        when(mAccessibilityManager.getRecommendedTimeoutMillis(anyInt(), anyInt())).thenReturn(
+                a11yTimeout);
 
-        // First invocation is in showButton.
-        verify(mViewPropertyAnimator, times(2)).cancel();
+        mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
+
+        verify(mAccessibilityManager).getRecommendedTimeoutMillis(
+                DEFAULT_FADE_OUT_ANIMATION_DELAY_MS, AccessibilityManager.FLAG_CONTENT_ICONS
+                        | AccessibilityManager.FLAG_CONTENT_CONTROLS);
+        final ArgumentCaptor<Runnable> fadeOutCaptor = ArgumentCaptor.forClass(Runnable.class);
+        final ArgumentCaptor<Long> fadeOutDelay = ArgumentCaptor.forClass(Long.class);
+        verify(mSpyImageView).postOnAnimationDelayed(fadeOutCaptor.capture(),
+                fadeOutDelay.capture());
+        assertEquals(a11yTimeout, (long) fadeOutDelay.getValue());
+
+        // Verify the end action after fade-out.
+        fadeOutCaptor.getValue().run();
+        final ArgumentCaptor<Runnable> endActionCaptor = ArgumentCaptor.forClass(Runnable.class);
+        verify(mViewPropertyAnimator).withEndAction(endActionCaptor.capture());
+
+        endActionCaptor.getValue().run();
+
+        verify(mViewPropertyAnimator).cancel();
         verify(mWindowManager).removeView(mSpyImageView);
     }
 
     @Test
-    public void onConfigurationChanged_setImageResource() {
+    public void onConfigurationChanged_buttonIsShowing_setImageResource() {
         mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+        resetMockImageViewAndAnimator();
+
         mMagnificationModeSwitch.onConfigurationChanged(ActivityInfo.CONFIG_DENSITY);
 
-        verify(mSpyImageView, times(2)).setImageResource(
+        verify(mSpyImageView).setImageResource(
                 getIconResId(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN));
     }
 
@@ -142,13 +175,7 @@
         resetMockImageViewAndAnimator();
         listener.onTouch(mSpyImageView, MotionEvent.obtain(
                 0, ViewConfiguration.getTapTimeout(), ACTION_UP, 100, 100, 0));
-        verify(mViewPropertyAnimator).cancel();
-        verify(mSpyImageView).setImageResource(
-                getIconResId(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW));
-        verify(mWindowManager).removeView(mSpyImageView);
-        final int actualMode = Settings.Secure.getInt(mContext.getContentResolver(),
-                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0);
-        assertEquals(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW, actualMode);
+        verifyTapAction(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
     }
 
     @Test
@@ -163,7 +190,6 @@
                 Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0);
         listener.onTouch(mSpyImageView, MotionEvent.obtain(
                 0, 0, ACTION_DOWN, 100, 100, 0));
-        verify(mSpyImageView).setAlpha(1.0f);
         verify(mViewPropertyAnimator).cancel();
 
         listener.onTouch(mSpyImageView, MotionEvent.obtain(
@@ -174,9 +200,8 @@
         resetMockImageViewAndAnimator();
         listener.onTouch(mSpyImageView, MotionEvent.obtain(
                 0, ViewConfiguration.getTapTimeout() + 10, ACTION_UP, 100 + offset, 100, 0));
-        verify(mSpyImageView).setAlpha(1.0f);
         assertModeUnchanged(previousMode);
-        assertShowButtonAnimation();
+        assertShowFadingAnimation(FADE_OUT_ALPHA);
     }
 
     @Test
@@ -194,9 +219,8 @@
         resetMockImageViewAndAnimator();
         listener.onTouch(mSpyImageView, MotionEvent.obtain(
                 0, ViewConfiguration.getTapTimeout(), ACTION_CANCEL, 100, 100, 0));
-        verify(mSpyImageView).setAlpha(1.0f);
         assertModeUnchanged(previousMode);
-        assertShowButtonAnimation();
+        assertShowFadingAnimation(FADE_OUT_ALPHA);
     }
 
     @Test
@@ -217,9 +241,49 @@
         resetMockImageViewAndAnimator();
         listener.onTouch(mSpyImageView, MotionEvent.obtain(
                 0, ViewConfiguration.getTapTimeout(), ACTION_CANCEL, 100 + offset, 100, 0));
-        verify(mSpyImageView).setAlpha(1.0f);
         assertModeUnchanged(previousMode);
-        assertShowButtonAnimation();
+        assertShowFadingAnimation(FADE_OUT_ALPHA);
+    }
+
+    @Test
+    public void initializeA11yNode_showWindowModeButton_expectedValues() {
+        mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
+        final AccessibilityNodeInfo nodeInfo = new AccessibilityNodeInfo();
+
+        mSpyImageView.onInitializeAccessibilityNodeInfo(nodeInfo);
+
+        assertEquals(mContext.getString(R.string.magnification_mode_switch_description),
+                nodeInfo.getContentDescription());
+        assertEquals(mContext.getString(R.string.magnification_mode_switch_state_window),
+                nodeInfo.getStateDescription());
+        assertThat(nodeInfo.getActionList(),
+                hasItems(new AccessibilityNodeInfo.AccessibilityAction(
+                        ACTION_CLICK.getId(), mContext.getResources().getString(
+                        R.string.magnification_mode_switch_click_label))));
+    }
+
+    @Test
+    public void performA11yActions_showWindowModeButton_verifyTapAction() {
+        mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
+        resetMockImageViewAndAnimator();
+
+        mSpyImageView.performAccessibilityAction(
+                ACTION_CLICK.getId(), null);
+
+        verifyTapAction(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+    }
+
+    @Test
+    public void showButton_showFadeOutAnimation_fadeOutAnimationCanceled() {
+        mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+        assertShowFadingAnimation(FADE_OUT_ALPHA);
+        resetMockImageViewAndAnimator();
+
+        mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+
+        verify(mViewPropertyAnimator).cancel();
+        assertEquals(1f, mSpyImageView.getAlpha());
+        assertShowFadingAnimation(FADE_OUT_ALPHA);
     }
 
     private void assertModeUnchanged(int expectedMode) {
@@ -228,30 +292,50 @@
         assertEquals(expectedMode, actualMode);
     }
 
-    private void assertShowButtonAnimation() {
-        verify(mViewPropertyAnimator).cancel();
-        verify(mViewPropertyAnimator).setDuration(anyLong());
-        verify(mViewPropertyAnimator).alpha(anyFloat());
+    private void assertShowFadingAnimation(float alpha) {
+        ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+        if (alpha == FADE_IN_ALPHA) { // Fade-in
+            verify(mSpyImageView).postOnAnimation(runnableCaptor.capture());
+        } else { // Fade-out
+            verify(mSpyImageView).postOnAnimationDelayed(runnableCaptor.capture(), anyLong());
+        }
+        resetMockAnimator();
+
+        runnableCaptor.getValue().run();
+
+        verify(mViewPropertyAnimator).setDuration(eq(FADING_ANIMATION_DURATION_MS));
+        verify(mViewPropertyAnimator).alpha(alpha);
         verify(mViewPropertyAnimator).start();
     }
 
-    private void initMockImageViewAndAnimator() {
+    private void resetMockImageViewAndAnimator() {
+        Mockito.reset(mSpyImageView);
+        doAnswer(invocation -> null).when(mSpyImageView).setOnTouchListener(
+                mTouchListenerCaptor.capture());
+        resetMockAnimator();
+    }
+
+    private void resetMockAnimator() {
+        Mockito.reset(mViewPropertyAnimator);
         when(mViewPropertyAnimator.setDuration(anyLong())).thenReturn(mViewPropertyAnimator);
         when(mViewPropertyAnimator.alpha(anyFloat())).thenReturn(mViewPropertyAnimator);
         when(mViewPropertyAnimator.withEndAction(any(Runnable.class))).thenReturn(
                 mViewPropertyAnimator);
-
         when(mSpyImageView.animate()).thenReturn(mViewPropertyAnimator);
-        doAnswer(invocation -> {
-            Runnable run = invocation.getArgument(0);
-            run.run();
-            return null;
-        }).when(mSpyImageView).postDelayed(any(), anyLong());
     }
 
-    private void resetMockImageViewAndAnimator() {
-        Mockito.reset(mViewPropertyAnimator);
-        Mockito.reset(mSpyImageView);
-        initMockImageViewAndAnimator();
+    /**
+     * Verifies the tap behaviour including the image of the button and the magnification mode.
+     *
+     * @param expectedMode the expected mode after tapping
+     */
+    private void verifyTapAction(int expectedMode) {
+        verify(mViewPropertyAnimator).cancel();
+        verify(mSpyImageView).setImageResource(
+                getIconResId(expectedMode));
+        verify(mWindowManager).removeView(mSpyImageView);
+        final int actualMode = Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0);
+        assertEquals(expectedMode, actualMode);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index 5f2fd69..f1606c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -224,11 +224,12 @@
 
 
     @Test
-    public void onDensityChanged_enabled_updateDimensionsAndLayout() {
+    public void onDensityChanged_enabled_updateDimensionsAndResetWindowMagnification() {
         mInstrumentation.runOnMainSync(() -> {
             mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
                     Float.NaN);
             Mockito.reset(mWindowManager);
+            Mockito.reset(mMirrorWindowControl);
         });
 
         mInstrumentation.runOnMainSync(() -> {
@@ -237,7 +238,9 @@
 
         verify(mResources, atLeastOnce()).getDimensionPixelSize(anyInt());
         verify(mWindowManager).removeView(any());
+        verify(mMirrorWindowControl).destroyControl();
         verify(mWindowManager).addView(any(), any());
+        verify(mMirrorWindowControl).showControl();
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 99e39b8..e24f4ca3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -222,7 +222,7 @@
         mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
         event.recycle();
         // THEN the event is passed to the FingerprintManager
-        verify(mFingerprintManager).onFingerDown(eq(mUdfpsController.mUdfpsSensorId), eq(0), eq(0),
+        verify(mFingerprintManager).onPointerDown(eq(mUdfpsController.mUdfpsSensorId), eq(0), eq(0),
                 eq(0f), eq(0f));
         // AND the scrim and dot is shown
         verify(mUdfpsView).showScrimAndDot();
@@ -236,7 +236,7 @@
         // WHEN fingerprint is requested because of AOD interrupt
         mUdfpsController.onAodInterrupt(0, 0);
         // THEN the event is passed to the FingerprintManager
-        verify(mFingerprintManager).onFingerDown(eq(mUdfpsController.mUdfpsSensorId), eq(0), eq(0),
+        verify(mFingerprintManager).onPointerDown(eq(mUdfpsController.mUdfpsSensorId), eq(0), eq(0),
                 anyFloat(), anyFloat());
         // AND the scrim and dot is shown
         verify(mUdfpsView).showScrimAndDot();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
index da00e7e..1a78ca4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
@@ -53,7 +53,7 @@
         receiver: BroadcastReceiver,
         filter: IntentFilter,
         executor: Executor?,
-        user: UserHandle
+        user: UserHandle?
     ) {
         registeredReceivers.add(receiver)
     }
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 08ccd854..d9e9a8b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -45,6 +45,9 @@
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.pm.LauncherApps;
+import android.content.res.Configuration;
+import android.graphics.Insets;
+import android.graphics.Rect;
 import android.hardware.display.AmbientDisplayConfiguration;
 import android.hardware.face.FaceManager;
 import android.os.Handler;
@@ -80,10 +83,8 @@
 import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
-import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.LockscreenLockIconController;
 import com.android.systemui.statusbar.phone.NotificationShadeWindowControllerImpl;
 import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
 import com.android.systemui.statusbar.phone.ShadeController;
@@ -91,6 +92,7 @@
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.WindowManagerShellWrapper;
 import com.android.wm.shell.common.FloatingContentCoordinator;
 
@@ -177,8 +179,6 @@
     @Mock
     private ShadeController mShadeController;
     @Mock
-    private NotificationShelfComponent mNotificationShelfComponent;
-    @Mock
     private NotifPipeline mNotifPipeline;
     @Mock
     private FeatureFlags mFeatureFlagsOldPipeline;
@@ -190,9 +190,12 @@
     private IStatusBarService mStatusBarService;
     @Mock
     private LauncherApps mLauncherApps;
-    @Mock private LockscreenLockIconController mLockIconController;
-
-    @Mock private WindowManagerShellWrapper mWindowManagerShellWrapper;
+    @Mock
+    private WindowManagerShellWrapper mWindowManagerShellWrapper;
+    @Mock
+    private BubbleLogger mBubbleLogger;
+    @Mock
+    private BubblePositioner mPositioner;
 
     private BubbleData mBubbleData;
 
@@ -207,7 +210,6 @@
         mContext.addMockSystemService(FaceManager.class, mFaceManager);
         when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors);
 
-        // Bubbles get added to status bar window view
         mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext,
                 mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
                 mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController,
@@ -238,6 +240,13 @@
                 mSysUiStateBubblesExpanded =
                         (sysUiFlags & QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED) != 0);
 
+        mBubbleData = new BubbleData(mContext, mBubbleLogger);
+
+        Rect availableRect = new Rect(0, 0, 1000, 5000);
+        when(mPositioner.getAvailableRect()).thenReturn(availableRect);
+        when(mPositioner.getOrientation()).thenReturn(Configuration.ORIENTATION_PORTRAIT);
+        when(mPositioner.getInsets()).thenReturn(Insets.of(0, 0, 0, 0));
+
         TestableNotificationInterruptStateProviderImpl interruptionStateProvider =
                 new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(),
                         mock(PowerManager.class),
@@ -249,7 +258,6 @@
                         mock(HeadsUpManager.class),
                         mock(Handler.class)
                 );
-        mBubbleData = new BubbleData(mContext);
         when(mFeatureFlagsOldPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
         mBubbleController = new TestableBubbleController(
                 mContext,
@@ -273,7 +281,11 @@
                 mStatusBarService,
                 mWindowManager,
                 mWindowManagerShellWrapper,
-                mLauncherApps);
+                mLauncherApps,
+                mBubbleLogger,
+                mock(Handler.class),
+                mock(ShellTaskOrganizer.class),
+                mPositioner);
         mBubbleController.setExpandListener(mBubbleExpandListener);
 
         // Get a reference to the BubbleController's entry listener
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
index 4bbc41e..0c872db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
@@ -16,9 +16,8 @@
 
 package com.android.systemui.bubbles;
 
-import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
-
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.reset;
@@ -29,7 +28,9 @@
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.graphics.drawable.Icon;
+import android.os.Bundle;
 import android.os.UserHandle;
+import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -39,10 +40,6 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.bubbles.BubbleData.TimeSource;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
 
 import com.google.common.collect.ImmutableList;
 
@@ -68,15 +65,15 @@
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class BubbleDataTest extends SysuiTestCase {
 
-    private NotificationEntry mEntryA1;
-    private NotificationEntry mEntryA2;
-    private NotificationEntry mEntryA3;
-    private NotificationEntry mEntryB1;
-    private NotificationEntry mEntryB2;
-    private NotificationEntry mEntryB3;
-    private NotificationEntry mEntryC1;
-    private NotificationEntry mEntryInterruptive;
-    private NotificationEntry mEntryDismissed;
+    private BubbleEntry mEntryA1;
+    private BubbleEntry mEntryA2;
+    private BubbleEntry mEntryA3;
+    private BubbleEntry mEntryB1;
+    private BubbleEntry mEntryB2;
+    private BubbleEntry mEntryB3;
+    private BubbleEntry mEntryC1;
+    private BubbleEntry mEntryInterruptive;
+    private BubbleEntry mEntryDismissed;
 
     private Bubble mBubbleA1;
     private Bubble mBubbleA2;
@@ -98,8 +95,8 @@
     private PendingIntent mExpandIntent;
     @Mock
     private PendingIntent mDeleteIntent;
-
-    private NotificationTestHelper mNotificationTestHelper;
+    @Mock
+    private BubbleLogger mBubbleLogger;
 
     @Captor
     private ArgumentCaptor<BubbleData.Update> mUpdateCaptor;
@@ -112,29 +109,23 @@
 
     @Before
     public void setUp() throws Exception {
-        mNotificationTestHelper = new NotificationTestHelper(
-                mContext,
-                mDependency,
-                TestableLooper.get(this));
         MockitoAnnotations.initMocks(this);
 
-        mEntryA1 = createBubbleEntry(1, "a1", "package.a");
-        mEntryA2 = createBubbleEntry(1, "a2", "package.a");
-        mEntryA3 = createBubbleEntry(1, "a3", "package.a");
-        mEntryB1 = createBubbleEntry(1, "b1", "package.b");
-        mEntryB2 = createBubbleEntry(1, "b2", "package.b");
-        mEntryB3 = createBubbleEntry(1, "b3", "package.b");
-        mEntryC1 = createBubbleEntry(1, "c1", "package.c");
+        mEntryA1 = createBubbleEntry(1, "a1", "package.a", null);
+        mEntryA2 = createBubbleEntry(1, "a2", "package.a", null);
+        mEntryA3 = createBubbleEntry(1, "a3", "package.a", null);
+        mEntryB1 = createBubbleEntry(1, "b1", "package.b", null);
+        mEntryB2 = createBubbleEntry(1, "b2", "package.b", null);
+        mEntryB3 = createBubbleEntry(1, "b3", "package.b", null);
+        mEntryC1 = createBubbleEntry(1, "c1", "package.c", null);
 
-        mEntryInterruptive = createBubbleEntry(1, "interruptive", "package.d");
-        modifyRanking(mEntryInterruptive)
-                .setVisuallyInterruptive(true)
-                .build();
+        NotificationListenerService.Ranking ranking =
+                mock(NotificationListenerService.Ranking.class);
+        when(ranking.visuallyInterruptive()).thenReturn(true);
+        mEntryInterruptive = createBubbleEntry(1, "interruptive", "package.d", ranking);
         mBubbleInterruptive = new Bubble(mEntryInterruptive, mSuppressionListener, null);
 
-        ExpandableNotificationRow row = mNotificationTestHelper.createBubble();
-        mEntryDismissed = createBubbleEntry(1, "dismissed", "package.d");
-        mEntryDismissed.setRow(row);
+        mEntryDismissed = createBubbleEntry(1, "dismissed", "package.d", null);
         mBubbleDismissed = new Bubble(mEntryDismissed, mSuppressionListener, null);
 
         mBubbleA1 = new Bubble(mEntryA1, mSuppressionListener, mPendingIntentCanceledListener);
@@ -145,7 +136,7 @@
         mBubbleB3 = new Bubble(mEntryB3, mSuppressionListener, mPendingIntentCanceledListener);
         mBubbleC1 = new Bubble(mEntryC1, mSuppressionListener, mPendingIntentCanceledListener);
 
-        mBubbleData = new BubbleData(getContext());
+        mBubbleData = new BubbleData(getContext(), mBubbleLogger);
 
         // Used by BubbleData to set lastAccessedTime
         when(mTimeSource.currentTimeMillis()).thenReturn(1000L);
@@ -813,47 +804,48 @@
 
     private void assertBubbleAdded(Bubble expected) {
         BubbleData.Update update = mUpdateCaptor.getValue();
-        assertThat(update.addedBubble).named("addedBubble").isEqualTo(expected);
+        assertWithMessage("addedBubble").that(update.addedBubble).isEqualTo(expected);
     }
 
     private void assertBubbleRemoved(Bubble expected, @BubbleController.DismissReason int reason) {
         BubbleData.Update update = mUpdateCaptor.getValue();
-        assertThat(update.removedBubbles).named("removedBubbles")
+        assertWithMessage("removedBubbles").that(update.removedBubbles)
                 .isEqualTo(ImmutableList.of(Pair.create(expected, reason)));
     }
 
     private void assertOrderNotChanged() {
         BubbleData.Update update = mUpdateCaptor.getValue();
-        assertThat(update.orderChanged).named("orderChanged").isFalse();
+        assertWithMessage("orderChanged").that(update.orderChanged).isFalse();
     }
 
     private void assertOrderChangedTo(Bubble... order) {
         BubbleData.Update update = mUpdateCaptor.getValue();
-        assertThat(update.orderChanged).named("orderChanged").isTrue();
-        assertThat(update.bubbles).named("bubble order").isEqualTo(ImmutableList.copyOf(order));
+        assertWithMessage("orderChanged").that(update.orderChanged).isTrue();
+        assertWithMessage("bubble order").that(update.bubbles)
+                .isEqualTo(ImmutableList.copyOf(order));
     }
 
     private void assertSelectionNotChanged() {
         BubbleData.Update update = mUpdateCaptor.getValue();
-        assertThat(update.selectionChanged).named("selectionChanged").isFalse();
+        assertWithMessage("selectionChanged").that(update.selectionChanged).isFalse();
     }
 
     private void assertSelectionChangedTo(Bubble bubble) {
         BubbleData.Update update = mUpdateCaptor.getValue();
-        assertThat(update.selectionChanged).named("selectionChanged").isTrue();
-        assertThat(update.selectedBubble).named("selectedBubble").isEqualTo(bubble);
+        assertWithMessage("selectionChanged").that(update.selectionChanged).isTrue();
+        assertWithMessage("selectedBubble").that(update.selectedBubble).isEqualTo(bubble);
     }
 
     private void assertSelectionCleared() {
         BubbleData.Update update = mUpdateCaptor.getValue();
-        assertThat(update.selectionChanged).named("selectionChanged").isTrue();
-        assertThat(update.selectedBubble).named("selectedBubble").isNull();
+        assertWithMessage("selectionChanged").that(update.selectionChanged).isTrue();
+        assertWithMessage("selectedBubble").that(update.selectedBubble).isNull();
     }
 
     private void assertExpandedChangedTo(boolean expected) {
         BubbleData.Update update = mUpdateCaptor.getValue();
-        assertThat(update.expandedChanged).named("expandedChanged").isTrue();
-        assertThat(update.expanded).named("expanded").isEqualTo(expected);
+        assertWithMessage("expandedChanged").that(update.expandedChanged).isTrue();
+        assertWithMessage("expanded").that(update.expanded).isEqualTo(expected);
     }
 
     private void assertOverflowChangedTo(ImmutableList<Bubble> bubbles) {
@@ -862,12 +854,13 @@
     }
 
 
-    private NotificationEntry createBubbleEntry(int userId, String notifKey, String packageName) {
-        return createBubbleEntry(userId, notifKey, packageName, 1000);
+    private BubbleEntry createBubbleEntry(int userId, String notifKey, String packageName,
+            NotificationListenerService.Ranking ranking) {
+        return createBubbleEntry(userId, notifKey, packageName, ranking, 1000);
     }
 
-    private void setPostTime(NotificationEntry entry, long postTime) {
-        when(entry.getSbn().getPostTime()).thenReturn(postTime);
+    private void setPostTime(BubbleEntry entry, long postTime) {
+        when(entry.getStatusBarNotification().getPostTime()).thenReturn(postTime);
     }
 
     /**
@@ -875,16 +868,19 @@
      * required for BubbleData functionality and verification. NotificationTestHelper is used only
      * as a convenience to create a Notification w/BubbleMetadata.
      */
-    private NotificationEntry createBubbleEntry(int userId, String notifKey, String packageName,
-            long postTime) {
+    private BubbleEntry createBubbleEntry(int userId, String notifKey, String packageName,
+            NotificationListenerService.Ranking ranking, long postTime) {
         // BubbleMetadata
         Notification.BubbleMetadata bubbleMetadata = new Notification.BubbleMetadata.Builder(
                 mExpandIntent, Icon.createWithResource("", 0))
                 .setDeleteIntent(mDeleteIntent)
                 .build();
         // Notification -> BubbleMetadata
-        Notification notification = mNotificationTestHelper.createNotification(false,
-                null /* groupKey */, bubbleMetadata);
+        Notification notification = mock(Notification.class);
+        notification.setBubbleMetadata(bubbleMetadata);
+
+        // Notification -> extras
+        notification.extras = new Bundle();
 
         // StatusBarNotification
         StatusBarNotification sbn = mock(StatusBarNotification.class);
@@ -895,18 +891,18 @@
         when(sbn.getNotification()).thenReturn(notification);
 
         // NotificationEntry -> StatusBarNotification -> Notification -> BubbleMetadata
-        return new NotificationEntryBuilder().setSbn(sbn).build();
+        return new BubbleEntry(sbn, ranking, true, false, false, false);
     }
 
     private void setCurrentTime(long time) {
         when(mTimeSource.currentTimeMillis()).thenReturn(time);
     }
 
-    private void sendUpdatedEntryAtTime(NotificationEntry entry, long postTime) {
+    private void sendUpdatedEntryAtTime(BubbleEntry entry, long postTime) {
         sendUpdatedEntryAtTime(entry, postTime, true /* visuallyInterruptive */);
     }
 
-    private void sendUpdatedEntryAtTime(NotificationEntry entry, long postTime,
+    private void sendUpdatedEntryAtTime(BubbleEntry entry, long postTime,
             boolean visuallyInterruptive) {
         setPostTime(entry, postTime);
         // BubbleController calls this:
@@ -921,4 +917,4 @@
         setCurrentTime(time);
         mBubbleData.setExpanded(shouldBeExpanded);
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
index f7f3a37..29ead59 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
@@ -24,12 +24,14 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.Intent;
 import android.graphics.drawable.Icon;
 import android.os.Bundle;
+import android.service.notification.StatusBarNotification;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
@@ -37,8 +39,6 @@
 
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -52,8 +52,10 @@
 public class BubbleTest extends SysuiTestCase {
     @Mock
     private Notification mNotif;
+    @Mock
+    private StatusBarNotification mSbn;
 
-    private NotificationEntry mEntry;
+    private BubbleEntry mBubbleEntry;
     private Bundle mExtras;
     private Bubble mBubble;
 
@@ -67,18 +69,16 @@
         mExtras = new Bundle();
         mNotif.extras = mExtras;
 
-        mEntry = new NotificationEntryBuilder()
-                .setNotification(mNotif)
-                .build();
-
-        mBubble = new Bubble(mEntry, mSuppressionListener, null);
-
         Intent target = new Intent(mContext, BubblesTestActivity.class);
         Notification.BubbleMetadata metadata = new Notification.BubbleMetadata.Builder(
                 PendingIntent.getActivity(mContext, 0, target, 0),
                         Icon.createWithResource(mContext, R.drawable.android))
                 .build();
-        mEntry.setBubbleMetadata(metadata);
+        when(mSbn.getNotification()).thenReturn(mNotif);
+        when(mNotif.getBubbleMetadata()).thenReturn(metadata);
+        when(mSbn.getKey()).thenReturn("mock");
+        mBubbleEntry = new BubbleEntry(mSbn, null, true, false, false, false);
+        mBubble = new Bubble(mBubbleEntry, mSuppressionListener, null);
     }
 
     @Test
@@ -86,7 +86,7 @@
         final String msg = "Hello there!";
         doReturn(Notification.Style.class).when(mNotif).getNotificationStyle();
         mExtras.putCharSequence(Notification.EXTRA_TEXT, msg);
-        assertEquals(msg, BubbleViewInfoTask.extractFlyoutMessage(mEntry).message);
+        assertEquals(msg, Bubble.extractFlyoutMessage(mBubbleEntry).message);
     }
 
     @Test
@@ -97,7 +97,7 @@
         mExtras.putCharSequence(Notification.EXTRA_BIG_TEXT, msg);
 
         // Should be big text, not the small text.
-        assertEquals(msg, BubbleViewInfoTask.extractFlyoutMessage(mEntry).message);
+        assertEquals(msg, Bubble.extractFlyoutMessage(mBubbleEntry).message);
     }
 
     @Test
@@ -105,7 +105,7 @@
         doReturn(Notification.MediaStyle.class).when(mNotif).getNotificationStyle();
 
         // Media notifs don't get update messages.
-        assertNull(BubbleViewInfoTask.extractFlyoutMessage(mEntry).message);
+        assertNull(Bubble.extractFlyoutMessage(mBubbleEntry).message);
     }
 
     @Test
@@ -121,7 +121,7 @@
 
         // Should be the last one only.
         assertEquals("Really? I prefer them that way.",
-                BubbleViewInfoTask.extractFlyoutMessage(mEntry).message);
+                Bubble.extractFlyoutMessage(mBubbleEntry).message);
     }
 
     @Test
@@ -136,8 +136,8 @@
                                 "Oh, hello!", 0, "Mady").toBundle()});
 
         // Should be the last one only.
-        assertEquals("Oh, hello!", BubbleViewInfoTask.extractFlyoutMessage(mEntry).message);
-        assertEquals("Mady", BubbleViewInfoTask.extractFlyoutMessage(mEntry).senderName);
+        assertEquals("Oh, hello!", Bubble.extractFlyoutMessage(mBubbleEntry).message);
+        assertEquals("Mady", Bubble.extractFlyoutMessage(mBubbleEntry).senderName);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
index 1eaa6a4..b9394ff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
@@ -42,7 +42,9 @@
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.pm.LauncherApps;
-import android.content.res.Resources;
+import android.content.res.Configuration;
+import android.graphics.Insets;
+import android.graphics.Rect;
 import android.hardware.display.AmbientDisplayConfiguration;
 import android.hardware.face.FaceManager;
 import android.os.Handler;
@@ -91,6 +93,7 @@
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.util.InjectionInflationController;
+import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.WindowManagerShellWrapper;
 import com.android.wm.shell.common.FloatingContentCoordinator;
 
@@ -169,8 +172,6 @@
     @Mock
     ColorExtractor.GradientColors mGradientColors;
     @Mock
-    private Resources mResources;
-    @Mock
     private ShadeController mShadeController;
     @Mock
     private NotificationShelfComponent mNotificationShelfComponent;
@@ -188,6 +189,10 @@
     private LauncherApps mLauncherApps;
     @Mock
     private WindowManagerShellWrapper mWindowManagerShellWrapper;
+    @Mock
+    private BubbleLogger mBubbleLogger;
+    @Mock
+    private BubblePositioner mPositioner;
 
     private BubbleData mBubbleData;
 
@@ -220,7 +225,6 @@
                 },
                 mLockIconController);
 
-        // Bubbles get added to status bar window view
         mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext,
                 mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
                 mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController,
@@ -240,6 +244,13 @@
         mZenModeConfig.suppressedVisualEffects = 0;
         when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);
 
+        mBubbleData = new BubbleData(mContext, mBubbleLogger);
+
+        Rect availableRect = new Rect(0, 0, 1000, 5000);
+        when(mPositioner.getAvailableRect()).thenReturn(availableRect);
+        when(mPositioner.getOrientation()).thenReturn(Configuration.ORIENTATION_PORTRAIT);
+        when(mPositioner.getInsets()).thenReturn(Insets.of(0, 0, 0, 0));
+
         TestableNotificationInterruptStateProviderImpl interruptionStateProvider =
                 new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(),
                         mock(PowerManager.class),
@@ -251,7 +262,6 @@
                         mock(HeadsUpManager.class),
                         mock(Handler.class)
                 );
-        mBubbleData = new BubbleData(mContext);
         when(mFeatureFlagsNewPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(true);
         mBubbleController = new TestableBubbleController(
                 mContext,
@@ -275,7 +285,11 @@
                 mStatusBarService,
                 mWindowManager,
                 mWindowManagerShellWrapper,
-                mLauncherApps);
+                mLauncherApps,
+                mBubbleLogger,
+                mock(Handler.class),
+                mock(ShellTaskOrganizer.class),
+                mPositioner);
         mBubbleController.addNotifCallback(mNotifCallback);
         mBubbleController.setExpandListener(mBubbleExpandListener);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TaskViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TaskViewTest.java
new file mode 100644
index 0000000..b13c6fc
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TaskViewTest.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.os.Handler;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.SurfaceControl;
+import android.view.SurfaceHolder;
+import android.view.SurfaceSession;
+import android.window.WindowContainerToken;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.HandlerExecutor;
+
+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;
+import org.mockito.invocation.InvocationOnMock;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+// TODO: Place in com.android.wm.shell vs. com.android.wm.shell.bubbles on shell migration.
+public class TaskViewTest extends SysuiTestCase {
+
+    @Mock
+    TaskView.Listener mViewListener;
+    @Mock
+    ActivityManager.RunningTaskInfo mTaskInfo;
+    @Mock
+    WindowContainerToken mToken;
+    @Mock
+    ShellTaskOrganizer mOrganizer;
+    @Mock
+    HandlerExecutor mExecutor;
+
+    SurfaceSession mSession;
+    SurfaceControl mLeash;
+
+    Context mContext;
+    TaskView mTaskView;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mLeash = new SurfaceControl.Builder(mSession)
+                .setName("test")
+                .build();
+
+        mContext = getContext();
+
+        mTaskInfo = new ActivityManager.RunningTaskInfo();
+        mTaskInfo.token = mToken;
+        mTaskInfo.taskId = 314;
+        mTaskInfo.taskDescription = mock(ActivityManager.TaskDescription.class);
+
+        doAnswer((InvocationOnMock invocationOnMock) -> {
+            final Runnable r = invocationOnMock.getArgument(0);
+            r.run();
+            return null;
+        }).when(mExecutor).execute(any());
+
+        mTaskView = new TaskView(mContext, mOrganizer, mExecutor);
+        mTaskView.setListener(mViewListener);
+    }
+
+    @After
+    public void tearDown() {
+        if (mTaskView != null) {
+            mTaskView.release();
+        }
+    }
+
+    @Test
+    public void testSetPendingListener_throwsException() {
+        TaskView taskView = new TaskView(mContext, mOrganizer, mExecutor);
+        taskView.setListener(mViewListener);
+        try {
+            taskView.setListener(mViewListener);
+        } catch (IllegalStateException e) {
+            // pass
+            return;
+        }
+        fail("Expected IllegalStateException");
+    }
+
+    @Test
+    public void testStartActivity() {
+        ActivityOptions options = ActivityOptions.makeBasic();
+        mTaskView.startActivity(mock(PendingIntent.class), null, options);
+
+        verify(mOrganizer).setPendingLaunchCookieListener(any(), eq(mTaskView));
+        assertThat(options.getLaunchWindowingMode()).isEqualTo(WINDOWING_MODE_MULTI_WINDOW);
+        assertThat(options.getTaskAlwaysOnTop()).isTrue();
+    }
+
+    @Test
+    public void testOnTaskAppeared_noSurface() {
+        mTaskView.onTaskAppeared(mTaskInfo, mLeash);
+
+        verify(mViewListener).onTaskCreated(eq(mTaskInfo.taskId), any());
+        verify(mViewListener, never()).onInitialized();
+        // If there's no surface the task should be made invisible
+        verify(mViewListener).onTaskVisibilityChanged(eq(mTaskInfo.taskId), eq(false));
+    }
+
+    @Test
+    public void testOnTaskAppeared_withSurface() {
+        mTaskView.surfaceCreated(mock(SurfaceHolder.class));
+        mTaskView.onTaskAppeared(mTaskInfo, mLeash);
+
+        verify(mViewListener).onTaskCreated(eq(mTaskInfo.taskId), any());
+        verify(mViewListener, never()).onTaskVisibilityChanged(anyInt(), anyBoolean());
+    }
+
+    @Test
+    public void testSurfaceCreated_noTask() {
+        mTaskView.surfaceCreated(mock(SurfaceHolder.class));
+
+        verify(mViewListener).onInitialized();
+        // No task, no visibility change
+        verify(mViewListener, never()).onTaskVisibilityChanged(anyInt(), anyBoolean());
+    }
+
+    @Test
+    public void testSurfaceCreated_withTask() {
+        mTaskView.onTaskAppeared(mTaskInfo, mLeash);
+        mTaskView.surfaceCreated(mock(SurfaceHolder.class));
+
+        verify(mViewListener).onInitialized();
+        verify(mViewListener).onTaskVisibilityChanged(eq(mTaskInfo.taskId), eq(true));
+    }
+
+    @Test
+    public void testSurfaceDestroyed_noTask() {
+        SurfaceHolder sh = mock(SurfaceHolder.class);
+        mTaskView.surfaceCreated(sh);
+        mTaskView.surfaceDestroyed(sh);
+
+        verify(mViewListener, never()).onTaskVisibilityChanged(anyInt(), anyBoolean());
+    }
+
+    @Test
+    public void testSurfaceDestroyed_withTask() {
+        SurfaceHolder sh = mock(SurfaceHolder.class);
+        mTaskView.onTaskAppeared(mTaskInfo, mLeash);
+        mTaskView.surfaceCreated(sh);
+        reset(mViewListener);
+        mTaskView.surfaceDestroyed(sh);
+
+        verify(mViewListener).onTaskVisibilityChanged(eq(mTaskInfo.taskId), eq(false));
+    }
+
+    @Test
+    public void testOnReleased() {
+        mTaskView.onTaskAppeared(mTaskInfo, mLeash);
+        mTaskView.surfaceCreated(mock(SurfaceHolder.class));
+        mTaskView.release();
+
+        verify(mOrganizer).removeListener(eq(mTaskView));
+        verify(mViewListener).onReleased();
+    }
+
+    @Test
+    public void testOnTaskVanished() {
+        mTaskView.onTaskAppeared(mTaskInfo, mLeash);
+        mTaskView.surfaceCreated(mock(SurfaceHolder.class));
+        mTaskView.onTaskVanished(mTaskInfo);
+
+        verify(mViewListener).onTaskRemovalStarted(eq(mTaskInfo.taskId));
+    }
+
+    @Test
+    public void testOnBackPressedOnTaskRoot() {
+        mTaskView.onTaskAppeared(mTaskInfo, mLeash);
+        mTaskView.onBackPressedOnTaskRoot(mTaskInfo);
+
+        verify(mViewListener).onBackPressedOnTaskRoot(eq(mTaskInfo.taskId));
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
index 87ea22a..aaeee16 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
@@ -19,6 +19,7 @@
 import android.app.INotificationManager;
 import android.content.Context;
 import android.content.pm.LauncherApps;
+import android.os.Handler;
 import android.view.WindowManager;
 
 import com.android.internal.statusbar.IStatusBarService;
@@ -35,10 +36,10 @@
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.WindowManagerShellWrapper;
 import com.android.wm.shell.common.FloatingContentCoordinator;
 
-
 /**
  * Testable BubbleController subclass that immediately synchronizes surfaces.
  */
@@ -66,14 +67,19 @@
             IStatusBarService statusBarService,
             WindowManager windowManager,
             WindowManagerShellWrapper windowManagerShellWrapper,
-            LauncherApps launcherApps) {
+            LauncherApps launcherApps,
+            BubbleLogger bubbleLogger,
+            Handler mainHandler,
+            ShellTaskOrganizer shellTaskOrganizer,
+            BubblePositioner positioner) {
         super(context,
                 notificationShadeWindowController, statusBarStateController, shadeController,
                 data, Runnable::run, configurationController, interruptionStateProvider,
                 zenModeController, lockscreenUserManager, groupManager, entryManager,
                 notifPipeline, featureFlags, dumpManager, floatingContentCoordinator,
                 dataRepository, sysUiState, notificationManager, statusBarService,
-                windowManager, windowManagerShellWrapper, launcherApps);
+                windowManager, windowManagerShellWrapper, launcherApps, bubbleLogger,
+                mainHandler, shellTaskOrganizer, positioner);
         setInflateSynchronously(true);
     }
 }
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 6a14863..a5bb8ea 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
@@ -17,26 +17,30 @@
 package com.android.systemui.bubbles.animation;
 
 import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
+import android.annotation.SuppressLint;
 import android.content.res.Configuration;
 import android.content.res.Resources;
-import android.graphics.Point;
+import android.graphics.Insets;
 import android.graphics.PointF;
+import android.graphics.Rect;
 import android.testing.AndroidTestingRunner;
 import android.view.View;
+import android.view.WindowManager;
 import android.widget.FrameLayout;
 
 import androidx.dynamicanimation.animation.DynamicAnimation;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.R;
+import com.android.systemui.bubbles.BubblePositioner;
 
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mockito;
 import org.mockito.Spy;
 
 @SmallTest
@@ -46,47 +50,45 @@
     private int mDisplayWidth = 500;
     private int mDisplayHeight = 1000;
     private int mExpandedViewPadding = 10;
-    private int mOrientation = Configuration.ORIENTATION_PORTRAIT;
-    private float mLauncherGridDiff = 30f;
 
-    private Runnable mOnBubbleAnimatedOutAction = Mockito.mock(Runnable.class);
-
+    private Runnable mOnBubbleAnimatedOutAction = mock(Runnable.class);
     @Spy
-    private ExpandedAnimationController mExpandedController =
-            new ExpandedAnimationController(
-                    new Point(mDisplayWidth, mDisplayHeight) /* displaySize */,
-                    mExpandedViewPadding, mOrientation, mOnBubbleAnimatedOutAction);
+    ExpandedAnimationController mExpandedController;
 
     private int mStackOffset;
-    private float mBubblePaddingTop;
-    private float mBubbleSize;
-
     private PointF mExpansionPoint;
 
+    @SuppressLint("VisibleForTests")
     @Before
     public void setUp() throws Exception {
         super.setUp();
+
+        BubblePositioner positioner = new BubblePositioner(getContext(), mock(WindowManager.class));
+        positioner.update(Configuration.ORIENTATION_PORTRAIT,
+                Insets.of(0, 0, 0, 0),
+                new Rect(0, 0, mDisplayWidth, mDisplayHeight));
+        mExpandedController = new ExpandedAnimationController(positioner, mExpandedViewPadding,
+                mOnBubbleAnimatedOutAction);
+
         addOneMoreThanBubbleLimitBubbles();
         mLayout.setActiveController(mExpandedController);
 
         Resources res = mLayout.getResources();
         mStackOffset = res.getDimensionPixelSize(R.dimen.bubble_stack_offset);
-        mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
-        mBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
         mExpansionPoint = new PointF(100, 100);
     }
 
     @Test
     @Ignore
     public void testExpansionAndCollapse() throws InterruptedException {
-        Runnable afterExpand = Mockito.mock(Runnable.class);
+        Runnable afterExpand = mock(Runnable.class);
         mExpandedController.expandFromStack(afterExpand);
         waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
 
         testBubblesInCorrectExpandedPositions();
         verify(afterExpand).run();
 
-        Runnable afterCollapse = Mockito.mock(Runnable.class);
+        Runnable afterCollapse = mock(Runnable.class);
         mExpandedController.collapseBackToStack(mExpansionPoint, afterCollapse);
         waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
 
@@ -121,7 +123,7 @@
 
     /** Expand the stack and wait for animations to finish. */
     private void expand() throws InterruptedException {
-        mExpandedController.expandFromStack(Mockito.mock(Runnable.class));
+        mExpandedController.expandFromStack(mock(Runnable.class));
         waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
     }
 
@@ -141,51 +143,12 @@
     private void testBubblesInCorrectExpandedPositions() {
         // Check all the visible bubbles to see if they're in the right place.
         for (int i = 0; i < mLayout.getChildCount(); i++) {
-            assertEquals(getBubbleLeft(i),
+            float expectedPosition = mExpandedController.getBubbleXOrYForOrientation(i);
+            assertEquals(expectedPosition,
                     mLayout.getChildAt(i).getTranslationX(),
                     2f);
-            assertEquals(mExpandedController.getExpandedY(),
+            assertEquals(expectedPosition,
                     mLayout.getChildAt(i).getTranslationY(), 2f);
         }
     }
-
-    /**
-     * @param index Bubble index in row.
-     * @return Bubble left x from left edge of screen.
-     */
-    public float getBubbleLeft(int index) {
-        final float bubbleLeft = index * (mBubbleSize + getSpaceBetweenBubbles());
-        return getRowLeft() + bubbleLeft;
-    }
-
-    private float getRowLeft() {
-        if (mLayout == null) {
-            return 0;
-        }
-        int bubbleCount = mLayout.getChildCount();
-        final float totalBubbleWidth = bubbleCount * mBubbleSize;
-        final float totalGapWidth = (bubbleCount - 1) * getSpaceBetweenBubbles();
-        final float rowWidth = totalGapWidth + totalBubbleWidth;
-
-        final float centerScreen = mDisplayWidth / 2f;
-        final float halfRow = rowWidth / 2f;
-        final float rowLeft = centerScreen - halfRow;
-
-        return rowLeft;
-    }
-
-    /**
-     * @return Space between bubbles in row above expanded view.
-     */
-    private float getSpaceBetweenBubbles() {
-        final float rowMargins = (mExpandedViewPadding + mLauncherGridDiff) * 2;
-        final float maxRowWidth = mDisplayWidth - rowMargins;
-
-        final float totalBubbleWidth = mMaxBubbles * mBubbleSize;
-        final float totalGapWidth = maxRowWidth - totalBubbleWidth;
-
-        final int gapCount = mMaxBubbles - 1;
-        final float gapWidth = totalGapWidth / gapCount;
-        return gapWidth;
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java
index 9242ce9..7d0abec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java
@@ -18,6 +18,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
@@ -33,6 +34,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.R;
+import com.android.systemui.bubbles.BubblePositioner;
 import com.android.wm.shell.common.FloatingContentCoordinator;
 
 import org.junit.Before;
@@ -40,7 +42,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.Mockito;
 
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -67,7 +68,7 @@
                     public int getAsInt() {
                         return mLayout.getChildCount();
                     }
-                }, Mockito.mock(Runnable.class)));
+                }, mock(Runnable.class)));
         mLayout.setActiveController(mStackController);
         addOneMoreThanBubbleLimitBubbles();
         mStackOffset = mLayout.getResources().getDimensionPixelSize(R.dimen.bubble_stack_offset);
@@ -306,7 +307,10 @@
                 FloatingContentCoordinator floatingContentCoordinator,
                 IntSupplier bubbleCountSupplier,
                 Runnable onBubbleAnimatedOutAction) {
-            super(floatingContentCoordinator, bubbleCountSupplier, onBubbleAnimatedOutAction);
+            super(floatingContentCoordinator,
+                    bubbleCountSupplier,
+                    onBubbleAnimatedOutAction,
+                    mock(BubblePositioner.class));
         }
 
         @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/emergency/EmergencyActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/emergency/EmergencyActivityTest.java
new file mode 100644
index 0000000..a52a598
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/emergency/EmergencyActivityTest.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.emergency;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+import com.android.systemui.R;
+
+/**
+ * Test activity for resolving {@link EmergencyGesture#ACTION_LAUNCH_EMERGENCY} action.
+ */
+public class EmergencyActivityTest extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
index b4af786..78ee593 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
@@ -53,6 +53,9 @@
 import android.widget.FrameLayout;
 
 import androidx.test.filters.SmallTest;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
 
 import com.android.internal.colorextraction.ColorExtractor;
 import com.android.internal.logging.MetricsLogger;
@@ -86,11 +89,16 @@
 
 import java.util.List;
 import java.util.concurrent.Executor;
+import java.util.regex.Pattern;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class GlobalActionsDialogTest extends SysuiTestCase {
+    private static final long UI_TIMEOUT_MILLIS = 5000; // 5 sec
+    private static final Pattern CANCEL_BUTTON =
+            Pattern.compile("cancel", Pattern.CASE_INSENSITIVE);
+
     private GlobalActionsDialog mGlobalActionsDialog;
 
     @Mock private GlobalActions.GlobalActionsManager mWindowManagerFuncs;
@@ -240,6 +248,13 @@
                 mGlobalActionsDialog.makeScreenshotActionForTesting();
         screenshotAction.onLongPress();
         verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_SCREENSHOT_LONG_PRESS);
+
+        // Dismiss ScreenRecordDialog opened by the long press above.
+        final UiObject2 cancelButton = getUiDevice().wait(
+                Until.findObject(By.text(CANCEL_BUTTON)), UI_TIMEOUT_MILLIS);
+        if (cancelButton != null) {
+            cancelButton.click();
+        }
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsImeTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsImeTest.java
index 58959c4..bfc7935 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsImeTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsImeTest.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.globalactions;
 
+import static android.provider.Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD;
 import static android.view.WindowInsets.Type.ime;
 
 import static org.junit.Assert.assertEquals;
@@ -24,9 +25,11 @@
 import static org.junit.Assert.fail;
 
 import android.app.Activity;
+import android.content.ContentResolver;
 import android.os.Bundle;
 import android.os.PowerManager;
 import android.os.SystemClock;
+import android.provider.Settings;
 import android.view.View;
 import android.view.WindowInsets;
 import android.view.WindowInsetsController;
@@ -41,6 +44,7 @@
 import com.android.systemui.SysuiTestCase;
 
 import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 
@@ -54,8 +58,23 @@
     public ActivityTestRule<TestActivity> mActivityTestRule = new ActivityTestRule<>(
             TestActivity.class, false, false);
 
+    private int mOriginalShowImeWithHardKeyboard;
+
+    @Before
+    public void setUp() {
+        final ContentResolver contentResolver = mContext.getContentResolver();
+        mOriginalShowImeWithHardKeyboard = Settings.Secure.getInt(
+                contentResolver, SHOW_IME_WITH_HARD_KEYBOARD, 0);
+        // Forcibly shows IME even when hardware keyboard is connected.
+        // To change USER_SYSTEM settings, we have to use settings shell command.
+        executeShellCommand("settings put secure " + SHOW_IME_WITH_HARD_KEYBOARD + " 1");
+    }
+
     @After
     public void tearDown() {
+        // To restore USER_SYSTEM settings, we have to use settings shell command.
+        executeShellCommand("settings put secure "
+                + SHOW_IME_WITH_HARD_KEYBOARD + " " + mOriginalShowImeWithHardKeyboard);
         executeShellCommand("input keyevent HOME");
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
index 3439fe5..cd5740d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
@@ -41,8 +41,8 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -62,7 +62,7 @@
     private static final int TASK_ID = 444;
 
     private @Mock Context mContext;
-    private @Mock ActivityManagerWrapper mActivityManager;
+    private @Mock TaskStackChangeListeners mTaskStackChangeListeners;
     private @Mock IActivityTaskManager mIActivityTaskManager;
 
     private WorkLockActivityController mController;
@@ -78,10 +78,10 @@
         // Construct controller. Save the TaskStackListener for injecting events.
         final ArgumentCaptor<TaskStackChangeListener> listenerCaptor =
                 ArgumentCaptor.forClass(TaskStackChangeListener.class);
-        mController = new WorkLockActivityController(mContext, mActivityManager,
+        mController = new WorkLockActivityController(mContext, mTaskStackChangeListeners,
                 mIActivityTaskManager);
 
-        verify(mActivityManager).registerTaskStackListener(listenerCaptor.capture());
+        verify(mTaskStackChangeListeners).registerTaskStackListener(listenerCaptor.capture());
         mTaskStackListener = listenerCaptor.getValue();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index 81139f19..af677c9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -38,6 +38,7 @@
 import androidx.lifecycle.LiveData
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.media.dialog.MediaOutputDialogFactory
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil
 import com.android.systemui.util.animation.TransitionLayout
@@ -92,6 +93,7 @@
     @Mock private lateinit var mediaDataManager: MediaDataManager
     @Mock private lateinit var expandedSet: ConstraintSet
     @Mock private lateinit var collapsedSet: ConstraintSet
+    @Mock private lateinit var mediaOutputDialogFactory: MediaOutputDialogFactory
     private lateinit var appIcon: ImageView
     private lateinit var appName: TextView
     private lateinit var albumView: ImageView
@@ -109,9 +111,11 @@
     private lateinit var action2: ImageButton
     private lateinit var action3: ImageButton
     private lateinit var action4: ImageButton
+    private lateinit var settingsText: TextView
     private lateinit var settings: View
     private lateinit var cancel: View
-    private lateinit var dismiss: View
+    private lateinit var dismiss: FrameLayout
+    private lateinit var dismissLabel: View
 
     private lateinit var session: MediaSession
     private val device = MediaDeviceData(true, null, DEVICE_NAME)
@@ -126,7 +130,8 @@
         whenever(mediaViewController.collapsedLayout).thenReturn(collapsedSet)
 
         player = MediaControlPanel(context, bgExecutor, activityStarter, mediaViewController,
-                seekBarViewModel, Lazy { mediaDataManager }, keyguardDismissUtil)
+                seekBarViewModel, Lazy { mediaDataManager }, keyguardDismissUtil,
+                mediaOutputDialogFactory)
         whenever(seekBarViewModel.progress).thenReturn(seekBarData)
 
         // Mock out a view holder for the player to attach to.
@@ -168,12 +173,16 @@
         whenever(holder.action3).thenReturn(action3)
         action4 = ImageButton(context)
         whenever(holder.action4).thenReturn(action4)
+        settingsText = TextView(context)
+        whenever(holder.settingsText).thenReturn(settingsText)
         settings = View(context)
         whenever(holder.settings).thenReturn(settings)
         cancel = View(context)
         whenever(holder.cancel).thenReturn(cancel)
-        dismiss = View(context)
+        dismiss = FrameLayout(context)
         whenever(holder.dismiss).thenReturn(dismiss)
+        dismissLabel = View(context)
+        whenever(holder.dismissLabel).thenReturn(dismissLabel)
 
         // Create media session
         val metadataBuilder = MediaMetadata.Builder().apply {
@@ -327,6 +336,7 @@
                 notificationKey = KEY)
         player.bind(state, mediaKey)
 
+        assertThat(dismiss.isEnabled).isEqualTo(true)
         dismiss.callOnClick()
         val captor = ArgumentCaptor.forClass(ActivityStarter.OnDismissAction::class.java)
         verify(keyguardDismissUtil).executeWhenUnlocked(captor.capture(), anyBoolean())
@@ -334,4 +344,16 @@
         captor.value.onDismiss()
         verify(mediaDataManager).dismissMediaData(eq(mediaKey), anyLong())
     }
+
+    @Test
+    fun dismissButtonDisabled() {
+        val mediaKey = "key for dismissal"
+        player.attach(holder)
+        val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
+                emptyList(), PACKAGE, session.getSessionToken(), null, null, true, null,
+                isClearable = false, notificationKey = KEY)
+        player.bind(state, mediaKey)
+
+        assertThat(dismiss.isEnabled).isEqualTo(false)
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
index b81ab74..1f9862c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
@@ -654,4 +654,21 @@
         fakeExecutor.runAllReady()
         verify(mockController).unregisterCallback(any())
     }
+
+    @Test
+    fun nullPlaybackStateUnregistersCallback() {
+        viewModel.updateController(mockController)
+        val captor = ArgumentCaptor.forClass(MediaController.Callback::class.java)
+        verify(mockController).registerCallback(captor.capture())
+        val callback = captor.value
+        // WHEN the callback receives a null state
+        callback.onPlaybackStateChanged(null)
+        with(fakeExecutor) {
+            advanceClockToNext()
+            runAllReady()
+        }
+        // THEN we unregister callback (as a result of clearing the controller)
+        fakeExecutor.runAllReady()
+        verify(mockController).unregisterCallback(any())
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 2d460aa..5218886 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -25,7 +25,7 @@
 import android.graphics.drawable.Icon;
 import android.testing.AndroidTestingRunner;
 import android.view.View;
-import android.widget.FrameLayout;
+import android.widget.LinearLayout;
 
 import androidx.core.graphics.drawable.IconCompat;
 import androidx.test.filters.SmallTest;
@@ -66,7 +66,7 @@
     public void setUp() {
         mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController);
         mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
-                .onCreateViewHolder(new FrameLayout(mContext), 0);
+                .onCreateViewHolder(new LinearLayout(mContext), 0);
 
         when(mMediaOutputController.getMediaDevices()).thenReturn(mMediaDevices);
         when(mMediaOutputController.hasAdjustVolumeUserRestriction()).thenReturn(false);
@@ -75,6 +75,7 @@
         when(mMediaOutputController.getDeviceIconCompat(mMediaDevice1)).thenReturn(mIconCompat);
         when(mMediaOutputController.getDeviceIconCompat(mMediaDevice2)).thenReturn(mIconCompat);
         when(mMediaOutputController.getCurrentConnectedMediaDevice()).thenReturn(mMediaDevice1);
+        when(mMediaOutputController.isActiveRemoteDevice(mMediaDevice1)).thenReturn(true);
         when(mIconCompat.toIcon(mContext)).thenReturn(mIcon);
         when(mMediaDevice1.getName()).thenReturn(TEST_DEVICE_NAME_1);
         when(mMediaDevice1.getId()).thenReturn(TEST_DEVICE_ID_1);
@@ -107,6 +108,11 @@
 
         assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mTitleText.getText()).isEqualTo(mContext.getText(
                 R.string.media_output_dialog_pairing_new));
     }
@@ -118,19 +124,41 @@
         assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
     }
 
     @Test
+    public void onBindViewHolder_bindNonActiveConnectedDevice_verifyView() {
+        mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
+
+        assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_2);
+    }
+
+    @Test
     public void onBindViewHolder_bindDisconnectedBluetoothDevice_verifyView() {
         when(mMediaDevice2.getDeviceType()).thenReturn(
                 MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE);
         when(mMediaDevice2.isConnected()).thenReturn(false);
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
 
+        assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(
                 mContext.getString(R.string.media_output_dialog_disconnected, TEST_DEVICE_NAME_2));
@@ -142,9 +170,13 @@
                 LocalMediaManager.MediaDeviceState.STATE_CONNECTING_FAILED);
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
 
+        assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mSubTitleText.getText()).isEqualTo(mContext.getText(
@@ -162,7 +194,11 @@
         assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
     }
@@ -174,8 +210,13 @@
                 LocalMediaManager.MediaDeviceState.STATE_CONNECTING);
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
 
-        assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
     }
 
@@ -183,7 +224,7 @@
     public void onItemClick_clickPairNew_verifyLaunchBluetoothPairing() {
         when(mMediaOutputController.isZeroMode()).thenReturn(true);
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 2);
-        mViewHolder.mFrameLayout.performClick();
+        mViewHolder.mContainerLayout.performClick();
 
         verify(mMediaOutputController).launchBluetoothPairing();
     }
@@ -194,7 +235,7 @@
                 LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED);
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
-        mViewHolder.mFrameLayout.performClick();
+        mViewHolder.mContainerLayout.performClick();
 
         verify(mMediaOutputController).connectDevice(mMediaDevice2);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index 27b5b7f..c897d8a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -39,6 +39,7 @@
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.phone.ShadeController;
 
 import org.junit.Before;
@@ -58,6 +59,8 @@
     private LocalBluetoothManager mLocalBluetoothManager = mock(LocalBluetoothManager.class);
     private ShadeController mShadeController = mock(ShadeController.class);
     private ActivityStarter mStarter = mock(ActivityStarter.class);
+    private NotificationEntryManager mNotificationEntryManager =
+            mock(NotificationEntryManager.class);
 
     private MediaOutputBaseDialogImpl mMediaOutputBaseDialogImpl;
     private MediaOutputController mMediaOutputController;
@@ -68,8 +71,9 @@
 
     @Before
     public void setUp() {
-        mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE,
-                mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter);
+        mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
+                mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
+                mNotificationEntryManager);
         mMediaOutputBaseDialogImpl = new MediaOutputBaseDialogImpl(mContext,
                 mMediaOutputController);
         mMediaOutputBaseDialogImpl.onCreate(new Bundle());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index 0dcdecf..6ceac13 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -27,14 +27,19 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.Notification;
 import android.content.Context;
+import android.graphics.drawable.Icon;
 import android.media.MediaDescription;
 import android.media.MediaMetadata;
 import android.media.RoutingSessionInfo;
 import android.media.session.MediaController;
 import android.media.session.MediaSessionManager;
+import android.service.notification.StatusBarNotification;
 import android.testing.AndroidTestingRunner;
+import android.text.TextUtils;
 
+import androidx.core.graphics.drawable.IconCompat;
 import androidx.test.filters.SmallTest;
 
 import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
@@ -44,6 +49,8 @@
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.phone.ShadeController;
 
 import org.junit.Before;
@@ -60,6 +67,9 @@
     private static final String TEST_PACKAGE_NAME = "com.test.package.name";
     private static final String TEST_DEVICE_1_ID = "test_device_1_id";
     private static final String TEST_DEVICE_2_ID = "test_device_2_id";
+    private static final String TEST_DEVICE_3_ID = "test_device_3_id";
+    private static final String TEST_DEVICE_4_ID = "test_device_4_id";
+    private static final String TEST_DEVICE_5_ID = "test_device_5_id";
     private static final String TEST_ARTIST = "test_artist";
     private static final String TEST_SONG = "test_song";
     private static final String TEST_SESSION_ID = "test_session_id";
@@ -77,6 +87,8 @@
     private RoutingSessionInfo mRemoteSessionInfo = mock(RoutingSessionInfo.class);
     private ShadeController mShadeController = mock(ShadeController.class);
     private ActivityStarter mStarter = mock(ActivityStarter.class);
+    private NotificationEntryManager mNotificationEntryManager =
+            mock(NotificationEntryManager.class);
 
     private Context mSpyContext;
     private MediaOutputController mMediaOutputController;
@@ -96,8 +108,10 @@
                 MediaSessionManager.class);
         when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(
                 mCachedBluetoothDeviceManager);
-        mMediaOutputController = new MediaOutputController(mSpyContext, TEST_PACKAGE_NAME,
-                mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter);
+
+        mMediaOutputController = new MediaOutputController(mSpyContext, TEST_PACKAGE_NAME, false,
+                mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
+                mNotificationEntryManager);
         mLocalMediaManager = spy(mMediaOutputController.mLocalMediaManager);
         mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
         MediaDescription.Builder builder = new MediaDescription.Builder();
@@ -139,8 +153,9 @@
 
     @Test
     public void start_withoutPackageName_verifyMediaControllerInit() {
-        mMediaOutputController = new MediaOutputController(mSpyContext, null, mMediaSessionManager,
-                mLocalBluetoothManager, mShadeController, mStarter);
+        mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
+                mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
+                mNotificationEntryManager);
 
         mMediaOutputController.start(mCb);
 
@@ -159,8 +174,10 @@
 
     @Test
     public void stop_withoutPackageName_verifyMediaControllerDeinit() {
-        mMediaOutputController = new MediaOutputController(mSpyContext, null, mMediaSessionManager,
-                mLocalBluetoothManager, mShadeController, mStarter);
+        mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
+                mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
+                mNotificationEntryManager);
+
         mMediaOutputController.start(mCb);
 
         mMediaOutputController.stop();
@@ -345,4 +362,127 @@
 
         assertThat(mMediaOutputController.isZeroMode()).isFalse();
     }
+
+    @Test
+    public void getGroupMediaDevices_differentDeviceOrder_showingSameOrder() {
+        final MediaDevice selectedMediaDevice1 = mock(MediaDevice.class);
+        final MediaDevice selectedMediaDevice2 = mock(MediaDevice.class);
+        final MediaDevice selectableMediaDevice1 = mock(MediaDevice.class);
+        final MediaDevice selectableMediaDevice2 = mock(MediaDevice.class);
+        final List<MediaDevice> selectedMediaDevices = new ArrayList<>();
+        final List<MediaDevice> selectableMediaDevices = new ArrayList<>();
+        when(selectedMediaDevice1.getId()).thenReturn(TEST_DEVICE_1_ID);
+        when(selectedMediaDevice2.getId()).thenReturn(TEST_DEVICE_2_ID);
+        when(selectableMediaDevice1.getId()).thenReturn(TEST_DEVICE_3_ID);
+        when(selectableMediaDevice2.getId()).thenReturn(TEST_DEVICE_4_ID);
+        selectedMediaDevices.add(selectedMediaDevice1);
+        selectedMediaDevices.add(selectedMediaDevice2);
+        selectableMediaDevices.add(selectableMediaDevice1);
+        selectableMediaDevices.add(selectableMediaDevice2);
+        doReturn(selectedMediaDevices).when(mLocalMediaManager).getSelectedMediaDevice();
+        doReturn(selectableMediaDevices).when(mLocalMediaManager).getSelectableMediaDevice();
+        final List<MediaDevice> groupMediaDevices = mMediaOutputController.getGroupMediaDevices();
+        // Reset order
+        selectedMediaDevices.clear();
+        selectedMediaDevices.add(selectedMediaDevice2);
+        selectedMediaDevices.add(selectedMediaDevice1);
+        selectableMediaDevices.clear();
+        selectableMediaDevices.add(selectableMediaDevice2);
+        selectableMediaDevices.add(selectableMediaDevice1);
+        final List<MediaDevice> newDevices = mMediaOutputController.getGroupMediaDevices();
+
+        assertThat(newDevices.size()).isEqualTo(groupMediaDevices.size());
+        for (int i = 0; i < groupMediaDevices.size(); i++) {
+            assertThat(TextUtils.equals(groupMediaDevices.get(i).getId(),
+                    newDevices.get(i).getId())).isTrue();
+        }
+    }
+
+    @Test
+    public void getGroupMediaDevices_newDevice_verifyDeviceOrder() {
+        final MediaDevice selectedMediaDevice1 = mock(MediaDevice.class);
+        final MediaDevice selectedMediaDevice2 = mock(MediaDevice.class);
+        final MediaDevice selectableMediaDevice1 = mock(MediaDevice.class);
+        final MediaDevice selectableMediaDevice2 = mock(MediaDevice.class);
+        final MediaDevice selectableMediaDevice3 = mock(MediaDevice.class);
+        final List<MediaDevice> selectedMediaDevices = new ArrayList<>();
+        final List<MediaDevice> selectableMediaDevices = new ArrayList<>();
+        when(selectedMediaDevice1.getId()).thenReturn(TEST_DEVICE_1_ID);
+        when(selectedMediaDevice2.getId()).thenReturn(TEST_DEVICE_2_ID);
+        when(selectableMediaDevice1.getId()).thenReturn(TEST_DEVICE_3_ID);
+        when(selectableMediaDevice2.getId()).thenReturn(TEST_DEVICE_4_ID);
+        when(selectableMediaDevice3.getId()).thenReturn(TEST_DEVICE_5_ID);
+        selectedMediaDevices.add(selectedMediaDevice1);
+        selectedMediaDevices.add(selectedMediaDevice2);
+        selectableMediaDevices.add(selectableMediaDevice1);
+        selectableMediaDevices.add(selectableMediaDevice2);
+        doReturn(selectedMediaDevices).when(mLocalMediaManager).getSelectedMediaDevice();
+        doReturn(selectableMediaDevices).when(mLocalMediaManager).getSelectableMediaDevice();
+        final List<MediaDevice> groupMediaDevices = mMediaOutputController.getGroupMediaDevices();
+        // Reset order
+        selectedMediaDevices.clear();
+        selectedMediaDevices.add(selectedMediaDevice2);
+        selectedMediaDevices.add(selectedMediaDevice1);
+        selectableMediaDevices.clear();
+        selectableMediaDevices.add(selectableMediaDevice3);
+        selectableMediaDevices.add(selectableMediaDevice2);
+        selectableMediaDevices.add(selectableMediaDevice1);
+        final List<MediaDevice> newDevices = mMediaOutputController.getGroupMediaDevices();
+
+        assertThat(newDevices.size()).isEqualTo(5);
+        for (int i = 0; i < groupMediaDevices.size(); i++) {
+            assertThat(TextUtils.equals(groupMediaDevices.get(i).getId(),
+                    newDevices.get(i).getId())).isTrue();
+        }
+        assertThat(newDevices.get(4).getId()).isEqualTo(TEST_DEVICE_5_ID);
+    }
+
+    @Test
+    public void getNotificationLargeIcon_withoutPackageName_returnsNull() {
+        mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
+                mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
+                mNotificationEntryManager);
+
+        assertThat(mMediaOutputController.getNotificationIcon()).isNull();
+    }
+
+    @Test
+    public void getNotificationLargeIcon_withPackageNameAndMediaSession_returnsIconCompat() {
+        final List<NotificationEntry> entryList = new ArrayList<>();
+        final NotificationEntry entry = mock(NotificationEntry.class);
+        final StatusBarNotification sbn = mock(StatusBarNotification.class);
+        final Notification notification = mock(Notification.class);
+        final Icon icon = mock(Icon.class);
+        entryList.add(entry);
+
+        when(mNotificationEntryManager.getActiveNotificationsForCurrentUser())
+                .thenReturn(entryList);
+        when(entry.getSbn()).thenReturn(sbn);
+        when(sbn.getNotification()).thenReturn(notification);
+        when(sbn.getPackageName()).thenReturn(TEST_PACKAGE_NAME);
+        when(notification.hasMediaSession()).thenReturn(true);
+        when(notification.getLargeIcon()).thenReturn(icon);
+
+        assertThat(mMediaOutputController.getNotificationIcon() instanceof IconCompat).isTrue();
+    }
+
+    @Test
+    public void getNotificationLargeIcon_withPackageNameAndNoMediaSession_returnsNull() {
+        final List<NotificationEntry> entryList = new ArrayList<>();
+        final NotificationEntry entry = mock(NotificationEntry.class);
+        final StatusBarNotification sbn = mock(StatusBarNotification.class);
+        final Notification notification = mock(Notification.class);
+        final Icon icon = mock(Icon.class);
+        entryList.add(entry);
+
+        when(mNotificationEntryManager.getActiveNotificationsForCurrentUser())
+                .thenReturn(entryList);
+        when(entry.getSbn()).thenReturn(sbn);
+        when(sbn.getNotification()).thenReturn(notification);
+        when(sbn.getPackageName()).thenReturn(TEST_PACKAGE_NAME);
+        when(notification.hasMediaSession()).thenReturn(false);
+        when(notification.getLargeIcon()).thenReturn(icon);
+
+        assertThat(mMediaOutputController.getNotificationIcon()).isNull();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
index 9ebb587..c1e7db1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
@@ -34,6 +34,7 @@
 import com.android.settingslib.media.MediaDevice;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.phone.ShadeController;
 
 import org.junit.After;
@@ -58,6 +59,8 @@
     private ActivityStarter mStarter = mock(ActivityStarter.class);
     private LocalMediaManager mLocalMediaManager = mock(LocalMediaManager.class);
     private MediaDevice mMediaDevice = mock(MediaDevice.class);
+    private NotificationEntryManager mNotificationEntryManager =
+            mock(NotificationEntryManager.class);
 
     private MediaOutputDialog mMediaOutputDialog;
     private MediaOutputController mMediaOutputController;
@@ -65,8 +68,9 @@
 
     @Before
     public void setUp() {
-        mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE,
-                mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter);
+        mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
+                mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
+                mNotificationEntryManager);
         mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
         mMediaOutputDialog = new MediaOutputDialog(mContext, false, mMediaOutputController);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupAdapterTest.java
new file mode 100644
index 0000000..1f85112
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupAdapterTest.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.media.dialog;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.graphics.drawable.Icon;
+import android.testing.AndroidTestingRunner;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import androidx.core.graphics.drawable.IconCompat;
+import androidx.test.filters.SmallTest;
+
+import com.android.settingslib.media.MediaDevice;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class MediaOutputGroupAdapterTest extends SysuiTestCase {
+
+    private static final String TEST_DEVICE_NAME_1 = "test_device_name_1";
+    private static final String TEST_DEVICE_NAME_2 = "test_device_name_2";
+    private static final String TEST_DEVICE_ID_1 = "test_device_id_1";
+    private static final String TEST_DEVICE_ID_2 = "test_device_id_2";
+    private static final int TEST_VOLUME = 10;
+    private static final int TEST_MAX_VOLUME = 50;
+
+    // Mock
+    private MediaOutputController mMediaOutputController = mock(MediaOutputController.class);
+    private MediaDevice mMediaDevice1 = mock(MediaDevice.class);
+    private MediaDevice mMediaDevice2 = mock(MediaDevice.class);
+    private Icon mIcon = mock(Icon.class);
+    private IconCompat mIconCompat = mock(IconCompat.class);
+
+    private MediaOutputGroupAdapter mGroupAdapter;
+    private MediaOutputGroupAdapter.GroupViewHolder mGroupViewHolder;
+    private List<MediaDevice> mGroupMediaDevices = new ArrayList<>();
+    private List<MediaDevice> mSelectableMediaDevices = new ArrayList<>();
+    private List<MediaDevice> mSelectedMediaDevices = new ArrayList<>();
+    private List<MediaDevice> mDeselectableMediaDevices = new ArrayList<>();
+
+    @Before
+    public void setUp() {
+        when(mMediaOutputController.getGroupMediaDevices()).thenReturn(mGroupMediaDevices);
+        when(mMediaOutputController.getDeviceIconCompat(mMediaDevice1)).thenReturn(mIconCompat);
+        when(mMediaOutputController.getDeviceIconCompat(mMediaDevice2)).thenReturn(mIconCompat);
+        when(mMediaOutputController.getSelectableMediaDevice()).thenReturn(mSelectableMediaDevices);
+        when(mMediaOutputController.getSelectedMediaDevice()).thenReturn(mSelectedMediaDevices);
+        when(mMediaOutputController.getDeselectableMediaDevice()).thenReturn(
+                mDeselectableMediaDevices);
+        when(mIconCompat.toIcon(mContext)).thenReturn(mIcon);
+        when(mMediaDevice1.getName()).thenReturn(TEST_DEVICE_NAME_1);
+        when(mMediaDevice1.getId()).thenReturn(TEST_DEVICE_ID_1);
+        when(mMediaDevice2.getName()).thenReturn(TEST_DEVICE_NAME_2);
+        when(mMediaDevice2.getId()).thenReturn(TEST_DEVICE_ID_2);
+        mGroupMediaDevices.add(mMediaDevice1);
+        mGroupMediaDevices.add(mMediaDevice2);
+        mSelectedMediaDevices.add(mMediaDevice1);
+        mSelectableMediaDevices.add(mMediaDevice2);
+        mDeselectableMediaDevices.add(mMediaDevice1);
+
+        mGroupAdapter = new MediaOutputGroupAdapter(mMediaOutputController);
+        mGroupViewHolder = (MediaOutputGroupAdapter.GroupViewHolder) mGroupAdapter
+                .onCreateViewHolder(new LinearLayout(mContext), 0);
+    }
+
+    @Test
+    public void onBindViewHolder_verifyGroupItem() {
+        mGroupAdapter.onBindViewHolder(mGroupViewHolder, 0);
+
+        assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mGroupViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mGroupViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mGroupViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mGroupViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mGroupViewHolder.mTwoLineTitleText.getText()).isEqualTo(mContext.getText(
+                R.string.media_output_dialog_group));
+    }
+
+    @Test
+    public void onBindViewHolder_singleSelectedDevice_verifyView() {
+        mGroupAdapter.onBindViewHolder(mGroupViewHolder, 1);
+
+        assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mGroupViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mGroupViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mGroupViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mGroupViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
+        assertThat(mGroupViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mGroupViewHolder.mCheckBox.isChecked()).isTrue();
+        // Disabled checkBox
+        assertThat(mGroupViewHolder.mCheckBox.isEnabled()).isFalse();
+    }
+
+    @Test
+    public void onBindViewHolder_multipleSelectedDevice_verifyView() {
+        mSelectedMediaDevices.clear();
+        mSelectedMediaDevices.add(mMediaDevice1);
+        mSelectedMediaDevices.add(mMediaDevice2);
+        mDeselectableMediaDevices.clear();
+        mDeselectableMediaDevices.add(mMediaDevice1);
+        mDeselectableMediaDevices.add(mMediaDevice2);
+        mSelectableMediaDevices.clear();
+
+        mGroupAdapter.onBindViewHolder(mGroupViewHolder, 1);
+
+        assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mGroupViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mGroupViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mGroupViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mGroupViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
+        assertThat(mGroupViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mGroupViewHolder.mCheckBox.isChecked()).isTrue();
+        // Enabled checkBox
+        assertThat(mGroupViewHolder.mCheckBox.isEnabled()).isTrue();
+    }
+
+    @Test
+    public void onBindViewHolder_notDeselectedDevice_verifyView() {
+        mSelectedMediaDevices.clear();
+        mSelectedMediaDevices.add(mMediaDevice1);
+        mSelectedMediaDevices.add(mMediaDevice2);
+        mDeselectableMediaDevices.clear();
+        mDeselectableMediaDevices.add(mMediaDevice1);
+        mSelectableMediaDevices.clear();
+
+        mGroupAdapter.onBindViewHolder(mGroupViewHolder, 2);
+
+        assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mGroupViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mGroupViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mGroupViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mGroupViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_2);
+        assertThat(mGroupViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mGroupViewHolder.mCheckBox.isChecked()).isTrue();
+        // Disabled checkBox
+        assertThat(mGroupViewHolder.mCheckBox.isEnabled()).isFalse();
+    }
+
+    @Test
+    public void onBindViewHolder_selectableDevice_verifyCheckBox() {
+        mGroupAdapter.onBindViewHolder(mGroupViewHolder, 2);
+
+        assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mGroupViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mGroupViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mGroupViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mGroupViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_2);
+        assertThat(mGroupViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mGroupViewHolder.mCheckBox.isChecked()).isFalse();
+        // Enabled checkBox
+        assertThat(mGroupViewHolder.mCheckBox.isEnabled()).isTrue();
+    }
+
+    @Test
+    public void onBindViewHolder_verifySessionVolume() {
+        when(mMediaOutputController.getSessionVolume()).thenReturn(TEST_VOLUME);
+        when(mMediaOutputController.getSessionVolumeMax()).thenReturn(TEST_MAX_VOLUME);
+
+        mGroupAdapter.onBindViewHolder(mGroupViewHolder, 0);
+
+        assertThat(mGroupViewHolder.mSeekBar.getProgress()).isEqualTo(TEST_VOLUME);
+        assertThat(mGroupViewHolder.mSeekBar.getMax()).isEqualTo(TEST_MAX_VOLUME);
+    }
+
+    @Test
+    public void onBindViewHolder_verifyDeviceVolume() {
+        when(mMediaDevice1.getCurrentVolume()).thenReturn(TEST_VOLUME);
+        when(mMediaDevice1.getMaxVolume()).thenReturn(TEST_MAX_VOLUME);
+
+        mGroupAdapter.onBindViewHolder(mGroupViewHolder, 1);
+
+        assertThat(mGroupViewHolder.mSeekBar.getProgress()).isEqualTo(TEST_VOLUME);
+        assertThat(mGroupViewHolder.mSeekBar.getMax()).isEqualTo(TEST_MAX_VOLUME);
+    }
+
+    @Test
+    public void clickSelectedDevice_verifyRemoveDeviceFromPlayMedia() {
+        mSelectedMediaDevices.clear();
+        mSelectedMediaDevices.add(mMediaDevice1);
+        mSelectedMediaDevices.add(mMediaDevice2);
+        mDeselectableMediaDevices.clear();
+        mDeselectableMediaDevices.add(mMediaDevice1);
+        mDeselectableMediaDevices.add(mMediaDevice2);
+        mSelectableMediaDevices.clear();
+
+        mGroupAdapter.onBindViewHolder(mGroupViewHolder, 1);
+        mGroupViewHolder.mCheckBox.performClick();
+
+        verify(mMediaOutputController).removeDeviceFromPlayMedia(mMediaDevice1);
+    }
+
+    @Test
+    public void clickSelectabelDevice_verifyAddDeviceToPlayMedia() {
+        mGroupAdapter.onBindViewHolder(mGroupViewHolder, 2);
+
+        mGroupViewHolder.mCheckBox.performClick();
+
+        verify(mMediaOutputController).addDeviceToPlayMedia(mMediaDevice2);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java
new file mode 100644
index 0000000..5813350
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.media.dialog;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.media.session.MediaSessionManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.media.LocalMediaManager;
+import com.android.settingslib.media.MediaDevice;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.phone.ShadeController;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class MediaOutputGroupDialogTest extends SysuiTestCase {
+
+    private static final String TEST_PACKAGE = "test_package";
+
+    // Mock
+    private MediaSessionManager mMediaSessionManager = mock(MediaSessionManager.class);
+    private LocalBluetoothManager mLocalBluetoothManager = mock(LocalBluetoothManager.class);
+    private ShadeController mShadeController = mock(ShadeController.class);
+    private ActivityStarter mStarter = mock(ActivityStarter.class);
+    private LocalMediaManager mLocalMediaManager = mock(LocalMediaManager.class);
+    private MediaDevice mMediaDevice = mock(MediaDevice.class);
+    private MediaDevice mMediaDevice1 = mock(MediaDevice.class);
+    private NotificationEntryManager mNotificationEntryManager =
+            mock(NotificationEntryManager.class);
+
+    private MediaOutputGroupDialog mMediaOutputGroupDialog;
+    private MediaOutputController mMediaOutputController;
+    private List<MediaDevice> mMediaDevices = new ArrayList<>();
+
+    @Before
+    public void setUp() {
+        mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
+                mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
+                mNotificationEntryManager);
+        mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
+        mMediaOutputGroupDialog = new MediaOutputGroupDialog(mContext, false,
+                mMediaOutputController);
+        when(mLocalMediaManager.getSelectedMediaDevice()).thenReturn(mMediaDevices);
+    }
+
+    @After
+    public void tearDown() {
+        mMediaOutputGroupDialog.dismissDialog();
+    }
+
+    @Test
+    public void getStopButtonVisibility_returnVisible() {
+        assertThat(mMediaOutputGroupDialog.getStopButtonVisibility()).isEqualTo(View.VISIBLE);
+    }
+
+    @Test
+    public void getHeaderSubtitle_singleDevice_verifyTitle() {
+        mMediaDevices.add(mMediaDevice);
+
+        assertThat(mMediaOutputGroupDialog.getHeaderSubtitle()).isEqualTo(
+                mContext.getText(R.string.media_output_dialog_single_device));
+    }
+
+    @Test
+    public void getHeaderSubtitle_multipleDevices_verifyTitle() {
+        mMediaDevices.add(mMediaDevice);
+        mMediaDevices.add(mMediaDevice1);
+
+        assertThat(mMediaOutputGroupDialog.getHeaderSubtitle()).isEqualTo(mContext.getString(
+                R.string.media_output_dialog_multiple_devices, mMediaDevices.size()));
+    }
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/KeyButtonViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/KeyButtonViewTest.java
index 9b6dd05..3494bd6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/KeyButtonViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/KeyButtonViewTest.java
@@ -32,15 +32,11 @@
 import static com.android.systemui.navigationbar.buttons.KeyButtonView.NavBarButtonEvent.NAVBAR_OVERVIEW_BUTTON_LONGPRESS;
 import static com.android.systemui.navigationbar.buttons.KeyButtonView.NavBarButtonEvent.NAVBAR_OVERVIEW_BUTTON_TAP;
 
-import static junit.framework.Assert.assertEquals;
-
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import android.hardware.input.InputManager;
 import android.testing.AndroidTestingRunner;
@@ -151,19 +147,4 @@
             verify(mUiEventLogger, times(1)).log(expected);
         }
     }
-
-    @Test
-    public void testBubbleEvents_bubbleExpanded() {
-        when(mBubbles.getExpandedDisplayId(mContext)).thenReturn(3);
-
-        int action = KeyEvent.ACTION_DOWN;
-        int flags = 0;
-        int code = KeyEvent.KEYCODE_BACK;
-        mKeyButtonView.setCode(code);
-        mKeyButtonView.sendEvent(action, flags);
-
-        verify(mInputManager, times(1)).injectInputEvent(mInputEventCaptor.capture(),
-                anyInt());
-        assertEquals(3, mInputEventCaptor.getValue().getDisplayId());
-    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
index cb17829..cd94f84 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
@@ -54,6 +54,7 @@
         private const val ALL_INDICATORS =
                 SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED
         private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED
+        private const val LOCATION = SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_ENABLED
     }
 
     @Mock
@@ -116,6 +117,15 @@
     }
 
     @Test
+    fun testLocationChanged() {
+        changeLocation(true)
+        executor.runAllReady()
+
+        verify(callback).onFlagLocationChanged(true)
+        assertTrue(privacyItemController.locationAvailable)
+    }
+
+    @Test
     fun testAllChanged() {
         changeAll(true)
         executor.runAllReady()
@@ -158,6 +168,14 @@
     }
 
     @Test
+    fun testLocation_listening() {
+        changeLocation(true)
+        executor.runAllReady()
+
+        verify(appOpsController).addCallback(eq(PrivacyItemController.OPS), any())
+    }
+
+    @Test
     @Ignore // TODO(b/168209929)
     fun testAllFalse_notListening() {
         changeAll(true)
@@ -205,6 +223,7 @@
     }
 
     private fun changeMicCamera(value: Boolean?) = changeProperty(MIC_CAMERA, value)
+    private fun changeLocation(value: Boolean?) = changeProperty(LOCATION, value)
     private fun changeAll(value: Boolean?) = changeProperty(ALL_INDICATORS, value)
 
     private fun changeProperty(name: String, value: Boolean?) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java
index 353efee..8039192 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java
@@ -70,7 +70,7 @@
             mQsDetail = (QSDetail) LayoutInflater.from(mContext).inflate(R.layout.qs_detail, null);
             mQsPanel = mock(QSPanel.class);
             mQuickHeader = mock(QuickStatusBarHeader.class);
-            mQsDetail.setQsPanel(mQsPanel, mQuickHeader, mock(View.class));
+            mQsDetail.setQsPanel(mQsPanel, mQuickHeader, mock(QSFooter.class));
 
             mMockDetailAdapter = mock(DetailAdapter.class);
             when(mMockDetailAdapter.createDetailView(any(), any(), any()))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplTest.java
deleted file mode 100644
index 99f2d80..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplTest.java
+++ /dev/null
@@ -1,109 +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.systemui.qs;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.ClipData;
-import android.content.ClipboardManager;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.TextView;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.R;
-import com.android.systemui.R.id;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.settings.UserTracker;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.UserInfoController;
-import com.android.systemui.utils.leaks.LeakCheckedTest;
-
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
-@SmallTest
-public class QSFooterImplTest extends LeakCheckedTest {
-
-    private QSFooterImpl mFooter;
-    private ActivityStarter mActivityStarter;
-    private DeviceProvisionedController mDeviceProvisionedController;
-    private UserInfoController mUserInfoController;
-    private UserTracker mUserTracker;
-    @Mock
-    private ClipboardManager mClipboardManager;
-
-    @Before
-    public void setup() throws Exception {
-        MockitoAnnotations.initMocks(this);
-
-        injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
-        mActivityStarter = mDependency.injectMockDependency(ActivityStarter.class);
-        mDeviceProvisionedController = mDependency.injectMockDependency(
-                DeviceProvisionedController.class);
-        mUserInfoController = mDependency.injectMockDependency(UserInfoController.class);
-        mUserTracker = mDependency.injectMockDependency(UserTracker.class);
-
-        mContext.addMockSystemService(ClipboardManager.class, mClipboardManager);
-
-        when(mUserTracker.getUserContext()).thenReturn(mContext);
-
-        TestableLooper.get(this).runWithLooper(
-                () -> mFooter = (QSFooterImpl) LayoutInflater.from(mContext).inflate(
-                        R.layout.qs_footer_impl, null));
-    }
-
-    @Test
-    public void testBuildTextCopy() {
-        TextView buildTextView = mFooter.requireViewById(R.id.build);
-        CharSequence buildText = "TEST";
-        buildTextView.setText(buildText);
-        buildTextView.setLongClickable(true);
-
-        buildTextView.performLongClick();
-
-        ArgumentCaptor<ClipData> captor = ArgumentCaptor.forClass(ClipData.class);
-        verify(mClipboardManager).setPrimaryClip(captor.capture());
-        assertThat(captor.getValue().getItemAt(0).getText()).isEqualTo(buildText);
-    }
-
-    @Test
-    @Ignore("failing")
-    public void testSettings_UserNotSetup() {
-        View settingsButton = mFooter.findViewById(id.settings_button);
-        when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(false);
-
-        mFooter.onClick(settingsButton);
-        // Verify Settings wasn't launched.
-        verify(mActivityStarter, never()).startActivity(any(), anyBoolean());
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
new file mode 100644
index 0000000..065f236
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.qs;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.os.UserManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.testing.FakeMetricsLogger;
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.phone.SettingsButton;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.UserInfoController;
+import com.android.systemui.tuner.TunerService;
+import com.android.systemui.utils.leaks.FakeTunerService;
+import com.android.systemui.utils.leaks.LeakCheckedTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class QSFooterViewControllerTest extends LeakCheckedTest {
+
+    @Mock
+    private QSFooterView mView;
+    @Mock
+    private UserManager mUserManager;
+    @Mock
+    private ActivityStarter mActivityStarter;
+    @Mock
+    private DeviceProvisionedController mDeviceProvisionedController;
+    @Mock
+    private UserInfoController mUserInfoController;
+    @Mock
+    private UserTracker mUserTracker;
+    @Mock
+    private QSPanelController mQSPanelController;
+    @Mock
+    private ClipboardManager mClipboardManager;
+    private FakeTunerService mFakeTunerService;
+    private MetricsLogger mMetricsLogger = new FakeMetricsLogger();
+
+    @Mock
+    private SettingsButton mSettingsButton;
+    @Mock
+    private TextView mBuildText;
+    @Mock
+    private View mEdit;
+
+    private QSFooterViewController mController;
+
+    @Before
+    public void setup() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
+
+        mFakeTunerService = (FakeTunerService) Dependency.get(TunerService.class);
+
+        mContext.addMockSystemService(ClipboardManager.class, mClipboardManager);
+
+        when(mView.getContext()).thenReturn(mContext);
+        when(mView.getResources()).thenReturn(mContext.getResources());
+        when(mUserTracker.getUserContext()).thenReturn(mContext);
+
+        when(mView.isAttachedToWindow()).thenReturn(true);
+        when(mView.findViewById(R.id.settings_button)).thenReturn(mSettingsButton);
+        when(mView.findViewById(R.id.build)).thenReturn(mBuildText);
+        when(mView.findViewById(android.R.id.edit)).thenReturn(mEdit);
+
+        mController = new QSFooterViewController(mView, mUserManager, mUserInfoController,
+                mActivityStarter, mDeviceProvisionedController, mUserTracker, mQSPanelController,
+                mFakeTunerService, mMetricsLogger);
+
+        mController.init();
+    }
+
+    @Test
+    public void testBuildTextCopy() {
+        String text = "TEST";
+        ArgumentCaptor<View.OnLongClickListener> onLongClickCaptor =
+                ArgumentCaptor.forClass(View.OnLongClickListener.class);
+
+        verify(mBuildText).setOnLongClickListener(onLongClickCaptor.capture());
+
+        when(mBuildText.getText()).thenReturn(text);
+        onLongClickCaptor.getValue().onLongClick(mBuildText);
+
+        ArgumentCaptor<ClipData> captor = ArgumentCaptor.forClass(ClipData.class);
+        verify(mClipboardManager).setPrimaryClip(captor.capture());
+        assertThat(captor.getValue().getItemAt(0).getText()).isEqualTo(text);
+    }
+
+    @Test
+    public void testSettings_UserNotSetup() {
+        ArgumentCaptor<View.OnClickListener> onClickCaptor =
+                ArgumentCaptor.forClass(View.OnClickListener.class);
+        verify(mSettingsButton).setOnClickListener(onClickCaptor.capture());
+
+        when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(false);
+
+        onClickCaptor.getValue().onClick(mSettingsButton);
+        // Verify Settings wasn't launched.
+        verify(mActivityStarter, never()).startActivity(any(), anyBoolean());
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index e472cb2..90609cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -16,7 +16,9 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 import android.app.Fragment;
 import android.content.Context;
@@ -42,6 +44,7 @@
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.qs.dagger.QSFragmentComponent;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSFactoryImpl;
 import com.android.systemui.settings.UserTracker;
@@ -61,6 +64,8 @@
 import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 import java.util.Optional;
 
@@ -71,6 +76,12 @@
 public class QSFragmentTest extends SysuiBaseFragmentTest {
 
     private MetricsLogger mMockMetricsLogger;
+    @Mock
+    private QSFragmentComponent.Factory mQsComponentFactory;
+    @Mock
+    private QSFragmentComponent mQsFragmentComponent;
+    @Mock
+    private QSPanelController mQSPanelController;
 
     public QSFragmentTest() {
         super(QSFragment.class);
@@ -80,6 +91,10 @@
     @Before
     @Ignore("failing")
     public void addLeakCheckDependencies() {
+        MockitoAnnotations.initMocks(this);
+        when(mQsComponentFactory.create(any(QSFragment.class))).thenReturn(mQsFragmentComponent);
+        when(mQsFragmentComponent.getQSPanelController()).thenReturn(mQSPanelController);
+
         mMockMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
         mContext.addMockSystemService(Context.LAYOUT_INFLATER_SERVICE,
                 new LayoutInflaterBuilder(mContext)
@@ -152,6 +167,6 @@
                 mock(QSTileHost.class),
                 mock(StatusBarStateController.class),
                 commandQueue,
-                mock(QSContainerImplController.Builder.class));
+                mQsComponentFactory);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
new file mode 100644
index 0000000..bf0e084
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.qs;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.logging.testing.UiEventLoggerFake;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.media.MediaHost;
+import com.android.systemui.plugins.qs.QSTileView;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Collections;
+
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+@SmallTest
+public class QSPanelControllerBaseTest extends SysuiTestCase {
+
+    @Mock
+    private QSPanel mQSPanel;
+    @Mock
+    private QSTileHost mQSTileHost;
+    @Mock
+    private MediaHost mMediaHost;
+    @Mock
+    private MetricsLogger mMetricsLogger;
+    private UiEventLoggerFake mUiEventLogger = new UiEventLoggerFake();
+    private DumpManager mDumpManager = new DumpManager();
+    @Mock
+    QSTileImpl mQSTile;
+    @Mock
+    QSTileView mQSTileView;
+
+    private QSPanelControllerBase<QSPanel> mController;
+
+    /** Implementation needed to ensure we have a reflectively-available class name. */
+    private static class TestableQSPanelControllerBase extends QSPanelControllerBase<QSPanel> {
+        protected TestableQSPanelControllerBase(QSPanel view, QSTileHost host,
+                MetricsLogger metricsLogger,
+                UiEventLogger uiEventLogger, DumpManager dumpManager) {
+            super(view, host, metricsLogger, uiEventLogger, dumpManager);
+        }
+    }
+
+    @Before
+    public void setup() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        when(mQSPanel.getMediaHost()).thenReturn(mMediaHost);
+        when(mQSPanel.isAttachedToWindow()).thenReturn(true);
+        when(mQSPanel.getDumpableTag()).thenReturn("QSPanel");
+        when(mQSPanel.openPanelEvent()).thenReturn(QSEvent.QS_PANEL_EXPANDED);
+        when(mQSPanel.closePanelEvent()).thenReturn(QSEvent.QS_PANEL_COLLAPSED);
+        when(mQSTileHost.getTiles()).thenReturn(Collections.singleton(mQSTile));
+        when(mQSTileHost.createTileView(eq(mQSTile), anyBoolean())).thenReturn(mQSTileView);
+
+        mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost,
+                mMetricsLogger, mUiEventLogger, mDumpManager);
+
+        mController.init();
+    }
+
+    @Test
+    public void testSetExpanded_Metrics() {
+        mController.setExpanded(true);
+        verify(mMetricsLogger).visibility(eq(MetricsEvent.QS_PANEL), eq(true));
+        assertEquals(1, mUiEventLogger.numLogs());
+        assertEquals(QSEvent.QS_PANEL_EXPANDED.getId(), mUiEventLogger.eventId(0));
+        mUiEventLogger.getLogs().clear();
+
+        mController.setExpanded(false);
+        verify(mMetricsLogger).visibility(eq(MetricsEvent.QS_PANEL), eq(false));
+        assertEquals(1, mUiEventLogger.numLogs());
+        assertEquals(QSEvent.QS_PANEL_COLLAPSED.getId(), mUiEventLogger.eventId(0));
+        mUiEventLogger.getLogs().clear();
+
+    }
+
+    @Test
+    public void testDump() {
+        String mockTileViewString = "Mock Tile View";
+        String mockTileString = "Mock Tile";
+        doAnswer(invocation -> {
+            PrintWriter pw = invocation.getArgument(1);
+            pw.println(mockTileString);
+            return null;
+        }).when(mQSTile).dump(any(FileDescriptor.class), any(PrintWriter.class),
+                any(String[].class));
+        when(mQSTileView.toString()).thenReturn(mockTileViewString);
+
+        StringWriter w = new StringWriter();
+        PrintWriter pw = new PrintWriter(w);
+        mController.dump(mock(FileDescriptor.class), pw, new String[]{});
+        String expected = "TestableQSPanelControllerBase:\n"
+                + "  Tile records:\n"
+                + "    " + mockTileString + "\n"
+                + "    " + mockTileViewString + "\n";
+        assertEquals(expected, w.getBuffer().toString());
+    }
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
new file mode 100644
index 0000000..0ba0214
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.qs;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.testing.UiEventLoggerFake;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.media.MediaHost;
+import com.android.systemui.plugins.qs.QSTileView;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.settings.BrightnessController;
+import com.android.systemui.settings.ToggleSlider;
+import com.android.systemui.tuner.TunerService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Collections;
+
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+@SmallTest
+public class QSPanelControllerTest extends SysuiTestCase {
+
+    @Mock
+    private QSPanel mQSPanel;
+    @Mock
+    private QSTileHost mQSTileHost;
+    @Mock
+    private MediaHost mMediaHost;
+    @Mock
+    private MetricsLogger mMetricsLogger;
+    private UiEventLogger mUiEventLogger = new UiEventLoggerFake();
+    private DumpManager mDumpManager = new DumpManager();
+    @Mock
+    private TunerService mTunerService;
+    @Mock
+    private QSSecurityFooter mQSSecurityFooter;
+    @Mock
+    private BrightnessController.Factory mBrightnessControllerFactory;
+    @Mock
+    private BrightnessController mBrightnessController;
+    @Mock
+    QSTileImpl mQSTile;
+    @Mock
+    QSTileView mQSTileView;
+
+    private QSPanelController mController;
+
+    @Before
+    public void setup() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        when(mQSPanel.getMediaHost()).thenReturn(mMediaHost);
+        when(mQSPanel.isAttachedToWindow()).thenReturn(true);
+        when(mQSPanel.getDumpableTag()).thenReturn("QSPanel");
+        when(mQSTileHost.getTiles()).thenReturn(Collections.singleton(mQSTile));
+        when(mQSTileHost.createTileView(eq(mQSTile), anyBoolean())).thenReturn(mQSTileView);
+        when(mBrightnessControllerFactory.create(any(ToggleSlider.class)))
+                .thenReturn(mBrightnessController);
+
+        mController = new QSPanelController(mQSPanel, mQSSecurityFooter, mTunerService,
+                mQSTileHost, mDumpManager, mMetricsLogger, mUiEventLogger,
+                mBrightnessControllerFactory);
+
+        mController.init();
+    }
+
+    @Test
+    public void testOpenDetailsWithNonExistingTile_NoException() {
+        mController.openDetails("none");
+
+        verify(mQSPanel, never()).openDetails(any());
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
index 4b7a268..e38d54b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
@@ -14,12 +14,9 @@
 
 package com.android.systemui.qs;
 
-import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
@@ -36,19 +33,15 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.logging.testing.UiEventLoggerFake;
 import com.android.systemui.Dependency;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.dump.DumpManager;
 import com.android.systemui.media.MediaHost;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.qs.QSTileView;
 import com.android.systemui.qs.customize.QSCustomizer;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
-import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.policy.SecurityController;
 import com.android.systemui.util.animation.DisappearParameters;
 import com.android.systemui.util.animation.UniqueObjectHostView;
@@ -59,9 +52,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.io.StringWriter;
 import java.util.Collections;
 
 @RunWith(AndroidTestingRunner.class)
@@ -79,9 +69,9 @@
     @Mock
     private QSTileImpl dndTile;
     @Mock
-    private BroadcastDispatcher mBroadcastDispatcher;
+    private QSTileImpl mNonTile;
     @Mock
-    private DumpManager mDumpManager;
+    private QSPanelControllerBase.TileRecord mDndTileRecord;
     @Mock
     private QSLogger mQSLogger;
     private ViewGroup mParentView;
@@ -93,9 +83,8 @@
     private MediaHost mMediaHost;
     @Mock
     private ActivityStarter mActivityStarter;
-    @Mock(stubOnly = true)
-    private UserTracker mUserTracker;
     private UiEventLoggerFake mUiEventLogger;
+    private String mCachedSpecs = "";
 
     @Before
     public void setup() throws Exception {
@@ -109,12 +98,13 @@
         mContext.addMockSystemService(Context.USER_SERVICE, mock(UserManager.class));
         when(mMediaHost.getHostView()).thenReturn(new UniqueObjectHostView(getContext()));
         when(mMediaHost.getDisappearParameters()).thenReturn(new DisappearParameters());
+        mDndTileRecord.tile = dndTile;
+        mDndTileRecord.tileView = mQSTileView;
 
         mUiEventLogger = new UiEventLoggerFake();
         mTestableLooper.runWithLooper(() -> {
             mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
-            mQsPanel = new QSPanel(mContext, null, mDumpManager, mBroadcastDispatcher,
-                    mQSLogger, mMediaHost, mUiEventLogger, mUserTracker);
+            mQsPanel = new QSPanel(mContext, null, mQSLogger, mMediaHost, mUiEventLogger);
             mQsPanel.onFinishInflate();
             // Provides a parent with non-zero size for QSPanel
             mParentView = new FrameLayout(mContext);
@@ -124,8 +114,8 @@
             when(mHost.getTiles()).thenReturn(Collections.emptyList());
             when(mHost.createTileView(any(), anyBoolean())).thenReturn(mQSTileView);
 
-            mQsPanel.setHost(mHost, mCustomizer);
-            mQsPanel.addTile(dndTile, true);
+            mQsPanel.setCustomizer(mCustomizer);
+            mQsPanel.addTile(mDndTileRecord);
             mQsPanel.setCallback(mCallback);
         });
     }
@@ -133,25 +123,16 @@
     @Test
     public void testSetExpanded_Metrics() {
         mQsPanel.setExpanded(true);
-        verify(mMetricsLogger).visibility(eq(MetricsEvent.QS_PANEL), eq(true));
         verify(mQSLogger).logPanelExpanded(true, mQsPanel.getDumpableTag());
-        assertEquals(1, mUiEventLogger.numLogs());
-        assertEquals(QSEvent.QS_PANEL_EXPANDED.getId(), mUiEventLogger.eventId(0));
-        mUiEventLogger.getLogs().clear();
 
         mQsPanel.setExpanded(false);
-        verify(mMetricsLogger).visibility(eq(MetricsEvent.QS_PANEL), eq(false));
         verify(mQSLogger).logPanelExpanded(false, mQsPanel.getDumpableTag());
-        assertEquals(1, mUiEventLogger.numLogs());
-        assertEquals(QSEvent.QS_PANEL_COLLAPSED.getId(), mUiEventLogger.eventId(0));
-        mUiEventLogger.getLogs().clear();
-
     }
 
     @Test
     public void testOpenDetailsWithExistingTile_NoException() {
         mTestableLooper.processAllMessages();
-        mQsPanel.openDetails("dnd");
+        mQsPanel.openDetails(dndTile);
         mTestableLooper.processAllMessages();
 
         verify(mCallback).onShowingDetail(any(), anyInt(), anyInt());
@@ -159,53 +140,19 @@
 
     @Test
     public void setListening() {
-        when(dndTile.getTileSpec()).thenReturn("dnd");
-
-        mQsPanel.setListening(true);
+        mQsPanel.setListening(true, "dnd");
         verify(mQSLogger).logAllTilesChangeListening(true, mQsPanel.getDumpableTag(), "dnd");
 
-        mQsPanel.setListening(false);
+        mQsPanel.setListening(false, "dnd");
         verify(mQSLogger).logAllTilesChangeListening(false, mQsPanel.getDumpableTag(), "dnd");
     }
 
-/*    @Test
+    @Test
     public void testOpenDetailsWithNullParameter_NoException() {
         mTestableLooper.processAllMessages();
         mQsPanel.openDetails(null);
         mTestableLooper.processAllMessages();
 
         verify(mCallback, never()).onShowingDetail(any(), anyInt(), anyInt());
-    }*/
-
-    @Test
-    public void testOpenDetailsWithNonExistingTile_NoException() {
-        mTestableLooper.processAllMessages();
-        mQsPanel.openDetails("invalid-name");
-        mTestableLooper.processAllMessages();
-
-        verify(mCallback, never()).onShowingDetail(any(), anyInt(), anyInt());
     }
-
-    @Test
-    public void testDump() {
-        String mockTileViewString = "Mock Tile View";
-        String mockTileString = "Mock Tile";
-        doAnswer(invocation -> {
-            PrintWriter pw = invocation.getArgument(1);
-            pw.println(mockTileString);
-            return null;
-        }).when(dndTile).dump(any(FileDescriptor.class), any(PrintWriter.class),
-                any(String[].class));
-        when(mQSTileView.toString()).thenReturn(mockTileViewString);
-
-        StringWriter w = new StringWriter();
-        PrintWriter pw = new PrintWriter(w);
-        mQsPanel.dump(mock(FileDescriptor.class), pw, new String[]{});
-        String expected = "QSPanel:\n"
-                + "  Tile records:\n"
-                + "    " + mockTileString + "\n"
-                + "    " + mockTileViewString + "\n";
-        assertEquals(expected, w.getBuffer().toString());
-    }
-
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java
index fef47bd..6c7c20a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java
@@ -57,8 +57,8 @@
                 mContext.getResources().getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal) * 3;
     }
 
-    private QSPanel.TileRecord createTileRecord() {
-        QSPanel.TileRecord tileRecord = new QSPanel.TileRecord();
+    private QSPanelControllerBase.TileRecord createTileRecord() {
+        QSPanelControllerBase.TileRecord tileRecord = new QSPanelControllerBase.TileRecord();
         tileRecord.tile = mock(QSTile.class);
         tileRecord.tileView = spy(new QSTileView(mContext, new QSIconViewImpl(mContext)));
         return tileRecord;
@@ -66,14 +66,14 @@
 
     @Test
     public void testAddTile_CallsSetListeningOnTile() {
-        QSPanel.TileRecord tileRecord = createTileRecord();
+        QSPanelControllerBase.TileRecord tileRecord = createTileRecord();
         mTileLayout.addTile(tileRecord);
         verify(tileRecord.tile, times(1)).setListening(mTileLayout, false);
     }
 
     @Test
     public void testSetListening_CallsSetListeningOnTile() {
-        QSPanel.TileRecord tileRecord = createTileRecord();
+        QSPanelControllerBase.TileRecord tileRecord = createTileRecord();
         mTileLayout.addTile(tileRecord);
         mTileLayout.setListening(true);
         verify(tileRecord.tile, times(1)).setListening(mTileLayout, true);
@@ -81,7 +81,7 @@
 
     @Test
     public void testSetListening_SameValueIsNoOp() {
-        QSPanel.TileRecord tileRecord = createTileRecord();
+        QSPanelControllerBase.TileRecord tileRecord = createTileRecord();
         mTileLayout.addTile(tileRecord);
         mTileLayout.setListening(false);
         verify(tileRecord.tile, times(1)).setListening(any(), anyBoolean());
@@ -89,7 +89,7 @@
 
     @Test
     public void testSetListening_ChangesValueForAddingFutureTiles() {
-        QSPanel.TileRecord tileRecord = createTileRecord();
+        QSPanelControllerBase.TileRecord tileRecord = createTileRecord();
         mTileLayout.setListening(true);
         mTileLayout.addTile(tileRecord);
         verify(tileRecord.tile, times(1)).setListening(mTileLayout, true);
@@ -97,7 +97,7 @@
 
     @Test
     public void testRemoveTile_CallsSetListeningFalseOnTile() {
-        QSPanel.TileRecord tileRecord = createTileRecord();
+        QSPanelControllerBase.TileRecord tileRecord = createTileRecord();
         mTileLayout.setListening(true);
         mTileLayout.addTile(tileRecord);
         mTileLayout.removeTile(tileRecord);
@@ -106,8 +106,8 @@
 
     @Test
     public void testRemoveAllViews_CallsSetListeningFalseOnAllTiles() {
-        QSPanel.TileRecord tileRecord1 = createTileRecord();
-        QSPanel.TileRecord tileRecord2 = createTileRecord();
+        QSPanelControllerBase.TileRecord tileRecord1 = createTileRecord();
+        QSPanelControllerBase.TileRecord tileRecord2 = createTileRecord();
         mTileLayout.setListening(true);
         mTileLayout.addTile(tileRecord1);
         mTileLayout.addTile(tileRecord2);
@@ -118,7 +118,7 @@
 
     @Test
     public void testMeasureLayout_CallsLayoutOnTile() {
-        QSPanel.TileRecord tileRecord = createTileRecord();
+        QSPanelControllerBase.TileRecord tileRecord = createTileRecord();
         mTileLayout.addTile(tileRecord);
         mTileLayout.measure(mLayoutSizeForOneTile, mLayoutSizeForOneTile);
         mTileLayout.layout(0, 0, mLayoutSizeForOneTile, mLayoutSizeForOneTile);
@@ -127,8 +127,8 @@
 
     @Test
     public void testMeasureLayout_CallsLayoutOnTilesWithNeighboredBounds() {
-        QSPanel.TileRecord tileRecord1 = createTileRecord();
-        QSPanel.TileRecord tileRecord2 = createTileRecord();
+        QSPanelControllerBase.TileRecord tileRecord1 = createTileRecord();
+        QSPanelControllerBase.TileRecord tileRecord2 = createTileRecord();
         mTileLayout.addTile(tileRecord1);
         mTileLayout.addTile(tileRecord2);
         mTileLayout.measure(mLayoutSizeForOneTile * 2, mLayoutSizeForOneTile * 2);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java
index 4aaafbd..de176b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java
@@ -16,9 +16,9 @@
 
 package com.android.systemui.screenshot;
 
-import static com.android.systemui.screenshot.GlobalScreenshot.ACTION_TYPE_SHARE;
-import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
-import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED;
+import static com.android.systemui.screenshot.ScreenshotController.ACTION_TYPE_SHARE;
+import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ID;
+import static com.android.systemui.screenshot.ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED;
 import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_SCREENSHOT;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -79,7 +79,7 @@
     public void setup() throws InterruptedException, ExecutionException, TimeoutException {
         MockitoAnnotations.initMocks(this);
         mIntent = new Intent(mContext, ActionProxyReceiver.class)
-                .putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, mMockPendingIntent);
+                .putExtra(ScreenshotController.EXTRA_ACTION_INTENT, mMockPendingIntent);
 
         when(mMockActivityManagerWrapper.closeSystemWindows(anyString())).thenReturn(mMockFuture);
         when(mMockFuture.get(anyLong(), any(TimeUnit.class))).thenReturn(null);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java
index b924913..14c7679 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java
@@ -16,10 +16,10 @@
 
 package com.android.systemui.screenshot;
 
-import static com.android.systemui.screenshot.GlobalScreenshot.ACTION_TYPE_DELETE;
-import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
-import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED;
-import static com.android.systemui.screenshot.GlobalScreenshot.SCREENSHOT_URI_ID;
+import static com.android.systemui.screenshot.ScreenshotController.ACTION_TYPE_DELETE;
+import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ID;
+import static com.android.systemui.screenshot.ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED;
+import static com.android.systemui.screenshot.ScreenshotController.SCREENSHOT_URI_ID;
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNotNull;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/RecyclerViewActivity.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/RecyclerViewActivity.java
new file mode 100644
index 0000000..fde56ba
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/RecyclerViewActivity.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.screenshot;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+import com.android.internal.widget.LinearLayoutManager;
+import com.android.internal.widget.RecyclerView;
+import com.android.internal.widget.RecyclerView.LayoutParams;
+
+import java.util.Random;
+
+public class RecyclerViewActivity extends Activity {
+    public static final int CHILD_VIEW_HEIGHT = 300;
+    private static final int CHILD_VIEWS = 12;
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        RecyclerView recyclerView = new RecyclerView(this);
+        recyclerView.setLayoutManager(new LinearLayoutManager(this));
+        recyclerView.setAdapter(new TestAdapter());
+        recyclerView.setLayoutParams(new LayoutParams(MATCH_PARENT, MATCH_PARENT));
+        setContentView(recyclerView);
+    }
+
+    static final class TestViewHolder extends RecyclerView.ViewHolder {
+        TestViewHolder(View itemView) {
+            super(itemView);
+        }
+    }
+
+    static final class TestAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
+        private final Random mRandom = new Random();
+
+        @Override
+        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+            return new TestViewHolder(new TextView(parent.getContext()));
+        }
+
+        @Override
+        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
+            TextView view = (TextView) holder.itemView;
+            view.setText("Child #" + position);
+            view.setTextColor(Color.WHITE);
+            view.setTextSize(30f);
+            view.setBackgroundColor(
+                    Color.rgb(mRandom.nextFloat(), mRandom.nextFloat(), mRandom.nextFloat()));
+            view.setMinHeight(CHILD_VIEW_HEIGHT);
+        }
+
+        @Override
+        public int getItemCount() {
+            return CHILD_VIEWS;
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
index e23f926..2374b82 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
@@ -169,8 +169,8 @@
             Looper.prepare();
         }
 
-        GlobalScreenshot.SaveImageInBackgroundData
-                data = new GlobalScreenshot.SaveImageInBackgroundData();
+        ScreenshotController.SaveImageInBackgroundData
+                data = new ScreenshotController.SaveImageInBackgroundData();
         data.image = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
         data.finisher = null;
         data.mActionsReadyListener = null;
@@ -183,9 +183,9 @@
         Intent intent = shareAction.actionIntent.getIntent();
         assertNotNull(intent);
         Bundle bundle = intent.getExtras();
-        assertTrue(bundle.containsKey(GlobalScreenshot.EXTRA_ID));
-        assertTrue(bundle.containsKey(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED));
-        assertEquals(GlobalScreenshot.ACTION_TYPE_SHARE, shareAction.title);
+        assertTrue(bundle.containsKey(ScreenshotController.EXTRA_ID));
+        assertTrue(bundle.containsKey(ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED));
+        assertEquals(ScreenshotController.ACTION_TYPE_SHARE, shareAction.title);
         assertEquals(Intent.ACTION_SEND, intent.getAction());
     }
 
@@ -196,8 +196,8 @@
             Looper.prepare();
         }
 
-        GlobalScreenshot.SaveImageInBackgroundData
-                data = new GlobalScreenshot.SaveImageInBackgroundData();
+        ScreenshotController.SaveImageInBackgroundData
+                data = new ScreenshotController.SaveImageInBackgroundData();
         data.image = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
         data.finisher = null;
         data.mActionsReadyListener = null;
@@ -210,9 +210,9 @@
         Intent intent = editAction.actionIntent.getIntent();
         assertNotNull(intent);
         Bundle bundle = intent.getExtras();
-        assertTrue(bundle.containsKey(GlobalScreenshot.EXTRA_ID));
-        assertTrue(bundle.containsKey(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED));
-        assertEquals(GlobalScreenshot.ACTION_TYPE_EDIT, editAction.title);
+        assertTrue(bundle.containsKey(ScreenshotController.EXTRA_ID));
+        assertTrue(bundle.containsKey(ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED));
+        assertEquals(ScreenshotController.ACTION_TYPE_EDIT, editAction.title);
         assertEquals(Intent.ACTION_EDIT, intent.getAction());
     }
 
@@ -223,8 +223,8 @@
             Looper.prepare();
         }
 
-        GlobalScreenshot.SaveImageInBackgroundData
-                data = new GlobalScreenshot.SaveImageInBackgroundData();
+        ScreenshotController.SaveImageInBackgroundData
+                data = new ScreenshotController.SaveImageInBackgroundData();
         data.image = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
         data.finisher = null;
         data.mActionsReadyListener = null;
@@ -238,9 +238,9 @@
         Intent intent = deleteAction.actionIntent.getIntent();
         assertNotNull(intent);
         Bundle bundle = intent.getExtras();
-        assertTrue(bundle.containsKey(GlobalScreenshot.EXTRA_ID));
-        assertTrue(bundle.containsKey(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED));
-        assertEquals(deleteAction.title, GlobalScreenshot.ACTION_TYPE_DELETE);
+        assertTrue(bundle.containsKey(ScreenshotController.EXTRA_ID));
+        assertTrue(bundle.containsKey(ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED));
+        assertEquals(deleteAction.title, ScreenshotController.ACTION_TYPE_DELETE);
         assertNull(intent.getAction());
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java
index e7ef64e..2b3ca7c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java
@@ -25,8 +25,8 @@
 import android.testing.AndroidTestingRunner;
 import android.util.Log;
 import android.view.Display;
-import android.view.IScrollCaptureClient;
-import android.view.IScrollCaptureController;
+import android.view.IScrollCaptureCallbacks;
+import android.view.IScrollCaptureConnection;
 import android.view.IWindowManager;
 import android.view.WindowManagerGlobal;
 
@@ -65,19 +65,20 @@
         final CountDownLatch latch = new CountDownLatch(1);
         try {
             wms.requestScrollCapture(Display.DEFAULT_DISPLAY, null, -1,
-                    new IScrollCaptureController.Stub() {
+                    new IScrollCaptureCallbacks.Stub() {
                         @Override
-                        public void onClientConnected(
-                                IScrollCaptureClient client, Rect scrollBounds,
+                        public void onConnected(
+                                IScrollCaptureConnection connection, Rect scrollBounds,
                                 Point positionInWindow) {
                             Log.d(TAG,
-                                    "client connected: " + client + "[scrollBounds= " + scrollBounds
-                                            + ", positionInWindow=" + positionInWindow + "]");
+                                    "client connected: " + connection + "[scrollBounds= "
+                                            + scrollBounds + ", "
+                                            + "positionInWindow=" + positionInWindow + "]");
                             latch.countDown();
                         }
 
                         @Override
-                        public void onClientUnavailable() {
+                        public void onUnavailable() {
                         }
 
                         @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java
index ce6f073..6f3a4a1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java
@@ -16,8 +16,8 @@
 
 package com.android.systemui.screenshot;
 
-import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ACTION_TYPE;
-import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
+import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ACTION_TYPE;
+import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ID;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
@@ -56,7 +56,7 @@
         MockitoAnnotations.initMocks(this);
         mSmartActionsReceiver = new SmartActionsReceiver(mMockScreenshotSmartActions);
         mIntent = new Intent(mContext, SmartActionsReceiver.class)
-                .putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, mMockPendingIntent);
+                .putExtra(ScreenshotController.EXTRA_ACTION_INTENT, mMockPendingIntent);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/MediaArtworkProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/MediaArtworkProcessorTest.kt
index 72e6df2..724ea02 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/MediaArtworkProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/MediaArtworkProcessorTest.kt
@@ -80,7 +80,7 @@
         val background2 = processor.processArtwork(context, artwork)!!
         // THEN the two bitmaps are the same
         // Note: This is currently broken and trying to use caching causes issues
-        assertThat(background1).isNotSameAs(background2)
+        assertThat(background1).isNotSameInstanceAs(background2)
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
index 9971e0c..71f146b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
@@ -74,8 +74,6 @@
         mMockResources = mock(Resources.class);
         mPackageManagerSpy = spy(getContext().getPackageManager());
         doReturn(mMockResources).when(mPackageManagerSpy)
-                .getResourcesForApplicationAsUser(eq("mockPackage"), anyInt());
-        doReturn(mMockResources).when(mPackageManagerSpy)
                 .getResourcesForApplication(eq("mockPackage"));
         doReturn(mMockResources).when(mPackageManagerSpy).getResourcesForApplication(argThat(
                 (ArgumentMatcher<ApplicationInfo>) o -> "mockPackage".equals(o.packageName)));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java
index d0dfb171..524ead2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java
@@ -34,6 +34,7 @@
 
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.media.dialog.MediaOutputDialogFactory;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -64,6 +65,7 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
+        mDependency.injectMockDependency(MediaOutputDialogFactory.class);
         allowTestableLooperAsMainThread();
         when(mBindStage.getStageParams(any())).thenReturn(new RowContentBindParams());
         mDynamicChildBindController =
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 3e1616c..0be9f7d 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
@@ -21,6 +21,8 @@
 
 import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.assertTrue;
@@ -346,7 +348,7 @@
         setSmartActions(mEntry.getKey(), null);
 
         mEntryManager.updateNotificationRanking(mRankingMap);
-        assertNull(mEntry.getSmartActions());
+        assertThat(mEntry.getSmartActions()).isEmpty();
     }
 
     @Test
@@ -378,6 +380,36 @@
     }
 
     @Test
+    public void testUpdatePendingNotification_rankingUpdated() {
+        // GIVEN a notification with ranking is pending
+        final Ranking originalRanking = mEntry.getRanking();
+        mEntryManager.mPendingNotifications.put(mEntry.getKey(), mEntry);
+
+        // WHEN the same notification has been updated with a new ranking
+        final int newRank = 2345;
+        doAnswer(invocationOnMock -> {
+            Ranking ranking = (Ranking)
+                    invocationOnMock.getArguments()[1];
+            ranking.populate(
+                    mEntry.getKey(),
+                    newRank, /* this changed!! */
+                    false,
+                    0,
+                    0,
+                    IMPORTANCE_DEFAULT,
+                    null, null,
+                    null, null, null, true,
+                    Ranking.USER_SENTIMENT_NEUTRAL, false, -1,
+                    false, null, null, false, false, false, null, 0, false);
+            return true;
+        }).when(mRankingMap).getRanking(eq(mEntry.getKey()), any(Ranking.class));
+        mEntryManager.addNotification(mSbn, mRankingMap);
+
+        // THEN ranking for the entry has been updated with new ranking
+        assertEquals(newRank, mEntry.getRanking().getRank());
+    }
+
+    @Test
     public void testLifetimeExtenders_ifNotificationIsRetainedItIsntRemoved() {
         // GIVEN an entry manager with a notification
         mEntryManager.addActiveNotificationForTest(mEntry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt
index 79fa436..5898664 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt
@@ -118,7 +118,7 @@
         val people = viewModel.people.toList()
         assertThat(people.size).isEqualTo(1)
         assertThat(people[0].name).isEqualTo("name")
-        assertThat(people[0].icon).isSameAs(fakePerson.avatar)
+        assertThat(people[0].icon).isSameInstanceAs(fakePerson.avatar)
 
         people[0].onClick()
 
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 8f1d71c..891179c 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
@@ -96,8 +96,8 @@
         mGroupRow.setSensitive(true, true);
         mGroupRow.setHideSensitive(true, false, 0, 0);
         mGroupRow.setHideSensitive(false, true, 0, 0);
-        assertTrue(mGroupRow.getChildrenContainer().getVisibleHeader().getVisibility()
-                == View.VISIBLE);
+        assertEquals(View.VISIBLE, mGroupRow.getChildrenContainer().getVisibleWrapper()
+                .getNotificationHeader().getVisibility());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index a2f8c1c..6b0a23f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -54,13 +54,13 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.media.MediaFeatureFlag;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.SmartReplyController;
 import com.android.systemui.statusbar.notification.ConversationNotificationProcessor;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams;
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback;
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
-import com.android.systemui.statusbar.policy.SmartReplyConstants;
+import com.android.systemui.statusbar.policy.InflatedSmartReplies;
+import com.android.systemui.statusbar.policy.SmartRepliesAndActionsInflater;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -87,6 +87,11 @@
 
     @Mock private NotifRemoteViewCache mCache;
     @Mock private ConversationNotificationProcessor mConversationNotificationProcessor;
+    @Mock private InflatedSmartReplies mInflatedSmartReplies;
+
+    private final SmartRepliesAndActionsInflater mSmartRepliesAndActionsInflater =
+            (sysuiContext, notifPackageContext, entry, existingRepliesAndAction) ->
+                    mInflatedSmartReplies;
 
     @Before
     public void setUp() throws Exception {
@@ -103,16 +108,13 @@
         ExpandableNotificationRow row = helper.createRow(mBuilder.build());
         mRow = spy(row);
 
-        final SmartReplyConstants smartReplyConstants = mock(SmartReplyConstants.class);
-        final SmartReplyController smartReplyController = mock(SmartReplyController.class);
         mNotificationInflater = new NotificationContentInflater(
                 mCache,
                 mock(NotificationRemoteInputManager.class),
-                () -> smartReplyConstants,
-                () -> smartReplyController,
                 mConversationNotificationProcessor,
                 mock(MediaFeatureFlag.class),
-                mock(Executor.class));
+                mock(Executor.class),
+                mSmartRepliesAndActionsInflater);
     }
 
     @Test
@@ -120,13 +122,15 @@
         BindParams params = new BindParams();
         params.usesIncreasedHeadsUpHeight = true;
         Notification.Builder builder = spy(mBuilder);
-        mNotificationInflater.inflateNotificationViews(mRow.getEntry(),
+        mNotificationInflater.inflateNotificationViews(
+                mRow.getEntry(),
                 mRow,
                 params,
                 true /* inflateSynchronously */,
                 FLAG_CONTENT_VIEW_ALL,
                 builder,
-                mContext);
+                mContext,
+                mSmartRepliesAndActionsInflater);
         verify(builder).createHeadsUpContentView(true);
     }
 
@@ -135,13 +139,15 @@
         BindParams params = new BindParams();
         params.usesIncreasedHeight = true;
         Notification.Builder builder = spy(mBuilder);
-        mNotificationInflater.inflateNotificationViews(mRow.getEntry(),
+        mNotificationInflater.inflateNotificationViews(
+                mRow.getEntry(),
                 mRow,
                 params,
                 true /* inflateSynchronously */,
                 FLAG_CONTENT_VIEW_ALL,
                 builder,
-                mContext);
+                mContext,
+                mSmartRepliesAndActionsInflater);
         verify(builder).createContentView(true);
     }
 
@@ -366,7 +372,7 @@
         }
     }
 
-    private class AsyncFailRemoteView extends RemoteViews {
+    private static class AsyncFailRemoteView extends RemoteViews {
         Handler mHandler = Handler.createAsync(Looper.getMainLooper());
 
         public AsyncFailRemoteView(String packageName, int layoutId) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
index c2091da..d08b2b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
@@ -25,8 +25,6 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.app.AppOpsManager;
-import android.util.ArraySet;
 import android.view.NotificationHeaderView;
 import android.view.View;
 import android.view.ViewPropertyAnimator;
@@ -37,6 +35,7 @@
 
 import com.android.internal.widget.NotificationExpandButton;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.media.dialog.MediaOutputDialogFactory;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -51,6 +50,8 @@
     @Before
     @UiThreadTest
     public void setup() {
+        mDependency.injectMockDependency(MediaOutputDialogFactory.class);
+
         mView = new NotificationContentView(mContext, null);
         ExpandableNotificationRow row = new ExpandableNotificationRow(mContext, null);
         ExpandableNotificationRow mockRow = spy(row);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
index aff8ade..8a5afe6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
@@ -45,6 +45,7 @@
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.media.MediaFeatureFlag;
+import com.android.systemui.media.dialog.MediaOutputDialogFactory;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.shared.plugins.PluginManager;
@@ -79,7 +80,7 @@
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.statusbar.policy.SmartReplyConstants;
+import com.android.systemui.statusbar.policy.InflatedSmartReplies;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.leak.LeakDetector;
 import com.android.systemui.util.time.FakeSystemClock;
@@ -138,6 +139,7 @@
     @Mock private ActivatableNotificationViewController mActivatableNotificationViewController;
     @Mock private NotificationRowComponent.Builder mNotificationRowComponentBuilder;
     @Mock private PeopleNotificationIdentifier mPeopleNotificationIdentifier;
+    @Mock private InflatedSmartReplies mInflatedSmartReplies;
 
     private StatusBarNotification mSbn;
     private NotificationListenerService.RankingMap mRankingMap;
@@ -151,6 +153,7 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mDependency.injectMockDependency(SmartReplyController.class);
+        mDependency.injectMockDependency(MediaOutputDialogFactory.class);
 
         mHandler = Handler.createAsync(TestableLooper.get(this).getLooper());
 
@@ -199,11 +202,11 @@
         NotificationContentInflater binder = new NotificationContentInflater(
                 cache,
                 mRemoteInputManager,
-                () -> mock(SmartReplyConstants.class),
-                () -> mock(SmartReplyController.class),
                 mock(ConversationNotificationProcessor.class),
                 mock(MediaFeatureFlag.class),
-                mBgExecutor);
+                mBgExecutor,
+                (sysuiContext, notifPackageContext, entry, existingRepliesAndAction) ->
+                        mInflatedSmartReplies);
         mRowContentBindStage = new RowContentBindStage(
                 binder,
                 mock(NotifInflationErrorManager.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 2ce8b34..847e0a4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -48,12 +48,12 @@
 import com.android.systemui.bubbles.Bubbles;
 import com.android.systemui.bubbles.BubblesTestActivity;
 import com.android.systemui.media.MediaFeatureFlag;
+import com.android.systemui.media.dialog.MediaOutputDialogFactory;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
-import com.android.systemui.statusbar.SmartReplyController;
 import com.android.systemui.statusbar.notification.ConversationNotificationProcessor;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
@@ -69,7 +69,7 @@
 import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.policy.SmartReplyConstants;
+import com.android.systemui.statusbar.policy.InflatedSmartReplies;
 
 import org.mockito.ArgumentCaptor;
 
@@ -116,6 +116,7 @@
         mTestLooper = testLooper;
         dependency.injectMockDependency(NotificationMediaManager.class);
         dependency.injectMockDependency(NotificationShadeWindowController.class);
+        dependency.injectMockDependency(MediaOutputDialogFactory.class);
         mStatusBarStateController = mock(StatusBarStateController.class);
         mGroupMembershipManager = new NotificationGroupManagerLegacy(
                 mStatusBarStateController,
@@ -134,11 +135,11 @@
         NotificationContentInflater contentBinder = new NotificationContentInflater(
                 mock(NotifRemoteViewCache.class),
                 mock(NotificationRemoteInputManager.class),
-                () -> mock(SmartReplyConstants.class),
-                () -> mock(SmartReplyController.class),
                 mock(ConversationNotificationProcessor.class),
                 mock(MediaFeatureFlag.class),
-                mock(Executor.class));
+                mock(Executor.class),
+                (sysuiContext, notifPackageContext, entry, existingRepliesAndAction) ->
+                        mock(InflatedSmartReplies.class));
         contentBinder.setInflateSynchronously(true);
         mBindStage = new RowContentBindStage(contentBinder,
                 mock(NotifInflationErrorManager.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
index b9055ec..7c41abba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
@@ -57,7 +57,7 @@
     public void testGetMaxAllowedVisibleChildren_lowPriority() {
         mChildrenContainer.setIsLowPriority(true);
         Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(),
-            NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED);
+                NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED);
     }
 
     @Test
@@ -72,7 +72,7 @@
         mChildrenContainer.setIsLowPriority(true);
         mChildrenContainer.setChildrenExpanded(true);
         Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(),
-            NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED);
+                NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED);
     }
 
     @Test
@@ -80,13 +80,13 @@
         mChildrenContainer.setIsLowPriority(true);
         mChildrenContainer.setUserLocked(true);
         Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(),
-            NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED);
+                NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED);
     }
 
     @Test
     public void testGetMaxAllowedVisibleChildren_likeCollapsed() {
         Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(true),
-            NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_COLLAPSED);
+                NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_COLLAPSED);
     }
 
 
@@ -136,12 +136,13 @@
     @Test
     public void testLowPriorityHeaderCleared() {
         mGroup.setIsLowPriority(true);
-        NotificationHeaderView lowPriorityHeaderView = mChildrenContainer.getLowPriorityHeaderView();
-        Assert.assertTrue(lowPriorityHeaderView.getVisibility() == View.VISIBLE);
-        Assert.assertTrue(lowPriorityHeaderView.getParent() == mChildrenContainer);
+        NotificationHeaderView lowPriorityHeaderView =
+                mChildrenContainer.getLowPriorityViewWrapper().getNotificationHeader();
+        Assert.assertEquals(View.VISIBLE, lowPriorityHeaderView.getVisibility());
+        Assert.assertSame(mChildrenContainer, lowPriorityHeaderView.getParent());
         mGroup.setIsLowPriority(false);
-        Assert.assertTrue(lowPriorityHeaderView.getParent() == null);
-        Assert.assertTrue(mChildrenContainer.getLowPriorityHeaderView() == null);
+        Assert.assertNull(lowPriorityHeaderView.getParent());
+        Assert.assertNull(mChildrenContainer.getLowPriorityViewWrapper());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index 7ee27c9..cb56f1f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -53,6 +53,7 @@
 import com.android.keyguard.KeyguardClockSwitch;
 import com.android.keyguard.KeyguardClockSwitchController;
 import com.android.keyguard.KeyguardStatusView;
+import com.android.keyguard.KeyguardStatusViewController;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.dagger.KeyguardStatusViewComponent;
 import com.android.systemui.R;
@@ -190,6 +191,8 @@
     @Mock
     private KeyguardClockSwitchController mKeyguardClockSwitchController;
     @Mock
+    private KeyguardStatusViewController mKeyguardStatusViewController;
+    @Mock
     private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
 
     private NotificationPanelViewController mNotificationPanelViewController;
@@ -246,6 +249,8 @@
                 .thenReturn(mKeyguardStatusViewComponent);
         when(mKeyguardStatusViewComponent.getKeyguardClockSwitchController())
                 .thenReturn(mKeyguardClockSwitchController);
+        when(mKeyguardStatusViewComponent.getKeyguardStatusViewController())
+                .thenReturn(mKeyguardStatusViewController);
         mNotificationPanelViewController = new NotificationPanelViewController(mView,
                 mResources,
                 mInjectionInflationController,
@@ -350,7 +355,7 @@
         mAccessibiltyDelegate.onInitializeAccessibilityNodeInfo(mView, nodeInfo);
 
         List<AccessibilityNodeInfo.AccessibilityAction> actionList = nodeInfo.getActionList();
-        assertThat(actionList).containsAllIn(
+        assertThat(actionList).containsAtLeastElementsIn(
                 new AccessibilityNodeInfo.AccessibilityAction[] {
                         AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD,
                         AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index a6ea9966a..d6a7acb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -24,6 +24,7 @@
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.TestCase.fail;
 
+import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -34,6 +35,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -43,6 +45,7 @@
 import android.app.trust.TrustManager;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
+import android.content.Intent;
 import android.content.IntentFilter;
 import android.hardware.display.AmbientDisplayConfiguration;
 import android.hardware.fingerprint.FingerprintManager;
@@ -84,6 +87,7 @@
 import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.demomode.DemoModeController;
+import com.android.systemui.emergency.EmergencyGesture;
 import com.android.systemui.keyguard.DismissCallbackRegistry;
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.keyguard.ScreenLifecycle;
@@ -146,6 +150,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -873,6 +878,19 @@
         verify(mDozeServiceHost).setDozeSuppressed(false);
     }
 
+    @Test
+    public void onEmergencyActionLaunchGesture_launchesEmergencyIntent() {
+        ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+        StatusBar statusBarSpy = spy(mStatusBar);
+
+        statusBarSpy.onEmergencyActionLaunchGestureDetected();
+
+        verify(statusBarSpy).startActivity(intentCaptor.capture(), eq(true));
+        Intent sentIntent = intentCaptor.getValue();
+        assertEquals(sentIntent.getAction(), EmergencyGesture.ACTION_LAUNCH_EMERGENCY);
+
+    }
+
     public static class TestableNotificationInterruptStateProviderImpl extends
             NotificationInterruptStateProviderImpl {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/InflatedSmartRepliesTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/InflatedSmartRepliesTest.java
index 53d8e58..8ee15bb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/InflatedSmartRepliesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/InflatedSmartRepliesTest.java
@@ -67,16 +67,20 @@
     private static final Intent WHITELISTED_TEST_INTENT =
             new Intent("com.android.WHITELISTED_TEST_ACTION");
 
-    @Mock SmartReplyConstants mSmartReplyConstants;
-    @Mock Notification mNotification;
-    NotificationEntry mEntry;
-    @Mock RemoteInput mRemoteInput;
-    @Mock RemoteInput mFreeFormRemoteInput;
-    @Mock ActivityManagerWrapper mActivityManagerWrapper;
-    @Mock PackageManagerWrapper mPackageManagerWrapper;
-    @Mock DevicePolicyManagerWrapper mDevicePolicyManagerWrapper;
+    @Mock private SmartReplyConstants mSmartReplyConstants;
+    @Mock private Notification mNotification;
+    @Mock private RemoteInput mRemoteInput;
+    @Mock private RemoteInput mFreeFormRemoteInput;
+    @Mock private ActivityManagerWrapper mActivityManagerWrapper;
+    @Mock private PackageManagerWrapper mPackageManagerWrapper;
+    @Mock private DevicePolicyManagerWrapper mDevicePolicyManagerWrapper;
+    @Mock private SmartRepliesAndActions mSmartRepliesAndActions;
+    @Mock private SmartReplyInflater mSmartReplyInflater;
+    @Mock private SmartActionInflater mSmartActionInflater;
 
     private Icon mActionIcon;
+    private NotificationEntry mEntry;
+    private SmartRepliesAndActionsInflaterImpl mSmartRepliesInflater;
 
     @Before
     @UiThreadTest
@@ -96,6 +100,14 @@
         mActionIcon = Icon.createWithResource(mContext, R.drawable.ic_person);
 
         when(mActivityManagerWrapper.isLockTaskKioskModeActive()).thenReturn(false);
+
+        mSmartRepliesInflater = new SmartRepliesAndActionsInflaterImpl(
+                mSmartReplyConstants,
+                mActivityManagerWrapper,
+                mPackageManagerWrapper,
+                mDevicePolicyManagerWrapper,
+                mSmartReplyInflater,
+                mSmartActionInflater);
     }
 
     @Test
@@ -107,7 +119,7 @@
         when(mSmartReplyConstants.isEnabled()).thenReturn(false);
 
         SmartRepliesAndActions repliesAndActions =
-                InflatedSmartReplies.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+                mSmartRepliesInflater.chooseSmartRepliesAndActions(mEntry);
 
         assertThat(repliesAndActions.smartReplies).isNull();
         assertThat(repliesAndActions.smartActions).isNull();
@@ -123,7 +135,7 @@
         when(mSmartReplyConstants.isEnabled()).thenReturn(false);
 
         SmartRepliesAndActions repliesAndActions =
-                InflatedSmartReplies.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+                mSmartRepliesInflater.chooseSmartRepliesAndActions(mEntry);
 
         assertThat(repliesAndActions.smartReplies).isNull();
         assertThat(repliesAndActions.smartActions).isNull();
@@ -135,7 +147,7 @@
         setupAppGeneratedReplies(smartReplies);
 
         SmartRepliesAndActions repliesAndActions =
-                InflatedSmartReplies.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+                mSmartRepliesInflater.chooseSmartRepliesAndActions(mEntry);
 
         assertThat(repliesAndActions.smartReplies.choices).isEqualTo(Arrays.asList(smartReplies));
         assertThat(repliesAndActions.smartReplies.fromAssistant).isFalse();
@@ -150,7 +162,7 @@
         setupAppGeneratedSuggestions(smartReplies, smartActions);
 
         SmartRepliesAndActions repliesAndActions =
-                InflatedSmartReplies.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+                mSmartRepliesInflater.chooseSmartRepliesAndActions(mEntry);
 
         assertThat(repliesAndActions.smartReplies.choices).isEqualTo(Arrays.asList(smartReplies));
         assertThat(repliesAndActions.smartReplies.fromAssistant).isFalse();
@@ -169,10 +181,9 @@
                 .build();
 
         SmartRepliesAndActions repliesAndActions =
-                InflatedSmartReplies.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+                mSmartRepliesInflater.chooseSmartRepliesAndActions(mEntry);
 
-        assertThat(repliesAndActions.smartReplies.choices).isEqualTo(
-                mEntry.getSmartReplies());
+        assertThat(repliesAndActions.smartReplies.choices).isEqualTo(mEntry.getSmartReplies());
         assertThat(repliesAndActions.smartReplies.fromAssistant).isTrue();
         assertThat(repliesAndActions.smartActions).isNull();
     }
@@ -187,7 +198,7 @@
                 .setSmartReplies(createReplies("Sys Smart Reply 1", "Sys Smart Reply 2"))
                 .build();
         SmartRepliesAndActions repliesAndActions =
-                InflatedSmartReplies.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+                mSmartRepliesInflater.chooseSmartRepliesAndActions(mEntry);
 
         assertThat(repliesAndActions.smartReplies).isNull();
         assertThat(repliesAndActions.smartActions).isNull();
@@ -202,8 +213,9 @@
         modifyRanking(mEntry)
                 .setSmartActions(createActions("Sys Smart Action 1", "Sys Smart Action 2"))
                 .build();
+
         SmartRepliesAndActions repliesAndActions =
-                InflatedSmartReplies.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+                mSmartRepliesInflater.chooseSmartRepliesAndActions(mEntry);
 
         assertThat(repliesAndActions.smartReplies).isNull();
         assertThat(repliesAndActions.smartActions.actions)
@@ -226,7 +238,7 @@
                 .build();
 
         SmartRepliesAndActions repliesAndActions =
-                InflatedSmartReplies.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+                mSmartRepliesInflater.chooseSmartRepliesAndActions(mEntry);
 
         assertThat(repliesAndActions.smartReplies.choices)
                 .isEqualTo(Arrays.asList(appGenSmartReplies));
@@ -248,7 +260,7 @@
                 .build();
 
         SmartRepliesAndActions repliesAndActions =
-                InflatedSmartReplies.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+                mSmartRepliesInflater.chooseSmartRepliesAndActions(mEntry);
 
         assertThat(repliesAndActions.smartActions).isNull();
         assertThat(repliesAndActions.smartReplies).isNull();
@@ -270,7 +282,7 @@
                 .build();
 
         SmartRepliesAndActions repliesAndActions =
-                InflatedSmartReplies.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+                mSmartRepliesInflater.chooseSmartRepliesAndActions(mEntry);
 
         assertThat(repliesAndActions.smartReplies.choices).isEqualTo(
                 mEntry.getSmartReplies());
@@ -306,7 +318,7 @@
                 .build();
 
         SmartRepliesAndActions repliesAndActions =
-                InflatedSmartReplies.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+                mSmartRepliesInflater.chooseSmartRepliesAndActions(mEntry);
 
         // Only the action for the whitelisted package should be allowed.
         assertThat(repliesAndActions.smartActions.actions.size()).isEqualTo(1);
@@ -329,7 +341,7 @@
                 .build();
 
         SmartRepliesAndActions repliesAndActions =
-                InflatedSmartReplies.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+                mSmartRepliesInflater.chooseSmartRepliesAndActions(mEntry);
 
         // We don't restrict replies or actions in screen pinning mode.
         assertThat(repliesAndActions.smartReplies.choices).isEqualTo(
@@ -356,8 +368,10 @@
                 new SmartReplies(rightReplies, null, null, false /* fromAssistant */),
                 new SmartActions(rightActions, false /* fromAssistant */));
 
-        assertThat(InflatedSmartReplies.areSuggestionsSimilar(
-                leftRepliesAndActions, rightRepliesAndActions)).isTrue();
+        assertThat(
+                SmartRepliesAndActionsInflaterKt
+                        .areSuggestionsSimilar(leftRepliesAndActions, rightRepliesAndActions))
+                .isTrue();
     }
 
     @Test
@@ -378,7 +392,7 @@
                 new SmartReplies(rightReplies, null, null, false /* fromAssistant */),
                 new SmartActions(rightActions, false /* fromAssistant */));
 
-        assertThat(InflatedSmartReplies.areSuggestionsSimilar(
+        assertThat(SmartRepliesAndActionsInflaterKt.areSuggestionsSimilar(
                 leftRepliesAndActions, rightRepliesAndActions)).isFalse();
     }
 
@@ -400,7 +414,7 @@
                 new SmartReplies(rightReplies, null, null, false /* fromAssistant */),
                 new SmartActions(rightActions, false /* fromAssistant */));
 
-        assertThat(InflatedSmartReplies.areSuggestionsSimilar(
+        assertThat(SmartRepliesAndActionsInflaterKt.areSuggestionsSimilar(
                 leftRepliesAndActions, rightRepliesAndActions)).isFalse();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index 7db1b83..13cf679 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -45,7 +45,6 @@
 import android.os.Handler;
 import android.provider.Settings;
 import android.provider.Settings.Global;
-import android.telephony.CdmaEriInformation;
 import android.telephony.CellSignalStrength;
 import android.telephony.NetworkRegistrationInfo;
 import android.telephony.PhoneStateListener;
@@ -121,8 +120,6 @@
     private NetworkCapabilities mNetCapabilities;
     private ConnectivityManager.NetworkCallback mNetworkCallback;
 
-    private CdmaEriInformation mEriInformation;
-
     @Rule
     public TestWatcher failWatcher = new TestWatcher() {
         @Override
@@ -184,11 +181,6 @@
         doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mTelephonyDisplayInfo).getNetworkType();
         doReturn(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE).when(mTelephonyDisplayInfo)
                 .getOverrideNetworkType();
-
-        mEriInformation = new CdmaEriInformation(CdmaEriInformation.ERI_OFF,
-                CdmaEriInformation.ERI_ICON_MODE_NORMAL);
-        when(mMockTm.getCdmaEriInformation()).thenReturn(mEriInformation);
-
         mConfig = new Config();
         mConfig.hspaDataDistinguishable = true;
         mCallbackHandler = mock(CallbackHandler.class);
@@ -322,9 +314,8 @@
     }
 
     public void setCdmaRoaming(boolean isRoaming) {
-        mEriInformation.setEriIconIndex(isRoaming ?
-                CdmaEriInformation.ERI_ON : CdmaEriInformation.ERI_OFF);
-        when(mMockTm.getCdmaEriInformation()).thenReturn(mEriInformation);
+        when(mMockTm.getCdmaEnhancedRoamingIndicatorIconIndex()).thenReturn(
+                isRoaming ? TelephonyManager.ERI_ON : TelephonyManager.ERI_OFF);
     }
 
     public void setVoiceRegState(int voiceRegState) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
index f1a6e67..836a81e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
@@ -70,6 +70,12 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+import kotlin.sequences.Sequence;
+import kotlin.sequences.SequencesKt;
 
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
@@ -98,31 +104,32 @@
     private int mDoubleLinePaddingHorizontal;
     private int mSpacing;
 
-    @Mock private SmartReplyController mLogger;
     private NotificationEntry mEntry;
     private Notification mNotification;
-    @Mock private SmartReplyConstants mConstants;
 
-    @Mock ActivityStarter mActivityStarter;
-    @Mock HeadsUpManager mHeadsUpManager;
+    private SmartReplyInflaterImpl mSmartReplyInflater;
+    private SmartActionInflaterImpl mSmartActionInflater;
+
+    @Mock private SmartReplyConstants mConstants;
+    @Mock private ActivityStarter mActivityStarter;
+    @Mock private HeadsUpManager mHeadsUpManager;
+    @Mock private NotificationRemoteInputManager mNotificationRemoteInputManager;
+    @Mock private SmartReplyController mSmartReplyController;
+
+    private final KeyguardDismissUtil mKeyguardDismissUtil = new KeyguardDismissUtil();
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mReceiver = new BlockingQueueIntentReceiver();
         mContext.registerReceiver(mReceiver, new IntentFilter(TEST_ACTION));
-        mDependency.get(KeyguardDismissUtil.class).setDismissHandler((action, unused) -> {
-            action.onDismiss();
-        });
+        mKeyguardDismissUtil.setDismissHandler((action, unused) -> action.onDismiss());
         mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
         mDependency.injectMockDependency(ShadeController.class);
         mDependency.injectMockDependency(NotificationRemoteInputManager.class);
         mDependency.injectTestDependency(ActivityStarter.class, mActivityStarter);
         mDependency.injectTestDependency(SmartReplyConstants.class, mConstants);
 
-        mContainer = new View(mContext, null);
-        mView = SmartReplyView.inflate(mContext);
-
         // Any number of replies are fine.
         when(mConstants.getMinNumSystemGeneratedReplies()).thenReturn(0);
         when(mConstants.getMaxSqueezeRemeasureAttempts()).thenReturn(3);
@@ -130,6 +137,9 @@
         // Ensure there's no delay before we can click smart suggestions.
         when(mConstants.getOnClickInitDelay()).thenReturn(0L);
 
+        mContainer = new View(mContext, null);
+        mView = SmartReplyView.inflate(mContext, mConstants);
+
         final Resources res = mContext.getResources();
         mSingleLinePaddingHorizontal = res.getDimensionPixelSize(
                 R.dimen.smart_reply_button_padding_horizontal_single_line);
@@ -147,6 +157,18 @@
                 .build();
 
         mActionIcon = Icon.createWithResource(mContext, R.drawable.ic_person);
+
+        mSmartReplyInflater = new SmartReplyInflaterImpl(
+                mConstants,
+                mKeyguardDismissUtil,
+                mNotificationRemoteInputManager,
+                mSmartReplyController,
+                mContext);
+        mSmartActionInflater = new SmartActionInflaterImpl(
+                mConstants,
+                mActivityStarter,
+                mSmartReplyController,
+                mHeadsUpManager);
     }
 
     @After
@@ -168,7 +190,7 @@
 
     @Test
     public void testSendSmartReply_keyguardCancelled() throws InterruptedException {
-        mDependency.get(KeyguardDismissUtil.class).setDismissHandler((action, unused) -> {});
+        mKeyguardDismissUtil.setDismissHandler((action, unused) -> { });
         setSmartReplies(TEST_CHOICES);
 
         mView.getChildAt(2).performClick();
@@ -179,9 +201,8 @@
     @Test
     public void testSendSmartReply_waitsForKeyguard() throws InterruptedException {
         AtomicReference<OnDismissAction> actionRef = new AtomicReference<>();
-        mDependency.get(KeyguardDismissUtil.class).setDismissHandler((action, unused) -> {
-            actionRef.set(action);
-        });
+
+        mKeyguardDismissUtil.setDismissHandler((action, unused) -> actionRef.set(action));
         setSmartReplies(TEST_CHOICES);
 
         mView.getChildAt(2).performClick();
@@ -202,7 +223,7 @@
     public void testSendSmartReply_controllerCalled() {
         setSmartReplies(TEST_CHOICES);
         mView.getChildAt(2).performClick();
-        verify(mLogger).smartReplySent(mEntry, 2, TEST_CHOICES[2],
+        verify(mSmartReplyController).smartReplySent(mEntry, 2, TEST_CHOICES[2],
                 MetricsEvent.LOCATION_UNKNOWN, false /* modifiedBeforeSending */);
     }
 
@@ -461,24 +482,28 @@
 
     private void setSmartReplies(CharSequence[] choices, boolean useDelayedOnClickListener) {
         mView.resetSmartSuggestions(mContainer);
-        List<Button> replyButtons = inflateSmartReplies(choices, false /* fromAssistant */,
-                useDelayedOnClickListener);
+        List<Button> replyButtons =
+                inflateSmartReplies(
+                        choices, false /* fromAssistant */, useDelayedOnClickListener)
+                .collect(Collectors.toList());
         mView.addPreInflatedButtons(replyButtons);
     }
 
-    private List<Button> inflateSmartReplies(CharSequence[] choices, boolean fromAssistant,
-            boolean useDelayedOnClickListener) {
-        PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0,
-                new Intent(TEST_ACTION), 0);
+    private SmartReplyView.SmartReplies createSmartReplies(CharSequence[] choices,
+            boolean fromAssistant) {
+        PendingIntent pendingIntent =
+                PendingIntent.getBroadcast(mContext, 0, new Intent(TEST_ACTION), 0);
         RemoteInput input = new RemoteInput.Builder(TEST_RESULT_KEY).setChoices(choices).build();
-        SmartReplyView.SmartReplies smartReplies =
-                new SmartReplyView.SmartReplies(
-                        Arrays.asList(choices),
-                        input,
-                        pendingIntent,
-                        fromAssistant);
-        return mView.inflateRepliesFromRemoteInput(smartReplies, mLogger, mEntry,
-                useDelayedOnClickListener);
+        return new SmartReplyView.SmartReplies(
+                Arrays.asList(choices), input, pendingIntent, fromAssistant);
+    }
+
+    private Stream<Button> inflateSmartReplies(CharSequence[] choices, boolean fromAssistant,
+            boolean useDelayedOnClickListener) {
+        SmartReplyView.SmartReplies smartReplies = createSmartReplies(choices, fromAssistant);
+        return IntStream.range(0, choices.length).mapToObj(idx ->
+                mSmartReplyInflater.inflateReplyButton(
+                        mView, mEntry, smartReplies, idx, choices[idx], useDelayedOnClickListener));
     }
 
     private Notification.Action createAction(String actionTitle) {
@@ -501,14 +526,20 @@
 
     private void setSmartActions(String[] actionTitles, boolean useDelayedOnClickListener) {
         mView.resetSmartSuggestions(mContainer);
-        List<Button> actions = mView.inflateSmartActions(
-                getContext(),
-                new SmartReplyView.SmartActions(createActions(actionTitles), false),
-                mLogger,
-                mEntry,
-                mHeadsUpManager,
-                useDelayedOnClickListener);
-        mView.addPreInflatedButtons(actions);
+        SmartReplyView.SmartActions smartActions = new SmartReplyView.SmartActions(
+                createActions(actionTitles), false);
+
+        Stream<Button> buttons = IntStream.range(0, smartActions.actions.size()).mapToObj(idx ->
+                mSmartActionInflater.inflateActionButton(
+                        mView,
+                        mEntry,
+                        smartActions,
+                        idx,
+                        smartActions.actions.get(idx),
+                        useDelayedOnClickListener,
+                        getContext()));
+
+        mView.addPreInflatedButtons(buttons.collect(Collectors.toList()));
     }
 
     private void setSmartRepliesAndActions(CharSequence[] choices, String[] actionTitles) {
@@ -520,16 +551,25 @@
             CharSequence[] choices, String[] actionTitles, boolean fromAssistant,
             boolean useDelayedOnClickListener) {
         mView.resetSmartSuggestions(mContainer);
-        List<Button> smartSuggestions = inflateSmartReplies(choices, fromAssistant,
-                useDelayedOnClickListener);
-        smartSuggestions.addAll(mView.inflateSmartActions(
-                getContext(),
-                new SmartReplyView.SmartActions(createActions(actionTitles), fromAssistant),
-                mLogger,
-                mEntry,
-                mHeadsUpManager,
-                useDelayedOnClickListener));
-        mView.addPreInflatedButtons(smartSuggestions);
+        Sequence<Button> inflatedReplies = SequencesKt.asSequence(
+                inflateSmartReplies(choices, fromAssistant, useDelayedOnClickListener)
+                        .iterator());
+        SmartReplyView.SmartActions smartActions = new SmartReplyView.SmartActions(
+                createActions(actionTitles), fromAssistant);
+        Sequence<Button> inflatedSmartActions = SequencesKt.asSequence(
+                IntStream.range(0, smartActions.actions.size())
+                        .mapToObj(idx -> mSmartActionInflater.inflateActionButton(
+                                mView,
+                                mEntry,
+                                smartActions,
+                                idx,
+                                smartActions.actions.get(idx),
+                                useDelayedOnClickListener,
+                                getContext()))
+                        .iterator());
+        mView.addPreInflatedButtons(
+                SequencesKt.toList(SequencesKt.plus(inflatedReplies, inflatedSmartActions)));
+        mView.setSmartRepliesGeneratedByAssistant(fromAssistant);
     }
 
     private ViewGroup buildExpectedView(CharSequence[] choices, int lineCount) {
@@ -564,10 +604,18 @@
         Button previous = null;
         SmartReplyView.SmartReplies smartReplies =
                 new SmartReplyView.SmartReplies(Arrays.asList(choices), null, null, false);
-        for (int i = 0; i < choices.length; ++i) {
-            Button current = SmartReplyView.inflateReplyButton(mView, mContext, i, smartReplies,
-                    null /* SmartReplyController */, null /* NotificationEntry */,
-                    true /* useDelayedOnClickListener */);
+
+        Iterable<Button> inflatedReplies = SequencesKt.asIterable(SequencesKt.asSequence(
+                IntStream.range(0, smartReplies.choices.size()).mapToObj(
+                        idx -> mSmartReplyInflater.inflateReplyButton(
+                                mView,
+                                mEntry,
+                                smartReplies,
+                                idx,
+                                smartReplies.choices.get(idx),
+                                true /* delayOnClickListener */))
+                        .iterator()));
+        for (Button current : inflatedReplies) {
             current.setPadding(paddingHorizontal, current.getPaddingTop(), paddingHorizontal,
                     current.getPaddingBottom());
             if (previous != null) {
@@ -583,9 +631,21 @@
             previous = current;
         }
 
+        SmartReplyView.SmartActions smartActions = new SmartReplyView.SmartActions(actions, false);
+        Iterable<Button> inflatedSmartActions = SequencesKt.asIterable(SequencesKt.asSequence(
+                IntStream.range(0, smartActions.actions.size())
+                        .mapToObj(idx -> mSmartActionInflater.inflateActionButton(
+                                mView,
+                                mEntry,
+                                smartActions,
+                                idx,
+                                smartActions.actions.get(idx),
+                                true /* delayOnClickListener */,
+                                getContext()))
+                        .iterator()));
+
         // Add smart actions
-        for (int i = 0; i < actions.size(); ++i) {
-            Button current = inflateActionButton(actions.get(i));
+        for (Button current : inflatedSmartActions) {
             current.setPadding(paddingHorizontal, current.getPaddingTop(), paddingHorizontal,
                     current.getPaddingBottom());
             if (previous != null) {
@@ -672,8 +732,8 @@
         Thread.sleep(delayMs);
         mView.getChildAt(2).performClick();
 
-        verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any(),
-                any());
+        verify(mActivityStarter, times(1))
+                .startPendingIntentDismissingKeyguard(any(), any(), any());
     }
 
     @Test
@@ -684,8 +744,8 @@
 
         mView.getChildAt(2).performClick();
 
-        verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any(),
-                any());
+        verify(mActivityStarter, times(1))
+                .startPendingIntentDismissingKeyguard(any(), any(), any());
     }
 
     @Test
@@ -869,18 +929,26 @@
         assertReplyButtonHidden(mView.getChildAt(2));
     }
 
-    private Button inflateActionButton(Notification.Action action) {
-        return SmartReplyView.inflateActionButton(mView, getContext(), getContext(), 0,
-                new SmartReplyView.SmartActions(Collections.singletonList(action), false),
-                mLogger, mEntry, mHeadsUpManager, true /* useDelayedOnClickListener */);
-    }
-
     @Test
     public void testInflateActionButton_smartActionIconSingleLineSizeForTwoLineButton() {
         // Ensure smart action icons are the same size regardless of the number of text rows in the
         // button.
-        Button singleLineButton = inflateActionButton(createAction("One line"));
-        Button doubleLineButton = inflateActionButton(createAction("Two\nlines"));
+        List<Notification.Action> actions = Stream.of("One line", "Two\nlines")
+                .map(this::createAction)
+                .collect(Collectors.toList());
+        SmartReplyView.SmartActions smartActions = new SmartReplyView.SmartActions(actions, false);
+        List<Button> buttons = IntStream.range(0, smartActions.actions.size())
+                .mapToObj(idx -> mSmartActionInflater.inflateActionButton(
+                        mView,
+                        mEntry,
+                        smartActions,
+                        idx,
+                        smartActions.actions.get(idx),
+                        true /* delayOnClickListener */,
+                        getContext()))
+                .collect(Collectors.toList());
+        Button singleLineButton = buttons.get(0);
+        Button doubleLineButton = buttons.get(1);
         Drawable singleLineDrawable = singleLineButton.getCompoundDrawables()[0]; // left drawable
         Drawable doubleLineDrawable = doubleLineButton.getCompoundDrawables()[0]; // left drawable
         assertEquals(singleLineDrawable.getBounds().width(),
@@ -1068,7 +1136,7 @@
 
     @Test
     public void testMeasure_minNumSystemGeneratedSmartReplies_notEnoughReplies() {
-        when(mConstants.getMinNumSystemGeneratedReplies()).thenReturn(3);
+        mView.setMinNumSystemGeneratedReplies(3);
 
         // Add 2 replies when the minimum is 3 -> we should end up with 0 replies.
         String[] choices = new String[] {"reply1", "reply2"};
@@ -1082,7 +1150,9 @@
                 choices, actions, true /* fromAssistant */, true /* useDelayedOnClickListener */);
         mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
 
+        // 395, 168
         assertEqualMeasures(expectedView, mView);
+
         // smart replies
         assertReplyButtonHidden(mView.getChildAt(0));
         assertReplyButtonHidden(mView.getChildAt(1));
@@ -1121,7 +1191,7 @@
      */
     @Test
     public void testMeasure_minNumSystemGeneratedSmartReplies_unSqueezeActions() {
-        when(mConstants.getMinNumSystemGeneratedReplies()).thenReturn(2);
+        mView.setMinNumSystemGeneratedReplies(2);
 
         // Add 2 replies when the minimum is 3 -> we should end up with 0 replies.
         String[] choices = new String[] {"This is a very long two-line reply."};
diff --git a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
index 0a10ab2..c743fd0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
@@ -54,7 +54,11 @@
 import com.android.internal.R;
 import com.android.internal.util.IntPair;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -84,6 +88,7 @@
     private static final String TEXT = "Hello World";
     private static final int MESSAGE_RES_ID = R.id.message;
 
+    private FakeExecutor mFakeDelayableExecutor = new FakeExecutor(new FakeSystemClock());
     private Context mContextSpy;
     private ToastUI mToastUI;
     @Mock private LayoutInflater mLayoutInflater;
@@ -91,6 +96,10 @@
     @Mock private WindowManager mWindowManager;
     @Mock private INotificationManager mNotificationManager;
     @Mock private IAccessibilityManager mAccessibilityManager;
+    @Mock private PluginManager mPluginManager;
+    @Mock private DumpManager mDumpManager;
+    @Mock private ToastLogger mToastLogger;
+
     @Mock private ITransientNotificationCallback mCallback;
     @Captor private ArgumentCaptor<View> mViewCaptor;
     @Captor private ArgumentCaptor<ViewGroup.LayoutParams> mParamsCaptor;
@@ -109,8 +118,10 @@
         mContextSpy = spy(mContext);
         doReturn(mContextSpy).when(mContextSpy).createContextAsUser(any(), anyInt());
 
+        doReturn(mContextSpy).when(mContextSpy).createContextAsUser(any(), anyInt());
         mToastUI = new ToastUI(mContextSpy, mCommandQueue, mNotificationManager,
-                mAccessibilityManager);
+                mAccessibilityManager, new ToastFactory(mPluginManager, mDumpManager),
+                mFakeDelayableExecutor, mToastLogger);
     }
 
     @Test
@@ -271,6 +282,29 @@
         verify(mCallback).onToastHidden();
     }
 
+    @Test
+    public void testShowToast_logs() {
+        mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG,
+                mCallback);
+
+        verify(mToastLogger).logOnShowToast(UID_1, PACKAGE_NAME_1, TEXT, TOKEN_1.toString());
+    }
+
+    @Test
+    public void testHideToast_logs() {
+        mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG,
+                mCallback);
+        mToastUI.hideToast(PACKAGE_NAME_1, TOKEN_1);
+        verify(mToastLogger).logOnHideToast(PACKAGE_NAME_1, TOKEN_1.toString());
+    }
+
+    @Test
+    public void testHideToast_error_noLog() {
+        // no toast was shown, so this hide is invalid
+        mToastUI.hideToast(PACKAGE_NAME_1, TOKEN_1);
+        verify(mToastLogger, never()).logOnHideToast(PACKAGE_NAME_1, TOKEN_1.toString());
+    }
+
     private View verifyWmAddViewAndAttachToParent() {
         ArgumentCaptor<View> viewCaptor = ArgumentCaptor.forClass(View.class);
         verify(mWindowManager).addView(viewCaptor.capture(), any());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeTunerService.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeTunerService.java
index 8db82e2..97d4aa7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeTunerService.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeTunerService.java
@@ -14,7 +14,7 @@
 
 package com.android.systemui.utils.leaks;
 
-import android.content.Context;
+import android.os.UserHandle;
 import android.testing.LeakCheck;
 
 import com.android.systemui.tuner.TunerService;
@@ -22,8 +22,10 @@
 public class FakeTunerService extends TunerService {
 
     private final BaseLeakChecker<Tunable> mBaseLeakChecker;
+    private boolean mEnabled;
 
     public FakeTunerService(LeakCheck test) {
+        super(null);
         mBaseLeakChecker = new BaseLeakChecker<>(test, "tunable");
     }
 
@@ -74,4 +76,14 @@
     public void setValue(String setting, int value) {
 
     }
+
+    @Override
+    public void setTunerEnabled(UserHandle user, boolean enabled) {
+        mEnabled = enabled;
+    }
+
+    @Override
+    public boolean isTunerEnabled(UserHandle user) {
+        return mEnabled;
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
index a5fbf19..dd7f263 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -16,29 +16,25 @@
 
 package com.android.systemui.wmshell;
 
-import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
-
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.pm.PackageManager;
 import android.test.suitebuilder.annotation.SmallTest;
-import android.testing.TestableContext;
 
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.SystemUIFactory;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.navigationbar.NavigationModeController;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.InputConsumerController;
 import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.tracing.ProtoTracer;
@@ -58,6 +54,7 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.Optional;
+import java.util.concurrent.ExecutionException;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -68,8 +65,7 @@
     @Mock CommandQueue mCommandQueue;
     @Mock ConfigurationController mConfigurationController;
     @Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-    @Mock ActivityManagerWrapper mActivityManagerWrapper;
-    @Mock DisplayImeController mDisplayImeController;
+    @Mock TaskStackChangeListeners mTaskStackChangeListeners;
     @Mock InputConsumerController mMockInputConsumerController;
     @Mock NavigationModeController mNavigationModeController;
     @Mock ScreenLifecycle mScreenLifecycle;
@@ -88,20 +84,11 @@
         mInputConsumerController = InputConsumerController.getPipInputConsumer();
 
         mWMShell = new WMShell(mContext, mCommandQueue, mConfigurationController,
-                mInputConsumerController, mKeyguardUpdateMonitor, mActivityManagerWrapper,
-                mDisplayImeController, mNavigationModeController, mScreenLifecycle, mSysUiState,
-                Optional.of(mPip), Optional.of(mSplitScreen), Optional.of(mOneHanded),
-                mTaskOrganizer, mProtoTracer);
+                mInputConsumerController, mKeyguardUpdateMonitor, mTaskStackChangeListeners,
+                mNavigationModeController, mScreenLifecycle, mSysUiState, Optional.of(mPip),
+                Optional.of(mSplitScreen), Optional.of(mOneHanded), mTaskOrganizer, mProtoTracer);
 
         when(mPip.getPipTouchHandler()).thenReturn(mPipTouchHandler);
-
-    }
-
-    @Test
-    public void start_startsMonitorDisplays() {
-        mWMShell.start();
-
-        verify(mDisplayImeController).startMonitorDisplays();
     }
 
     @Test
@@ -112,37 +99,16 @@
     }
 
     @Test
-    public void nonPipDevice_shouldNotInitPip() {
-        final TestableContext nonPipContext = getNonPipFeatureContext();
-        final WMShell nonPipWMShell = new WMShell(nonPipContext, mCommandQueue,
-                mConfigurationController, mMockInputConsumerController, mKeyguardUpdateMonitor,
-                mActivityManagerWrapper, mDisplayImeController, mNavigationModeController,
-                mScreenLifecycle, mSysUiState, Optional.of(mPip), Optional.of(mSplitScreen),
-                Optional.of(mOneHanded), mTaskOrganizer, mProtoTracer);
-        nonPipWMShell.initPip(mPip);
-
-        verify(mCommandQueue, never()).addCallback(any());
-        verify(mKeyguardUpdateMonitor, never()).registerCallback(any());
-        verify(mConfigurationController, never()).addCallback(any());
-        verify(mSysUiState, never()).addCallback(any());
-        verify(mActivityManagerWrapper, never()).registerTaskStackListener(any());
-        verify(mMockInputConsumerController, never()).setInputListener(any());
-        verify(mMockInputConsumerController, never()).setRegistrationListener(any());
-        verify(mPip, never()).registerSessionListenerForCurrentUser();
-    }
-
-    @Test
     public void initSplitScreen_registersCallbacks() {
         mWMShell.initSplitScreen(mSplitScreen);
 
         verify(mKeyguardUpdateMonitor).registerCallback(any(KeyguardUpdateMonitorCallback.class));
-        verify(mActivityManagerWrapper).registerTaskStackListener(
+        verify(mTaskStackChangeListeners).registerTaskStackListener(
                 any(TaskStackChangeListener.class));
     }
 
     @Test
     public void initOneHanded_registersCallbacks() {
-        when(mOneHanded.hasOneHandedFeature()).thenReturn(true);
         mWMShell.initOneHanded(mOneHanded);
 
         verify(mKeyguardUpdateMonitor).registerCallback(any(KeyguardUpdateMonitorCallback.class));
@@ -150,18 +116,11 @@
         verify(mScreenLifecycle).addObserver(any(ScreenLifecycle.Observer.class));
         verify(mNavigationModeController).addListener(
                 any(NavigationModeController.ModeChangedListener.class));
-        verify(mActivityManagerWrapper).registerTaskStackListener(
+        verify(mTaskStackChangeListeners).registerTaskStackListener(
                 any(TaskStackChangeListener.class));
 
         verify(mOneHanded).registerGestureCallback(any(
                 OneHandedGestureHandler.OneHandedGestureEventCallback.class));
         verify(mOneHanded).registerTransitionCallback(any(OneHandedTransitionCallback.class));
     }
-
-    TestableContext getNonPipFeatureContext() {
-        TestableContext spiedContext = spy(mContext);
-        when(mMockPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)).thenReturn(false);
-        when(spiedContext.getPackageManager()).thenReturn(mMockPackageManager);
-        return spiedContext;
-    }
 }
diff --git a/packages/Tethering/apex/manifest.json b/packages/Tethering/apex/manifest.json
index 538ffb3..11e205d 100644
--- a/packages/Tethering/apex/manifest.json
+++ b/packages/Tethering/apex/manifest.json
@@ -1,4 +1,4 @@
 {
   "name": "com.android.tethering",
-  "version": 300000000
+  "version": 309999900
 }
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
index 645b000..0b223f4 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -34,7 +33,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class TetheredClient implements Parcelable {
     @NonNull
     private final MacAddress mMacAddress;
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
index db84368..13b05a8 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
@@ -23,7 +23,6 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.content.Context;
 import android.os.Bundle;
 import android.os.ConditionVariable;
@@ -55,7 +54,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public class TetheringManager {
     private static final String TAG = TetheringManager.class.getSimpleName();
     private static final int DEFAULT_TIMEOUT_MS = 60_000;
diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java
index 336124d..52d59fc 100644
--- a/packages/Tethering/src/android/net/ip/IpServer.java
+++ b/packages/Tethering/src/android/net/ip/IpServer.java
@@ -616,7 +616,7 @@
         if (VDBG) Log.d(TAG, "configureIPv4(" + enabled + ")");
 
         if (enabled) {
-            mIpv4Address = requestIpv4Address();
+            mIpv4Address = requestIpv4Address(true /* useLastAddress */);
         }
 
         if (mIpv4Address == null) {
@@ -661,14 +661,14 @@
         return configureDhcp(enabled, mIpv4Address, mStaticIpv4ClientAddr);
     }
 
-    private LinkAddress requestIpv4Address() {
+    private LinkAddress requestIpv4Address(final boolean useLastAddress) {
         if (mStaticIpv4ServerAddr != null) return mStaticIpv4ServerAddr;
 
         if (mInterfaceType == TetheringManager.TETHERING_BLUETOOTH) {
             return new LinkAddress(BLUETOOTH_IFACE_ADDR);
         }
 
-        return mPrivateAddressCoordinator.requestDownstreamAddress(this);
+        return mPrivateAddressCoordinator.requestDownstreamAddress(this, useLastAddress);
     }
 
     private boolean startIPv6() {
@@ -957,7 +957,7 @@
         }
 
         final LinkAddress deprecatedLinkAddress = mIpv4Address;
-        mIpv4Address = requestIpv4Address();
+        mIpv4Address = requestIpv4Address(false);
         if (mIpv4Address == null) {
             mLog.e("Fail to request a new downstream prefix");
             return;
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java b/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
index b285849..4f616cd 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
@@ -16,7 +16,13 @@
 package com.android.networkstack.tethering;
 
 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
+import static android.net.TetheringManager.TETHERING_BLUETOOTH;
 import static android.net.TetheringManager.TETHERING_WIFI_P2P;
+import static android.net.util.PrefixUtils.asIpPrefix;
+
+import static com.android.net.module.util.Inet4AddressUtils.inet4AddressToIntHTH;
+import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH;
+import static com.android.net.module.util.Inet4AddressUtils.prefixLengthToV4NetmaskIntHTH;
 
 import static java.util.Arrays.asList;
 
@@ -26,18 +32,20 @@
 import android.net.LinkAddress;
 import android.net.Network;
 import android.net.ip.IpServer;
-import android.net.util.PrefixUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.SparseArray;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
 
+import java.net.Inet4Address;
 import java.net.InetAddress;
-import java.net.UnknownHostException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Random;
@@ -56,13 +64,6 @@
 public class PrivateAddressCoordinator {
     public static final int PREFIX_LENGTH = 24;
 
-    private static final int MAX_UBYTE = 256;
-    private static final int BYTE_MASK = 0xff;
-    // reserved for bluetooth tethering.
-    private static final int BLUETOOTH_RESERVED = 44;
-    private static final int WIFI_P2P_RESERVED = 49;
-    private static final byte DEFAULT_ID = (byte) 42;
-
     // Upstream monitor would be stopped when tethering is down. When tethering restart, downstream
     // address may be requested before coordinator get current upstream notification. To ensure
     // coordinator do not select conflict downstream prefix, mUpstreamPrefixMap would not be cleared
@@ -70,22 +71,30 @@
     // mUpstreamPrefixMap when tethering is starting. See #maybeRemoveDeprecatedUpstreams().
     private final ArrayMap<Network, List<IpPrefix>> mUpstreamPrefixMap;
     private final ArraySet<IpServer> mDownstreams;
-    // IANA has reserved the following three blocks of the IP address space for private intranets:
-    // 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
-    // Tethering use 192.168.0.0/16 that has 256 contiguous class C network numbers.
-    private static final String DEFAULT_TETHERING_PREFIX = "192.168.0.0/16";
     private static final String LEGACY_WIFI_P2P_IFACE_ADDRESS = "192.168.49.1/24";
-    private final IpPrefix mTetheringPrefix;
+    private static final String LEGACY_BLUETOOTH_IFACE_ADDRESS = "192.168.44.1/24";
+    private final List<IpPrefix> mTetheringPrefixes;
     private final ConnectivityManager mConnectivityMgr;
     private final TetheringConfiguration mConfig;
+    // keyed by downstream type(TetheringManager.TETHERING_*).
+    private final SparseArray<LinkAddress> mCachedAddresses;
 
     public PrivateAddressCoordinator(Context context, TetheringConfiguration config) {
         mDownstreams = new ArraySet<>();
         mUpstreamPrefixMap = new ArrayMap<>();
-        mTetheringPrefix = new IpPrefix(DEFAULT_TETHERING_PREFIX);
         mConnectivityMgr = (ConnectivityManager) context.getSystemService(
                 Context.CONNECTIVITY_SERVICE);
         mConfig = config;
+        mCachedAddresses = new SparseArray<>();
+        // Reserved static addresses for bluetooth and wifi p2p.
+        mCachedAddresses.put(TETHERING_BLUETOOTH, new LinkAddress(LEGACY_BLUETOOTH_IFACE_ADDRESS));
+        mCachedAddresses.put(TETHERING_WIFI_P2P, new LinkAddress(LEGACY_WIFI_P2P_IFACE_ADDRESS));
+
+        mTetheringPrefixes = new ArrayList<>(Arrays.asList(new IpPrefix("192.168.0.0/16")));
+        if (config.isSelectAllPrefixRangeEnabled()) {
+            mTetheringPrefixes.add(new IpPrefix("172.16.0.0/12"));
+            mTetheringPrefixes.add(new IpPrefix("10.0.0.0/8"));
+        }
     }
 
     /**
@@ -94,7 +103,8 @@
      * UpstreamNetworkState must have an already populated LinkProperties.
      */
     public void updateUpstreamPrefix(final UpstreamNetworkState ns) {
-        // Do not support VPN as upstream
+        // Do not support VPN as upstream. Normally, networkCapabilities is not expected to be null,
+        // but just checking to be sure.
         if (ns.networkCapabilities != null && ns.networkCapabilities.hasTransport(TRANSPORT_VPN)) {
             removeUpstreamPrefix(ns.network);
             return;
@@ -116,7 +126,7 @@
         for (LinkAddress address : linkAddresses) {
             if (!address.isIpv4()) continue;
 
-            list.add(PrefixUtils.asIpPrefix(address));
+            list.add(asIpPrefix(address));
         }
 
         return list;
@@ -125,7 +135,6 @@
     private void handleMaybePrefixConflict(final List<IpPrefix> prefixes) {
         for (IpServer downstream : mDownstreams) {
             final IpPrefix target = getDownstreamPrefix(downstream);
-            if (target == null) continue;
 
             for (IpPrefix source : prefixes) {
                 if (isConflictPrefix(source, target)) {
@@ -155,65 +164,166 @@
         mUpstreamPrefixMap.removeAll(toBeRemoved);
     }
 
-    private boolean isReservedSubnet(final int subnet) {
-        return subnet == BLUETOOTH_RESERVED || subnet == WIFI_P2P_RESERVED;
-    }
-
     /**
      * Pick a random available address and mark its prefix as in use for the provided IpServer,
      * returns null if there is no available address.
      */
     @Nullable
-    public LinkAddress requestDownstreamAddress(final IpServer ipServer) {
+    public LinkAddress requestDownstreamAddress(final IpServer ipServer, boolean useLastAddress) {
         if (mConfig.shouldEnableWifiP2pDedicatedIp()
                 && ipServer.interfaceType() == TETHERING_WIFI_P2P) {
             return new LinkAddress(LEGACY_WIFI_P2P_IFACE_ADDRESS);
         }
 
-        // Address would be 192.168.[subAddress]/24.
-        final byte[] bytes = mTetheringPrefix.getRawAddress();
-        final int subAddress = getRandomSubAddr();
-        final int subNet = (subAddress >> 8) & BYTE_MASK;
-        bytes[3] = getSanitizedAddressSuffix(subAddress, (byte) 0, (byte) 1, (byte) 0xff);
-        for (int i = 0; i < MAX_UBYTE; i++) {
-            final int newSubNet = (subNet + i) & BYTE_MASK;
-            if (isReservedSubnet(newSubNet)) continue;
-
-            bytes[2] = (byte) newSubNet;
-            final InetAddress addr;
-            try {
-                addr = InetAddress.getByAddress(bytes);
-            } catch (UnknownHostException e) {
-                throw new IllegalStateException("Invalid address, shouldn't happen.", e);
-            }
-
-            final IpPrefix prefix = new IpPrefix(addr, PREFIX_LENGTH);
-            // Check whether this prefix is in use.
-            if (isDownstreamPrefixInUse(prefix)) continue;
-            // Check whether this prefix is conflict with any current upstream network.
-            if (isConflictWithUpstream(prefix)) continue;
-
+        final LinkAddress cachedAddress = mCachedAddresses.get(ipServer.interfaceType());
+        if (useLastAddress && cachedAddress != null
+                && !isConflictWithUpstream(asIpPrefix(cachedAddress))) {
             mDownstreams.add(ipServer);
-            return new LinkAddress(addr, PREFIX_LENGTH);
+            return cachedAddress;
+        }
+
+        for (IpPrefix prefixRange : mTetheringPrefixes) {
+            final LinkAddress newAddress = chooseDownstreamAddress(prefixRange);
+            if (newAddress != null) {
+                mDownstreams.add(ipServer);
+                mCachedAddresses.put(ipServer.interfaceType(), newAddress);
+                return newAddress;
+            }
         }
 
         // No available address.
         return null;
     }
 
-    /** Get random sub address value. Return value is in 0 ~ 0xffff. */
-    @VisibleForTesting
-    public int getRandomSubAddr() {
-        return ((new Random()).nextInt()) & 0xffff; // subNet is in 0 ~ 0xffff.
+    private int getPrefixBaseAddress(final IpPrefix prefix) {
+        return inet4AddressToIntHTH((Inet4Address) prefix.getAddress());
     }
 
-    private byte getSanitizedAddressSuffix(final int source, byte... excluded) {
-        final byte subId = (byte) (source & BYTE_MASK);
-        for (byte value : excluded) {
-            if (subId == value) return DEFAULT_ID;
+    /**
+     * Check whether input prefix conflict with upstream prefixes or in-use downstream prefixes.
+     * If yes, return one of them.
+     */
+    private IpPrefix getConflictPrefix(final IpPrefix prefix) {
+        final IpPrefix upstream = getConflictWithUpstream(prefix);
+        if (upstream != null) return upstream;
+
+        return getInUseDownstreamPrefix(prefix);
+    }
+
+    // Get the next non-conflict sub prefix. E.g: To get next sub prefix from 10.0.0.0/8, if the
+    // previously selected prefix is 10.20.42.0/24(subPrefix: 0.20.42.0) and the conflicting prefix
+    // is 10.16.0.0/20 (10.16.0.0 ~ 10.16.15.255), then the max address under subPrefix is
+    // 0.16.15.255 and the next subPrefix is 0.16.16.255/24 (0.16.15.255 + 0.0.1.0).
+    // Note: the sub address 0.0.0.255 here is fine to be any value that it will be replaced as
+    // selected random sub address later.
+    private int getNextSubPrefix(final IpPrefix conflictPrefix, final int prefixRangeMask) {
+        final int suffixMask = ~prefixLengthToV4NetmaskIntHTH(conflictPrefix.getPrefixLength());
+        // The largest offset within the prefix assignment block that still conflicts with
+        // conflictPrefix.
+        final int maxConflict =
+                (getPrefixBaseAddress(conflictPrefix) | suffixMask) & ~prefixRangeMask;
+
+        final int prefixMask = prefixLengthToV4NetmaskIntHTH(PREFIX_LENGTH);
+        // Pick a sub prefix a full prefix (1 << (32 - PREFIX_LENGTH) addresses) greater than
+        // maxConflict. This ensures that the selected prefix never overlaps with conflictPrefix.
+        // There is no need to mask the result with PREFIX_LENGTH bits because this is done by
+        // findAvailablePrefixFromRange when it constructs the prefix.
+        return maxConflict + (1 << (32 - PREFIX_LENGTH));
+    }
+
+    private LinkAddress chooseDownstreamAddress(final IpPrefix prefixRange) {
+        // The netmask of the prefix assignment block (e.g., 0xfff00000 for 172.16.0.0/12).
+        final int prefixRangeMask = prefixLengthToV4NetmaskIntHTH(prefixRange.getPrefixLength());
+
+        // The zero address in the block (e.g., 0xac100000 for 172.16.0.0/12).
+        final int baseAddress = getPrefixBaseAddress(prefixRange);
+
+        // The subnet mask corresponding to PREFIX_LENGTH.
+        final int prefixMask = prefixLengthToV4NetmaskIntHTH(PREFIX_LENGTH);
+
+        // The offset within prefixRange of a randomly-selected prefix of length PREFIX_LENGTH.
+        // This may not be the prefix of the address returned by this method:
+        // - If it is already in use, the method will return an address in another prefix.
+        // - If all prefixes within prefixRange are in use, the method will return null. For
+        // example, for a /24 prefix within 172.26.0.0/12, this will be a multiple of 256 in
+        // [0, 1048576). In other words, a random 32-bit number with mask 0x000fff00.
+        //
+        // prefixRangeMask is required to ensure no wrapping. For example, consider:
+        // - prefixRange 127.0.0.0/8
+        // - randomPrefixStart 127.255.255.0
+        // - A conflicting prefix of 127.255.254.0/23
+        // In this case without prefixRangeMask, getNextSubPrefix would return 128.0.0.0, which
+        // means the "start < end" check in findAvailablePrefixFromRange would not reject the prefix
+        // because Java doesn't have unsigned integers, so 128.0.0.0 = 0x80000000 = -2147483648
+        // is less than 127.0.0.0 = 0x7f000000 = 2130706432.
+        //
+        // Additionally, it makes debug output easier to read by making the numbers smaller.
+        final int randomPrefixStart = getRandomInt() & ~prefixRangeMask & prefixMask;
+
+        // A random offset within the prefix. Used to determine the local address once the prefix
+        // is selected. It does not result in an IPv4 address ending in .0, .1, or .255
+        // For a PREFIX_LENGTH of 255, this is a number between 2 and 254.
+        final int subAddress = getSanitizedSubAddr(~prefixMask);
+
+        // Find a prefix length PREFIX_LENGTH between randomPrefixStart and the end of the block,
+        // such that the prefix does not conflict with any upstream.
+        IpPrefix downstreamPrefix = findAvailablePrefixFromRange(
+                 randomPrefixStart, (~prefixRangeMask) + 1, baseAddress, prefixRangeMask);
+        if (downstreamPrefix != null) return getLinkAddress(downstreamPrefix, subAddress);
+
+        // If that failed, do the same, but between 0 and randomPrefixStart.
+        downstreamPrefix = findAvailablePrefixFromRange(
+                0, randomPrefixStart, baseAddress, prefixRangeMask);
+
+        return getLinkAddress(downstreamPrefix, subAddress);
+    }
+
+    private LinkAddress getLinkAddress(final IpPrefix prefix, final int subAddress) {
+        if (prefix == null) return null;
+
+        final InetAddress address = intToInet4AddressHTH(getPrefixBaseAddress(prefix) | subAddress);
+        return new LinkAddress(address, PREFIX_LENGTH);
+    }
+
+    private IpPrefix findAvailablePrefixFromRange(final int start, final int end,
+            final int baseAddress, final int prefixRangeMask) {
+        int newSubPrefix = start;
+        while (newSubPrefix < end) {
+            final InetAddress address = intToInet4AddressHTH(baseAddress | newSubPrefix);
+            final IpPrefix prefix = new IpPrefix(address, PREFIX_LENGTH);
+
+            final IpPrefix conflictPrefix = getConflictPrefix(prefix);
+
+            if (conflictPrefix == null) return prefix;
+
+            newSubPrefix = getNextSubPrefix(conflictPrefix, prefixRangeMask);
         }
 
-        return subId;
+        return null;
+    }
+
+    /** Get random int which could be used to generate random address. */
+    @VisibleForTesting
+    public int getRandomInt() {
+        return (new Random()).nextInt();
+    }
+
+    /** Get random subAddress and avoid selecting x.x.x.0, x.x.x.1 and x.x.x.255 address. */
+    private int getSanitizedSubAddr(final int subAddrMask) {
+        final int randomSubAddr = getRandomInt() & subAddrMask;
+        // If prefix length > 30, the selecting speace would be less than 4 which may be hard to
+        // avoid 3 consecutive address.
+        if (PREFIX_LENGTH > 30) return randomSubAddr;
+
+        // TODO: maybe it is not necessary to avoid .0, .1 and .255 address because tethering
+        // address would not be conflicted. This code only works because PREFIX_LENGTH is not longer
+        // than 24
+        final int candidate = randomSubAddr & 0xff;
+        if (candidate == 0 || candidate == 1 || candidate == 255) {
+            return (randomSubAddr & 0xfffffffc) + 2;
+        }
+
+        return randomSubAddr;
     }
 
     /** Release downstream record for IpServer. */
@@ -226,14 +336,18 @@
         mUpstreamPrefixMap.clear();
     }
 
-    private boolean isConflictWithUpstream(final IpPrefix source) {
+    private IpPrefix getConflictWithUpstream(final IpPrefix prefix) {
         for (int i = 0; i < mUpstreamPrefixMap.size(); i++) {
             final List<IpPrefix> list = mUpstreamPrefixMap.valueAt(i);
-            for (IpPrefix target : list) {
-                if (isConflictPrefix(source, target)) return true;
+            for (IpPrefix upstream : list) {
+                if (isConflictPrefix(prefix, upstream)) return upstream;
             }
         }
-        return false;
+        return null;
+    }
+
+    private boolean isConflictWithUpstream(final IpPrefix prefix) {
+        return getConflictWithUpstream(prefix) != null;
     }
 
     private boolean isConflictPrefix(final IpPrefix prefix1, final IpPrefix prefix2) {
@@ -244,35 +358,59 @@
         return prefix1.contains(prefix2.getAddress());
     }
 
-    private boolean isDownstreamPrefixInUse(final IpPrefix source) {
-        // This class always generates downstream prefixes with the same prefix length, so
-        // prefixes cannot be contained in each other. They can only be equal to each other.
-        for (IpServer downstream : mDownstreams) {
-            final IpPrefix prefix = getDownstreamPrefix(downstream);
-            if (source.equals(prefix)) return true;
+    // InUse Prefixes are prefixes of mCachedAddresses which are active downstream addresses, last
+    // downstream addresses(reserved for next time) and static addresses(e.g. bluetooth, wifi p2p).
+    private IpPrefix getInUseDownstreamPrefix(final IpPrefix prefix) {
+        for (int i = 0; i < mCachedAddresses.size(); i++) {
+            final IpPrefix downstream = asIpPrefix(mCachedAddresses.valueAt(i));
+            if (isConflictPrefix(prefix, downstream)) return downstream;
         }
-        return false;
+
+        // IpServer may use manually-defined address (mStaticIpv4ServerAddr) which does not include
+        // in mCachedAddresses.
+        for (IpServer downstream : mDownstreams) {
+            final IpPrefix target = getDownstreamPrefix(downstream);
+
+            if (isConflictPrefix(prefix, target)) return target;
+        }
+
+        return null;
     }
 
+    @NonNull
     private IpPrefix getDownstreamPrefix(final IpServer downstream) {
         final LinkAddress address = downstream.getAddress();
-        if (address == null) return null;
 
-        return PrefixUtils.asIpPrefix(address);
+        return asIpPrefix(address);
     }
 
     void dump(final IndentingPrintWriter pw) {
+        pw.println("mTetheringPrefixes:");
+        pw.increaseIndent();
+        for (IpPrefix prefix : mTetheringPrefixes) {
+            pw.println(prefix);
+        }
+        pw.decreaseIndent();
+
         pw.println("mUpstreamPrefixMap:");
         pw.increaseIndent();
         for (int i = 0; i < mUpstreamPrefixMap.size(); i++) {
             pw.println(mUpstreamPrefixMap.keyAt(i) + " - " + mUpstreamPrefixMap.valueAt(i));
         }
         pw.decreaseIndent();
+
         pw.println("mDownstreams:");
         pw.increaseIndent();
         for (IpServer ipServer : mDownstreams) {
             pw.println(ipServer.interfaceType() + " - " + ipServer.getAddress());
         }
         pw.decreaseIndent();
+
+        pw.println("mCachedAddresses:");
+        pw.increaseIndent();
+        for (int i = 0; i < mCachedAddresses.size(); i++) {
+            pw.println(mCachedAddresses.keyAt(i) + " - " + mCachedAddresses.valueAt(i));
+        }
+        pw.decreaseIndent();
     }
 }
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 474f4e8..5a0c5b0 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -326,7 +326,7 @@
         // It is OK for the configuration to be passed to the PrivateAddressCoordinator at
         // construction time because the only part of the configuration it uses is
         // shouldEnableWifiP2pDedicatedIp(), and currently do not support changing that.
-        mPrivateAddressCoordinator = new PrivateAddressCoordinator(mContext, mConfig);
+        mPrivateAddressCoordinator = mDeps.getPrivateAddressCoordinator(mContext, mConfig);
 
         // Must be initialized after tethering configuration is loaded because BpfCoordinator
         // constructor needs to use the configuration.
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
index 5783805..799637c 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
@@ -40,7 +40,6 @@
 import java.util.Collection;
 import java.util.StringJoiner;
 
-
 /**
  * A utility class to encapsulate the various tethering configuration elements.
  *
@@ -88,6 +87,13 @@
             "use_legacy_wifi_p2p_dedicated_ip";
 
     /**
+     * Flag use to enable select all prefix ranges feature.
+     * TODO: Remove this flag if there are no problems after M-2020-12 rolls out.
+     */
+    public static final String TETHER_ENABLE_SELECT_ALL_PREFIX_RANGES =
+            "tether_enable_select_all_prefix_ranges";
+
+    /**
      * Default value that used to periodic polls tether offload stats from tethering offload HAL
      * to make the data warnings work.
      */
@@ -118,6 +124,8 @@
     private final boolean mEnableBpfOffload;
     private final boolean mEnableWifiP2pDedicatedIp;
 
+    private final boolean mEnableSelectAllPrefixRange;
+
     public TetheringConfiguration(Context ctx, SharedLog log, int id) {
         final SharedLog configLog = log.forSubComponent("config");
 
@@ -164,6 +172,11 @@
                 R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip,
                 false /* defaultValue */);
 
+        // Flags should normally not be booleans, but this is a kill-switch flag that is only used
+        // to turn off the feature, so binary rollback problems do not apply.
+        mEnableSelectAllPrefixRange = getDeviceConfigBoolean(
+                TETHER_ENABLE_SELECT_ALL_PREFIX_RANGES, true /* defaultValue */);
+
         configLog.log(toString());
     }
 
@@ -249,6 +262,9 @@
 
         pw.print("enableWifiP2pDedicatedIp: ");
         pw.println(mEnableWifiP2pDedicatedIp);
+
+        pw.print("mEnableSelectAllPrefixRange: ");
+        pw.println(mEnableSelectAllPrefixRange);
     }
 
     /** Returns the string representation of this object.*/
@@ -310,6 +326,10 @@
         return mEnableBpfOffload;
     }
 
+    public boolean isSelectAllPrefixRangeEnabled() {
+        return mEnableSelectAllPrefixRange;
+    }
+
     private static Collection<Integer> getUpstreamIfaceTypes(Resources res, boolean dunRequired) {
         final int[] ifaceTypes = res.getIntArray(R.array.config_tether_upstream_types);
         final ArrayList<Integer> upstreamIfaceTypes = new ArrayList<>(ifaceTypes.length);
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
index 131a5fb..45b9141 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
@@ -156,4 +156,12 @@
     public boolean isTetheringDenied() {
         return TextUtils.equals(SystemProperties.get("ro.tether.denied"), "true");
     }
+
+    /**
+     * Get a reference to PrivateAddressCoordinator to be used by Tethering.
+     */
+    public PrivateAddressCoordinator getPrivateAddressCoordinator(Context ctx,
+            TetheringConfiguration cfg) {
+        return new PrivateAddressCoordinator(ctx, cfg);
+    }
 }
diff --git a/packages/Tethering/tests/mts/Android.bp b/packages/Tethering/tests/mts/Android.bp
new file mode 100644
index 0000000..f925b0a
--- /dev/null
+++ b/packages/Tethering/tests/mts/Android.bp
@@ -0,0 +1,56 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 {
+    // This tests for functionality that is not required for devices that
+    // don't use Tethering mainline module.
+    name: "MtsTetheringTest",
+
+    libs: [
+        "android.test.base",
+    ],
+
+    srcs: [
+        "src/**/*.java",
+    ],
+
+    static_libs: [
+        "androidx.test.rules",
+        // mockito-target-extended-minus-junit4 used in this lib have dependency with
+        // jni_libs libdexmakerjvmtiagent and libstaticjvmtiagent.
+        "cts-net-utils",
+        // This is needed for androidx.test.runner.AndroidJUnitRunner.
+        "ctstestrunner-axt",
+        "junit",
+        "junit-params",
+    ],
+
+    jni_libs: [
+        // For mockito extended which is pulled in from -net-utils -> net-tests-utils
+        // (mockito-target-extended-minus-junit4).
+        "libdexmakerjvmtiagent",
+        "libstaticjvmtiagent",
+    ],
+
+    platform_apis: true,
+
+    // Tag this module as a mts test artifact
+    test_suites: [
+        "general-tests",
+        "mts",
+    ],
+
+    // Include both the 32 and 64 bit versions
+    compile_multilib: "both",
+}
diff --git a/packages/Tethering/tests/mts/AndroidManifest.xml b/packages/Tethering/tests/mts/AndroidManifest.xml
new file mode 100644
index 0000000..6d2abca
--- /dev/null
+++ b/packages/Tethering/tests/mts/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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="android.tethering.mts">
+
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.INTERNET"/>
+
+    <application android:debuggable="true">
+        <uses-library android:name="android.test.runner" />
+    </application>
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="android.tethering.mts"
+                     android:label="MTS tests of android.tethering">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
+
+</manifest>
diff --git a/packages/Tethering/tests/mts/AndroidTest.xml b/packages/Tethering/tests/mts/AndroidTest.xml
new file mode 100644
index 0000000..80788df
--- /dev/null
+++ b/packages/Tethering/tests/mts/AndroidTest.xml
@@ -0,0 +1,36 @@
+<?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="Config for MTS Tethering test cases">
+    <option name="test-suite-tag" value="mts" />
+    <option name="config-descriptor:metadata" key="component" value="networking" />
+    <!-- Instant app do not have INTERNET permission. -->
+    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+    <!-- Feature is not backed by native code. -->
+    <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+    <!-- Allow running this against a secondary user. -->
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="MtsTetheringTest.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.tethering.mts" />
+    </test>
+
+    <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+        <option name="mainline-module-package-name" value="com.google.android.tethering" />
+    </object>
+</configuration>
diff --git a/packages/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java b/packages/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java
new file mode 100644
index 0000000..7ffe37a
--- /dev/null
+++ b/packages/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.tethering.mts;
+
+import static android.Manifest.permission.MANAGE_TEST_NETWORKS;
+import static android.Manifest.permission.NETWORK_SETTINGS;
+import static android.Manifest.permission.READ_DEVICE_CONFIG;
+import static android.Manifest.permission.TETHER_PRIVILEGED;
+import static android.Manifest.permission.WRITE_SETTINGS;
+import static android.net.cts.util.CtsTetheringUtils.isWifiTetheringSupported;
+import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
+
+import static com.android.testutils.TestNetworkTrackerKt.initTestNetwork;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+import android.app.UiAutomation;
+import android.content.Context;
+import android.net.IpPrefix;
+import android.net.LinkAddress;
+import android.net.TetheringManager;
+import android.net.cts.util.CtsTetheringUtils;
+import android.net.cts.util.CtsTetheringUtils.TestTetheringEventCallback;
+import android.provider.DeviceConfig;
+
+import androidx.annotation.NonNull;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.testutils.TestNetworkTracker;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.InterfaceAddress;
+import java.net.NetworkInterface;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class TetheringModuleTest {
+    private Context mContext;
+    private TetheringManager mTm;
+    private CtsTetheringUtils mCtsTetheringUtils;
+
+    private UiAutomation mUiAutomation =
+            InstrumentationRegistry.getInstrumentation().getUiAutomation();
+
+    @Before
+    public void setUp() throws Exception {
+        mUiAutomation.adoptShellPermissionIdentity(MANAGE_TEST_NETWORKS, NETWORK_SETTINGS,
+                WRITE_SETTINGS, READ_DEVICE_CONFIG, TETHER_PRIVILEGED);
+        mContext = InstrumentationRegistry.getContext();
+        mTm = mContext.getSystemService(TetheringManager.class);
+        mCtsTetheringUtils = new CtsTetheringUtils(mContext);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mUiAutomation.dropShellPermissionIdentity();
+    }
+
+    private static final String TETHER_ENABLE_SELECT_ALL_PREFIX_RANGES =
+            "tether_enable_select_all_prefix_ranges";
+    @Test
+    public void testSwitchBasePrefixRangeWhenConflict() throws Exception {
+        assumeTrue(isFeatureEnabled(TETHER_ENABLE_SELECT_ALL_PREFIX_RANGES, true));
+
+        addressConflictTest(true);
+    }
+
+    @Test
+    public void testSwitchPrefixRangeWhenConflict() throws Exception {
+        addressConflictTest(false);
+    }
+
+    private void addressConflictTest(final boolean wholeRangeConflict) throws Exception {
+        final TestTetheringEventCallback tetherEventCallback =
+                mCtsTetheringUtils.registerTetheringEventCallback();
+
+        TestNetworkTracker tnt = null;
+        try {
+            tetherEventCallback.assumeTetheringSupported();
+            assumeTrue(isWifiTetheringSupported(tetherEventCallback));
+
+            mCtsTetheringUtils.startWifiTethering(tetherEventCallback);
+
+            final List<String> tetheredIfaces = tetherEventCallback.getTetheredInterfaces();
+            assertEquals(1, tetheredIfaces.size());
+            final String wifiTetheringIface = tetheredIfaces.get(0);
+
+            NetworkInterface nif = NetworkInterface.getByName(wifiTetheringIface);
+            // Tethering downstream only have one ipv4 address.
+            final LinkAddress hotspotAddr = getFirstIpv4Address(nif);
+            assertNotNull(hotspotAddr);
+
+            final IpPrefix testPrefix = getConflictingPrefix(hotspotAddr, wholeRangeConflict);
+            assertNotNull(testPrefix);
+
+            tnt = setUpTestNetwork(
+                    new LinkAddress(testPrefix.getAddress(), testPrefix.getPrefixLength()));
+
+            tetherEventCallback.expectTetheredInterfacesChanged(null);
+            final List<String> wifiRegexs =
+                    tetherEventCallback.getTetheringInterfaceRegexps().getTetherableWifiRegexs();
+
+            tetherEventCallback.expectTetheredInterfacesChanged(wifiRegexs);
+            nif = NetworkInterface.getByName(wifiTetheringIface);
+            final LinkAddress newHotspotAddr = getFirstIpv4Address(nif);
+            assertNotNull(newHotspotAddr);
+
+            assertFalse(testPrefix.containsPrefix(
+                    new IpPrefix(newHotspotAddr.getAddress(), newHotspotAddr.getPrefixLength())));
+
+            mCtsTetheringUtils.stopWifiTethering(tetherEventCallback);
+        } finally {
+            if (tnt != null) {
+                tnt.teardown();
+            }
+            mTm.stopAllTethering();
+            mCtsTetheringUtils.unregisterTetheringEventCallback(tetherEventCallback);
+        }
+    }
+
+    private LinkAddress getFirstIpv4Address(final NetworkInterface nif) {
+        for (InterfaceAddress ia : nif.getInterfaceAddresses()) {
+            final LinkAddress addr = new LinkAddress(ia.getAddress(), ia.getNetworkPrefixLength());
+            if (addr.isIpv4()) return addr;
+        }
+        return null;
+    }
+
+    @NonNull
+    private IpPrefix getConflictingPrefix(final LinkAddress address,
+            final boolean wholeRangeConflict) {
+        if (!wholeRangeConflict) {
+            return new IpPrefix(address.getAddress(), address.getPrefixLength());
+        }
+
+        final ArrayList<IpPrefix> prefixPool = new ArrayList<>(Arrays.asList(
+                new IpPrefix("192.168.0.0/16"),
+                new IpPrefix("172.16.0.0/12"),
+                new IpPrefix("10.0.0.0/8")));
+
+        for (IpPrefix prefix : prefixPool) {
+            if (prefix.contains(address.getAddress())) return prefix;
+        }
+
+        fail("Could not find sutiable conflict prefix");
+
+        // Never go here.
+        return null;
+    }
+
+    private TestNetworkTracker setUpTestNetwork(final LinkAddress address) throws Exception {
+        return initTestNetwork(mContext, address, 10_000L /* test timeout ms*/);
+
+    }
+
+    public static boolean isFeatureEnabled(final String name, final boolean defaultValue) {
+        return DeviceConfig.getBoolean(NAMESPACE_CONNECTIVITY, name, defaultValue);
+    }
+}
diff --git a/packages/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java b/packages/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java
index 747d3e8..42a91aa 100644
--- a/packages/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java
+++ b/packages/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java
@@ -17,9 +17,9 @@
 package android.net.ip;
 
 import static android.system.OsConstants.IPPROTO_ICMPV6;
-import static android.system.OsConstants.IPPROTO_TCP;
 
-import static com.android.internal.util.BitUtils.uint16;
+import static com.android.net.module.util.IpUtils.icmpv6Checksum;
+import static com.android.net.module.util.NetworkStackConstants.ETHER_SRC_ADDR_OFFSET;
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -30,34 +30,29 @@
 import android.net.INetd;
 import android.net.InetAddresses;
 import android.net.MacAddress;
-import android.net.TestNetworkInterface;
-import android.net.TestNetworkManager;
 import android.net.util.InterfaceParams;
-import android.net.util.IpUtils;
 import android.net.util.TetheringUtils;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.Looper;
-import android.system.ErrnoException;
-import android.system.Os;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.testutils.TapPacketReader;
+import com.android.testutils.TapPacketReaderRule;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.BeforeClass;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.MockitoAnnotations;
 
-import java.io.FileDescriptor;
 import java.nio.ByteBuffer;
-import java.util.concurrent.atomic.AtomicReference;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
@@ -65,16 +60,18 @@
     private static final int DATA_BUFFER_LEN = 4096;
     private static final int PACKET_TIMEOUT_MS = 5_000;
 
-    // TODO: make NetworkStackConstants accessible to this test and use the constant from there.
-    private static final int ETHER_SRC_ADDR_OFFSET = 6;
+    // Start the readers manually on a common handler shared with DadProxy, for simplicity
+    @Rule
+    public final TapPacketReaderRule mUpstreamReader = new TapPacketReaderRule(
+            DATA_BUFFER_LEN, false /* autoStart */);
+    @Rule
+    public final TapPacketReaderRule mTetheredReader = new TapPacketReaderRule(
+            DATA_BUFFER_LEN, false /* autoStart */);
 
-    private DadProxy mProxy;
-    TestNetworkInterface mUpstreamTestIface, mTetheredTestIface;
     private InterfaceParams mUpstreamParams, mTetheredParams;
     private HandlerThread mHandlerThread;
     private Handler mHandler;
     private TapPacketReader mUpstreamPacketReader, mTetheredPacketReader;
-    private FileDescriptor mUpstreamTapFd, mTetheredTapFd;
 
     private static INetd sNetd;
 
@@ -106,12 +103,12 @@
 
     @After
     public void tearDown() throws Exception {
+        mUpstreamReader.stop();
+        mTetheredReader.stop();
+
         if (mHandlerThread != null) {
-            mHandler.post(mUpstreamPacketReader::stop); // Also closes the socket
-            mHandler.post(mTetheredPacketReader::stop); // Also closes the socket
-            mUpstreamTapFd = null;
-            mTetheredTapFd = null;
             mHandlerThread.quitSafely();
+            mHandlerThread.join(PACKET_TIMEOUT_MS);
         }
 
         if (mTetheredParams != null) {
@@ -120,54 +117,20 @@
         if (mUpstreamParams != null) {
             sNetd.networkRemoveInterface(INetd.LOCAL_NET_ID, mUpstreamParams.name);
         }
-
-        if (mUpstreamTestIface != null) {
-            try {
-                Os.close(mUpstreamTestIface.getFileDescriptor().getFileDescriptor());
-            } catch (ErrnoException e) { }
-        }
-
-        if (mTetheredTestIface != null) {
-            try {
-                Os.close(mTetheredTestIface.getFileDescriptor().getFileDescriptor());
-            } catch (ErrnoException e) { }
-        }
-    }
-
-    private TestNetworkInterface setupTapInterface() {
-        final Instrumentation inst = InstrumentationRegistry.getInstrumentation();
-        AtomicReference<TestNetworkInterface> iface = new AtomicReference<>();
-
-        inst.getUiAutomation().adoptShellPermissionIdentity();
-        try {
-            final TestNetworkManager tnm = (TestNetworkManager) inst.getContext().getSystemService(
-                    Context.TEST_NETWORK_SERVICE);
-            iface.set(tnm.createTapInterface());
-        } finally {
-            inst.getUiAutomation().dropShellPermissionIdentity();
-        }
-
-        return iface.get();
     }
 
     private void setupTapInterfaces() {
         // Create upstream test iface.
-        mUpstreamTestIface = setupTapInterface();
-        mUpstreamParams = InterfaceParams.getByName(mUpstreamTestIface.getInterfaceName());
+        mUpstreamReader.start(mHandler);
+        mUpstreamParams = InterfaceParams.getByName(mUpstreamReader.iface.getInterfaceName());
         assertNotNull(mUpstreamParams);
-        mUpstreamTapFd = mUpstreamTestIface.getFileDescriptor().getFileDescriptor();
-        mUpstreamPacketReader = new TapPacketReader(mHandler, mUpstreamTapFd,
-                                                    DATA_BUFFER_LEN);
-        mHandler.post(mUpstreamPacketReader::start);
+        mUpstreamPacketReader = mUpstreamReader.getReader();
 
         // Create tethered test iface.
-        mTetheredTestIface = setupTapInterface();
-        mTetheredParams = InterfaceParams.getByName(mTetheredTestIface.getInterfaceName());
+        mTetheredReader.start(mHandler);
+        mTetheredParams = InterfaceParams.getByName(mTetheredReader.getIface().getInterfaceName());
         assertNotNull(mTetheredParams);
-        mTetheredTapFd = mTetheredTestIface.getFileDescriptor().getFileDescriptor();
-        mTetheredPacketReader = new TapPacketReader(mHandler, mTetheredTapFd,
-                                                    DATA_BUFFER_LEN);
-        mHandler.post(mTetheredPacketReader::start);
+        mTetheredPacketReader = mTetheredReader.getReader();
     }
 
     private static final int IPV6_HEADER_LEN = 40;
@@ -177,31 +140,6 @@
     private static final int ICMPV6_CHECKSUM_OFFSET = 2;
     private static final int ETHER_TYPE_IPV6 = 0x86dd;
 
-    // TODO: move the IpUtils code to frameworks/lib/net and link it statically.
-    private static int checksumFold(int sum) {
-        while (sum > 0xffff) {
-            sum = (sum >> 16) + (sum & 0xffff);
-        }
-        return sum;
-    }
-
-    // TODO: move the IpUtils code to frameworks/lib/net and link it statically.
-    private static short checksumAdjust(short checksum, short oldWord, short newWord) {
-        checksum = (short) ~checksum;
-        int tempSum = checksumFold(uint16(checksum) + uint16(newWord) + 0xffff - uint16(oldWord));
-        return (short) ~tempSum;
-    }
-
-    // TODO: move the IpUtils code to frameworks/lib/net and link it statically.
-    private static short icmpv6Checksum(ByteBuffer buf, int ipOffset, int transportOffset,
-            int transportLen) {
-        // The ICMPv6 checksum is the same as the TCP checksum, except the pseudo-header uses
-        // 58 (ICMPv6) instead of 6 (TCP). Calculate the TCP checksum, and then do an incremental
-        // checksum adjustment  for the change in the next header byte.
-        short checksum = IpUtils.tcpChecksum(buf, ipOffset, transportOffset, transportLen);
-        return checksumAdjust(checksum, (short) IPPROTO_TCP, (short) IPPROTO_ICMPV6);
-    }
-
     private static ByteBuffer createDadPacket(int type) {
         // Refer to buildArpPacket()
         int icmpLen = ICMPV6_NA_NS_LEN
diff --git a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index 1a976ad..2eb7589 100644
--- a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -47,6 +47,7 @@
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doAnswer;
@@ -230,7 +231,8 @@
             dispatchTetherConnectionChanged(upstreamIface, lp, 0);
         }
         reset(mNetd, mCallback, mAddressCoordinator);
-        when(mAddressCoordinator.requestDownstreamAddress(any())).thenReturn(mTestAddress);
+        when(mAddressCoordinator.requestDownstreamAddress(any(), anyBoolean())).thenReturn(
+                mTestAddress);
     }
 
     private void setUpDhcpServer() throws Exception {
@@ -250,7 +252,8 @@
     @Before public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         when(mSharedLog.forSubComponent(anyString())).thenReturn(mSharedLog);
-        when(mAddressCoordinator.requestDownstreamAddress(any())).thenReturn(mTestAddress);
+        when(mAddressCoordinator.requestDownstreamAddress(any(), anyBoolean())).thenReturn(
+                mTestAddress);
         when(mTetherConfig.isBpfOffloadEnabled()).thenReturn(true /* default value */);
 
         mBpfCoordinator = spy(new BpfCoordinator(
@@ -372,7 +375,7 @@
 
         dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
         InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator);
-        inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any());
+        inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(), eq(true));
         inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg ->
                   IFACE_NAME.equals(cfg.ifName) && assertContainsFlag(cfg.flags, IF_STATE_UP)));
         inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
@@ -393,7 +396,7 @@
 
         dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_LOCAL_ONLY);
         InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator);
-        inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any());
+        inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(), eq(true));
         inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg ->
                   IFACE_NAME.equals(cfg.ifName) && assertNotContainsFlag(cfg.flags, IF_STATE_UP)));
         inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
@@ -607,7 +610,7 @@
         final ArgumentCaptor<LinkProperties> lpCaptor =
                 ArgumentCaptor.forClass(LinkProperties.class);
         InOrder inOrder = inOrder(mNetd, mCallback, mAddressCoordinator);
-        inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any());
+        inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(), eq(true));
         inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
         // One for ipv4 route, one for ipv6 link local route.
         inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME),
@@ -620,11 +623,12 @@
         // Simulate the DHCP server receives DHCPDECLINE on MirrorLink and then signals
         // onNewPrefixRequest callback.
         final LinkAddress newAddress = new LinkAddress("192.168.100.125/24");
-        when(mAddressCoordinator.requestDownstreamAddress(any())).thenReturn(newAddress);
+        when(mAddressCoordinator.requestDownstreamAddress(any(), anyBoolean())).thenReturn(
+                newAddress);
         eventCallbacks.onNewPrefixRequest(new IpPrefix("192.168.42.0/24"));
         mLooper.dispatchAll();
 
-        inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any());
+        inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(), eq(false));
         inOrder.verify(mNetd).tetherApplyDnsInterfaces();
         inOrder.verify(mCallback).updateLinkProperties(eq(mIpServer), lpCaptor.capture());
         verifyNoMoreInteractions(mCallback);
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
index 7b6632c..41d46e5 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
@@ -23,6 +23,7 @@
 import static android.net.TetheringManager.TETHERING_USB;
 import static android.net.TetheringManager.TETHERING_WIFI;
 import static android.net.TetheringManager.TETHERING_WIFI_P2P;
+import static android.net.util.PrefixUtils.asIpPrefix;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
@@ -40,7 +41,6 @@
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.ip.IpServer;
-import android.net.util.PrefixUtils;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -51,6 +51,9 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public final class PrivateAddressCoordinatorTest {
@@ -65,12 +68,22 @@
     @Mock private TetheringConfiguration mConfig;
 
     private PrivateAddressCoordinator mPrivateAddressCoordinator;
-    private final IpPrefix mBluetoothPrefix = new IpPrefix("192.168.44.0/24");
+    private final LinkAddress mBluetoothAddress = new LinkAddress("192.168.44.1/24");
     private final LinkAddress mLegacyWifiP2pAddress = new LinkAddress("192.168.49.1/24");
     private final Network mWifiNetwork = new Network(1);
     private final Network mMobileNetwork = new Network(2);
     private final Network mVpnNetwork = new Network(3);
-    private final Network[] mAllNetworks = {mMobileNetwork, mWifiNetwork, mVpnNetwork};
+    private final Network mMobileNetwork2 = new Network(4);
+    private final Network mMobileNetwork3 = new Network(5);
+    private final Network mMobileNetwork4 = new Network(6);
+    private final Network mMobileNetwork5 = new Network(7);
+    private final Network mMobileNetwork6 = new Network(8);
+    private final Network[] mAllNetworks = {mMobileNetwork, mWifiNetwork, mVpnNetwork,
+            mMobileNetwork2, mMobileNetwork3, mMobileNetwork4, mMobileNetwork5, mMobileNetwork6};
+    private final ArrayList<IpPrefix> mTetheringPrefixes = new ArrayList<>(Arrays.asList(
+            new IpPrefix("192.168.0.0/16"),
+            new IpPrefix("172.16.0.0/12"),
+            new IpPrefix("10.0.0.0/8")));
 
     private void setUpIpServers() throws Exception {
         when(mUsbIpServer.interfaceType()).thenReturn(TETHERING_USB);
@@ -86,103 +99,133 @@
         when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(mConnectivityMgr);
         when(mConnectivityMgr.getAllNetworks()).thenReturn(mAllNetworks);
         when(mConfig.shouldEnableWifiP2pDedicatedIp()).thenReturn(false);
+        when(mConfig.isSelectAllPrefixRangeEnabled()).thenReturn(true);
         setUpIpServers();
         mPrivateAddressCoordinator = spy(new PrivateAddressCoordinator(mContext, mConfig));
     }
 
-    @Test
-    public void testDownstreamPrefixRequest() throws Exception {
-        LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress(
-                mHotspotIpServer);
-        final IpPrefix hotspotPrefix = PrefixUtils.asIpPrefix(address);
-        assertNotEquals(hotspotPrefix, mBluetoothPrefix);
+    private LinkAddress requestDownstreamAddress(final IpServer ipServer, boolean useLastAddress) {
+        final LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress(
+                ipServer, useLastAddress);
+        when(ipServer.getAddress()).thenReturn(address);
+        return address;
+    }
 
-        address = mPrivateAddressCoordinator.requestDownstreamAddress(
-                mHotspotIpServer);
-        final IpPrefix testDupRequest = PrefixUtils.asIpPrefix(address);
+    @Test
+    public void testRequestDownstreamAddressWithoutUsingLastAddress() throws Exception {
+        final IpPrefix bluetoothPrefix = asIpPrefix(mBluetoothAddress);
+        final LinkAddress address = requestDownstreamAddress(mHotspotIpServer,
+                false /* useLastAddress */);
+        final IpPrefix hotspotPrefix = asIpPrefix(address);
+        assertNotEquals(hotspotPrefix, bluetoothPrefix);
+
+        final LinkAddress newAddress = requestDownstreamAddress(mHotspotIpServer,
+                false /* useLastAddress */);
+        final IpPrefix testDupRequest = asIpPrefix(newAddress);
         assertNotEquals(hotspotPrefix, testDupRequest);
-        assertNotEquals(mBluetoothPrefix, testDupRequest);
+        assertNotEquals(bluetoothPrefix, testDupRequest);
         mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
 
-        address = mPrivateAddressCoordinator.requestDownstreamAddress(
-                mUsbIpServer);
-        final IpPrefix usbPrefix = PrefixUtils.asIpPrefix(address);
-        assertNotEquals(usbPrefix, mBluetoothPrefix);
+        final LinkAddress usbAddress = requestDownstreamAddress(mUsbIpServer,
+                false /* useLastAddress */);
+        final IpPrefix usbPrefix = asIpPrefix(usbAddress);
+        assertNotEquals(usbPrefix, bluetoothPrefix);
         assertNotEquals(usbPrefix, hotspotPrefix);
         mPrivateAddressCoordinator.releaseDownstream(mUsbIpServer);
     }
 
     @Test
-    public void testRequestDownstreamAddress() throws Exception {
-        LinkAddress expectedAddress = new LinkAddress("192.168.43.42/24");
-        int fakeSubAddr = 0x2b00;
-        when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeSubAddr);
-        LinkAddress actualAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
-                mHotspotIpServer);
-        assertEquals(actualAddress, expectedAddress);
+    public void testSanitizedAddress() throws Exception {
+        int fakeSubAddr = 0x2b00; // 43.0.
+        when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeSubAddr);
+        LinkAddress actualAddress = requestDownstreamAddress(mHotspotIpServer,
+                false /* useLastAddress */);
+        assertEquals(new LinkAddress("192.168.43.2/24"), actualAddress);
         mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
 
-        fakeSubAddr = 0x2b01;
-        when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeSubAddr);
-        actualAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
-                mHotspotIpServer);
-        assertEquals(actualAddress, expectedAddress);
+        fakeSubAddr = 0x2d01; // 45.1.
+        when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeSubAddr);
+        actualAddress = requestDownstreamAddress(mHotspotIpServer, false /* useLastAddress */);
+        assertEquals(new LinkAddress("192.168.45.2/24"), actualAddress);
         mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
 
-        fakeSubAddr = 0x2bff;
-        when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeSubAddr);
-        actualAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
-                mHotspotIpServer);
-        assertEquals(actualAddress, expectedAddress);
+        fakeSubAddr = 0x2eff; // 46.255.
+        when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeSubAddr);
+        actualAddress = requestDownstreamAddress(mHotspotIpServer, false /* useLastAddress */);
+        assertEquals(new LinkAddress("192.168.46.254/24"), actualAddress);
         mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
 
-        expectedAddress = new LinkAddress("192.168.43.5/24");
-        fakeSubAddr = 0x2b05;
-        when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeSubAddr);
-        actualAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
-                mHotspotIpServer);
-        assertEquals(actualAddress, expectedAddress);
-        mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
-    }
-
-    private int getBluetoothSubAddress() {
-        final byte[] rawAddress = mBluetoothPrefix.getRawAddress();
-        int bluetoothSubNet = rawAddress[2] & 0xff;
-        return (bluetoothSubNet << 8) + 0x5;
-    }
-
-    @Test
-    public void testReserveBluetoothPrefix() throws Exception {
-        when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(getBluetoothSubAddress());
-        LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress(
-                mHotspotIpServer);
-        final IpPrefix hotspotPrefix = PrefixUtils.asIpPrefix(address);
-        assertNotEquals("Should not get reserved prefix: ", mBluetoothPrefix, hotspotPrefix);
+        fakeSubAddr = 0x2f05; // 47.5.
+        when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeSubAddr);
+        actualAddress = requestDownstreamAddress(mHotspotIpServer, false /* useLastAddress */);
+        assertEquals(new LinkAddress("192.168.47.5/24"), actualAddress);
         mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
     }
 
     @Test
-    public void testNoConflictDownstreamPrefix() throws Exception {
-        final int fakeHotspotSubAddr = 0x2b05;
-        final IpPrefix predefinedPrefix = new IpPrefix("192.168.43.0/24");
-        when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeHotspotSubAddr);
-        LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress(
-                mHotspotIpServer);
-        final IpPrefix hotspotPrefix = PrefixUtils.asIpPrefix(address);
-        assertEquals("Wrong wifi prefix: ", predefinedPrefix, hotspotPrefix);
-        when(mHotspotIpServer.getAddress()).thenReturn(address);
+    public void testReservedPrefix() throws Exception {
+        // - Test bluetooth prefix is reserved.
+        when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(
+                getSubAddress(mBluetoothAddress.getAddress().getAddress()));
+        final LinkAddress hotspotAddress = requestDownstreamAddress(mHotspotIpServer,
+                false /* useLastAddress */);
+        final IpPrefix hotspotPrefix = asIpPrefix(hotspotAddress);
+        assertNotEquals(asIpPrefix(mBluetoothAddress), hotspotPrefix);
+        mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
 
-        address = mPrivateAddressCoordinator.requestDownstreamAddress(
-                mUsbIpServer);
-        final IpPrefix usbPrefix = PrefixUtils.asIpPrefix(address);
-        assertNotEquals(predefinedPrefix, usbPrefix);
+        // - Test previous enabled hotspot prefix(cached prefix) is reserved.
+        when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(
+                getSubAddress(hotspotAddress.getAddress().getAddress()));
+        final LinkAddress usbAddress = requestDownstreamAddress(mUsbIpServer,
+                false /* useLastAddress */);
+        final IpPrefix usbPrefix = asIpPrefix(usbAddress);
+        assertNotEquals(asIpPrefix(mBluetoothAddress), usbPrefix);
+        assertNotEquals(hotspotPrefix, usbPrefix);
+        mPrivateAddressCoordinator.releaseDownstream(mUsbIpServer);
+
+        // - Test wifi p2p prefix is reserved.
+        when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(
+                getSubAddress(mLegacyWifiP2pAddress.getAddress().getAddress()));
+        final LinkAddress etherAddress = requestDownstreamAddress(mEthernetIpServer,
+                false /* useLastAddress */);
+        final IpPrefix etherPrefix = asIpPrefix(etherAddress);
+        assertNotEquals(asIpPrefix(mLegacyWifiP2pAddress), etherPrefix);
+        assertNotEquals(asIpPrefix(mBluetoothAddress), etherPrefix);
+        assertNotEquals(hotspotPrefix, etherPrefix);
+        mPrivateAddressCoordinator.releaseDownstream(mEthernetIpServer);
+    }
+
+    @Test
+    public void testRequestLastDownstreamAddress() throws Exception {
+        final int fakeHotspotSubAddr = 0x2b05; // 43.5
+        when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeHotspotSubAddr);
+        final LinkAddress hotspotAddress = requestDownstreamAddress(mHotspotIpServer,
+                true /* useLastAddress */);
+        assertEquals("Wrong wifi prefix: ", new LinkAddress("192.168.43.5/24"), hotspotAddress);
+
+        final LinkAddress usbAddress = requestDownstreamAddress(mUsbIpServer,
+                true /* useLastAddress */);
+        assertEquals("Wrong wifi prefix: ", new LinkAddress("192.168.45.5/24"), usbAddress);
 
         mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
         mPrivateAddressCoordinator.releaseDownstream(mUsbIpServer);
-        address = mPrivateAddressCoordinator.requestDownstreamAddress(
-                mUsbIpServer);
-        final IpPrefix allowUseFreePrefix = PrefixUtils.asIpPrefix(address);
-        assertEquals("Fail to reselect available prefix: ", predefinedPrefix, allowUseFreePrefix);
+
+        final int newFakeSubAddr = 0x3c05;
+        when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeHotspotSubAddr);
+
+        final LinkAddress newHotspotAddress = requestDownstreamAddress(mHotspotIpServer,
+                true /* useLastAddress */);
+        assertEquals(hotspotAddress, newHotspotAddress);
+        final LinkAddress newUsbAddress = requestDownstreamAddress(mUsbIpServer,
+                true /* useLastAddress */);
+        assertEquals(usbAddress, newUsbAddress);
+
+        final UpstreamNetworkState wifiUpstream = buildUpstreamNetworkState(mWifiNetwork,
+                new LinkAddress("192.168.88.23/16"), null,
+                makeNetworkCapabilities(TRANSPORT_WIFI));
+        mPrivateAddressCoordinator.updateUpstreamPrefix(wifiUpstream);
+        verify(mHotspotIpServer).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
+        verify(mUsbIpServer).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
     }
 
     private UpstreamNetworkState buildUpstreamNetworkState(final Network network,
@@ -208,19 +251,18 @@
 
     @Test
     public void testNoConflictUpstreamPrefix() throws Exception {
-        final int fakeHotspotSubId = 43;
-        final int fakeHotspotSubAddr = 0x2b05;
+        final int fakeHotspotSubAddr = 0x2b05; // 43.5
         final IpPrefix predefinedPrefix = new IpPrefix("192.168.43.0/24");
         // Force always get subAddress "43.5" for conflict testing.
-        when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeHotspotSubAddr);
+        when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeHotspotSubAddr);
         // - Enable hotspot with prefix 192.168.43.0/24
-        final LinkAddress hotspotAddr = mPrivateAddressCoordinator.requestDownstreamAddress(
-                mHotspotIpServer);
-        final IpPrefix hotspotPrefix = PrefixUtils.asIpPrefix(hotspotAddr);
+        final LinkAddress hotspotAddr = requestDownstreamAddress(mHotspotIpServer,
+                true /* useLastAddress */);
+        final IpPrefix hotspotPrefix = asIpPrefix(hotspotAddr);
         assertEquals("Wrong wifi prefix: ", predefinedPrefix, hotspotPrefix);
-        when(mHotspotIpServer.getAddress()).thenReturn(hotspotAddr);
-        // - test mobile network with null NetworkCapabilities. Ideally this should not happen,
-        // just make sure no crash in this case.
+        // - test mobile network with null NetworkCapabilities. Ideally this should not happen
+        // because NetworkCapabilities update should always happen before LinkProperties update
+        // and the UpstreamNetworkState update, just make sure no crash in this case.
         final UpstreamNetworkState noCapUpstream = buildUpstreamNetworkState(mMobileNetwork,
                 new LinkAddress("10.0.0.8/24"), null, null);
         mPrivateAddressCoordinator.updateUpstreamPrefix(noCapUpstream);
@@ -268,28 +310,211 @@
         reset(mHotspotIpServer);
         // - Restart hotspot again and its prefix is different previous.
         mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
-        final LinkAddress hotspotAddr2 = mPrivateAddressCoordinator.requestDownstreamAddress(
-                mHotspotIpServer);
-        final IpPrefix hotspotPrefix2 = PrefixUtils.asIpPrefix(hotspotAddr2);
+        final LinkAddress hotspotAddr2 = requestDownstreamAddress(mHotspotIpServer,
+                true /* useLastAddress */);
+        final IpPrefix hotspotPrefix2 = asIpPrefix(hotspotAddr2);
         assertNotEquals(hotspotPrefix, hotspotPrefix2);
-        when(mHotspotIpServer.getAddress()).thenReturn(hotspotAddr2);
         mPrivateAddressCoordinator.updateUpstreamPrefix(v4OnlyWifi);
         verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
         // - Usb tethering can be enabled and its prefix is different with conflict one.
-        final LinkAddress usbAddr = mPrivateAddressCoordinator.requestDownstreamAddress(
-                mUsbIpServer);
-        final IpPrefix usbPrefix = PrefixUtils.asIpPrefix(usbAddr);
+        final LinkAddress usbAddr = requestDownstreamAddress(mUsbIpServer,
+                true /* useLastAddress */);
+        final IpPrefix usbPrefix = asIpPrefix(usbAddr);
         assertNotEquals(predefinedPrefix, usbPrefix);
         assertNotEquals(hotspotPrefix2, usbPrefix);
-        when(mUsbIpServer.getAddress()).thenReturn(usbAddr);
         // - Disable wifi upstream, then wifi's prefix can be selected again.
         mPrivateAddressCoordinator.removeUpstreamPrefix(mWifiNetwork);
-        final LinkAddress ethAddr = mPrivateAddressCoordinator.requestDownstreamAddress(
-                mEthernetIpServer);
-        final IpPrefix ethPrefix = PrefixUtils.asIpPrefix(ethAddr);
+        final LinkAddress ethAddr = requestDownstreamAddress(mEthernetIpServer,
+                true /* useLastAddress */);
+        final IpPrefix ethPrefix = asIpPrefix(ethAddr);
         assertEquals(predefinedPrefix, ethPrefix);
     }
 
+    @Test
+    public void testChooseAvailablePrefix() throws Exception {
+        final int randomAddress = 0x8605; // 134.5
+        when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(randomAddress);
+        final LinkAddress addr0 = requestDownstreamAddress(mHotspotIpServer,
+                true /* useLastAddress */);
+        // Check whether return address is prefix 192.168.0.0/16 + subAddress 0.0.134.5.
+        assertEquals("Wrong prefix: ", new LinkAddress("192.168.134.5/24"), addr0);
+        final UpstreamNetworkState wifiUpstream = buildUpstreamNetworkState(mWifiNetwork,
+                new LinkAddress("192.168.134.13/26"), null,
+                makeNetworkCapabilities(TRANSPORT_WIFI));
+        mPrivateAddressCoordinator.updateUpstreamPrefix(wifiUpstream);
+
+        // Check whether return address is next prefix of 192.168.134.0/24.
+        final LinkAddress addr1 = requestDownstreamAddress(mHotspotIpServer,
+                true /* useLastAddress */);
+        assertEquals("Wrong prefix: ", new LinkAddress("192.168.135.5/24"), addr1);
+        final UpstreamNetworkState wifiUpstream2 = buildUpstreamNetworkState(mWifiNetwork,
+                new LinkAddress("192.168.149.16/19"), null,
+                makeNetworkCapabilities(TRANSPORT_WIFI));
+        mPrivateAddressCoordinator.updateUpstreamPrefix(wifiUpstream2);
+
+
+        // The conflict range is 128 ~ 159, so the address is 192.168.160.5/24.
+        final LinkAddress addr2 = requestDownstreamAddress(mHotspotIpServer,
+                true /* useLastAddress */);
+        assertEquals("Wrong prefix: ", new LinkAddress("192.168.160.5/24"), addr2);
+        final UpstreamNetworkState mobileUpstream = buildUpstreamNetworkState(mMobileNetwork,
+                new LinkAddress("192.168.129.53/18"), null,
+                makeNetworkCapabilities(TRANSPORT_CELLULAR));
+        // Update another conflict upstream which is covered by the previous one (but not the first
+        // one) and verify whether this would affect the result.
+        final UpstreamNetworkState mobileUpstream2 = buildUpstreamNetworkState(mMobileNetwork2,
+                new LinkAddress("192.168.170.7/19"), null,
+                makeNetworkCapabilities(TRANSPORT_CELLULAR));
+        mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream);
+        mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream2);
+
+        // The conflict range are 128 ~ 159 and 159 ~ 191, so the address is 192.168.192.5/24.
+        final LinkAddress addr3 = requestDownstreamAddress(mHotspotIpServer,
+                true /* useLastAddress */);
+        assertEquals("Wrong prefix: ", new LinkAddress("192.168.192.5/24"), addr3);
+        final UpstreamNetworkState mobileUpstream3 = buildUpstreamNetworkState(mMobileNetwork3,
+                new LinkAddress("192.168.188.133/17"), null,
+                makeNetworkCapabilities(TRANSPORT_CELLULAR));
+        mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream3);
+
+        // Conflict range: 128 ~ 255. The next available address is 192.168.0.5 because
+        // 192.168.134/24 ~ 192.168.255.255/24 is not available.
+        final LinkAddress addr4 = requestDownstreamAddress(mHotspotIpServer,
+                true /* useLastAddress */);
+        assertEquals("Wrong prefix: ", new LinkAddress("192.168.0.5/24"), addr4);
+        final UpstreamNetworkState mobileUpstream4 = buildUpstreamNetworkState(mMobileNetwork4,
+                new LinkAddress("192.168.3.59/21"), null,
+                makeNetworkCapabilities(TRANSPORT_CELLULAR));
+        mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream4);
+
+        // Conflict ranges: 128 ~ 255 and 0 ~ 7, so the address is 192.168.8.5/24.
+        final LinkAddress addr5 = requestDownstreamAddress(mHotspotIpServer,
+                true /* useLastAddress */);
+        assertEquals("Wrong prefix: ", new LinkAddress("192.168.8.5/24"), addr5);
+        final UpstreamNetworkState mobileUpstream5 = buildUpstreamNetworkState(mMobileNetwork5,
+                new LinkAddress("192.168.68.43/21"), null,
+                makeNetworkCapabilities(TRANSPORT_CELLULAR));
+        mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream5);
+
+        // Update an upstream that does *not* conflict, check whether return the same address
+        // 192.168.5/24.
+        final LinkAddress addr6 = requestDownstreamAddress(mHotspotIpServer,
+                true /* useLastAddress */);
+        assertEquals("Wrong prefix: ", new LinkAddress("192.168.8.5/24"), addr6);
+        final UpstreamNetworkState mobileUpstream6 = buildUpstreamNetworkState(mMobileNetwork6,
+                new LinkAddress("192.168.10.97/21"), null,
+                makeNetworkCapabilities(TRANSPORT_CELLULAR));
+        mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream6);
+
+        // Conflict ranges: 0 ~ 15 and 128 ~ 255, so the address is 192.168.16.5/24.
+        final LinkAddress addr7 = requestDownstreamAddress(mHotspotIpServer,
+                true /* useLastAddress */);
+        assertEquals("Wrong prefix: ", new LinkAddress("192.168.16.5/24"), addr7);
+        final UpstreamNetworkState mobileUpstream7 = buildUpstreamNetworkState(mMobileNetwork6,
+                new LinkAddress("192.168.0.0/17"), null,
+                makeNetworkCapabilities(TRANSPORT_CELLULAR));
+        mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream7);
+
+        // Choose prefix from next range(172.16.0.0/12) when no available prefix in 192.168.0.0/16.
+        final LinkAddress addr8 = requestDownstreamAddress(mHotspotIpServer,
+                true /* useLastAddress */);
+        assertEquals("Wrong prefix: ", new LinkAddress("172.16.134.5/24"), addr8);
+    }
+
+    @Test
+    public void testChoosePrefixFromDifferentRanges() throws Exception {
+        final int randomAddress = 0x1f2b2a; // 31.43.42
+        when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(randomAddress);
+        final LinkAddress classC1 = requestDownstreamAddress(mHotspotIpServer,
+                true /* useLastAddress */);
+        // Check whether return address is prefix 192.168.0.0/16 + subAddress 0.0.43.42.
+        assertEquals("Wrong prefix: ", new LinkAddress("192.168.43.42/24"), classC1);
+        final UpstreamNetworkState wifiUpstream = buildUpstreamNetworkState(mWifiNetwork,
+                new LinkAddress("192.168.88.23/17"), null,
+                makeNetworkCapabilities(TRANSPORT_WIFI));
+        mPrivateAddressCoordinator.updateUpstreamPrefix(wifiUpstream);
+        verifyNotifyConflictAndRelease(mHotspotIpServer);
+
+        // Check whether return address is next address of prefix 192.168.128.0/17.
+        final LinkAddress classC2 = requestDownstreamAddress(mHotspotIpServer,
+                true /* useLastAddress */);
+        assertEquals("Wrong prefix: ", new LinkAddress("192.168.128.42/24"), classC2);
+        final UpstreamNetworkState mobileUpstream = buildUpstreamNetworkState(mMobileNetwork,
+                new LinkAddress("192.1.2.3/8"), null,
+                makeNetworkCapabilities(TRANSPORT_CELLULAR));
+        mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream);
+        verifyNotifyConflictAndRelease(mHotspotIpServer);
+
+        // Check whether return address is under prefix 172.16.0.0/12.
+        final LinkAddress classB1 = requestDownstreamAddress(mHotspotIpServer,
+                true /* useLastAddress */);
+        assertEquals("Wrong prefix: ", new LinkAddress("172.31.43.42/24"), classB1);
+        final UpstreamNetworkState mobileUpstream2 = buildUpstreamNetworkState(mMobileNetwork2,
+                new LinkAddress("172.28.123.100/14"), null,
+                makeNetworkCapabilities(TRANSPORT_CELLULAR));
+        mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream2);
+        verifyNotifyConflictAndRelease(mHotspotIpServer);
+
+        // 172.28.0.0 ~ 172.31.255.255 is not available.
+        // Check whether return address is next address of prefix 172.16.0.0/14.
+        final LinkAddress classB2 = requestDownstreamAddress(mHotspotIpServer,
+                true /* useLastAddress */);
+        assertEquals("Wrong prefix: ", new LinkAddress("172.16.0.42/24"), classB2);
+
+        // Check whether new downstream is next address of address 172.16.0.42/24.
+        final LinkAddress classB3 = requestDownstreamAddress(mUsbIpServer,
+                true /* useLastAddress */);
+        assertEquals("Wrong prefix: ", new LinkAddress("172.16.1.42/24"), classB3);
+        final UpstreamNetworkState mobileUpstream3 = buildUpstreamNetworkState(mMobileNetwork3,
+                new LinkAddress("172.16.0.1/24"), null,
+                makeNetworkCapabilities(TRANSPORT_CELLULAR));
+        mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream3);
+        verifyNotifyConflictAndRelease(mHotspotIpServer);
+        verify(mUsbIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
+
+        // Check whether return address is next address of prefix 172.16.1.42/24.
+        final LinkAddress classB4 = requestDownstreamAddress(mHotspotIpServer,
+                true /* useLastAddress */);
+        assertEquals("Wrong prefix: ", new LinkAddress("172.16.2.42/24"), classB4);
+        final UpstreamNetworkState mobileUpstream4 = buildUpstreamNetworkState(mMobileNetwork4,
+                new LinkAddress("172.16.0.1/13"), null,
+                makeNetworkCapabilities(TRANSPORT_CELLULAR));
+        mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream4);
+        verifyNotifyConflictAndRelease(mHotspotIpServer);
+        verifyNotifyConflictAndRelease(mUsbIpServer);
+
+        // Check whether return address is next address of prefix 172.16.0.1/13.
+        final LinkAddress classB5 = requestDownstreamAddress(mHotspotIpServer,
+                true /* useLastAddress */);
+        assertEquals("Wrong prefix: ", new LinkAddress("172.24.0.42/24"), classB5);
+        // Check whether return address is next address of prefix 172.24.0.42/24.
+        final LinkAddress classB6 = requestDownstreamAddress(mUsbIpServer,
+                true /* useLastAddress */);
+        assertEquals("Wrong prefix: ", new LinkAddress("172.24.1.42/24"), classB6);
+        final UpstreamNetworkState mobileUpstream5 = buildUpstreamNetworkState(mMobileNetwork5,
+                new LinkAddress("172.24.0.1/12"), null,
+                makeNetworkCapabilities(TRANSPORT_CELLULAR));
+        mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream5);
+        verifyNotifyConflictAndRelease(mHotspotIpServer);
+        verifyNotifyConflictAndRelease(mUsbIpServer);
+
+        // Check whether return address is prefix 10.0.0.0/8 + subAddress 0.31.43.42.
+        final LinkAddress classA1 = requestDownstreamAddress(mHotspotIpServer,
+                true /* useLastAddress */);
+        assertEquals("Wrong prefix: ", new LinkAddress("10.31.43.42/24"), classA1);
+        // Check whether new downstream is next address of address 10.31.43.42/24.
+        final LinkAddress classA2 = requestDownstreamAddress(mUsbIpServer,
+                true /* useLastAddress */);
+        assertEquals("Wrong prefix: ", new LinkAddress("10.31.44.42/24"), classA2);
+    }
+
+    private void verifyNotifyConflictAndRelease(final IpServer ipServer) throws Exception {
+        verify(ipServer).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
+        mPrivateAddressCoordinator.releaseDownstream(ipServer);
+        reset(ipServer);
+        setUpIpServers();
+    }
+
     private int getSubAddress(final byte... ipv4Address) {
         assertEquals(4, ipv4Address.length);
 
@@ -298,17 +523,17 @@
     }
 
     private void assertReseveredWifiP2pPrefix() throws Exception {
-        LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress(
-                mHotspotIpServer);
-        final IpPrefix hotspotPrefix = PrefixUtils.asIpPrefix(address);
-        final IpPrefix legacyWifiP2pPrefix = PrefixUtils.asIpPrefix(mLegacyWifiP2pAddress);
+        LinkAddress address = requestDownstreamAddress(mHotspotIpServer,
+                true /* useLastAddress */);
+        final IpPrefix hotspotPrefix = asIpPrefix(address);
+        final IpPrefix legacyWifiP2pPrefix = asIpPrefix(mLegacyWifiP2pAddress);
         assertNotEquals(legacyWifiP2pPrefix, hotspotPrefix);
         mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
     }
 
     @Test
     public void testEnableLegacyWifiP2PAddress() throws Exception {
-        when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(
+        when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(
                 getSubAddress(mLegacyWifiP2pAddress.getAddress().getAddress()));
         // No matter #shouldEnableWifiP2pDedicatedIp() is enabled or not, legacy wifi p2p prefix
         // is resevered.
@@ -318,8 +543,8 @@
         assertReseveredWifiP2pPrefix();
 
         // If #shouldEnableWifiP2pDedicatedIp() is enabled, wifi P2P gets the configured address.
-        LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress(
-                mWifiP2pIpServer);
+        LinkAddress address = requestDownstreamAddress(mWifiP2pIpServer,
+                true /* useLastAddress */);
         assertEquals(mLegacyWifiP2pAddress, address);
         mPrivateAddressCoordinator.releaseDownstream(mWifiP2pIpServer);
     }
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
index dc0940c..237e2c2 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
@@ -131,6 +131,7 @@
         when(mResources.getBoolean(R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip))
                 .thenReturn(false);
         initializeBpfOffloadConfiguration(true, null /* unset */);
+        initEnableSelectAllPrefixRangeFlag(null /* unset */);
 
         mHasTelephonyManager = true;
         mMockContext = new MockContext(mContext);
@@ -428,4 +429,30 @@
                 mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
         assertTrue(testCfg.shouldEnableWifiP2pDedicatedIp());
     }
+
+    private void initEnableSelectAllPrefixRangeFlag(final String value) {
+        doReturn(value).when(
+                () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY),
+                eq(TetheringConfiguration.TETHER_ENABLE_SELECT_ALL_PREFIX_RANGES)));
+    }
+
+    @Test
+    public void testSelectAllPrefixRangeFlag() throws Exception {
+        // Test default value.
+        final TetheringConfiguration defaultCfg = new TetheringConfiguration(
+                mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+        assertTrue(defaultCfg.isSelectAllPrefixRangeEnabled());
+
+        // Test disable flag.
+        initEnableSelectAllPrefixRangeFlag("false");
+        final TetheringConfiguration testDisable = new TetheringConfiguration(
+                mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+        assertFalse(testDisable.isSelectAllPrefixRangeEnabled());
+
+        // Test enable flag.
+        initEnableSelectAllPrefixRangeFlag("true");
+        final TetheringConfiguration testEnable = new TetheringConfiguration(
+                mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+        assertTrue(testEnable.isSelectAllPrefixRangeEnabled());
+    }
 }
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index df57020..114cb7c 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -24,6 +24,9 @@
 import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
 import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
 import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
+import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.net.RouteInfo.RTN_UNICAST;
 import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED;
 import static android.net.TetheringManager.EXTRA_ACTIVE_LOCAL_ONLY;
@@ -179,6 +182,7 @@
     private static final String TEST_P2P_IFNAME = "test_p2p-p2p0-0";
     private static final String TEST_NCM_IFNAME = "test_ncm0";
     private static final String TEST_ETH_IFNAME = "test_eth0";
+    private static final String TEST_BT_IFNAME = "test_pan0";
     private static final String TETHERING_NAME = "Tethering";
     private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
     private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app";
@@ -230,6 +234,7 @@
     private TetheringConfiguration mConfig;
     private EntitlementManager mEntitleMgr;
     private OffloadController mOffloadCtrl;
+    private PrivateAddressCoordinator mPrivateAddressCoordinator;
 
     private class TestContext extends BroadcastInterceptingContext {
         TestContext(Context base) {
@@ -446,6 +451,14 @@
         public boolean isTetheringDenied() {
             return false;
         }
+
+
+        @Override
+        public PrivateAddressCoordinator getPrivateAddressCoordinator(Context ctx,
+                TetheringConfiguration cfg) {
+            mPrivateAddressCoordinator = super.getPrivateAddressCoordinator(ctx, cfg);
+            return mPrivateAddressCoordinator;
+        }
     }
 
     private static UpstreamNetworkState buildMobileUpstreamState(boolean withIPv4,
@@ -1875,27 +1888,36 @@
         sendConfigurationChanged();
     }
 
-    private static UpstreamNetworkState buildV4WifiUpstreamState(final String ipv4Address,
-            final int prefixLength, final Network network) {
+    private static UpstreamNetworkState buildV4UpstreamState(final LinkAddress address,
+            final Network network, final String iface, final int transportType) {
         final LinkProperties prop = new LinkProperties();
-        prop.setInterfaceName(TEST_WIFI_IFNAME);
+        prop.setInterfaceName(iface);
 
-        prop.addLinkAddress(
-                new LinkAddress(InetAddresses.parseNumericAddress(ipv4Address),
-                        prefixLength));
+        prop.addLinkAddress(address);
 
         final NetworkCapabilities capabilities = new NetworkCapabilities()
-                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
+                .addTransportType(transportType);
         return new UpstreamNetworkState(prop, capabilities, network);
     }
 
+    private void updateV4Upstream(final LinkAddress ipv4Address, final Network network,
+            final String iface, final int transportType) {
+        final UpstreamNetworkState upstream = buildV4UpstreamState(ipv4Address, network, iface,
+                transportType);
+        mTetheringDependencies.mUpstreamNetworkMonitorSM.sendMessage(
+                Tethering.TetherMainSM.EVENT_UPSTREAM_CALLBACK,
+                UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES,
+                0,
+                upstream);
+        mLooper.dispatchAll();
+    }
+
     @Test
     public void testHandleIpConflict() throws Exception {
         final Network wifiNetwork = new Network(200);
         final Network[] allNetworks = { wifiNetwork };
         when(mCm.getAllNetworks()).thenReturn(allNetworks);
-        UpstreamNetworkState upstreamNetwork = null;
-        runUsbTethering(upstreamNetwork);
+        runUsbTethering(null);
         final ArgumentCaptor<InterfaceConfigurationParcel> ifaceConfigCaptor =
                 ArgumentCaptor.forClass(InterfaceConfigurationParcel.class);
         verify(mNetd).interfaceSetCfg(ifaceConfigCaptor.capture());
@@ -1903,13 +1925,10 @@
         verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
                 any(), any());
         reset(mNetd, mUsbManager);
-        upstreamNetwork = buildV4WifiUpstreamState(ipv4Address, 30, wifiNetwork);
-        mTetheringDependencies.mUpstreamNetworkMonitorSM.sendMessage(
-                Tethering.TetherMainSM.EVENT_UPSTREAM_CALLBACK,
-                UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES,
-                0,
-                upstreamNetwork);
-        mLooper.dispatchAll();
+
+        // Cause a prefix conflict by assigning a /30 out of the downstream's /24 to the upstream.
+        updateV4Upstream(new LinkAddress(InetAddresses.parseNumericAddress(ipv4Address), 30),
+                wifiNetwork, TEST_WIFI_IFNAME, TRANSPORT_WIFI);
         // verify turn off usb tethering
         verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_NONE);
         mTethering.interfaceRemoved(TEST_USB_IFNAME);
@@ -1921,9 +1940,10 @@
     @Test
     public void testNoAddressAvailable() throws Exception {
         final Network wifiNetwork = new Network(200);
-        final Network[] allNetworks = { wifiNetwork };
+        final Network btNetwork = new Network(201);
+        final Network mobileNetwork = new Network(202);
+        final Network[] allNetworks = { wifiNetwork, btNetwork, mobileNetwork };
         when(mCm.getAllNetworks()).thenReturn(allNetworks);
-        final String upstreamAddress = "192.168.0.100";
         runUsbTethering(null);
         verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
                 any(), any());
@@ -1940,13 +1960,13 @@
         mLooper.dispatchAll();
         reset(mUsbManager, mEm);
 
-        final UpstreamNetworkState upstreamNetwork = buildV4WifiUpstreamState(
-                upstreamAddress, 16, wifiNetwork);
-        mTetheringDependencies.mUpstreamNetworkMonitorSM.sendMessage(
-                Tethering.TetherMainSM.EVENT_UPSTREAM_CALLBACK,
-                UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES,
-                0,
-                upstreamNetwork);
+        updateV4Upstream(new LinkAddress("192.168.0.100/16"), wifiNetwork, TEST_WIFI_IFNAME,
+                TRANSPORT_WIFI);
+        updateV4Upstream(new LinkAddress("172.16.0.0/12"), btNetwork, TEST_BT_IFNAME,
+                TRANSPORT_BLUETOOTH);
+        updateV4Upstream(new LinkAddress("10.0.0.0/8"), mobileNetwork, TEST_MOBILE_IFNAME,
+                TRANSPORT_CELLULAR);
+
         mLooper.dispatchAll();
         // verify turn off usb tethering
         verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_NONE);
diff --git a/services/Android.bp b/services/Android.bp
index 25a0d7e..8c9c487 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -5,6 +5,7 @@
     ],
     errorprone: {
         javacflags: [
+            "-Xep:AndroidFrameworkBinderIdentity:ERROR",
             "-Xep:AndroidFrameworkCompatChange:ERROR",
             "-Xep:AndroidFrameworkUid:ERROR",
         ],
@@ -34,6 +35,7 @@
         ":services.coverage-sources",
         ":services.devicepolicy-sources",
         ":services.midi-sources",
+        ":services.musicsearch-sources",
         ":services.net-sources",
         ":services.print-sources",
         ":services.profcollect-sources",
@@ -77,6 +79,7 @@
         "services.coverage",
         "services.devicepolicy",
         "services.midi",
+        "services.musicsearch",
         "services.net",
         "services.people",
         "services.print",
@@ -89,6 +92,7 @@
         "services.voiceinteraction",
         "services.wifi",
         "service-blobstore",
+        "service-connectivity",
         "service-jobscheduler",
         "android.hidl.base-V1.0-java",
     ],
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index a167ab1..d6d4e4f 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -150,6 +150,8 @@
 
     private boolean mRequestTwoFingerPassthrough;
 
+    private boolean mSendMotionEvents;
+
     boolean mRequestFilterKeyEvents;
 
     boolean mRetrieveInteractiveWindows;
@@ -329,6 +331,8 @@
                 & AccessibilityServiceInfo.FLAG_REQUEST_MULTI_FINGER_GESTURES) != 0;
         mRequestTwoFingerPassthrough =
                 (info.flags & AccessibilityServiceInfo.FLAG_REQUEST_2_FINGER_PASSTHROUGH) != 0;
+        mSendMotionEvents =
+                (info.flags & AccessibilityServiceInfo.FLAG_SEND_MOTION_EVENTS) != 0;
         mRequestFilterKeyEvents =
                 (info.flags & AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS) != 0;
         mRetrieveInteractiveWindows = (info.flags
@@ -1780,6 +1784,10 @@
         return mRequestTwoFingerPassthrough;
     }
 
+    public boolean isSendMotionEventsEnabled() {
+        return mSendMotionEvents;
+    }
+
     @Override
     public void setGestureDetectionPassthroughRegion(int displayId, Region region) {
         mSystemSupport.setGestureDetectionPassthroughRegion(displayId, region);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index cd9ab8d..857ac6a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -119,12 +119,19 @@
     static final int FLAG_REQUEST_MULTI_FINGER_GESTURES = 0x00000100;
 
     /**
-     * Flag for enabling multi-finger gestures.
+     * Flag for enabling two-finger passthrough when multi-finger gestures are enabled.
      *
      * @see #setUserAndEnabledFeatures(int, int)
      */
     static final int FLAG_REQUEST_2_FINGER_PASSTHROUGH = 0x00000200;
 
+    /**
+     * Flag for including motion events when dispatching a gesture.
+     *
+     * @see #setUserAndEnabledFeatures(int, int)
+     */
+    static final int FLAG_SEND_MOTION_EVENTS = 0x00000400;
+
     static final int FEATURES_AFFECTING_MOTION_EVENTS =
             FLAG_FEATURE_INJECT_MOTION_EVENTS
                     | FLAG_FEATURE_AUTOCLICK
@@ -432,6 +439,9 @@
                 if ((mEnabledFeatures & FLAG_REQUEST_2_FINGER_PASSTHROUGH) != 0) {
                     explorer.setTwoFingerPassthroughEnabled(true);
                 }
+                if ((mEnabledFeatures & FLAG_SEND_MOTION_EVENTS) != 0) {
+                    explorer.setSendMotionEventsEnabled(true);
+                }
                 addFirstEventHandler(displayId, explorer);
                 mTouchExplorer.put(displayId, explorer);
             }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index ae33f0c..35481a2 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -941,10 +941,19 @@
             if (resolvedUserId != mCurrentUserId) {
                 return null;
             }
-            if (mA11yWindowManager.findA11yWindowInfoByIdLocked(windowId) == null) {
+            final AccessibilityWindowInfo accessibilityWindowInfo = mA11yWindowManager
+                    .findA11yWindowInfoByIdLocked(windowId);
+            if (accessibilityWindowInfo == null) {
                 return null;
             }
-            return mA11yWindowManager.getWindowTokenForUserAndWindowIdLocked(userId, windowId);
+            // We use AccessibilityWindowInfo#getId instead of windowId. When the windowId comes
+            // from an embedded hierarchy, the system can't find correct window token because
+            // embedded hierarchy doesn't have windowInfo. Calling
+            // AccessibilityWindowManager#findA11yWindowInfoByIdLocked can look for its parent's
+            // windowInfo, so it is safer to use AccessibilityWindowInfo#getId
+            // to get window token to find real window.
+            return mA11yWindowManager.getWindowTokenForUserAndWindowIdLocked(userId,
+                    accessibilityWindowInfo.getId());
         }
     }
 
@@ -1858,6 +1867,10 @@
             if (userState.isFilterKeyEventsEnabledLocked()) {
                 flags |= AccessibilityInputFilter.FLAG_FEATURE_FILTER_KEY_EVENTS;
             }
+            if (userState.isSendMotionEventsEnabled()) {
+                flags |= AccessibilityInputFilter.FLAG_SEND_MOTION_EVENTS;
+            }
+
             if (userState.isAutoclickEnabledLocked()) {
                 flags |= AccessibilityInputFilter.FLAG_FEATURE_AUTOCLICK;
             }
@@ -2073,9 +2086,9 @@
     }
 
     private void updateAccessibilityEnabledSettingLocked(AccessibilityUserState userState) {
-        final long identity = Binder.clearCallingIdentity();
         final boolean isA11yEnabled = mUiAutomationManager.isUiAutomationRunningLocked()
                 || userState.isHandlingAccessibilityEventsLocked();
+        final long identity = Binder.clearCallingIdentity();
         try {
             Settings.Secure.putIntForUser(mContext.getContentResolver(),
                     Settings.Secure.ACCESSIBILITY_ENABLED,
@@ -2138,6 +2151,7 @@
         boolean serviceHandlesDoubleTapEnabled = false;
         boolean requestMultiFingerGestures = false;
         boolean requestTwoFingerPassthrough = false;
+        boolean sendMotionEvents = false;
         final int serviceCount = userState.mBoundServices.size();
         for (int i = 0; i < serviceCount; i++) {
             AccessibilityServiceConnection service = userState.mBoundServices.get(i);
@@ -2146,6 +2160,7 @@
                 serviceHandlesDoubleTapEnabled = service.isServiceHandlesDoubleTapEnabled();
                 requestMultiFingerGestures = service.isMultiFingerGesturesEnabled();
                 requestTwoFingerPassthrough = service.isTwoFingerPassthroughEnabled();
+                sendMotionEvents = service.isSendMotionEventsEnabled();
                 break;
             }
         }
@@ -2163,6 +2178,7 @@
         userState.setServiceHandlesDoubleTapLocked(serviceHandlesDoubleTapEnabled);
         userState.setMultiFingerGesturesLocked(requestMultiFingerGestures);
         userState.setTwoFingerPassthroughLocked(requestTwoFingerPassthrough);
+        userState.setSendMotionEventsEnabled(sendMotionEvents);
     }
 
     private boolean readAccessibilityShortcutKeySettingLocked(AccessibilityUserState userState) {
@@ -2384,8 +2400,8 @@
                 int numServices = services.size();
                 for (int i = 0; i < numServices; i++) {
                     if (services.get(i).isCapturingFingerprintGestures()) {
-                        final long identity = Binder.clearCallingIdentity();
                         IFingerprintService service = null;
+                        final long identity = Binder.clearCallingIdentity();
                         try {
                             service = IFingerprintService.Stub.asInterface(
                                     ServiceManager.getService(Context.FINGERPRINT_SERVICE));
@@ -2624,6 +2640,7 @@
     }
 
     @Override
+    @SuppressWarnings("AndroidFrameworkPendingIntentMutability")
     public PendingIntent getPendingIntentActivity(Context context, int requestCode, Intent intent,
             int flags) {
         return PendingIntent.getActivity(context, requestCode, intent, flags);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
index 41f3207..d766431 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
@@ -456,7 +456,7 @@
     }
 
     private boolean isShellAllowedToRetrieveWindowLocked(int userId, int windowId) {
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             IBinder windowToken = mAccessibilityWindowManager
                     .getWindowTokenForUserAndWindowIdLocked(userId, windowId);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index 4c9e444..240c7ff 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -111,6 +111,7 @@
     private boolean mServiceHandlesDoubleTap;
     private boolean mRequestMultiFingerGestures;
     private boolean mRequestTwoFingerPassthrough;
+    private boolean mSendMotionEventsEnabled;
     private int mUserInteractiveUiTimeout;
     private int mUserNonInteractiveUiTimeout;
     private int mNonInteractiveUiTimeout = 0;
@@ -171,6 +172,7 @@
         mServiceHandlesDoubleTap = false;
         mRequestMultiFingerGestures = false;
         mRequestTwoFingerPassthrough = false;
+        mSendMotionEventsEnabled = false;
         mIsDisplayMagnificationEnabled = false;
         mIsAutoclickEnabled = false;
         mUserNonInteractiveUiTimeout = 0;
@@ -460,6 +462,7 @@
                 .append(String.valueOf(mRequestMultiFingerGestures));
         pw.append(", requestTwoFingerPassthrough=")
                 .append(String.valueOf(mRequestTwoFingerPassthrough));
+        pw.append(", sendMotionEventsEnabled").append(String.valueOf(mSendMotionEventsEnabled));
         pw.append(", displayMagnificationEnabled=").append(String.valueOf(
                 mIsDisplayMagnificationEnabled));
         pw.append(", autoclickEnabled=").append(String.valueOf(mIsAutoclickEnabled));
@@ -802,6 +805,13 @@
         mRequestTwoFingerPassthrough = enabled;
     }
 
+    public boolean isSendMotionEventsEnabled() {
+        return mSendMotionEventsEnabled;
+    }
+
+    public void setSendMotionEventsEnabled(boolean mode) {
+        mSendMotionEventsEnabled = mode;
+    }
 
     public int getUserInteractiveUiTimeoutLocked() {
         return mUserInteractiveUiTimeout;
diff --git a/services/accessibility/java/com/android/server/accessibility/FingerprintGestureDispatcher.java b/services/accessibility/java/com/android/server/accessibility/FingerprintGestureDispatcher.java
index 96418aac..c9ec16e 100644
--- a/services/accessibility/java/com/android/server/accessibility/FingerprintGestureDispatcher.java
+++ b/services/accessibility/java/com/android/server/accessibility/FingerprintGestureDispatcher.java
@@ -118,7 +118,7 @@
     public boolean isFingerprintGestureDetectionAvailable() {
         if (!mHardwareSupportsGestures) return false;
 
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             return !mFingerprintService.isClientActive();
         } catch (RemoteException re) {
@@ -173,7 +173,7 @@
     @Override
     public boolean handleMessage(Message message) {
         if (message.what == MSG_REGISTER) {
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 mFingerprintService.addClientActiveCallback(this);
                 mRegisteredReadOnlyExceptInHandler = true;
@@ -184,7 +184,7 @@
             }
             return false;
         } else if (message.what == MSG_UNREGISTER) {
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 mFingerprintService.removeClientActiveCallback(this);
             } catch (RemoteException re) {
diff --git a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
index 3505499..eaf2694 100644
--- a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
+++ b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
@@ -308,14 +308,15 @@
 
     private void sendDownAndUpKeyEvents(int keyCode) {
         final long token = Binder.clearCallingIdentity();
-
-        // Inject down.
-        final long downTime = SystemClock.uptimeMillis();
-        sendKeyEventIdentityCleared(keyCode, KeyEvent.ACTION_DOWN, downTime, downTime);
-        sendKeyEventIdentityCleared(
-                keyCode, KeyEvent.ACTION_UP, downTime, SystemClock.uptimeMillis());
-
-        Binder.restoreCallingIdentity(token);
+        try {
+            // Inject down.
+            final long downTime = SystemClock.uptimeMillis();
+            sendKeyEventIdentityCleared(keyCode, KeyEvent.ACTION_DOWN, downTime, downTime);
+            sendKeyEventIdentityCleared(
+                    keyCode, KeyEvent.ACTION_UP, downTime, SystemClock.uptimeMillis());
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     private void sendKeyEventIdentityCleared(int keyCode, int action, long downTime, long time) {
@@ -329,22 +330,24 @@
 
     private void expandNotifications() {
         final long token = Binder.clearCallingIdentity();
-
-        StatusBarManager statusBarManager = (StatusBarManager) mContext.getSystemService(
-                android.app.Service.STATUS_BAR_SERVICE);
-        statusBarManager.expandNotificationsPanel();
-
-        Binder.restoreCallingIdentity(token);
+        try {
+            StatusBarManager statusBarManager = (StatusBarManager) mContext.getSystemService(
+                    android.app.Service.STATUS_BAR_SERVICE);
+            statusBarManager.expandNotificationsPanel();
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     private void expandQuickSettings() {
         final long token = Binder.clearCallingIdentity();
-
-        StatusBarManager statusBarManager = (StatusBarManager) mContext.getSystemService(
-                android.app.Service.STATUS_BAR_SERVICE);
-        statusBarManager.expandSettingsPanel();
-
-        Binder.restoreCallingIdentity(token);
+        try {
+            StatusBarManager statusBarManager = (StatusBarManager) mContext.getSystemService(
+                    android.app.Service.STATUS_BAR_SERVICE);
+            statusBarManager.expandSettingsPanel();
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     private boolean openRecents() {
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
index a860db3..2c38dc3 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
@@ -19,6 +19,7 @@
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD;
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD;
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_DOWN;
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_LEFT;
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_RIGHT;
@@ -27,11 +28,13 @@
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_DOWN;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_LEFT;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_RIGHT;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_UP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_TRIPLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_TRIPLE_TAP_AND_HOLD;
 import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_DOUBLE_TAP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD;
 import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SINGLE_TAP;
@@ -96,11 +99,15 @@
     boolean mMultiFingerGesturesEnabled;
     // Whether the two-finger passthrough is enabled when multi-finger gestures are enabled.
     private boolean mTwoFingerPassthroughEnabled;
+    // Whether to send the motion events during gesture dispatch.
+    private boolean mSendMotionEventsEnabled = false;
     // A list of all the multi-finger gestures, for easy adding and removal.
     private final List<GestureMatcher> mMultiFingerGestures = new ArrayList<>();
     // A list of two-finger swipes, for easy adding and removal when turning on or off two-finger
     // passthrough.
     private final List<GestureMatcher> mTwoFingerSwipes = new ArrayList<>();
+    // The list of motion events for the current gesture.
+    private List<MotionEvent> mEvents = new ArrayList<>();
     // Shared state information.
     private TouchState mState;
 
@@ -140,6 +147,9 @@
         mMultiFingerGestures.add(
                 new MultiFingerMultiTap(mContext, 2, 1, GESTURE_2_FINGER_SINGLE_TAP, this));
         mMultiFingerGestures.add(
+                new MultiFingerMultiTapAndHold(
+                        mContext, 2, 1, GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD, this));
+        mMultiFingerGestures.add(
                 new MultiFingerMultiTap(mContext, 2, 2, GESTURE_2_FINGER_DOUBLE_TAP, this));
         mMultiFingerGestures.add(
                 new MultiFingerMultiTapAndHold(
@@ -153,9 +163,17 @@
                 new MultiFingerMultiTap(mContext, 3, 2, GESTURE_3_FINGER_DOUBLE_TAP, this));
         mMultiFingerGestures.add(
                 new MultiFingerMultiTapAndHold(
+                        mContext, 3, 1, GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD, this));
+        mMultiFingerGestures.add(
+                new MultiFingerMultiTapAndHold(
                         mContext, 3, 2, GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD, this));
         mMultiFingerGestures.add(
                 new MultiFingerMultiTap(mContext, 3, 3, GESTURE_3_FINGER_TRIPLE_TAP, this));
+        mMultiFingerGestures.add(
+                new MultiFingerMultiTapAndHold(
+                        mContext, 3, 3, GESTURE_3_FINGER_TRIPLE_TAP_AND_HOLD, this));
+        mMultiFingerGestures.add(
+                new MultiFingerMultiTap(mContext, 3, 3, GESTURE_3_FINGER_TRIPLE_TAP, this));
         // Four-finger taps.
         mMultiFingerGestures.add(
                 new MultiFingerMultiTap(mContext, 4, 1, GESTURE_4_FINGER_SINGLE_TAP, this));
@@ -216,6 +234,9 @@
                 return false;
             }
         }
+        if (mSendMotionEventsEnabled) {
+            mEvents.add(MotionEvent.obtainNoHistory(rawEvent));
+        }
         for (GestureMatcher matcher : mGestures) {
             if (matcher.getState() != GestureMatcher.STATE_GESTURE_CANCELED) {
                 if (DEBUG) {
@@ -226,9 +247,8 @@
                     Slog.d(LOG_TAG, matcher.toString());
                 }
                 if (matcher.getState() == GestureMatcher.STATE_GESTURE_COMPLETED) {
-                    // Here we just clear and return. The actual gesture dispatch is done in
+                    // Here we just return. The actual gesture dispatch is done in
                     // onStateChanged().
-                    clear();
                     // No need to process this event any further.
                     return true;
                 }
@@ -241,6 +261,11 @@
         for (GestureMatcher matcher : mGestures) {
             matcher.clear();
         }
+        if (mEvents != null) {
+            while (mEvents.size() > 0) {
+                mEvents.remove(0).recycle();
+            }
+        }
     }
 
     /**
@@ -326,29 +351,28 @@
             case GESTURE_DOUBLE_TAP:
                 if (mServiceHandlesDoubleTap) {
                     AccessibilityGestureEvent gestureEvent =
-                            new AccessibilityGestureEvent(gestureId, event.getDisplayId());
+                            new AccessibilityGestureEvent(gestureId, event.getDisplayId(), mEvents);
                     mListener.onGestureCompleted(gestureEvent);
                 } else {
                     mListener.onDoubleTap(event, rawEvent, policyFlags);
                 }
-                clear();
                 break;
             case GESTURE_DOUBLE_TAP_AND_HOLD:
                 if (mServiceHandlesDoubleTap) {
                     AccessibilityGestureEvent gestureEvent =
-                            new AccessibilityGestureEvent(gestureId, event.getDisplayId());
+                            new AccessibilityGestureEvent(gestureId, event.getDisplayId(), mEvents);
                     mListener.onGestureCompleted(gestureEvent);
                 } else {
                     mListener.onDoubleTapAndHold(event, rawEvent, policyFlags);
                 }
-                clear();
                 break;
             default:
                 AccessibilityGestureEvent gestureEvent =
-                        new AccessibilityGestureEvent(gestureId, event.getDisplayId());
+                        new AccessibilityGestureEvent(gestureId, event.getDisplayId(), mEvents);
                 mListener.onGestureCompleted(gestureEvent);
                 break;
         }
+        clear();
     }
 
     public boolean isMultiFingerGesturesEnabled() {
@@ -392,4 +416,25 @@
     public boolean isServiceHandlesDoubleTapEnabled() {
         return mServiceHandlesDoubleTap;
     }
+
+    public void setSendMotionEventsEnabled(boolean mode) {
+        mSendMotionEventsEnabled = mode;
+        if (!mode) {
+            while (mEvents.size() > 0) {
+                mEvents.remove(0).recycle();
+            }
+        }
+    }
+
+    public boolean isSendMotionEventsEnabled() {
+        return mSendMotionEventsEnabled;
+    }
+
+    /**
+     * Returns the current list of motion events. It is the caller's responsibility to copy the list
+     * if they want it to persist after a call to clear().
+     */
+    public List<MotionEvent> getMotionEvents() {
+        return mEvents;
+    }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTap.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTap.java
index e15c495..46b4628 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTap.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTap.java
@@ -162,6 +162,7 @@
         // Accept down only before target number of fingers are down
         // or the finger count is not more than target.
         if ((currentFingerCount > mTargetFingerCount) || mIsTargetFingerCountReached) {
+            mIsTargetFingerCountReached = false;
             cancelGesture(event, rawEvent, policyFlags);
             return;
         }
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index d8c692b8..5460e80 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -37,6 +37,7 @@
 import static com.android.server.accessibility.gestures.TouchState.ALL_POINTER_ID_BITS;
 
 import android.accessibilityservice.AccessibilityGestureEvent;
+import android.accessibilityservice.AccessibilityService;
 import android.annotation.NonNull;
 import android.content.Context;
 import android.graphics.Region;
@@ -340,6 +341,14 @@
     public void onDoubleTapAndHold(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
         if (mDispatcher.longPressWithTouchEvents(event, policyFlags)) {
             sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
+            if (isSendMotionEventsEnabled()) {
+                AccessibilityGestureEvent gestureEvent =
+                        new AccessibilityGestureEvent(
+                                AccessibilityService.GESTURE_DOUBLE_TAP_AND_HOLD,
+                                event.getDisplayId(),
+                                mGestureDetector.getMotionEvents());
+                mAms.onGesture(gestureEvent);
+            }
             mState.startDelegating();
         }
     }
@@ -350,7 +359,14 @@
         // Remove pending event deliveries.
         mSendHoverEnterAndMoveDelayed.cancel();
         mSendHoverExitDelayed.cancel();
-
+        if (isSendMotionEventsEnabled()) {
+            AccessibilityGestureEvent gestureEvent =
+                    new AccessibilityGestureEvent(
+                            AccessibilityService.GESTURE_DOUBLE_TAP,
+                            event.getDisplayId(),
+                            mGestureDetector.getMotionEvents());
+            mAms.onGesture(gestureEvent);
+        }
         if (mSendTouchExplorationEndDelayed.isPending()) {
             mSendTouchExplorationEndDelayed.forceSendAndRemove();
         }
@@ -385,6 +401,9 @@
 
     @Override
     public boolean onGestureCompleted(AccessibilityGestureEvent gestureEvent) {
+        if (DEBUG) {
+            Slog.d(LOG_TAG, "Dispatching gesture event:" + gestureEvent.toString());
+        }
         endGestureDetection(true);
         mSendTouchInteractionEndDelayed.cancel();
         mAms.onGesture(gestureEvent);
@@ -411,12 +430,24 @@
                 mDispatcher.sendMotionEvent(
                         event,
                         ACTION_HOVER_MOVE,
-                        mState.getLastReceivedEvent(),
+                        event,
                         pointerIdBits,
                         policyFlags);
                 return true;
             }
         }
+        if (isSendMotionEventsEnabled()) {
+            // Send a gesture with motion events to represent the cancelled gesture.
+            AccessibilityGestureEvent gestureEvent =
+                    new AccessibilityGestureEvent(
+                            AccessibilityService.GESTURE_UNKNOWN,
+                            event.getDisplayId(),
+                            mGestureDetector.getMotionEvents());
+            if (DEBUG) {
+                Slog.d(LOG_TAG, "Dispatching gesture event:" + gestureEvent.toString());
+            }
+            mAms.onGesture(gestureEvent);
+        }
         return false;
     }
 
@@ -620,6 +651,14 @@
                 if (isDraggingGesture(event)) {
                     // Two pointers moving in the same direction within
                     // a given distance perform a drag.
+                    if (isSendMotionEventsEnabled()) {
+                        AccessibilityGestureEvent gestureEvent =
+                                new AccessibilityGestureEvent(
+                                        AccessibilityService.GESTURE_PASSTHROUGH,
+                                        event.getDisplayId(),
+                                        mGestureDetector.getMotionEvents());
+                        mAms.onGesture(gestureEvent);
+                    }
                     computeDraggingPointerIdIfNeeded(event);
                     pointerIdBits = 1 << mDraggingPointerId;
                     event.setEdgeFlags(mReceivedPointerTracker.getLastReceivedDownEdgeFlags());
@@ -636,6 +675,14 @@
                     mState.startDragging();
                 } else {
                     // Two pointers moving arbitrary are delegated to the view hierarchy.
+                    if (isSendMotionEventsEnabled()) {
+                        AccessibilityGestureEvent gestureEvent =
+                                new AccessibilityGestureEvent(
+                                        AccessibilityService.GESTURE_PASSTHROUGH,
+                                        event.getDisplayId(),
+                                        mGestureDetector.getMotionEvents());
+                        mAms.onGesture(gestureEvent);
+                    }
                     mState.startDelegating();
                     mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags);
                 }
@@ -650,6 +697,14 @@
                                 if (DEBUG) {
                                     Slog.d(LOG_TAG, "Three-finger edge swipe detected.");
                                 }
+                                if (isSendMotionEventsEnabled()) {
+                                    AccessibilityGestureEvent gestureEvent =
+                                            new AccessibilityGestureEvent(
+                                                    AccessibilityService.GESTURE_PASSTHROUGH,
+                                                    event.getDisplayId(),
+                                                    mGestureDetector.getMotionEvents());
+                                    mAms.onGesture(gestureEvent);
+                                }
                                 mState.startDelegating();
                                 if (mState.isTouchExploring()) {
                                     mDispatcher.sendDownForAllNotInjectedPointers(event,
@@ -663,6 +718,14 @@
                     }
                 } else {
                     // More than two pointers are delegated to the view hierarchy.
+                    if (isSendMotionEventsEnabled()) {
+                        AccessibilityGestureEvent gestureEvent =
+                                new AccessibilityGestureEvent(
+                                        AccessibilityService.GESTURE_PASSTHROUGH,
+                                        event.getDisplayId(),
+                                        mGestureDetector.getMotionEvents());
+                        mAms.onGesture(gestureEvent);
+                    }
                     mState.startDelegating();
                     event = MotionEvent.obtainNoHistory(event);
                     mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags);
@@ -1109,6 +1172,7 @@
     public void setTwoFingerPassthroughEnabled(boolean enabled) {
         mGestureDetector.setTwoFingerPassthroughEnabled(enabled);
     }
+
     public void setGestureDetectionPassthroughRegion(Region region) {
         mGestureDetectionPassthroughRegion = region;
     }
@@ -1117,6 +1181,17 @@
         mTouchExplorationPassthroughRegion = region;
     }
 
+    /**
+     * Whether to send the motion events that make up each gesture to the accessibility service.
+     */
+    public void setSendMotionEventsEnabled(boolean mode) {
+        mGestureDetector.setSendMotionEventsEnabled(mode);
+    }
+
+    public boolean isSendMotionEventsEnabled() {
+        return mGestureDetector.isSendMotionEventsEnabled();
+    }
+
     private boolean shouldPerformGestureDetection(MotionEvent event) {
         if (mState.isDelegating()) {
             return false;
@@ -1213,7 +1288,14 @@
         public void run() {
             // Send an accessibility event to announce the touch exploration start.
             mDispatcher.sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_START);
-
+            if (isSendMotionEventsEnabled()) {
+                AccessibilityGestureEvent gestureEvent =
+                        new AccessibilityGestureEvent(
+                                AccessibilityService.GESTURE_TOUCH_EXPLORATION,
+                                mState.getLastReceivedEvent().getDisplayId(),
+                                mGestureDetector.getMotionEvents());
+                mAms.onGesture(gestureEvent);
+            }
             if (!mEvents.isEmpty() && !mRawEvents.isEmpty()) {
                 // Deliver a down event.
                 mDispatcher.sendMotionEvent(mEvents.get(0), ACTION_HOVER_ENTER,
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index c583dcc..189a390 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -79,6 +79,8 @@
 
     private final ScreenStateObserver mScreenStateObserver;
 
+    private final MagnificationRequestObserver mMagnificationRequestObserver;
+
     private int mUserId;
 
     private final long mMainThreadId;
@@ -479,6 +481,8 @@
             sendSpecToAnimation(mCurrentMagnificationSpec, animationCallback);
             if (isMagnifying() && (id != INVALID_ID)) {
                 mIdOfLastServiceToMagnify = id;
+                mMagnificationRequestObserver.onRequestMagnificationSpec(mDisplayId,
+                        mIdOfLastServiceToMagnify);
             }
             return changed;
         }
@@ -609,22 +613,27 @@
      * FullScreenMagnificationController Constructor
      */
     public FullScreenMagnificationController(@NonNull Context context,
-            @NonNull AccessibilityManagerService ams, @NonNull Object lock) {
+            @NonNull AccessibilityManagerService ams, @NonNull Object lock,
+            @NonNull MagnificationRequestObserver magnificationRequestObserver) {
         this(new ControllerContext(context, ams,
                 LocalServices.getService(WindowManagerInternal.class),
                 new Handler(context.getMainLooper()),
-                context.getResources().getInteger(R.integer.config_longAnimTime)), lock);
+                context.getResources().getInteger(R.integer.config_longAnimTime)), lock,
+                magnificationRequestObserver);
     }
 
     /**
      * Constructor for tests
      */
     @VisibleForTesting
-    public FullScreenMagnificationController(@NonNull ControllerContext ctx, @NonNull Object lock) {
+    public FullScreenMagnificationController(@NonNull ControllerContext ctx,
+            @NonNull Object lock,
+            @NonNull MagnificationRequestObserver magnificationRequestObserver) {
         mControllerCtx = ctx;
         mLock = lock;
         mMainThreadId = mControllerCtx.getContext().getMainLooper().getThread().getId();
         mScreenStateObserver = new ScreenStateObserver(mControllerCtx.getContext(), this);
+        mMagnificationRequestObserver = magnificationRequestObserver;
     }
 
     /**
@@ -1487,4 +1496,16 @@
     private static MagnificationAnimationCallback transformToStubCallback(boolean animate) {
         return animate ? STUB_ANIMATION_CALLBACK : null;
     }
+
+    interface  MagnificationRequestObserver {
+
+        /**
+         * Called when the {@link MagnificationSpec} is changed with non-default
+         * scale by the service.
+         *
+         * @param displayId the logical display id
+         * @param serviceId the ID of the service requesting the change
+         */
+        void onRequestMagnificationSpec(int displayId, int serviceId);
+    }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index 0dc5267..b3fc07a 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -128,6 +128,19 @@
         setDisableMagnificationCallbackLocked(displayId, animationEndCallback);
     }
 
+    void onRequestMagnificationSpec(int displayId, int serviceId) {
+        synchronized (mLock) {
+            if (serviceId == AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID) {
+                return;
+            }
+            if (mWindowMagnificationMgr == null
+                    || !mWindowMagnificationMgr.isWindowMagnifierEnabled(displayId)) {
+                return;
+            }
+            mWindowMagnificationMgr.disableWindowMagnification(displayId, false);
+        }
+    }
+
     /**
      * Updates the active user ID of {@link FullScreenMagnificationController} and {@link
      * WindowMagnificationManager}.
@@ -184,7 +197,7 @@
         synchronized (mLock) {
             if (mFullScreenMagnificationController == null) {
                 mFullScreenMagnificationController = new FullScreenMagnificationController(mContext,
-                        mAms, mLock);
+                        mAms, mLock, this::onRequestMagnificationSpec);
                 mFullScreenMagnificationController.setUserId(mAms.getCurrentUserIdLocked());
             }
         }
diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java
index 59ba82e..e9a099a 100644
--- a/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java
+++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java
@@ -189,7 +189,7 @@
                 throw new SecurityException(msg);
             }
 
-            long origId = Binder.clearCallingIdentity();
+            final long origId = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
                     final AppPredictionPerUserService service = getServiceForUserLocked(userId);
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 060d097..1c4d752 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -630,8 +630,7 @@
         final long identity = Binder.clearCallingIdentity();
         try {
             if (provider.maskedBySuspendedPackage) {
-                UserInfo userInfo = mUserManager.getUserInfo(providerUserId);
-                showBadge = userInfo.isManagedProfile();
+                showBadge = mUserManager.hasBadge(providerUserId);
                 final String suspendingPackage = mPackageManagerInternal.getSuspendingPackage(
                         providerPackage, providerUserId);
                 if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) {
@@ -665,8 +664,12 @@
                 if (targetWidget != null && targetWidget != widget) continue;
                 PendingIntent intent = null;
                 if (onClickIntent != null) {
+                    // Rare informational activity click is okay being
+                    // immutable; the tradeoff is more security in exchange for
+                    // losing bounds-based window animations
                     intent = PendingIntent.getActivity(mContext, widget.appWidgetId,
-                            onClickIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+                            onClickIntent,
+                            PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
                 }
                 RemoteViews views = createMaskedWidgetRemoteViews(iconBitmap, showBadge, intent);
                 if (widget.replaceWithMaskedViewsLocked(views)) {
@@ -2408,10 +2411,12 @@
             Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
             intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
             intent.setComponent(provider.info.provider);
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
+                // Broadcast alarms sent by system are immutable
                 provider.broadcast = PendingIntent.getBroadcastAsUser(mContext, 1, intent,
-                        PendingIntent.FLAG_UPDATE_CURRENT, provider.info.getProfile());
+                        PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE,
+                        provider.info.getProfile());
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -3616,10 +3621,10 @@
     }
 
     private boolean isProfileWithLockedParent(int userId) {
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             UserInfo userInfo = mUserManager.getUserInfo(userId);
-            if (userInfo != null && userInfo.isManagedProfile()) {
+            if (userInfo != null && userInfo.isProfile()) {
                 UserInfo parentInfo = mUserManager.getProfileParent(userId);
                 if (parentInfo != null
                         && !isUserRunningAndUnlocked(parentInfo.getUserHandle().getIdentifier())) {
@@ -3634,7 +3639,7 @@
 
     private boolean isProfileWithUnlockedParent(int userId) {
         UserInfo userInfo = mUserManager.getUserInfo(userId);
-        if (userInfo != null && userInfo.isManagedProfile()) {
+        if (userInfo != null && userInfo.isProfile()) {
             UserInfo parentInfo = mUserManager.getProfileParent(userId);
             if (parentInfo != null
                     && mUserManager.isUserUnlockingOrUnlocked(parentInfo.getUserHandle())) {
diff --git a/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java
index c25dd37..dd5e58a 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java
@@ -35,8 +35,8 @@
  * Controls the interaction with the IME for the {@link AutofillInlineSuggestionsRequestSession}s.
  *
  * <p>The class maintains the inline suggestion session with the autofill service. There is at most
- * one active inline suggestion session at any given  corresponding to one focused view.
- * New sessions are created only when {@link #onCreateInlineSuggestionsRequestLocked} is called.</p>
+ * one active inline suggestion session at any given  corresponding to one focused view. New
+ * sessions are created only when {@link #onCreateInlineSuggestionsRequestLocked} is called.</p>
  *
  * <p>The class manages the interaction between the {@link com.android.server.autofill.Session} and
  * the inline suggestion session whenever inline suggestions can be provided. All calls to the
@@ -83,7 +83,6 @@
     @GuardedBy("mLock")
     void onCreateInlineSuggestionsRequestLocked(@NonNull AutofillId autofillId,
             @NonNull Consumer<InlineSuggestionsRequest> requestConsumer, @NonNull Bundle uiExtras) {
-        // TODO(b/151123764): rename the method to better reflect what it does.
         if (mSession != null) {
             // Destroy the existing session.
             mSession.destroySessionLocked();
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index d59c955..864ead1 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -55,6 +55,7 @@
 import android.service.autofill.FieldClassification.Match;
 import android.service.autofill.FillEventHistory;
 import android.service.autofill.FillEventHistory.Event;
+import android.service.autofill.FillEventHistory.Event.NoSaveReason;
 import android.service.autofill.FillResponse;
 import android.service.autofill.IAutoFillService;
 import android.service.autofill.InlineSuggestionRenderService;
@@ -429,9 +430,15 @@
             return;
         }
 
-        session.logContextCommitted();
+        final Session.SaveResult saveResult = session.showSaveLocked();
 
-        final boolean finished = session.showSaveLocked();
+        session.logContextCommitted(saveResult.getNoSaveReason());
+
+        if (saveResult.isLogSaveShown()) {
+            session.logSaveUiShown();
+        }
+
+        final boolean finished = saveResult.isRemoveSession();
         if (sVerbose) Slog.v(TAG, "finishSessionLocked(): session finished on save? " + finished);
 
         if (finished) {
@@ -868,7 +875,9 @@
             @NonNull ComponentName appComponentName, boolean compatMode) {
         logContextCommittedLocked(sessionId, clientState, selectedDatasets, ignoredDatasets,
                 changedFieldIds, changedDatasetIds, manuallyFilledFieldIds,
-                manuallyFilledDatasetIds, null, null, appComponentName, compatMode);
+                manuallyFilledDatasetIds, /* detectedFieldIdsList= */ null,
+                /* detectedFieldClassificationsList= */ null, appComponentName, compatMode,
+                Event.NO_SAVE_REASON_NONE);
     }
 
     @GuardedBy("mLock")
@@ -881,7 +890,8 @@
             @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
             @Nullable ArrayList<AutofillId> detectedFieldIdsList,
             @Nullable ArrayList<FieldClassification> detectedFieldClassificationsList,
-            @NonNull ComponentName appComponentName, boolean compatMode) {
+            @NonNull ComponentName appComponentName, boolean compatMode,
+            @NoSaveReason int saveDialogNotShowReason) {
         if (isValidEventLocked("logDatasetNotSelected()", sessionId)) {
             if (sVerbose) {
                 Slog.v(TAG, "logContextCommitted() with FieldClassification: id=" + sessionId
@@ -893,7 +903,8 @@
                         + ", detectedFieldIds=" + detectedFieldIdsList
                         + ", detectedFieldClassifications=" + detectedFieldClassificationsList
                         + ", appComponentName=" + appComponentName.toShortString()
-                        + ", compatMode=" + compatMode);
+                        + ", compatMode=" + compatMode
+                        + ", saveDialogNotShowReason=" + saveDialogNotShowReason);
             }
             AutofillId[] detectedFieldsIds = null;
             FieldClassification[] detectedFieldClassifications = null;
@@ -929,7 +940,7 @@
                     clientState, selectedDatasets, ignoredDatasets,
                     changedFieldIds, changedDatasetIds,
                     manuallyFilledFieldIds, manuallyFilledDatasetIds,
-                    detectedFieldsIds, detectedFieldClassifications));
+                    detectedFieldsIds, detectedFieldClassifications, saveDialogNotShowReason));
         }
     }
 
@@ -1044,7 +1055,7 @@
             pw.println(compatPkgs);
         }
         pw.print(prefix); pw.print("Inline Suggestions Enabled: ");
-        pw.println(isInlineSuggestionsEnabled());
+        pw.println(isInlineSuggestionsEnabledLocked());
         pw.print(prefix); pw.print("Last prune: "); pw.println(mLastPrune);
 
         mDisabledInfoCache.dump(mUserId, prefix, pw);
@@ -1157,7 +1168,7 @@
     }
 
     @GuardedBy("mLock")
-    boolean isInlineSuggestionsEnabled() {
+    boolean isInlineSuggestionsEnabledLocked() {
         if (mInfo != null) {
             return mInfo.isInlineSuggestionsEnabled();
         }
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index 80b0375..e35c0ee 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -24,6 +24,9 @@
 import android.content.ComponentName;
 import android.metrics.LogMaker;
 import android.service.autofill.Dataset;
+import android.service.autofill.InternalSanitizer;
+import android.service.autofill.SaveInfo;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Slog;
@@ -38,6 +41,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
+import java.util.Arrays;
 
 public final class Helper {
 
@@ -234,6 +238,46 @@
         }
     }
 
+    @Nullable
+    static ArrayMap<AutofillId, InternalSanitizer> createSanitizers(@Nullable SaveInfo saveInfo) {
+        if (saveInfo == null) return null;
+
+        final InternalSanitizer[] sanitizerKeys = saveInfo.getSanitizerKeys();
+        if (sanitizerKeys == null) return null;
+
+        final int size = sanitizerKeys.length;
+        final ArrayMap<AutofillId, InternalSanitizer> sanitizers = new ArrayMap<>(size);
+        if (sDebug) Slog.d(TAG, "Service provided " + size + " sanitizers");
+        final AutofillId[][] sanitizerValues = saveInfo.getSanitizerValues();
+        for (int i = 0; i < size; i++) {
+            final InternalSanitizer sanitizer = sanitizerKeys[i];
+            final AutofillId[] ids = sanitizerValues[i];
+            if (sDebug) {
+                Slog.d(TAG, "sanitizer #" + i + " (" + sanitizer + ") for ids "
+                        + Arrays.toString(ids));
+            }
+            for (AutofillId id : ids) {
+                sanitizers.put(id, sanitizer);
+            }
+        }
+        return sanitizers;
+    }
+
+    /**
+     * Returns true if {@code s1} contains all characters of {@code s2}, in order.
+     */
+    static boolean containsCharsInOrder(String s1, String s2) {
+        int prevIndex = -1;
+        for (char ch : s2.toCharArray()) {
+            int index = TextUtils.indexOf(s1, ch, prevIndex + 1);
+            if (index == -1) {
+                return false;
+            }
+            prevIndex = index;
+        }
+        return true;
+    }
+
     private interface ViewNodeFilter {
         boolean matches(ViewNode node);
     }
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 4d2e4f6..f596b07 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -30,6 +30,8 @@
 import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString;
 
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+import static com.android.server.autofill.Helper.containsCharsInOrder;
+import static com.android.server.autofill.Helper.createSanitizers;
 import static com.android.server.autofill.Helper.getNumericValue;
 import static com.android.server.autofill.Helper.sDebug;
 import static com.android.server.autofill.Helper.sVerbose;
@@ -71,6 +73,8 @@
 import android.service.autofill.FieldClassification.Match;
 import android.service.autofill.FieldClassificationUserData;
 import android.service.autofill.FillContext;
+import android.service.autofill.FillEventHistory.Event;
+import android.service.autofill.FillEventHistory.Event.NoSaveReason;
 import android.service.autofill.FillRequest;
 import android.service.autofill.FillResponse;
 import android.service.autofill.InlinePresentation;
@@ -115,7 +119,6 @@
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
-import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Consumer;
 import java.util.function.Function;
@@ -370,26 +373,24 @@
      * CountDownLatch.
      */
     private final class AssistDataReceiverImpl extends IAssistDataReceiver.Stub {
-
+        @GuardedBy("mLock")
+        private boolean mWaitForInlineRequest;
         @GuardedBy("mLock")
         private InlineSuggestionsRequest mPendingInlineSuggestionsRequest;
         @GuardedBy("mLock")
         private FillRequest mPendingFillRequest;
-        @GuardedBy("mLock")
-        private CountDownLatch mCountDownLatch = new CountDownLatch(0);
 
         @Nullable Consumer<InlineSuggestionsRequest> newAutofillRequestLocked(ViewState viewState,
                 boolean isInlineRequest) {
-            mCountDownLatch = new CountDownLatch(isInlineRequest ? 2 : 1);
             mPendingFillRequest = null;
+            mWaitForInlineRequest = isInlineRequest;
             mPendingInlineSuggestionsRequest = null;
             return isInlineRequest ? (inlineSuggestionsRequest) -> {
                 synchronized (mLock) {
-                    if (mCountDownLatch.getCount() == 0) {
+                    if (!mWaitForInlineRequest || mPendingInlineSuggestionsRequest != null) {
                         return;
                     }
                     mPendingInlineSuggestionsRequest = inlineSuggestionsRequest;
-                    mCountDownLatch.countDown();
                     maybeRequestFillLocked();
                     viewState.resetState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST);
                 }
@@ -397,16 +398,23 @@
         }
 
         void maybeRequestFillLocked() {
-            if (mCountDownLatch.getCount() > 0 || mPendingFillRequest == null) {
+            if (mPendingFillRequest == null) {
                 return;
             }
-            if (mPendingInlineSuggestionsRequest != null) {
+
+            if (mWaitForInlineRequest) {
+                if (mPendingInlineSuggestionsRequest == null) {
+                    return;
+                }
+
                 mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(),
                         mPendingFillRequest.getFillContexts(), mPendingFillRequest.getClientState(),
                         mPendingFillRequest.getFlags(), mPendingInlineSuggestionsRequest);
             }
+
             mRemoteFillService.onFillRequest(mPendingFillRequest);
             mPendingInlineSuggestionsRequest = null;
+            mWaitForInlineRequest = false;
             mPendingFillRequest = null;
         }
 
@@ -507,15 +515,8 @@
                 request = new FillRequest(requestId, contexts, mClientState, flags,
                         /*inlineSuggestionsRequest=*/null);
 
-                if (mCountDownLatch.getCount() > 0) {
-                    mPendingFillRequest = request;
-                    mCountDownLatch.countDown();
-                    maybeRequestFillLocked();
-                } else {
-                    // TODO(b/151867668): ideally this case should not happen, but it was
-                    //  observed, we should figure out why and fix.
-                    mRemoteFillService.onFillRequest(request);
-                }
+                mPendingFillRequest = request;
+                maybeRequestFillLocked();
             }
 
             if (mActivityToken != null) {
@@ -543,6 +544,10 @@
         return ids;
     }
 
+    /**
+     * Returns the String value of an {@link AutofillValue} by {@link AutofillId id} if it is of
+     * type {@code AUTOFILL_TYPE_TEXT} or {@code AUTOFILL_TYPE_LIST}.
+     */
     @Override
     @Nullable
     public String findByAutofillId(@NonNull AutofillId id) {
@@ -704,7 +709,7 @@
      * Autofill provider).
      */
     private boolean isInlineSuggestionsEnabledByAutofillProviderLocked() {
-        return mService.isInlineSuggestionsEnabled();
+        return mService.isInlineSuggestionsEnabledLocked();
     }
 
     private boolean isViewFocusedLocked(int flags) {
@@ -765,7 +770,7 @@
         // triggers a new partition and we end up with many duplicate partitions. This is
         // enhanced as the focus change can be much faster than the taking of the assist structure.
         // Hence remove the currently queued request and replace it with the one queued after the
-        // structure is taken. This causes only one fill request per bust of focus changes.
+        // structure is taken. This causes only one fill request per burst of focus changes.
         cancelCurrentRequestLocked();
 
         // Only ask IME to create inline suggestions request if Autofill provider supports it and
@@ -778,7 +783,7 @@
                 && isViewFocusedLocked(flags)) {
             Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer =
                     mAssistReceiver.newAutofillRequestLocked(viewState,
-                            /*isInlineRequest=*/ true);
+                            /* isInlineRequest= */ true);
             if (inlineSuggestionsRequestConsumer != null) {
                 final AutofillId focusedId = mCurrentViewId;
                 remoteRenderService.getInlineSuggestionsRendererInfo(
@@ -792,8 +797,7 @@
                 viewState.setState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST);
             }
         } else {
-            mAssistReceiver.newAutofillRequestLocked(viewState,
-                    /*isInlineRequest=*/ false);
+            mAssistReceiver.newAutofillRequestLocked(viewState, /* isInlineRequest= */ false);
         }
 
         // Now request the assist structure data.
@@ -1170,7 +1174,14 @@
         return null;
     }
 
-    // FillServiceCallbacks
+    // VultureCallback
+    @Override
+    public void onServiceDied(@NonNull RemoteFillService service) {
+        Slog.w(TAG, "removing session because service died");
+        forceRemoveSelfLocked();
+    }
+
+    // AutoFillUiCallback
     @Override
     public void authenticate(int requestId, int datasetIndex, IntentSender intent, Bundle extras,
             boolean authenticateInline) {
@@ -1200,13 +1211,6 @@
                 this, authenticationId, intent, fillInIntent, authenticateInline));
     }
 
-    // VultureCallback
-    @Override
-    public void onServiceDied(@NonNull RemoteFillService service) {
-        Slog.w(TAG, "removing session because service died");
-        forceRemoveSelfLocked();
-    }
-
     // AutoFillUiCallback
     @Override
     public void fill(int requestId, int datasetIndex, Dataset dataset) {
@@ -1280,6 +1284,7 @@
         }
     }
 
+    // AutoFillUiCallback
     @Override
     public void dispatchUnhandledKey(AutofillId id, KeyEvent keyEvent) {
         synchronized (mLock) {
@@ -1596,10 +1601,22 @@
      * when necessary.
      */
     public void logContextCommitted() {
-        mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this));
+        mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this,
+                Event.NO_SAVE_REASON_NONE));
     }
 
-    private void handleLogContextCommitted() {
+    /**
+     * Generates a {@link android.service.autofill.FillEventHistory.Event#TYPE_CONTEXT_COMMITTED}
+     * when necessary.
+     *
+     * @param saveDialogNotShowReason The reason why a save dialog was not shown.
+     */
+    public void logContextCommitted(@NoSaveReason int saveDialogNotShowReason) {
+        mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this,
+                saveDialogNotShowReason));
+    }
+
+    private void handleLogContextCommitted(@NoSaveReason int saveDialogNotShowReason) {
         final FillResponse lastResponse;
         synchronized (mLock) {
             lastResponse = getLastResponseLocked("logContextCommited(%s)");
@@ -1629,22 +1646,25 @@
 
         // Sets field classification scores
         if (userData != null && fcStrategy != null) {
-            logFieldClassificationScore(fcStrategy, userData);
+            logFieldClassificationScore(fcStrategy, userData, saveDialogNotShowReason);
         } else {
-            logContextCommitted(null, null);
+            logContextCommitted(null, null, saveDialogNotShowReason);
         }
     }
 
     private void logContextCommitted(@Nullable ArrayList<AutofillId> detectedFieldIds,
-            @Nullable ArrayList<FieldClassification> detectedFieldClassifications) {
+            @Nullable ArrayList<FieldClassification> detectedFieldClassifications,
+            @NoSaveReason int saveDialogNotShowReason) {
         synchronized (mLock) {
-            logContextCommittedLocked(detectedFieldIds, detectedFieldClassifications);
+            logContextCommittedLocked(detectedFieldIds, detectedFieldClassifications,
+                    saveDialogNotShowReason);
         }
     }
 
     @GuardedBy("mLock")
     private void logContextCommittedLocked(@Nullable ArrayList<AutofillId> detectedFieldIds,
-            @Nullable ArrayList<FieldClassification> detectedFieldClassifications) {
+            @Nullable ArrayList<FieldClassification> detectedFieldClassifications,
+            @NoSaveReason int saveDialogNotShowReason) {
         final FillResponse lastResponse = getLastResponseLocked("logContextCommited(%s)");
         if (lastResponse == null) return;
 
@@ -1822,10 +1842,10 @@
             }
         }
 
-        mService.logContextCommittedLocked(id, mClientState, mSelectedDatasetIds,
-                ignoredDatasets, changedFieldIds, changedDatasetIds,
-                manuallyFilledFieldIds, manuallyFilledDatasetIds, detectedFieldIds,
-                detectedFieldClassifications, mComponentName, mCompatMode);
+        mService.logContextCommittedLocked(id, mClientState, mSelectedDatasetIds, ignoredDatasets,
+                changedFieldIds, changedDatasetIds, manuallyFilledFieldIds,
+                manuallyFilledDatasetIds, detectedFieldIds, detectedFieldClassifications,
+                mComponentName, mCompatMode, saveDialogNotShowReason);
     }
 
     /**
@@ -1833,7 +1853,8 @@
      * {@code fieldId} based on its {@code currentValue} and {@code userData}.
      */
     private void logFieldClassificationScore(@NonNull FieldClassificationStrategy fcStrategy,
-            @NonNull FieldClassificationUserData userData) {
+            @NonNull FieldClassificationUserData userData,
+            @NoSaveReason int saveDialogNotShowReason) {
 
         final String[] userValues = userData.getValues();
         final String[] categoryIds = userData.getCategoryIds();
@@ -1879,7 +1900,7 @@
         final RemoteCallback callback = new RemoteCallback((result) -> {
             if (result == null) {
                 if (sDebug) Slog.d(TAG, "setFieldClassificationScore(): no results");
-                logContextCommitted(null, null);
+                logContextCommitted(null, null, saveDialogNotShowReason);
                 return;
             }
             final Scores scores = result.getParcelable(EXTRA_SCORES);
@@ -1940,7 +1961,8 @@
                 return;
             }
 
-            logContextCommitted(detectedFieldIds, detectedFieldClassifications);
+            logContextCommitted(detectedFieldIds, detectedFieldClassifications,
+                    saveDialogNotShowReason);
         });
 
         fcStrategy.calculateScores(callback, currentValues, userValues, categoryIds,
@@ -1948,17 +1970,28 @@
     }
 
     /**
+     * Generates a {@link android.service.autofill.FillEventHistory.Event#TYPE_SAVE_SHOWN}
+     * when necessary.
+     *
+     * <p>Note: It is necessary to call logContextCommitted() first before calling this method.
+     */
+    public void logSaveUiShown() {
+        mHandler.sendMessage(obtainMessage(Session::logSaveShown, this));
+    }
+
+    /**
      * Shows the save UI, when session can be saved.
      *
-     * @return {@code true} if session is done and could be removed, or {@code false} if it's
-     * pending user action or the service asked to keep it alive (for multi-screens workflow).
+     * @return {@link SaveResult} that contains the save ui display status information.
      */
     @GuardedBy("mLock")
-    public boolean showSaveLocked() {
+    @NonNull
+    public SaveResult showSaveLocked() {
         if (mDestroyed) {
             Slog.w(TAG, "Call to Session#showSaveLocked() rejected - session: "
                     + id + " destroyed");
-            return false;
+            return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ false,
+                    Event.NO_SAVE_REASON_NONE);
         }
         final FillResponse response = getLastResponseLocked("showSaveLocked(%s)");
         final SaveInfo saveInfo = response == null ? null : response.getSaveInfo();
@@ -1975,13 +2008,15 @@
          */
         if (saveInfo == null) {
             if (sVerbose) Slog.v(TAG, "showSaveLocked(" + this.id + "): no saveInfo from service");
-            return true;
+            return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true,
+                    Event.NO_SAVE_REASON_NO_SAVE_INFO);
         }
 
         if ((saveInfo.getFlags() & SaveInfo.FLAG_DELAY_SAVE) != 0) {
             // TODO(b/113281366): log metrics
             if (sDebug) Slog.v(TAG, "showSaveLocked(" + this.id + "): service asked to delay save");
-            return false;
+            return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ false,
+                    Event.NO_SAVE_REASON_WITH_DELAY_SAVE_FLAG);
         }
 
         final ArrayMap<AutofillId, InternalSanitizer> sanitizers = createSanitizers(saveInfo);
@@ -2073,7 +2108,10 @@
             Slog.v(TAG, "allRequiredAreNotEmpty: " + allRequiredAreNotEmpty + " hasOptional: "
                     + (optionalIds != null));
         }
-        if (allRequiredAreNotEmpty) {
+        int saveDialogNotShowReason;
+        if (!allRequiredAreNotEmpty) {
+            saveDialogNotShowReason = Event.NO_SAVE_REASON_HAS_EMPTY_REQUIRED;
+        } else {
             // Must look up all optional ids in 2 scenarios:
             // - if no required id changed but an optional id did, it should trigger save / update
             // - if at least one required id changed but it was not part of a filled dataset, we
@@ -2124,7 +2162,9 @@
                     }
                 }
             }
-            if (atLeastOneChanged) {
+            if (!atLeastOneChanged) {
+                saveDialogNotShowReason = Event.NO_SAVE_REASON_NO_VALUE_CHANGED;
+            } else {
                 if (sDebug) {
                     Slog.d(TAG, "at least one field changed, validate fields for save UI");
                 }
@@ -2142,13 +2182,15 @@
                         Slog.e(TAG, "Not showing save UI because validation failed:", e);
                         log.setType(MetricsEvent.TYPE_FAILURE);
                         mMetricsLogger.write(log);
-                        return true;
+                        return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true,
+                                Event.NO_SAVE_REASON_FIELD_VALIDATION_FAILED);
                     }
 
                     mMetricsLogger.write(log);
                     if (!isValid) {
                         Slog.i(TAG, "not showing save UI because fields failed validation");
-                        return true;
+                        return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true,
+                                Event.NO_SAVE_REASON_FIELD_VALIDATION_FAILED);
                     }
                 }
 
@@ -2187,7 +2229,8 @@
                             Slog.d(TAG, "ignoring Save UI because all fields match contents of "
                                     + "dataset #" + i + ": " + dataset);
                         }
-                        return true;
+                        return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true,
+                                Event.NO_SAVE_REASON_DATASET_MATCH);
                     }
                 }
 
@@ -2196,9 +2239,6 @@
                             + id + "!");
                 }
 
-                // Use handler so logContextCommitted() is logged first
-                mHandler.sendMessage(obtainMessage(Session::logSaveShown, this));
-
                 final IAutoFillManagerClient client = getClient();
                 mPendingSaveUi = new PendingUi(new Binder(), id, client);
 
@@ -2210,8 +2250,10 @@
                 }
                 if (serviceLabel == null || serviceIcon == null) {
                     wtf(null, "showSaveLocked(): no service label or icon");
-                    return true;
+                    return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true,
+                            Event.NO_SAVE_REASON_NONE);
                 }
+
                 getUiForShowing().showSaveUi(serviceLabel, serviceIcon,
                         mService.getServicePackageName(), saveInfo, this,
                         mComponentName, this, mPendingSaveUi, isUpdate, mCompatMode);
@@ -2223,7 +2265,8 @@
                     }
                 }
                 mIsSaving = true;
-                return false;
+                return new SaveResult(/* logSaveShown= */ true, /* removeSession= */ false,
+                        Event.NO_SAVE_REASON_NONE);
             }
         }
         // Nothing changed...
@@ -2232,7 +2275,8 @@
                     + "allRequiredAreNotNull=" + allRequiredAreNotEmpty
                     + ", atLeastOneChanged=" + atLeastOneChanged);
         }
-        return true;
+        return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true,
+                saveDialogNotShowReason);
     }
 
     private void logSaveShown() {
@@ -2240,31 +2284,6 @@
     }
 
     @Nullable
-    private ArrayMap<AutofillId, InternalSanitizer> createSanitizers(@Nullable SaveInfo saveInfo) {
-        if (saveInfo == null) return null;
-
-        final InternalSanitizer[] sanitizerKeys = saveInfo.getSanitizerKeys();
-        if (sanitizerKeys == null) return null;
-
-        final int size = sanitizerKeys.length ;
-        final ArrayMap<AutofillId, InternalSanitizer> sanitizers = new ArrayMap<>(size);
-        if (sDebug) Slog.d(TAG, "Service provided " + size + " sanitizers");
-        final AutofillId[][] sanitizerValues = saveInfo.getSanitizerValues();
-        for (int i = 0; i < size; i++) {
-            final InternalSanitizer sanitizer = sanitizerKeys[i];
-            final AutofillId[] ids = sanitizerValues[i];
-            if (sDebug) {
-                Slog.d(TAG, "sanitizer #" + i + " (" + sanitizer + ") for ids "
-                        + Arrays.toString(ids));
-            }
-            for (AutofillId id : ids) {
-                sanitizers.put(id, sanitizer);
-            }
-        }
-        return sanitizers;
-    }
-
-    @Nullable
     private AutofillValue getSanitizedValue(
             @Nullable ArrayMap<AutofillId, InternalSanitizer> sanitizers,
             @NonNull AutofillId id,
@@ -2328,12 +2347,12 @@
      */
     @GuardedBy("mLock")
     @Nullable
-    private CharSequence[] getAutofillOptionsFromContextsLocked(AutofillId id) {
+    private CharSequence[] getAutofillOptionsFromContextsLocked(@NonNull AutofillId autofillId) {
         final int numContexts = mContexts.size();
-
         for (int i = numContexts - 1; i >= 0; i--) {
             final FillContext context = mContexts.get(i);
-            final ViewNode node = Helper.findViewNodeByAutofillId(context.getStructure(), id);
+            final ViewNode node = Helper.findViewNodeByAutofillId(context.getStructure(),
+                    autofillId);
             if (node != null && node.getAutofillOptions() != null) {
                 return node.getAutofillOptions();
             }
@@ -2439,7 +2458,7 @@
     // TODO(b/113281366): rather than merge it here, it might be better to simply reuse the old
     // session instead of creating a new one. But we need to consider what would happen on corner
     // cases such as "Main Activity M -> activity A with username -> activity B with password"
-    // If user follows the normal workflow, than session A would be merged with session B as
+    // If user follows the normal workflow, then session A would be merged with session B as
     // expected. But if when on Activity A the user taps back or somehow launches another activity,
     // session A could be merged with the wrong session.
     /**
@@ -2532,11 +2551,11 @@
             }
             requestNewFillResponseLocked(viewState, ViewState.STATE_STARTED_PARTITION, flags);
             return true;
-        } else {
-            if (sVerbose) {
-                Slog.v(TAG, "Not starting new partition for view " + id + ": "
-                        + viewState.getStateAsString());
-            }
+        }
+
+        if (sVerbose) {
+            Slog.v(TAG, "Not starting new partition for view " + id + ": "
+                    + viewState.getStateAsString());
         }
         return false;
     }
@@ -2897,21 +2916,6 @@
         }
     }
 
-    /**
-     * Returns true if {@code s1} contains all characters of {@code s2}, in order.
-     */
-    private static boolean containsCharsInOrder(String s1, String s2) {
-        int prevIndex = -1;
-        for (char ch : s2.toCharArray()) {
-            int index = TextUtils.indexOf(s1, ch, prevIndex + 1);
-            if (index == -1) {
-                return false;
-            }
-            prevIndex = index;
-        }
-        return true;
-    }
-
     @Override
     public void onFillReady(@NonNull FillResponse response, @NonNull AutofillId filledId,
             @Nullable AutofillValue value) {
@@ -3380,7 +3384,7 @@
                     mInlineSessionController.getInlineSuggestionsRequestLocked().orElse(null));
         }
         if (mAugmentedAutofillDestroyer == null) {
-            mAugmentedAutofillDestroyer = () -> remoteService.onDestroyAutofillWindowsRequest();
+            mAugmentedAutofillDestroyer = remoteService::onDestroyAutofillWindowsRequest;
         }
         return mAugmentedAutofillDestroyer;
     }
@@ -3608,6 +3612,97 @@
         }
     }
 
+    /**
+     * The result of checking whether to show the save dialog, when session can be saved.
+     *
+     * @hide
+     */
+    static final class SaveResult {
+        /**
+         * Whether to record the save dialog has been shown.
+         */
+        private boolean mLogSaveShown;
+
+        /**
+         * Whether to remove the session.
+         */
+        private boolean mRemoveSession;
+
+        /**
+         * The reason why a save dialog was not shown.
+         */
+        @NoSaveReason private int mSaveDialogNotShowReason;
+
+        SaveResult(boolean logSaveShown, boolean removeSession,
+                @NoSaveReason int saveDialogNotShowReason) {
+            mLogSaveShown = logSaveShown;
+            mRemoveSession = removeSession;
+            mSaveDialogNotShowReason = saveDialogNotShowReason;
+        }
+
+        /**
+         * Returns whether to record the save dialog has been shown.
+         *
+         * @return Whether to record the save dialog has been shown.
+         */
+        public boolean isLogSaveShown() {
+            return mLogSaveShown;
+        }
+
+        /**
+         * Sets whether to record the save dialog has been shown.
+         *
+         * @param logSaveShown Whether to record the save dialog has been shown.
+         */
+        public void setLogSaveShown(boolean logSaveShown) {
+            mLogSaveShown = logSaveShown;
+        }
+
+        /**
+         * Returns whether to remove the session.
+         *
+         * @return Whether to remove the session.
+         */
+        public boolean isRemoveSession() {
+            return mRemoveSession;
+        }
+
+        /**
+         * Sets whether to remove the session.
+         *
+         * @param removeSession Whether to remove the session.
+         */
+        public void setRemoveSession(boolean removeSession) {
+            mRemoveSession = removeSession;
+        }
+
+        /**
+         * Returns the reason why a save dialog was not shown.
+         *
+         * @return The reason why a save dialog was not shown.
+         */
+        @NoSaveReason
+        public int getNoSaveReason() {
+            return mSaveDialogNotShowReason;
+        }
+
+        /**
+         * Sets the reason why a save dialog was not shown.
+         *
+         * @param saveDialogNotShowReason The reason why a save dialog was not shown.
+         */
+        public void setSaveDialogNotShowReason(@NoSaveReason int saveDialogNotShowReason) {
+            mSaveDialogNotShowReason = saveDialogNotShowReason;
+        }
+
+        @Override
+        public String toString() {
+            return "SaveResult: [logSaveShown=" + mLogSaveShown
+                    + ", removeSession=" + mRemoveSession
+                    + ", saveDialogNotShowReason=" + mSaveDialogNotShowReason + "]";
+        }
+    }
+
     @Override
     public String toString() {
         return "Session: [id=" + id + ", component=" + mComponentName + "]";
diff --git a/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java b/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java
index 0e99b34..bcec9c6 100644
--- a/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java
+++ b/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java
@@ -19,6 +19,7 @@
 
 import android.content.ContentResolver;
 import android.os.Handler;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.KeyValueListParser;
 import android.util.KeyValueSettingObserver;
@@ -53,10 +54,18 @@
             "restore_agent_timeout_millis";
 
     @VisibleForTesting
+    public static final String SETTING_RESTORE_SYSTEM_AGENT_TIMEOUT_MILLIS =
+            "restore_system_agent_timeout_millis";
+
+    @VisibleForTesting
     public static final String SETTING_RESTORE_AGENT_FINISHED_TIMEOUT_MILLIS =
             "restore_agent_finished_timeout_millis";
 
     @VisibleForTesting
+    public static final String SETTING_RESTORE_SESSION_TIMEOUT_MILLIS =
+            "restore_session_timeout_millis";
+
+    @VisibleForTesting
     public static final String SETTING_QUOTA_EXCEEDED_TIMEOUT_MILLIS =
             "quota_exceeded_timeout_millis";
 
@@ -71,9 +80,14 @@
 
     @VisibleForTesting public static final long DEFAULT_RESTORE_AGENT_TIMEOUT_MILLIS = 60 * 1000;
 
+    @VisibleForTesting public static final long DEFAULT_RESTORE_SYSTEM_AGENT_TIMEOUT_MILLIS =
+            180 * 1000;
+
     @VisibleForTesting
     public static final long DEFAULT_RESTORE_AGENT_FINISHED_TIMEOUT_MILLIS = 30 * 1000;
 
+    @VisibleForTesting public static final long DEFAULT_RESTORE_SESSION_TIMEOUT_MILLIS = 60 * 1000;
+
     @VisibleForTesting
     public static final long DEFAULT_QUOTA_EXCEEDED_TIMEOUT_MILLIS = 3 * 1000;
 
@@ -90,6 +104,12 @@
     private long mRestoreAgentTimeoutMillis;
 
     @GuardedBy("mLock")
+    private long mRestoreSystemAgentTimeoutMillis;
+
+    @GuardedBy("mLock")
+    private long mRestoreSessionTimeoutMillis;
+
+    @GuardedBy("mLock")
     private long mRestoreAgentFinishedTimeoutMillis;
 
     @GuardedBy("mLock")
@@ -123,10 +143,18 @@
                     parser.getLong(
                             SETTING_RESTORE_AGENT_TIMEOUT_MILLIS,
                             DEFAULT_RESTORE_AGENT_TIMEOUT_MILLIS);
+            mRestoreSystemAgentTimeoutMillis =
+                    parser.getLong(
+                            SETTING_RESTORE_SYSTEM_AGENT_TIMEOUT_MILLIS,
+                            DEFAULT_RESTORE_SYSTEM_AGENT_TIMEOUT_MILLIS);
             mRestoreAgentFinishedTimeoutMillis =
                     parser.getLong(
                             SETTING_RESTORE_AGENT_FINISHED_TIMEOUT_MILLIS,
                             DEFAULT_RESTORE_AGENT_FINISHED_TIMEOUT_MILLIS);
+            mRestoreSessionTimeoutMillis =
+                    parser.getLong(
+                            SETTING_RESTORE_SESSION_TIMEOUT_MILLIS,
+                            DEFAULT_RESTORE_SESSION_TIMEOUT_MILLIS);
             mQuotaExceededTimeoutMillis =
                     parser.getLong(
                             SETTING_QUOTA_EXCEEDED_TIMEOUT_MILLIS,
@@ -152,9 +180,20 @@
         }
     }
 
-    public long getRestoreAgentTimeoutMillis() {
+    /**
+     * @param applicationUid UID of the application for which to get restore timeout
+     * @return restore timeout in milliseconds
+     */
+    public long getRestoreAgentTimeoutMillis(int applicationUid) {
         synchronized (mLock) {
-            return mRestoreAgentTimeoutMillis;
+            return UserHandle.isCore(applicationUid) ? mRestoreSystemAgentTimeoutMillis :
+                    mRestoreAgentTimeoutMillis;
+        }
+    }
+
+    public long getRestoreSessionTimeoutMillis() {
+        synchronized (mLock) {
+            return mRestoreSessionTimeoutMillis;
         }
     }
 
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 186812b..75bbec6 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -478,7 +478,7 @@
                 if (getUserManager().isUserUnlocked(userId)) {
                     // Clear calling identity as initialization enforces the system identity but we
                     // can be coming from shell.
-                    long oldId = Binder.clearCallingIdentity();
+                    final long oldId = Binder.clearCallingIdentity();
                     try {
                         startServiceForUser(userId);
                     } finally {
@@ -1412,8 +1412,8 @@
             return null;
         }
         int callingUserId = Binder.getCallingUserHandle().getIdentifier();
-        long oldId = Binder.clearCallingIdentity();
         final int[] userIds;
+        final long oldId = Binder.clearCallingIdentity();
         try {
             userIds = getUserManager().getProfileIds(callingUserId, false);
         } finally {
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index e68c07ed..2ff66b5 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -650,7 +650,7 @@
                         context,
                         /* requestCode */ 0,
                         initIntent,
-                        /* flags */ 0,
+                        /* flags */ PendingIntent.FLAG_IMMUTABLE,
                         UserHandle.of(userId));
 
         // Set up the backup-request journaling
@@ -2889,16 +2889,18 @@
                     mBackupHandler.sendMessageDelayed(msg, TRANSPORT_RETRY_INTERVAL);
                     return;
                 }
-                long oldId = Binder.clearCallingIdentity();
-                OnTaskFinishedListener listener =
-                        caller ->
-                                mTransportManager.disposeOfTransportClient(transportClient, caller);
-                mWakelock.acquire();
-                Message msg = mBackupHandler.obtainMessage(
-                        MSG_RUN_CLEAR,
-                        new ClearParams(transportClient, info, listener));
-                mBackupHandler.sendMessage(msg);
-                Binder.restoreCallingIdentity(oldId);
+                final long oldId = Binder.clearCallingIdentity();
+                try {
+                    OnTaskFinishedListener listener = caller -> mTransportManager
+                            .disposeOfTransportClient(transportClient, caller);
+                    mWakelock.acquire();
+                    Message msg = mBackupHandler.obtainMessage(
+                            MSG_RUN_CLEAR,
+                            new ClearParams(transportClient, info, listener));
+                    mBackupHandler.sendMessage(msg);
+                } finally {
+                    Binder.restoreCallingIdentity(oldId);
+                }
             }
         }
     }
@@ -2910,7 +2912,7 @@
     public void backupNow() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow");
 
-        long oldId = Binder.clearCallingIdentity();
+        final long oldId = Binder.clearCallingIdentity();
         try {
             final PowerSaveState result =
                     mPowerManager.getPowerSaveState(ServiceType.KEYVALUE_BACKUP);
@@ -2999,7 +3001,7 @@
             }
         }
 
-        long oldId = Binder.clearCallingIdentity();
+        final long oldId = Binder.clearCallingIdentity();
         try {
             if (!mSetupComplete) {
                 Slog.i(TAG, addUserIdToLogMessage(mUserId, "Backup not supported before setup"));
@@ -3156,7 +3158,7 @@
             throw new IllegalStateException("Restore supported only for the device owner");
         }
 
-        long oldId = Binder.clearCallingIdentity();
+        final long oldId = Binder.clearCallingIdentity();
 
         try {
             if (!mSetupComplete) {
@@ -3287,7 +3289,7 @@
         mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
                 "acknowledgeAdbBackupOrRestore");
 
-        long oldId = Binder.clearCallingIdentity();
+        final long oldId = Binder.clearCallingIdentity();
         try {
 
             AdbParams params;
@@ -3348,7 +3350,7 @@
 
         Slog.i(TAG, addUserIdToLogMessage(mUserId, "Backup enabled => " + enable));
 
-        long oldId = Binder.clearCallingIdentity();
+        final long oldId = Binder.clearCallingIdentity();
         try {
             boolean wasEnabled = mEnabled;
             synchronized (this) {
@@ -3477,7 +3479,7 @@
     public ComponentName getCurrentTransportComponent() {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BACKUP, "getCurrentTransportComponent");
-        long oldId = Binder.clearCallingIdentity();
+        final long oldId = Binder.clearCallingIdentity();
         try {
             return mTransportManager.getCurrentTransportComponent();
         } catch (TransportNotRegisteredException e) {
@@ -4088,7 +4090,7 @@
             mActiveRestoreSession = new ActiveRestoreSession(this, packageName, transport,
                     getEligibilityRulesForOperation(operationType));
             mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT,
-                    mAgentTimeoutParameters.getRestoreAgentTimeoutMillis());
+                    mAgentTimeoutParameters.getRestoreSessionTimeoutMillis());
         }
         return mActiveRestoreSession;
     }
@@ -4165,7 +4167,7 @@
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BACKUP, "isAppEligibleForBackup");
 
-        long oldToken = Binder.clearCallingIdentity();
+        final long oldToken = Binder.clearCallingIdentity();
         try {
             String callerLogString = "BMS.isAppEligibleForBackup";
             TransportClient transportClient =
@@ -4187,7 +4189,7 @@
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BACKUP, "filterAppsEligibleForBackup");
 
-        long oldToken = Binder.clearCallingIdentity();
+        final long oldToken = Binder.clearCallingIdentity();
         try {
             String callerLogString = "BMS.filterAppsEligibleForBackup";
             TransportClient transportClient =
@@ -4221,7 +4223,7 @@
 
     /** Prints service state for 'dumpsys backup'. */
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        long identityToken = Binder.clearCallingIdentity();
+        final long identityToken = Binder.clearCallingIdentity();
         try {
             if (args != null) {
                 for (String arg : args) {
diff --git a/services/backup/java/com/android/server/backup/internal/BackupHandler.java b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
index 100dbae..1cb7c11 100644
--- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java
+++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
@@ -390,7 +390,7 @@
                     // Done: reset the session timeout clock
                     removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
                     sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT,
-                            mAgentTimeoutParameters.getRestoreAgentTimeoutMillis());
+                            mAgentTimeoutParameters.getRestoreSessionTimeoutMillis());
 
                     params.listener.onFinished(callerLogString);
                 }
diff --git a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
index 3102b5f..602dc24 100644
--- a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
+++ b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
@@ -98,7 +98,7 @@
             return -1;
         }
 
-        long oldId = Binder.clearCallingIdentity();
+        final long oldId = Binder.clearCallingIdentity();
         try {
             TransportClient transportClient =
                     mTransportManager.getTransportClient(
@@ -173,7 +173,7 @@
         synchronized (mBackupManagerService.getQueueLock()) {
             for (int i = 0; i < mRestoreSets.length; i++) {
                 if (token == mRestoreSets[i].token) {
-                    long oldId = Binder.clearCallingIdentity();
+                    final long oldId = Binder.clearCallingIdentity();
                     try {
                         return sendRestoreToHandlerLocked(
                                 (transportClient, listener) ->
@@ -265,7 +265,7 @@
         synchronized (mBackupManagerService.getQueueLock()) {
             for (int i = 0; i < mRestoreSets.length; i++) {
                 if (token == mRestoreSets[i].token) {
-                    long oldId = Binder.clearCallingIdentity();
+                    final long oldId = Binder.clearCallingIdentity();
                     try {
                         return sendRestoreToHandlerLocked(
                                 (transportClient, listener) ->
@@ -341,7 +341,7 @@
         }
 
         // So far so good; we're allowed to try to restore this package.
-        long oldId = Binder.clearCallingIdentity();
+        final long oldId = Binder.clearCallingIdentity();
         try {
             // Check whether there is data for it in the current dataset, falling back
             // to the ancestral dataset if not.
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 16077cb..5718bdf 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -403,7 +403,8 @@
                         final boolean isSharedStorage = pkg.equals(SHARED_BACKUP_AGENT_PACKAGE);
                         final long timeout = isSharedStorage ?
                                 mAgentTimeoutParameters.getSharedBackupAgentTimeoutMillis() :
-                                mAgentTimeoutParameters.getRestoreAgentTimeoutMillis();
+                                mAgentTimeoutParameters.getRestoreAgentTimeoutMillis(
+                                        mTargetApp.uid);
                         try {
                             mBackupManagerService.prepareOperationTimeout(token,
                                     timeout,
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 abf11bd..261ebe6 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -46,6 +46,7 @@
 import android.os.Bundle;
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -433,6 +434,8 @@
             // Pull the Package Manager metadata from the restore set first
             mCurrentPackage = new PackageInfo();
             mCurrentPackage.packageName = PACKAGE_MANAGER_SENTINEL;
+            mCurrentPackage.applicationInfo = new ApplicationInfo();
+            mCurrentPackage.applicationInfo.uid = Process.SYSTEM_UID;
             mPmAgent = backupManagerService.makeMetadataAgent(null);
             mAgent = IBackupAgent.Stub.asInterface(mPmAgent.onBind());
             if (MORE_DEBUG) {
@@ -760,7 +763,8 @@
             // Kick off the restore, checking for hung agents.  The timeout or
             // the operationComplete() callback will schedule the next step,
             // so we do not do that here.
-            long restoreAgentTimeoutMillis = mAgentTimeoutParameters.getRestoreAgentTimeoutMillis();
+            long restoreAgentTimeoutMillis = mAgentTimeoutParameters.getRestoreAgentTimeoutMillis(
+                    app.applicationInfo.uid);
             backupManagerService.prepareOperationTimeout(
                     mEphemeralOpToken, restoreAgentTimeoutMillis, this, OP_TYPE_RESTORE_WAIT);
             startedAgentRestore = true;
@@ -1122,7 +1126,8 @@
         } else {
             // We were invoked via an active restore session, not by the Package
             // Manager, so start up the session timeout again.
-            long restoreAgentTimeoutMillis = mAgentTimeoutParameters.getRestoreAgentTimeoutMillis();
+            long restoreAgentTimeoutMillis =
+                    mAgentTimeoutParameters.getRestoreSessionTimeoutMillis();
             backupManagerService.getBackupHandler().sendEmptyMessageDelayed(
                     MSG_RESTORE_SESSION_TIMEOUT,
                     restoreAgentTimeoutMillis);
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 40b1718..032820d 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -391,7 +391,10 @@
 
             checkArgument(getCallingUserId() == userId,
                     "Must be called by either same user or system");
-            mAppOpsManager.checkPackage(Binder.getCallingUid(), pkg);
+            int callingUid = Binder.getCallingUid();
+            if (mAppOpsManager.checkPackage(callingUid, pkg) != AppOpsManager.MODE_ALLOWED) {
+                throw new SecurityException(pkg + " doesn't belong to uid " + callingUid);
+            }
         }
 
         @Override
@@ -408,7 +411,7 @@
                                     PackageItemInfo.SAFE_LABEL_FLAG_TRIM
                                             | PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE)
                             .toString());
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 return PendingIntent.getActivityAsUser(getContext(),
                         0 /* request code */,
diff --git a/services/core/Android.bp b/services/core/Android.bp
index addaa65..f76dd3f 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -1,6 +1,7 @@
 filegroup {
     name: "services.core-sources",
     srcs: ["java/**/*.java"],
+    exclude_srcs: [":connectivity-service-srcs"],
     path: "java",
     visibility: [
         "//frameworks/base/services",
@@ -72,6 +73,7 @@
         ":vold_aidl",
         ":platform-compat-config",
         ":display-device-config",
+        ":cec-config",
         "java/com/android/server/EventLogTags.logtags",
         "java/com/android/server/am/EventLogTags.logtags",
         "java/com/android/server/wm/EventLogTags.logtags",
@@ -109,11 +111,13 @@
         "android.hardware.weaver-V1.0-java",
         "android.hardware.biometrics.face-V1.1-java",
         "android.hardware.biometrics.fingerprint-V2.3-java",
+        "android.hardware.biometrics.fingerprint-java",
         "android.hardware.oemlock-V1.0-java",
         "android.hardware.configstore-V1.0-java",
         "android.hardware.contexthub-V1.0-java",
         "android.hardware.rebootescrow-java",
         "android.hardware.soundtrigger-V2.3-java",
+        "android.hardware.power.stats-java",
         "android.hidl.manager-V1.2-java",
         "capture_state_listener-aidl-java",
         "dnsresolver_aidl_interface-java",
@@ -121,6 +125,7 @@
         "netd_aidl_interfaces-platform-java",
         "overlayable_policy_aidl-java",
         "SurfaceFlingerProperties",
+        "com.android.sysprop.watchdog",
     ],
 }
 
@@ -165,3 +170,50 @@
     name: "protolog.conf.json.gz",
     src: ":services.core.json.gz",
 }
+
+// TODO: Move connectivity service sources to independent directory.
+filegroup {
+    name: "connectivity-service-srcs",
+    srcs: [
+        "java/com/android/server/ConnectivityService.java",
+        "java/com/android/server/ConnectivityServiceInitializer.java",
+        "java/com/android/server/TestNetworkService.java",
+        "java/com/android/server/connectivity/AutodestructReference.java",
+        "java/com/android/server/connectivity/ConnectivityConstants.java",
+        "java/com/android/server/connectivity/DataConnectionStats.java",
+        "java/com/android/server/connectivity/DefaultNetworkMetrics.java",
+        "java/com/android/server/connectivity/DnsManager.java",
+        "java/com/android/server/connectivity/IpConnectivityEventBuilder.java",
+        "java/com/android/server/connectivity/IpConnectivityMetrics.java",
+        "java/com/android/server/connectivity/KeepaliveTracker.java",
+        "java/com/android/server/connectivity/LingerMonitor.java",
+        "java/com/android/server/connectivity/MockableSystemProperties.java",
+        "java/com/android/server/connectivity/MultipathPolicyTracker.java",
+        "java/com/android/server/connectivity/Nat464Xlat.java",
+        "java/com/android/server/connectivity/NetdEventListenerService.java",
+        "java/com/android/server/connectivity/NetworkAgentInfo.java",
+        "java/com/android/server/connectivity/NetworkDiagnostics.java",
+        "java/com/android/server/connectivity/NetworkNotificationManager.java",
+        "java/com/android/server/connectivity/NetworkRanker.java",
+        "java/com/android/server/connectivity/PermissionMonitor.java",
+        "java/com/android/server/connectivity/ProxyTracker.java",
+        "java/com/android/server/connectivity/TcpKeepaliveController.java",
+        "java/com/android/server/connectivity/Vpn.java",
+        "java/com/android/server/connectivity/VpnIkev2Utils.java",
+        "java/com/android/server/net/LockdownVpnTracker.java",
+    ],
+}
+
+java_library {
+    name: "service-connectivity",
+    srcs: [
+        ":connectivity-service-srcs",
+    ],
+    installable: true,
+    libs: [
+        "android.net.ipsec.ike",
+        "services.core",
+        "services.net",
+        "unsupportedappusage",
+    ],
+}
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 0c56d46..031ad42 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -60,6 +60,27 @@
  * @hide Only for use within the system server.
  */
 public abstract class PackageManagerInternal {
+    @IntDef(prefix = "PACKAGE_", value = {
+            PACKAGE_SYSTEM,
+            PACKAGE_SETUP_WIZARD,
+            PACKAGE_INSTALLER,
+            PACKAGE_VERIFIER,
+            PACKAGE_BROWSER,
+            PACKAGE_SYSTEM_TEXT_CLASSIFIER,
+            PACKAGE_PERMISSION_CONTROLLER,
+            PACKAGE_WELLBEING,
+            PACKAGE_DOCUMENTER,
+            PACKAGE_CONFIGURATOR,
+            PACKAGE_INCIDENT_REPORT_APPROVER,
+            PACKAGE_APP_PREDICTOR,
+            PACKAGE_OVERLAY_CONFIG_SIGNATURE,
+            PACKAGE_WIFI,
+            PACKAGE_COMPANION,
+            PACKAGE_RETAIL_DEMO,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface KnownPackage {}
+
     public static final int PACKAGE_SYSTEM = 0;
     public static final int PACKAGE_SETUP_WIZARD = 1;
     public static final int PACKAGE_INSTALLER = 2;
@@ -72,10 +93,13 @@
     public static final int PACKAGE_CONFIGURATOR = 9;
     public static final int PACKAGE_INCIDENT_REPORT_APPROVER = 10;
     public static final int PACKAGE_APP_PREDICTOR = 11;
+    public static final int PACKAGE_OVERLAY_CONFIG_SIGNATURE = 12;
     public static final int PACKAGE_WIFI = 13;
     public static final int PACKAGE_COMPANION = 14;
     public static final int PACKAGE_RETAIL_DEMO = 15;
-    public static final int PACKAGE_OVERLAY_CONFIG_SIGNATURE = 16;
+    // Integer value of the last known package ID. Increases as new ID is added to KnownPackage.
+    // Please note the numbers should be continuous.
+    public static final int LAST_KNOWN_PACKAGE = PACKAGE_RETAIL_DEMO;
 
     @IntDef(flag = true, prefix = "RESOLVE_", value = {
             RESOLVE_NON_BROWSER_ONLY,
@@ -117,26 +141,6 @@
      */
     public static final int INTEGRITY_VERIFICATION_REJECT = 0;
 
-    @IntDef(value = {
-        PACKAGE_SYSTEM,
-        PACKAGE_SETUP_WIZARD,
-        PACKAGE_INSTALLER,
-        PACKAGE_VERIFIER,
-        PACKAGE_BROWSER,
-        PACKAGE_SYSTEM_TEXT_CLASSIFIER,
-        PACKAGE_PERMISSION_CONTROLLER,
-        PACKAGE_WELLBEING,
-        PACKAGE_DOCUMENTER,
-        PACKAGE_CONFIGURATOR,
-        PACKAGE_INCIDENT_REPORT_APPROVER,
-        PACKAGE_APP_PREDICTOR,
-        PACKAGE_WIFI,
-        PACKAGE_COMPANION,
-        PACKAGE_RETAIL_DEMO,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface KnownPackage {}
-
     /** Observer called whenever the list of packages changes */
     public interface PackageListObserver {
         /** A package was added to the system. */
@@ -1016,6 +1020,51 @@
             @NonNull InstalledLoadingProgressCallback callback);
 
     /**
+     * Returns the string representation of a known package. For example,
+     * {@link #PACKAGE_SETUP_WIZARD} is represented by the string Setup Wizard.
+     *
+     * @param knownPackage The known package.
+     * @return The string representation.
+     */
+    public static @NonNull String knownPackageToString(@KnownPackage int knownPackage) {
+        switch (knownPackage) {
+            case PACKAGE_SYSTEM:
+                return "System";
+            case PACKAGE_SETUP_WIZARD:
+                return "Setup Wizard";
+            case PACKAGE_INSTALLER:
+                return "Installer";
+            case PACKAGE_VERIFIER:
+                return "Verifier";
+            case PACKAGE_BROWSER:
+                return "Browser";
+            case PACKAGE_SYSTEM_TEXT_CLASSIFIER:
+                return "System Text Classifier";
+            case PACKAGE_PERMISSION_CONTROLLER:
+                return "Permission Controller";
+            case PACKAGE_WELLBEING:
+                return "Wellbeing";
+            case PACKAGE_DOCUMENTER:
+                return "Documenter";
+            case PACKAGE_CONFIGURATOR:
+                return "Configurator";
+            case PACKAGE_INCIDENT_REPORT_APPROVER:
+                return "Incident Report Approver";
+            case PACKAGE_APP_PREDICTOR:
+                return "App Predictor";
+            case PACKAGE_WIFI:
+                return "Wi-Fi";
+            case PACKAGE_COMPANION:
+                return "Companion";
+            case PACKAGE_RETAIL_DEMO:
+                return "Retail Demo";
+            case PACKAGE_OVERLAY_CONFIG_SIGNATURE:
+                return "Overlay Config Signature";
+        }
+        return "Unknown";
+    }
+
+    /**
      * Callback to listen for loading progress of a package installed on Incremental File System.
      */
     public abstract static class InstalledLoadingProgressCallback {
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index d92706d..9455051a 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -916,7 +916,7 @@
                 mHealthInfo.chargerAcOnline = false;
                 mHealthInfo.chargerUsbOnline = false;
                 mHealthInfo.chargerWirelessOnline = false;
-                long ident = Binder.clearCallingIdentity();
+                final long ident = Binder.clearCallingIdentity();
                 try {
                     mUpdatesStopped = true;
                     processValuesFromShellLocked(pw, opts);
@@ -979,7 +979,7 @@
                             break;
                     }
                     if (update) {
-                        long ident = Binder.clearCallingIdentity();
+                        final long ident = Binder.clearCallingIdentity();
                         try {
                             mUpdatesStopped = true;
                             processValuesFromShellLocked(pw, opts);
@@ -996,7 +996,7 @@
                 int opts = parseOptions(shell);
                 getContext().enforceCallingOrSelfPermission(
                         android.Manifest.permission.DEVICE_POWER, null);
-                long ident = Binder.clearCallingIdentity();
+                final long ident = Binder.clearCallingIdentity();
                 try {
                     if (mUpdatesStopped) {
                         mUpdatesStopped = false;
diff --git a/services/core/java/com/android/server/BluetoothAirplaneModeListener.java b/services/core/java/com/android/server/BluetoothAirplaneModeListener.java
index 31cd5d5..4d9680c 100644
--- a/services/core/java/com/android/server/BluetoothAirplaneModeListener.java
+++ b/services/core/java/com/android/server/BluetoothAirplaneModeListener.java
@@ -16,22 +16,14 @@
 
 package com.android.server;
 
-import android.bluetooth.BluetoothA2dp;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothHearingAid;
-import android.bluetooth.BluetoothProfile;
-import android.bluetooth.BluetoothProfile.ServiceListener;
 import android.content.Context;
-import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.provider.Settings;
 import android.util.Log;
-import android.widget.Toast;
 
-import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 
 /**
@@ -53,7 +45,7 @@
 
     private final BluetoothManagerService mBluetoothManager;
     private final BluetoothAirplaneModeHandler mHandler;
-    private AirplaneModeHelper mAirplaneHelper;
+    private BluetoothModeChangeHelper mAirplaneHelper;
 
     @VisibleForTesting int mToastCount = 0;
 
@@ -97,7 +89,7 @@
      * Call after boot complete
      */
     @VisibleForTesting
-    void start(AirplaneModeHelper helper) {
+    void start(BluetoothModeChangeHelper helper) {
         Log.i(TAG, "start");
         mAirplaneHelper = helper;
         mToastCount = mAirplaneHelper.getSettingsInt(TOAST_COUNT);
@@ -141,118 +133,4 @@
         }
         return true;
     }
-
-    /**
-     * Helper class that handles callout and callback methods without
-     * complex logic.
-     */
-    @VisibleForTesting
-    public static class AirplaneModeHelper {
-        private volatile BluetoothA2dp mA2dp;
-        private volatile BluetoothHearingAid mHearingAid;
-        private final BluetoothAdapter mAdapter;
-        private final Context mContext;
-
-        AirplaneModeHelper(Context context) {
-            mAdapter = BluetoothAdapter.getDefaultAdapter();
-            mContext = context;
-
-            mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.A2DP);
-            mAdapter.getProfileProxy(mContext, mProfileServiceListener,
-                    BluetoothProfile.HEARING_AID);
-        }
-
-        private final ServiceListener mProfileServiceListener = new ServiceListener() {
-            @Override
-            public void onServiceConnected(int profile, BluetoothProfile proxy) {
-                // Setup Bluetooth profile proxies
-                switch (profile) {
-                    case BluetoothProfile.A2DP:
-                        mA2dp = (BluetoothA2dp) proxy;
-                        break;
-                    case BluetoothProfile.HEARING_AID:
-                        mHearingAid = (BluetoothHearingAid) proxy;
-                        break;
-                    default:
-                        break;
-                }
-            }
-
-            @Override
-            public void onServiceDisconnected(int profile) {
-                // Clear Bluetooth profile proxies
-                switch (profile) {
-                    case BluetoothProfile.A2DP:
-                        mA2dp = null;
-                        break;
-                    case BluetoothProfile.HEARING_AID:
-                        mHearingAid = null;
-                        break;
-                    default:
-                        break;
-                }
-            }
-        };
-
-        @VisibleForTesting
-        public boolean isA2dpOrHearingAidConnected() {
-            return isA2dpConnected() || isHearingAidConnected();
-        }
-
-        @VisibleForTesting
-        public boolean isBluetoothOn() {
-            final BluetoothAdapter adapter = mAdapter;
-            if (adapter == null) {
-                return false;
-            }
-            return adapter.getLeState() == BluetoothAdapter.STATE_ON;
-        }
-
-        @VisibleForTesting
-        public boolean isAirplaneModeOn() {
-            return Settings.Global.getInt(mContext.getContentResolver(),
-                    Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
-        }
-
-        @VisibleForTesting
-        public void onAirplaneModeChanged(BluetoothManagerService managerService) {
-            managerService.onAirplaneModeChanged();
-        }
-
-        @VisibleForTesting
-        public int getSettingsInt(String name) {
-            return Settings.Global.getInt(mContext.getContentResolver(),
-                    name, 0);
-        }
-
-        @VisibleForTesting
-        public void setSettingsInt(String name, int value) {
-            Settings.Global.putInt(mContext.getContentResolver(),
-                    name, value);
-        }
-
-        @VisibleForTesting
-        public void showToastMessage() {
-            Resources r = mContext.getResources();
-            final CharSequence text = r.getString(
-                    R.string.bluetooth_airplane_mode_toast, 0);
-            Toast.makeText(mContext, text, Toast.LENGTH_LONG).show();
-        }
-
-        private boolean isA2dpConnected() {
-            final BluetoothA2dp a2dp = mA2dp;
-            if (a2dp == null) {
-                return false;
-            }
-            return a2dp.getConnectedDevices().size() > 0;
-        }
-
-        private boolean isHearingAidConnected() {
-            final BluetoothHearingAid hearingAid = mHearingAid;
-            if (hearingAid == null) {
-                return false;
-            }
-            return hearingAid.getConnectedDevices().size() > 0;
-        }
-    };
 }
diff --git a/services/core/java/com/android/server/BluetoothDeviceConfigListener.java b/services/core/java/com/android/server/BluetoothDeviceConfigListener.java
new file mode 100644
index 0000000..2dcf82f
--- /dev/null
+++ b/services/core/java/com/android/server/BluetoothDeviceConfigListener.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
+
+import android.provider.DeviceConfig;
+
+/**
+ * The BluetoothDeviceConfigListener handles system device config change callback and checks
+ * whether we need to inform BluetoothManagerService on this change.
+ *
+ * The information of device config change would not be passed to the BluetoothManagerService
+ * when Bluetooth is on and Bluetooth is in one of the following situations:
+ *   1. Bluetooth A2DP is connected.
+ *   2. Bluetooth Hearing Aid profile is connected.
+ */
+class BluetoothDeviceConfigListener {
+    private static final String TAG = "BluetoothDeviceConfigListener";
+
+    BluetoothManagerService mService;
+
+    BluetoothDeviceConfigListener(BluetoothManagerService service) {
+        mService = service;
+        DeviceConfig.addOnPropertiesChangedListener(
+                DeviceConfig.NAMESPACE_BLUETOOTH,
+                (Runnable r) -> r.run(),
+                mDeviceConfigChangedListener);
+    }
+
+    private final DeviceConfig.OnPropertiesChangedListener mDeviceConfigChangedListener =
+            new DeviceConfig.OnPropertiesChangedListener() {
+                @Override
+                public void onPropertiesChanged(DeviceConfig.Properties properties) {
+                    if (!properties.getNamespace().equals(DeviceConfig.NAMESPACE_BLUETOOTH)) {
+                        return;
+                    }
+                    boolean foundInit = false;
+                    for (String name : properties.getKeyset()) {
+                        if (name.startsWith("INIT_")) {
+                            foundInit = true;
+                            break;
+                        }
+                    }
+                    if (!foundInit) {
+                        return;
+                    }
+                    mService.onInitFlagsChanged();
+                }
+            };
+
+}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 4e405cc..f6a29aa 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -23,7 +23,9 @@
 import android.app.ActivityManager;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
+import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothHearingAid;
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothProtoEnums;
 import android.bluetooth.IBluetooth;
@@ -63,7 +65,6 @@
 import android.os.UserManager;
 import android.os.UserManagerInternal;
 import android.os.UserManagerInternal.UserRestrictionsListener;
-import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.text.TextUtils;
@@ -118,6 +119,7 @@
     // Delay for retrying enable and disable in msec
     private static final int ENABLE_DISABLE_DELAY_MS = 300;
     private static final int DELAY_BEFORE_RESTART_DUE_TO_INIT_FLAGS_CHANGED_MS = 300;
+    private static final int DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS = 86400;
 
     private static final int MESSAGE_ENABLE = 1;
     private static final int MESSAGE_DISABLE = 2;
@@ -178,8 +180,12 @@
     private int mWaitForEnableRetry;
     private int mWaitForDisableRetry;
 
+    private BluetoothModeChangeHelper mBluetoothModeChangeHelper;
+
     private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener;
 
+    private BluetoothDeviceConfigListener mBluetoothDeviceConfigListener;
+
     // used inside handler thread
     private boolean mQuietEnable = false;
     private boolean mEnable;
@@ -284,29 +290,13 @@
                 }
             };
 
-    private final DeviceConfig.OnPropertiesChangedListener mDeviceConfigChangedListener =
-            new DeviceConfig.OnPropertiesChangedListener() {
-                @Override
-                public void onPropertiesChanged(DeviceConfig.Properties properties) {
-                    if (!properties.getNamespace().equals(DeviceConfig.NAMESPACE_BLUETOOTH)) {
-                        return;
-                    }
-                    boolean foundInit = false;
-                    for (String name : properties.getKeyset()) {
-                        if (name.startsWith("INIT_")) {
-                            foundInit = true;
-                            break;
-                        }
-                    }
-                    if (!foundInit) {
-                        return;
-                    }
-                    mHandler.removeMessages(MESSAGE_INIT_FLAGS_CHANGED);
-                    mHandler.sendEmptyMessageDelayed(
-                            MESSAGE_INIT_FLAGS_CHANGED,
-                            DELAY_BEFORE_RESTART_DUE_TO_INIT_FLAGS_CHANGED_MS);
-                }
-            };
+    @VisibleForTesting
+    public void onInitFlagsChanged() {
+        mHandler.removeMessages(MESSAGE_INIT_FLAGS_CHANGED);
+        mHandler.sendEmptyMessageDelayed(
+                MESSAGE_INIT_FLAGS_CHANGED,
+                DELAY_BEFORE_RESTART_DUE_TO_INIT_FLAGS_CHANGED_MS);
+    }
 
     public boolean onFactoryReset() {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED,
@@ -457,6 +447,15 @@
                         mHandler.sendMessage(msg);
                     }
                 }
+            } else if (BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(action)
+                    || BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
+                final int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
+                        BluetoothProfile.STATE_CONNECTED);
+                if (mHandler.hasMessages(MESSAGE_INIT_FLAGS_CHANGED)
+                        && state == BluetoothProfile.STATE_DISCONNECTED
+                        && !mBluetoothModeChangeHelper.isA2dpOrHearingAidConnected()) {
+                    onInitFlagsChanged();
+                }
             }
         }
     };
@@ -508,6 +507,8 @@
         filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
         filter.addAction(BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED);
         filter.addAction(Intent.ACTION_SETTING_RESTORED);
+        filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
+        filter.addAction(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
         filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
         mContext.registerReceiver(mReceiver, filter);
 
@@ -542,10 +543,6 @@
             Slog.w(TAG, "Unable to resolve SystemUI's UID.");
         }
         mSystemUiUid = systemUiUid;
-        DeviceConfig.addOnPropertiesChangedListener(
-                DeviceConfig.NAMESPACE_BLUETOOTH,
-                (Runnable r) -> r.run(),
-                mDeviceConfigChangedListener);
     }
 
     /**
@@ -604,7 +601,7 @@
             Slog.d(TAG, "Persisting Bluetooth Setting: " + value);
         }
         // waive WRITE_SECURE_SETTINGS permission check
-        long callingIdentity = Binder.clearCallingIdentity();
+        final long callingIdentity = Binder.clearCallingIdentity();
         Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.BLUETOOTH_ON, value);
         Binder.restoreCallingIdentity(callingIdentity);
     }
@@ -1383,10 +1380,12 @@
             Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
             mHandler.sendMessage(getMsg);
         }
+
+        mBluetoothModeChangeHelper = new BluetoothModeChangeHelper(mContext);
         if (mBluetoothAirplaneModeListener != null) {
-            mBluetoothAirplaneModeListener.start(
-                    new BluetoothAirplaneModeListener.AirplaneModeHelper(mContext));
+            mBluetoothAirplaneModeListener.start(mBluetoothModeChangeHelper);
         }
+        mBluetoothDeviceConfigListener = new BluetoothDeviceConfigListener(this);
     }
 
     /**
@@ -2229,6 +2228,12 @@
                         Slog.d(TAG, "MESSAGE_INIT_FLAGS_CHANGED");
                     }
                     mHandler.removeMessages(MESSAGE_INIT_FLAGS_CHANGED);
+                    if (mBluetoothModeChangeHelper.isA2dpOrHearingAidConnected()) {
+                        mHandler.sendEmptyMessageDelayed(
+                                MESSAGE_INIT_FLAGS_CHANGED,
+                                DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS);
+                        break;
+                    }
                     if (mBluetooth != null && isEnabled()) {
                         restartForReason(
                                 BluetoothProtoEnums.ENABLE_DISABLE_REASON_INIT_FLAGS_CHANGED);
@@ -2378,7 +2383,7 @@
         int foregroundUser;
         int callingUser = UserHandle.getCallingUserId();
         int callingUid = Binder.getCallingUid();
-        long callingIdentity = Binder.clearCallingIdentity();
+        final long callingIdentity = Binder.clearCallingIdentity();
         UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
         UserInfo ui = um.getProfileParent(callingUser);
         int parentUser = (ui != null) ? ui.id : UserHandle.USER_NULL;
@@ -2605,7 +2610,7 @@
     }
 
     private boolean isBluetoothDisallowed() {
-        long callingIdentity = Binder.clearCallingIdentity();
+        final long callingIdentity = Binder.clearCallingIdentity();
         try {
             return mContext.getSystemService(UserManager.class)
                     .hasUserRestriction(UserManager.DISALLOW_BLUETOOTH, UserHandle.SYSTEM);
diff --git a/services/core/java/com/android/server/BluetoothModeChangeHelper.java b/services/core/java/com/android/server/BluetoothModeChangeHelper.java
new file mode 100644
index 0000000..242fa84
--- /dev/null
+++ b/services/core/java/com/android/server/BluetoothModeChangeHelper.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
+
+import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothHearingAid;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothProfile.ServiceListener;
+import android.content.Context;
+import android.content.res.Resources;
+import android.provider.Settings;
+import android.widget.Toast;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Helper class that handles callout and callback methods without
+ * complex logic.
+ */
+public class BluetoothModeChangeHelper {
+    private volatile BluetoothA2dp mA2dp;
+    private volatile BluetoothHearingAid mHearingAid;
+    private final BluetoothAdapter mAdapter;
+    private final Context mContext;
+
+    BluetoothModeChangeHelper(Context context) {
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
+        mContext = context;
+
+        mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.A2DP);
+        mAdapter.getProfileProxy(mContext, mProfileServiceListener,
+                BluetoothProfile.HEARING_AID);
+    }
+
+    private final ServiceListener mProfileServiceListener = new ServiceListener() {
+        @Override
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
+            // Setup Bluetooth profile proxies
+            switch (profile) {
+                case BluetoothProfile.A2DP:
+                    mA2dp = (BluetoothA2dp) proxy;
+                    break;
+                case BluetoothProfile.HEARING_AID:
+                    mHearingAid = (BluetoothHearingAid) proxy;
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(int profile) {
+            // Clear Bluetooth profile proxies
+            switch (profile) {
+                case BluetoothProfile.A2DP:
+                    mA2dp = null;
+                    break;
+                case BluetoothProfile.HEARING_AID:
+                    mHearingAid = null;
+                    break;
+                default:
+                    break;
+            }
+        }
+    };
+
+    @VisibleForTesting
+    public boolean isA2dpOrHearingAidConnected() {
+        return isA2dpConnected() || isHearingAidConnected();
+    }
+
+    @VisibleForTesting
+    public boolean isBluetoothOn() {
+        final BluetoothAdapter adapter = mAdapter;
+        if (adapter == null) {
+            return false;
+        }
+        return adapter.getLeState() == BluetoothAdapter.STATE_ON;
+    }
+
+    @VisibleForTesting
+    public boolean isAirplaneModeOn() {
+        return Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
+    }
+
+    @VisibleForTesting
+    public void onAirplaneModeChanged(BluetoothManagerService managerService) {
+        managerService.onAirplaneModeChanged();
+    }
+
+    @VisibleForTesting
+    public int getSettingsInt(String name) {
+        return Settings.Global.getInt(mContext.getContentResolver(),
+                name, 0);
+    }
+
+    @VisibleForTesting
+    public void setSettingsInt(String name, int value) {
+        Settings.Global.putInt(mContext.getContentResolver(),
+                name, value);
+    }
+
+    @VisibleForTesting
+    public void showToastMessage() {
+        Resources r = mContext.getResources();
+        final CharSequence text = r.getString(
+                R.string.bluetooth_airplane_mode_toast, 0);
+        Toast.makeText(mContext, text, Toast.LENGTH_LONG).show();
+    }
+
+    private boolean isA2dpConnected() {
+        final BluetoothA2dp a2dp = mA2dp;
+        if (a2dp == null) {
+            return false;
+        }
+        return a2dp.getConnectedDevices().size() > 0;
+    }
+
+    private boolean isHearingAidConnected() {
+        final BluetoothHearingAid hearingAid = mHearingAid;
+        if (hearingAid == null) {
+            return false;
+        }
+        return hearingAid.getConnectedDevices().size() > 0;
+    }
+}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 8a1baf2..afea976 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -140,6 +140,7 @@
 import android.net.util.LinkPropertiesUtils.CompareResult;
 import android.net.util.MultinetworkPolicyTracker;
 import android.net.util.NetdService;
+import android.os.BasicShellCommandHandler;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
@@ -156,11 +157,8 @@
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
-import android.os.ResultReceiver;
 import android.os.ServiceManager;
 import android.os.ServiceSpecificException;
-import android.os.ShellCallback;
-import android.os.ShellCommand;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
@@ -238,7 +236,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.Comparator;
 import java.util.ConcurrentModificationException;
 import java.util.HashMap;
@@ -932,13 +929,6 @@
         }
 
         /**
-         * @see ServiceManager#checkService(String)
-         */
-        public boolean hasService(@NonNull String name) {
-            return ServiceManager.checkService(name) != null;
-        }
-
-        /**
          * @see IpConnectivityMetrics.Logger
          */
         public IpConnectivityMetrics.Logger getMetricsLogger() {
@@ -1081,7 +1071,8 @@
 
         // Do the same for Ethernet, since it's often not specified in the configs, although many
         // devices can use it via USB host adapters.
-        if (mNetConfigs[TYPE_ETHERNET] == null && mDeps.hasService(Context.ETHERNET_SERVICE)) {
+        if (mNetConfigs[TYPE_ETHERNET] == null
+                && mContext.getSystemService(Context.ETHERNET_SERVICE) != null) {
             mLegacyTypeTracker.addSupportedType(TYPE_ETHERNET);
             mNetworksDefined++;
         }
@@ -1126,7 +1117,6 @@
 
         // Listen to package add and removal events for all users.
         intentFilter = new IntentFilter();
-        intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
         intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
         intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
         intentFilter.addDataScheme("package");
@@ -1173,7 +1163,7 @@
 
         mMultipathPolicyTracker = new MultipathPolicyTracker(mContext, mHandler);
 
-        mDnsManager = new DnsManager(mContext, mDnsResolver, mSystemProperties);
+        mDnsManager = new DnsManager(mContext, mDnsResolver);
         registerPrivateDnsSettingsCallbacks();
     }
 
@@ -2304,10 +2294,21 @@
     }
 
     /**
-     * Called when the system is ready and ConnectivityService can initialize remaining components.
+     * Called by SystemServer through ConnectivityManager when the system is ready.
+     */
+    @Override
+    public void systemReady() {
+        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+            throw new SecurityException("Calling Uid is not system uid.");
+        }
+        systemReadyInternal();
+    }
+
+    /**
+     * Called when ConnectivityService can initialize remaining components.
      */
     @VisibleForTesting
-    public void systemReady() {
+    public void systemReadyInternal() {
         // Let PermissionMonitor#startMonitoring() running in the beginning of the systemReady
         // before MultipathPolicyTracker.start(). Since mApps in PermissionMonitor needs to be
         // populated first to ensure that listening network request which is sent by
@@ -2464,12 +2465,11 @@
             loge("Can't set TCP buffer sizes:" + e);
         }
 
-        Integer rwndValue = Settings.Global.getInt(mContext.getContentResolver(),
-            Settings.Global.TCP_DEFAULT_INIT_RWND,
+        final Integer rwndValue = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.TCP_DEFAULT_INIT_RWND,
                     mSystemProperties.getInt("net.tcp.default_init_rwnd", 0));
-        final String sysctlKey = "sys.sysctl.tcp_def_init_rwnd";
         if (rwndValue != 0) {
-            mSystemProperties.set(sysctlKey, rwndValue.toString());
+            mSystemProperties.setTcpInitRwnd(rwndValue);
         }
     }
 
@@ -6195,20 +6195,12 @@
             return;  // no updating necessary
         }
 
-        final NetworkAgentInfo defaultNai = getDefaultNetwork();
-        final boolean isDefaultNetwork = (defaultNai != null && defaultNai.network.netId == netId);
-
         if (DBG) {
             final Collection<InetAddress> dnses = newLp.getDnsServers();
             log("Setting DNS servers for network " + netId + " to " + dnses);
         }
         try {
             mDnsManager.noteDnsServersForNetwork(netId, newLp);
-            // TODO: netd should listen on [::1]:53 and proxy queries to the current
-            // default network, and we should just set net.dns1 to ::1, not least
-            // because applications attempting to use net.dns resolvers will bypass
-            // the privacy protections of things like DNS-over-TLS.
-            if (isDefaultNetwork) mDnsManager.setDefaultDnsSystemProperties(newLp.getDnsServers());
             mDnsManager.flushVmDnsCache();
         } catch (Exception e) {
             loge("Exception in setDnsConfigurationForNetwork: " + e);
@@ -6723,8 +6715,6 @@
                 ? newNetwork.linkProperties.getHttpProxy() : null);
         updateTcpBufferSizes(null != newNetwork
                 ? newNetwork.linkProperties.getTcpBufferSizes() : null);
-        mDnsManager.setDefaultDnsSystemProperties(null != newNetwork
-                ? newNetwork.linkProperties.getDnsServers() : Collections.EMPTY_LIST);
         notifyIfacesChangedForNetworkStats();
         // Fix up the NetworkCapabilities of any VPNs that don't specify underlying networks.
         updateAllVpnsCapabilities();
@@ -7659,14 +7649,14 @@
     }
 
     @Override
-    public void onShellCommand(@NonNull FileDescriptor in, @NonNull FileDescriptor out,
-            FileDescriptor err, @NonNull String[] args, ShellCallback callback,
-            @NonNull ResultReceiver resultReceiver) {
-        (new ShellCmd()).exec(this, in, out, err, args, callback, resultReceiver);
+    public int handleShellCommand(@NonNull ParcelFileDescriptor in,
+            @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err,
+            @NonNull String[] args) {
+        return new ShellCmd().exec(this, in.getFileDescriptor(), out.getFileDescriptor(),
+                err.getFileDescriptor(), args);
     }
 
-    private class ShellCmd extends ShellCommand {
-
+    private class ShellCmd extends BasicShellCommandHandler {
         @Override
         public int onCommand(String cmd) {
             if (cmd == null) {
diff --git a/services/core/java/com/android/server/ConnectivityServiceInitializer.java b/services/core/java/com/android/server/ConnectivityServiceInitializer.java
new file mode 100644
index 0000000..2bc8925
--- /dev/null
+++ b/services/core/java/com/android/server/ConnectivityServiceInitializer.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
+
+import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
+import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
+
+import android.content.Context;
+import android.net.INetworkPolicyManager;
+import android.net.INetworkStatsService;
+import android.os.INetworkManagementService;
+import android.os.ServiceManager;
+import android.util.Log;
+
+/**
+ * Connectivity service initializer for core networking. This is called by system server to create
+ * a new instance of ConnectivityService.
+ */
+public final class ConnectivityServiceInitializer extends SystemService {
+    private static final String TAG = ConnectivityServiceInitializer.class.getSimpleName();
+    private final ConnectivityService mConnectivity;
+
+    public ConnectivityServiceInitializer(Context context) {
+        super(context);
+        // TODO: Define formal APIs to get the needed services.
+        mConnectivity = new ConnectivityService(context, getNetworkManagementService(),
+                getNetworkStatsService(), getNetworkPolicyManager());
+    }
+
+    @Override
+    public void onStart() {
+        Log.i(TAG, "Registering " + Context.CONNECTIVITY_SERVICE);
+        publishBinderService(Context.CONNECTIVITY_SERVICE, mConnectivity,
+                /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL);
+    }
+
+    private INetworkManagementService getNetworkManagementService() {
+        return INetworkManagementService.Stub.asInterface(
+               ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
+    }
+
+    private INetworkStatsService getNetworkStatsService() {
+        return INetworkStatsService.Stub.asInterface(
+                ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
+    }
+
+    private INetworkPolicyManager getNetworkPolicyManager() {
+        return INetworkPolicyManager.Stub.asInterface(
+                ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
+    }
+
+}
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index b3d4085..20f68da 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -38,7 +38,6 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.provider.Settings;
-import android.telecom.TelecomManager;
 import android.util.MutableBoolean;
 import android.util.Slog;
 import android.view.KeyEvent;
@@ -46,7 +45,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.server.LocalServices;
 import com.android.server.statusbar.StatusBarManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
@@ -430,11 +428,18 @@
                 mPowerButtonConsecutiveTaps++;
                 mPowerButtonSlowConsecutiveTaps++;
             }
-            if (mPanicButtonGestureEnabled
-                    && mPowerButtonConsecutiveTaps == PANIC_POWER_TAP_COUNT_THRESHOLD) {
-                launchPanic = true;
-                intercept = interactive;
-            } else if (mCameraDoubleTapPowerEnabled
+            // Check if we need to launch camera or panic flows
+            if (mPanicButtonGestureEnabled) {
+                // Commit to intercepting the powerkey event after the second "quick" tap to avoid
+                // lockscreen changes between launching camera and the panic flow.
+                if (mPowerButtonConsecutiveTaps > 1) {
+                    intercept = interactive;
+                }
+                if (mPowerButtonConsecutiveTaps == PANIC_POWER_TAP_COUNT_THRESHOLD) {
+                    launchPanic = true;
+                }
+            }
+            if (mCameraDoubleTapPowerEnabled
                     && powerTapInterval < CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS
                     && mPowerButtonConsecutiveTaps == CAMERA_POWER_TAP_COUNT_THRESHOLD) {
                 launchCamera = true;
@@ -464,8 +469,11 @@
         mMetricsLogger.histogram("power_consecutive_short_tap_count",
                 mPowerButtonSlowConsecutiveTaps);
         mMetricsLogger.histogram("power_double_tap_interval", (int) powerTapInterval);
+
         outLaunched.value = launchCamera || launchPanic;
-        return intercept && (launchCamera || launchPanic);
+        // Intercept power key event if the press is part of a gesture (camera, panic) and the user
+        // has completed setup.
+        return intercept && isUserSetupComplete();
     }
 
     /**
@@ -475,8 +483,7 @@
     boolean handleCameraGesture(boolean useWakelock, int source) {
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "GestureLauncher:handleCameraGesture");
         try {
-            boolean userSetupComplete = Settings.Secure.getIntForUser(mContext.getContentResolver(),
-                    Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
+            boolean userSetupComplete = isUserSetupComplete();
             if (!userSetupComplete) {
                 if (DBG) {
                     Slog.d(TAG, String.format(
@@ -514,8 +521,7 @@
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                 "GestureLauncher:handlePanicButtonGesture");
         try {
-            boolean userSetupComplete = Settings.Secure.getIntForUser(mContext.getContentResolver(),
-                    Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
+            boolean userSetupComplete = isUserSetupComplete();
             if (!userSetupComplete) {
                 if (DBG) {
                     Slog.d(TAG, String.format(
@@ -529,21 +535,20 @@
                         "userSetupComplete = %s, performing panic gesture.",
                         userSetupComplete));
             }
-            // TODO(b/160006048): Not all devices have telephony. Check system feature first.
-            TelecomManager telecomManager = (TelecomManager) mContext.getSystemService(
-                    Context.TELECOM_SERVICE);
-            mContext.startActivity(telecomManager.createLaunchEmergencyDialerIntent(null).addFlags(
-                    Intent.FLAG_ACTIVITY_NEW_TASK
-                            | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
-                            | Intent.FLAG_ACTIVITY_SINGLE_TOP).putExtra(
-                    "com.android.phone.EmergencyDialer.extra.ENTRY_TYPE",
-                    2)); // 2 maps to power button, forcing into fast emergency dialer experience.
+            StatusBarManagerInternal service = LocalServices.getService(
+                    StatusBarManagerInternal.class);
+            service.onEmergencyActionLaunchGestureDetected();
             return true;
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
         }
     }
 
+    private boolean isUserSetupComplete() {
+        return Settings.Secure.getIntForUser(mContext.getContentResolver(),
+                Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
+    }
+
     private final BroadcastReceiver mUserReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
diff --git a/services/core/java/com/android/server/MmsServiceBroker.java b/services/core/java/com/android/server/MmsServiceBroker.java
index 1804b7f..593c406 100644
--- a/services/core/java/com/android/server/MmsServiceBroker.java
+++ b/services/core/java/com/android/server/MmsServiceBroker.java
@@ -511,7 +511,7 @@
                 contentUri = ContentProvider.maybeAddUserId(contentUri, callingUserId);
             }
 
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 final UriGrantsManagerInternal ugm = LocalServices
                         .getService(UriGrantsManagerInternal.class);
diff --git a/services/core/java/com/android/server/MountServiceIdler.java b/services/core/java/com/android/server/MountServiceIdler.java
index 6bc1a57..0f4c94b 100644
--- a/services/core/java/com/android/server/MountServiceIdler.java
+++ b/services/core/java/com/android/server/MountServiceIdler.java
@@ -113,6 +113,7 @@
         JobInfo.Builder builder = new JobInfo.Builder(MOUNT_JOB_ID, sIdleService);
         builder.setRequiresDeviceIdle(true);
         builder.setRequiresBatteryNotLow(true);
+        builder.setRequiresCharging(true);
         builder.setMinimumLatency(nextScheduleTime);
         tm.schedule(builder.build());
     }
diff --git a/services/core/java/com/android/server/NetworkTimeUpdateService.java b/services/core/java/com/android/server/NetworkTimeUpdateService.java
index c34dd98..ff2308c 100644
--- a/services/core/java/com/android/server/NetworkTimeUpdateService.java
+++ b/services/core/java/com/android/server/NetworkTimeUpdateService.java
@@ -103,7 +103,9 @@
         mCM = mContext.getSystemService(ConnectivityManager.class);
 
         Intent pollIntent = new Intent(ACTION_POLL, null);
-        mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
+        // Broadcast alarms sent by system are immutable
+        mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent,
+                PendingIntent.FLAG_IMMUTABLE);
 
         mPollingIntervalMs = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_ntpPollingInterval);
diff --git a/services/core/java/com/android/server/NsdService.java b/services/core/java/com/android/server/NsdService.java
index 4a1820a..78bd4cd 100644
--- a/services/core/java/com/android/server/NsdService.java
+++ b/services/core/java/com/android/server/NsdService.java
@@ -824,7 +824,7 @@
 
         @Override
         public String toString() {
-            StringBuffer sb = new StringBuffer();
+            StringBuilder sb = new StringBuilder();
             sb.append("mChannel ").append(mChannel).append("\n");
             sb.append("mMessenger ").append(mMessenger).append("\n");
             sb.append("mResolvedService ").append(mResolvedService).append("\n");
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 7775354..ab933a8 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -1742,7 +1742,7 @@
         UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
         final int callingUserId = UserHandle.getCallingUserId();
         boolean isAdmin;
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             isAdmin = um.getUserInfo(callingUserId).isAdmin();
         } finally {
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index b874684..ff2661b 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -26,12 +26,14 @@
 import android.os.Trace;
 import android.os.UserManagerInternal;
 import android.util.ArrayMap;
+import android.util.EventLog;
 import android.util.Slog;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
 import com.android.server.SystemService.TargetUser;
+import com.android.server.am.EventLogTags;
 import com.android.server.utils.TimingsTraceAndSlog;
 
 import dalvik.system.PathClassLoader;
@@ -53,12 +55,14 @@
     private static final int SERVICE_CALL_WARN_TIME_MS = 50;
 
     // Constants used on onUser(...)
-    private static final String START = "Start";
-    private static final String UNLOCKING = "Unlocking";
-    private static final String UNLOCKED = "Unlocked";
-    private static final String SWITCH = "Switch";
-    private static final String STOP = "Stop";
-    private static final String CLEANUP = "Cleanup";
+    // NOTE: do not change their values, as they're used on Trace calls and changes might break
+    // performance tests that rely on them.
+    private static final String USER_STARTING = "Start";
+    private static final String USER_UNLOCKING = "Unlocking";
+    private static final String USER_UNLOCKED = "Unlocked";
+    private static final String USER_SWITCHING = "Switch";
+    private static final String USER_STOPPING = "Stop";
+    private static final String USER_STOPPED = "Cleanup";
 
     private static File sSystemDir;
     private final Context mContext;
@@ -86,7 +90,7 @@
 
     /**
      * Reference to the current user, it's used to set the {@link TargetUser} on
-     * {@link #switchUser(int, int)} as the previous user might have been removed already.
+     * {@link #onUserSwitching(int, int)} as the previous user might have been removed already.
      */
     @GuardedBy("mTargetUsers")
     private @Nullable TargetUser mCurrentUser;
@@ -275,33 +279,38 @@
     /**
      * Starts the given user.
      */
-    public void startUser(@NonNull TimingsTraceAndSlog t, @UserIdInt int userId) {
+    public void onUserStarting(@NonNull TimingsTraceAndSlog t, @UserIdInt int userId) {
+        EventLog.writeEvent(EventLogTags.SSM_USER_STARTING, userId);
+
         final TargetUser targetUser = newTargetUser(userId);
         synchronized (mTargetUsers) {
             mTargetUsers.put(userId, targetUser);
         }
 
-        onUser(t, START, /* prevUser= */ null, targetUser);
+        onUser(t, USER_STARTING, /* prevUser= */ null, targetUser);
     }
 
     /**
      * Unlocks the given user.
      */
-    public void unlockUser(@UserIdInt int userId) {
-        onUser(UNLOCKING, userId);
+    public void onUserUnlocking(@UserIdInt int userId) {
+        EventLog.writeEvent(EventLogTags.SSM_USER_UNLOCKING, userId);
+        onUser(USER_UNLOCKING, userId);
     }
 
     /**
      * Called after the user was unlocked.
      */
     public void onUserUnlocked(@UserIdInt int userId) {
-        onUser(UNLOCKED, userId);
+        EventLog.writeEvent(EventLogTags.SSM_USER_UNLOCKED, userId);
+        onUser(USER_UNLOCKED, userId);
     }
 
     /**
      * Switches to the given user.
      */
-    public void switchUser(@UserIdInt int from, @UserIdInt int to) {
+    public void onUserSwitching(@UserIdInt int from, @UserIdInt int to) {
+        EventLog.writeEvent(EventLogTags.SSM_USER_SWITCHING, from, to);
         final TargetUser curUser, prevUser;
         synchronized (mTargetUsers) {
             if (mCurrentUser == null) {
@@ -321,21 +330,23 @@
                 Slog.d(TAG, "Set mCurrentUser to " + mCurrentUser);
             }
         }
-        onUser(TimingsTraceAndSlog.newAsyncLog(), SWITCH, prevUser, curUser);
+        onUser(TimingsTraceAndSlog.newAsyncLog(), USER_SWITCHING, prevUser, curUser);
     }
 
     /**
      * Stops the given user.
      */
-    public void stopUser(@UserIdInt int userId) {
-        onUser(STOP, userId);
+    public void onUserStopping(@UserIdInt int userId) {
+        EventLog.writeEvent(EventLogTags.SSM_USER_STOPPING, userId);
+        onUser(USER_STOPPING, userId);
     }
 
     /**
      * Cleans up the given user.
      */
-    public void cleanupUser(@UserIdInt int userId) {
-        onUser(CLEANUP, userId);
+    public void onUserStopped(@UserIdInt int userId) {
+        EventLog.writeEvent(EventLogTags.SSM_USER_STOPPED, userId);
+        onUser(USER_STOPPED, userId);
 
         // Remove cached TargetUser
         synchronized (mTargetUsers) {
@@ -351,6 +362,7 @@
     private void onUser(@NonNull TimingsTraceAndSlog t, @NonNull String onWhat,
             @Nullable TargetUser prevUser, @NonNull TargetUser curUser) {
         final int curUserId = curUser.getUserIdentifier();
+        // NOTE: do not change label below, or it might break performance tests that rely on it.
         t.traceBegin("ssm." + onWhat + "User-" + curUserId);
         Slog.i(TAG, "Calling on" + onWhat + "User " + curUserId
                 + (prevUser != null ? " (from " + prevUser + ")" : ""));
@@ -381,22 +393,22 @@
             long time = SystemClock.elapsedRealtime();
             try {
                 switch (onWhat) {
-                    case SWITCH:
+                    case USER_SWITCHING:
                         service.onUserSwitching(prevUser, curUser);
                         break;
-                    case START:
+                    case USER_STARTING:
                         service.onUserStarting(curUser);
                         break;
-                    case UNLOCKING:
+                    case USER_UNLOCKING:
                         service.onUserUnlocking(curUser);
                         break;
-                    case UNLOCKED:
+                    case USER_UNLOCKED:
                         service.onUserUnlocked(curUser);
                         break;
-                    case STOP:
+                    case USER_STOPPING:
                         service.onUserStopping(curUser);
                         break;
-                    case CLEANUP:
+                    case USER_STOPPED:
                         service.onUserStopped(curUser);
                         break;
                     default:
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
index 1cb7c4d..ebeec39 100644
--- a/services/core/java/com/android/server/TEST_MAPPING
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -23,6 +23,10 @@
             "file_patterns": ["NotificationManagerService\\.java"]
         },
         {
+            "name": "CtsScopedStorageCoreHostTest",
+            "file_patterns": ["StorageManagerService\\.java"]
+        },
+        {
             "name": "CtsScopedStorageHostTest",
             "file_patterns": ["StorageManagerService\\.java"]
         }
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index ba2e147..8ea71b3 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1623,10 +1623,12 @@
         if (!checkNotifyPermission("notifyDisplayInfoChanged()")) {
             return;
         }
+        String str = "notifyDisplayInfoChanged: PhoneId=" + phoneId + " subId=" + subId
+                + " telephonyDisplayInfo=" + telephonyDisplayInfo;
         if (VDBG) {
-            log("notifyDisplayInfoChanged: PhoneId=" + phoneId
-                    + " subId=" + subId + " telephonyDisplayInfo=" + telephonyDisplayInfo);
+            log(str);
         }
+        mLocalLog.log(str);
         synchronized (mRecords) {
             if (validatePhoneId(phoneId)) {
                 mTelephonyDisplayInfos[phoneId] = telephonyDisplayInfo;
@@ -2354,10 +2356,10 @@
                 pw.println("mOutgoingCallEmergencyNumber=" + mOutgoingCallEmergencyNumber[i]);
                 pw.println("mOutgoingSmsEmergencyNumber=" + mOutgoingSmsEmergencyNumber[i]);
                 pw.println("mBarringInfo=" + mBarringInfo.get(i));
+                pw.println("mTelephonyDisplayInfo=" + mTelephonyDisplayInfos[i]);
                 pw.decreaseIndent();
             }
             pw.println("mCarrierNetworkChangeState=" + mCarrierNetworkChangeState);
-
             pw.println("mPhoneCapability=" + mPhoneCapability);
             pw.println("mActiveDataSubId=" + mActiveDataSubId);
             pw.println("mRadioPowerState=" + mRadioPowerState);
@@ -2418,7 +2420,7 @@
     public static final String ACTION_SIGNAL_STRENGTH_CHANGED = "android.intent.action.SIG_STR";
 
     private void broadcastServiceStateChanged(ServiceState state, int phoneId, int subId) {
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             mBatteryStats.notePhoneState(state.getState());
         } catch (RemoteException re) {
@@ -2442,7 +2444,7 @@
 
     private void broadcastSignalStrengthChanged(SignalStrength signalStrength, int phoneId,
             int subId) {
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             mBatteryStats.notePhoneSignalStrength(signalStrength);
         } catch (RemoteException e) {
@@ -2487,7 +2489,7 @@
      */
     private void broadcastCallStateChanged(int state, String incomingNumber, int phoneId,
                 int subId) {
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             if (state == TelephonyManager.CALL_STATE_IDLE) {
                 mBatteryStats.notePhoneOff();
@@ -2676,7 +2678,7 @@
 
     private boolean validateEventsAndUserLocked(Record r, int events) {
         int foregroundUser;
-        long callingIdentity = Binder.clearCallingIdentity();
+        final long callingIdentity = Binder.clearCallingIdentity();
         boolean valid = false;
         try {
             foregroundUser = ActivityManager.getCurrentUser();
diff --git a/services/core/java/com/android/server/UpdateLockService.java b/services/core/java/com/android/server/UpdateLockService.java
index 06f73e2..38bbbdf7 100644
--- a/services/core/java/com/android/server/UpdateLockService.java
+++ b/services/core/java/com/android/server/UpdateLockService.java
@@ -74,7 +74,7 @@
 
     void sendLockChangedBroadcast(boolean state) {
         // Safe early during boot because this broadcast only goes to registered receivers.
-        long oldIdent = Binder.clearCallingIdentity();
+        final long oldIdent = Binder.clearCallingIdentity();
         try {
             Intent intent = new Intent(UpdateLock.UPDATE_LOCK_CHANGED)
                     .putExtra(UpdateLock.NOW_IS_CONVENIENT, state)
diff --git a/services/core/java/com/android/server/VibratorManagerService.java b/services/core/java/com/android/server/VibratorManagerService.java
index 2f35da7..356cd0c 100644
--- a/services/core/java/com/android/server/VibratorManagerService.java
+++ b/services/core/java/com/android/server/VibratorManagerService.java
@@ -16,12 +16,15 @@
 
 package com.android.server;
 
+import android.annotation.Nullable;
 import android.content.Context;
+import android.os.CombinedVibrationEffect;
 import android.os.IBinder;
 import android.os.IVibratorManagerService;
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
 import android.os.ShellCommand;
+import android.os.VibrationAttributes;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -66,6 +69,17 @@
         return Arrays.copyOf(mVibratorIds, mVibratorIds.length);
     }
 
+    @Override // Binder call
+    public void vibrate(int uid, String opPkg, CombinedVibrationEffect effect,
+            @Nullable VibrationAttributes attrs, String reason, IBinder token) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override // Binder call
+    public void cancelVibrate(IBinder token) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
     @Override
     public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
             String[] args, ShellCallback cb, ResultReceiver resultReceiver) {
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index cb6f616..afddd65 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -33,7 +33,6 @@
 import android.hardware.input.InputManager;
 import android.hardware.vibrator.IVibrator;
 import android.hardware.vibrator.V1_0.EffectStrength;
-import android.icu.text.DateFormat;
 import android.media.AudioManager;
 import android.os.BatteryStats;
 import android.os.Binder;
@@ -80,6 +79,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Date;
@@ -90,6 +90,8 @@
 public class VibratorService extends IVibratorService.Stub
         implements InputManager.InputDeviceListener {
     private static final String TAG = "VibratorService";
+    private static final SimpleDateFormat DEBUG_DATE_FORMAT =
+            new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
     private static final boolean DEBUG = false;
     private static final String EXTERNAL_VIBRATOR_SERVICE = "external_vibrator_service";
 
@@ -126,7 +128,7 @@
     private final LinkedList<VibrationInfo> mPreviousRingVibrations;
     private final LinkedList<VibrationInfo> mPreviousNotificationVibrations;
     private final LinkedList<VibrationInfo> mPreviousAlarmVibrations;
-    private final LinkedList<ExternalVibration> mPreviousExternalVibrations;
+    private final LinkedList<VibrationInfo> mPreviousExternalVibrations;
     private final LinkedList<VibrationInfo> mPreviousVibrations;
     private final int mPreviousVibrationsLimit;
     private final boolean mAllowPriorityVibrationsInLowPowerMode;
@@ -151,7 +153,7 @@
     private SettingsObserver mSettingObserver;
 
     private final NativeWrapper mNativeWrapper;
-    private volatile VibrateThread mThread;
+    private volatile VibrateWaveformThread mThread;
 
     // mInputDeviceVibrators lock should be acquired after mLock, if both are
     // to be acquired
@@ -162,7 +164,7 @@
     @GuardedBy("mLock")
     private Vibration mCurrentVibration;
     private int mCurVibUid = -1;
-    private ExternalVibration mCurrentExternalVibration;
+    private ExternalVibrationHolder mCurrentExternalVibration;
     private boolean mVibratorUnderExternalControl;
     private boolean mLowPowerMode;
     @GuardedBy("mLock")
@@ -231,19 +233,12 @@
         void onComplete(long vibrationId);
     }
 
-    /**
-     * Holder for a vibration to be played. This class can be shared with native methods for
-     * hardware callback support.
-     */
+    /** Holder for a {@link VibrationEffect}. */
     private final class Vibration implements IBinder.DeathRecipient {
 
         public final IBinder token;
         // Start time in CLOCK_BOOTTIME base.
         public final long startTime;
-        // Start time in unix epoch time. Only to be used for debugging purposes and to correlate
-        // with other system events, any duration calculations should be done use startTime so as
-        // not to be affected by discontinuities created by RTC adjustments.
-        public final long startTimeDebug;
         public final VibrationAttributes attrs;
         public final long id;
         public final int uid;
@@ -255,6 +250,15 @@
         // The original effect that was requested. Typically these two things differ because
         // the effect was scaled based on the users vibration intensity settings.
         public VibrationEffect originalEffect;
+        // The scale applied to the original effect.
+        public float scale;
+
+        // Start/end times in unix epoch time. Only to be used for debugging purposes and to
+        // correlate with other system events, any duration calculations should be done use
+        // startTime so as not to be affected by discontinuities created by RTC adjustments.
+        private final long mStartTimeDebug;
+        private long mEndTimeDebug;
+        private VibrationInfo.Status mStatus;
 
         private Vibration(IBinder token, VibrationEffect effect,
                 VibrationAttributes attrs, int uid, String opPkg, String reason) {
@@ -262,11 +266,12 @@
             this.effect = effect;
             this.id = mNextVibrationId.getAndIncrement();
             this.startTime = SystemClock.elapsedRealtime();
-            this.startTimeDebug = System.currentTimeMillis();
             this.attrs = attrs;
             this.uid = uid;
             this.opPkg = opPkg;
             this.reason = reason;
+            mStartTimeDebug = System.currentTimeMillis();
+            mStatus = VibrationInfo.Status.RUNNING;
         }
 
         @Override
@@ -276,11 +281,24 @@
                     if (DEBUG) {
                         Slog.d(TAG, "Vibration finished because binder died, cleaning up");
                     }
-                    doCancelVibrateLocked();
+                    doCancelVibrateLocked(VibrationInfo.Status.CANCELLED);
                 }
             }
         }
 
+        public void end(VibrationInfo.Status status) {
+            if (hasEnded()) {
+                // Vibration already ended, keep first ending status set and ignore this one.
+                return;
+            }
+            mStatus = status;
+            mEndTimeDebug = System.currentTimeMillis();
+        }
+
+        public boolean hasEnded() {
+            return mStatus != VibrationInfo.Status.RUNNING;
+        }
+
         public boolean hasTimeoutLongerThan(long millis) {
             final long duration = effect.getDuration();
             return duration >= 0 && duration > millis;
@@ -308,40 +326,109 @@
 
         public VibrationInfo toInfo() {
             return new VibrationInfo(
-                    startTimeDebug, effect, originalEffect, attrs, uid, opPkg, reason);
+                    mStartTimeDebug, mEndTimeDebug, effect, originalEffect, scale, attrs,
+                    uid, opPkg, reason, mStatus);
         }
     }
 
-    private static class VibrationInfo {
+    /** Holder for a {@link ExternalVibration}. */
+    private final class ExternalVibrationHolder {
+
+        public final ExternalVibration externalVibration;
+        public int scale;
+
         private final long mStartTimeDebug;
+        private long mEndTimeDebug;
+        private VibrationInfo.Status mStatus;
+
+        private ExternalVibrationHolder(ExternalVibration externalVibration) {
+            this.externalVibration = externalVibration;
+            this.scale = SCALE_NONE;
+            mStartTimeDebug = System.currentTimeMillis();
+            mStatus = VibrationInfo.Status.RUNNING;
+        }
+
+        public void end(VibrationInfo.Status status) {
+            if (mStatus != VibrationInfo.Status.RUNNING) {
+                // Vibration already ended, keep first ending status set and ignore this one.
+                return;
+            }
+            mStatus = status;
+            mEndTimeDebug = System.currentTimeMillis();
+        }
+
+        public VibrationInfo toInfo() {
+            return new VibrationInfo(
+                    mStartTimeDebug, mEndTimeDebug, /* effect= */ null, /* originalEffect= */ null,
+                    scale, externalVibration.getVibrationAttributes(),
+                    externalVibration.getUid(), externalVibration.getPackage(),
+                    /* reason= */ null, mStatus);
+        }
+    }
+
+    /** Debug information about vibrations. */
+    private static class VibrationInfo {
+
+        public enum Status {
+            RUNNING,
+            FINISHED,
+            CANCELLED,
+            ERROR_APP_OPS,
+            IGNORED,
+            IGNORED_APP_OPS,
+            IGNORED_BACKGROUND,
+            IGNORED_RINGTONE,
+            IGNORED_UNKNOWN_VIBRATION,
+            IGNORED_UNSUPPORTED,
+            IGNORED_FOR_ALARM,
+            IGNORED_FOR_EXTERNAL,
+            IGNORED_FOR_ONGOING,
+            IGNORED_FOR_POWER,
+            IGNORED_FOR_SETTINGS,
+        }
+
+        private final long mStartTimeDebug;
+        private final long mEndTimeDebug;
         private final VibrationEffect mEffect;
         private final VibrationEffect mOriginalEffect;
+        private final float mScale;
         private final VibrationAttributes mAttrs;
         private final int mUid;
         private final String mOpPkg;
         private final String mReason;
+        private final VibrationInfo.Status mStatus;
 
-        VibrationInfo(long startTimeDebug, VibrationEffect effect,
-                VibrationEffect originalEffect, VibrationAttributes attrs, int uid,
-                String opPkg, String reason) {
+        VibrationInfo(long startTimeDebug, long endTimeDebug, VibrationEffect effect,
+                VibrationEffect originalEffect, float scale, VibrationAttributes attrs,
+                int uid, String opPkg, String reason, VibrationInfo.Status status) {
             mStartTimeDebug = startTimeDebug;
+            mEndTimeDebug = endTimeDebug;
             mEffect = effect;
             mOriginalEffect = originalEffect;
+            mScale = scale;
             mAttrs = attrs;
             mUid = uid;
             mOpPkg = opPkg;
             mReason = reason;
+            mStatus = status;
         }
 
         @Override
         public String toString() {
             return new StringBuilder()
                     .append("startTime: ")
-                    .append(DateFormat.getDateTimeInstance().format(new Date(mStartTimeDebug)))
+                    .append(DEBUG_DATE_FORMAT.format(new Date(mStartTimeDebug)))
+                    .append(", endTime: ")
+                    .append(mEndTimeDebug == 0 ? null
+                            : DEBUG_DATE_FORMAT.format(new Date(mEndTimeDebug)))
+                    .append(", status: ")
+                    .append(mStatus.name().toLowerCase())
                     .append(", effect: ")
                     .append(mEffect)
                     .append(", originalEffect: ")
                     .append(mOriginalEffect)
+                    .append(", scale: ")
+                    .append(String.format("%.2f", mScale))
                     .append(", attrs: ")
                     .append(mAttrs)
                     .append(", uid: ")
@@ -354,11 +441,80 @@
         }
 
         void dumpProto(ProtoOutputStream proto, long fieldId) {
-            synchronized (this) {
-                final long token = proto.start(fieldId);
-                proto.write(VibrationProto.START_TIME, mStartTimeDebug);
-                proto.end(token);
+            final long token = proto.start(fieldId);
+            proto.write(VibrationProto.START_TIME, mStartTimeDebug);
+            proto.write(VibrationProto.END_TIME, mEndTimeDebug);
+            proto.write(VibrationProto.STATUS, mStatus.ordinal());
+
+            final long attrsToken = proto.start(VibrationProto.ATTRIBUTES);
+            proto.write(VibrationAttributesProto.USAGE, mAttrs.getUsage());
+            proto.write(VibrationAttributesProto.AUDIO_USAGE, mAttrs.getAudioUsage());
+            proto.write(VibrationAttributesProto.FLAGS, mAttrs.getFlags());
+            proto.end(attrsToken);
+
+            if (mEffect != null) {
+                dumpEffect(proto, VibrationProto.EFFECT, mEffect);
             }
+            if (mOriginalEffect != null) {
+                dumpEffect(proto, VibrationProto.ORIGINAL_EFFECT, mOriginalEffect);
+            }
+
+            proto.end(token);
+        }
+
+        private void dumpEffect(ProtoOutputStream proto, long fieldId, VibrationEffect effect) {
+            final long token = proto.start(fieldId);
+            if (effect instanceof VibrationEffect.OneShot) {
+                dumpEffect(proto, VibrationEffectProto.ONESHOT, (VibrationEffect.OneShot) effect);
+            } else if (effect instanceof VibrationEffect.Waveform) {
+                dumpEffect(proto, VibrationEffectProto.WAVEFORM, (VibrationEffect.Waveform) effect);
+            } else if (effect instanceof VibrationEffect.Prebaked) {
+                dumpEffect(proto, VibrationEffectProto.PREBAKED, (VibrationEffect.Prebaked) effect);
+            } else if (effect instanceof VibrationEffect.Composed) {
+                dumpEffect(proto, VibrationEffectProto.COMPOSED, (VibrationEffect.Composed) effect);
+            }
+            proto.end(token);
+        }
+
+        private void dumpEffect(ProtoOutputStream proto, long fieldId,
+                VibrationEffect.OneShot effect) {
+            final long token = proto.start(fieldId);
+            proto.write(OneShotProto.DURATION, (int) effect.getDuration());
+            proto.write(OneShotProto.AMPLITUDE, effect.getAmplitude());
+            proto.end(token);
+        }
+
+        private void dumpEffect(ProtoOutputStream proto, long fieldId,
+                VibrationEffect.Waveform effect) {
+            final long token = proto.start(fieldId);
+            for (long timing : effect.getTimings()) {
+                proto.write(WaveformProto.TIMINGS, (int) timing);
+            }
+            for (int amplitude : effect.getAmplitudes()) {
+                proto.write(WaveformProto.AMPLITUDES, amplitude);
+            }
+            proto.write(WaveformProto.REPEAT, effect.getRepeatIndex() >= 0);
+            proto.end(token);
+        }
+
+        private void dumpEffect(ProtoOutputStream proto, long fieldId,
+                VibrationEffect.Prebaked effect) {
+            final long token = proto.start(fieldId);
+            proto.write(PrebakedProto.EFFECT_ID, effect.getId());
+            proto.write(PrebakedProto.EFFECT_STRENGTH, effect.getEffectStrength());
+            proto.write(PrebakedProto.FALLBACK, effect.shouldFallback());
+            proto.end(token);
+        }
+
+        private void dumpEffect(ProtoOutputStream proto, long fieldId,
+                VibrationEffect.Composed effect) {
+            final long token = proto.start(fieldId);
+            for (PrimitiveEffect primitive : effect.getPrimitiveEffects()) {
+                proto.write(ComposedProto.EFFECT_IDS, primitive.id);
+                proto.write(ComposedProto.EFFECT_SCALES, primitive.scale);
+                proto.write(ComposedProto.DELAYS, primitive.delay);
+            }
+            proto.end(token);
         }
     }
 
@@ -549,7 +705,7 @@
                 if (DEBUG) {
                     Slog.d(TAG, "Vibration finished by callback, cleaning up");
                 }
-                doCancelVibrateLocked();
+                doCancelVibrateLocked(VibrationInfo.Status.FINISHED);
             }
         }
     }
@@ -792,6 +948,7 @@
             }
 
             attrs = fixupVibrationAttributes(attrs);
+            Vibration vib = new Vibration(token, effect, attrs, uid, opPkg, reason);
 
             // If our current vibration is longer than the new vibration and is the same amplitude,
             // then just let the current one finish.
@@ -808,6 +965,7 @@
                             Slog.d(TAG,
                                     "Ignoring incoming vibration in favor of current vibration");
                         }
+                        endVibrationLocked(vib, VibrationInfo.Status.IGNORED_FOR_ONGOING);
                         return;
                     }
                 }
@@ -819,6 +977,7 @@
                     if (DEBUG) {
                         Slog.d(TAG, "Ignoring incoming vibration for current external vibration");
                     }
+                    endVibrationLocked(vib, VibrationInfo.Status.IGNORED_FOR_EXTERNAL);
                     return;
                 }
 
@@ -832,24 +991,29 @@
                     if (DEBUG) {
                         Slog.d(TAG, "Ignoring incoming vibration in favor of alarm vibration");
                     }
+                    endVibrationLocked(vib, VibrationInfo.Status.IGNORED_FOR_ALARM);
                     return;
                 }
 
-                Vibration vib = new Vibration(token, effect, attrs, uid, opPkg, reason);
                 if (mProcStatesCache.get(uid, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND)
                         > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
                         && !vib.isNotification() && !vib.isRingtone() && !vib.isAlarm()) {
                     Slog.e(TAG, "Ignoring incoming vibration as process with"
                             + " uid= " + uid + " is background,"
                             + " attrs= " + vib.attrs);
+                    endVibrationLocked(vib, VibrationInfo.Status.IGNORED_BACKGROUND);
                     return;
                 }
                 linkVibration(vib);
-                long ident = Binder.clearCallingIdentity();
+                final long ident = Binder.clearCallingIdentity();
                 try {
-                    doCancelVibrateLocked();
+                    doCancelVibrateLocked(VibrationInfo.Status.CANCELLED);
                     startVibrationLocked(vib);
-                    addToPreviousVibrationsLocked(vib);
+
+                    if (!vib.hasEnded() && mCurrentVibration.id != vib.id) {
+                        // Vibration was unexpectedly ignored: add to list for debugging
+                        endVibrationLocked(vib, VibrationInfo.Status.IGNORED);
+                    }
                 } finally {
                     Binder.restoreCallingIdentity(ident);
                 }
@@ -868,7 +1032,7 @@
         return effect.getDuration() == Long.MAX_VALUE;
     }
 
-    private void addToPreviousVibrationsLocked(Vibration vib) {
+    private void endVibrationLocked(Vibration vib, VibrationInfo.Status status) {
         final LinkedList<VibrationInfo> previousVibrations;
         if (vib.isRingtone()) {
             previousVibrations = mPreviousRingVibrations;
@@ -883,9 +1047,18 @@
         if (previousVibrations.size() > mPreviousVibrationsLimit) {
             previousVibrations.removeFirst();
         }
+        vib.end(status);
         previousVibrations.addLast(vib.toInfo());
     }
 
+    private void endVibrationLocked(ExternalVibrationHolder vib, VibrationInfo.Status status) {
+        if (mPreviousExternalVibrations.size() > mPreviousVibrationsLimit) {
+            mPreviousExternalVibrations.removeFirst();
+        }
+        vib.end(status);
+        mPreviousExternalVibrations.addLast(vib.toInfo());
+    }
+
     @Override // Binder call
     public void cancelVibrate(IBinder token) {
         mContext.enforceCallingOrSelfPermission(
@@ -897,9 +1070,9 @@
                 if (DEBUG) {
                     Slog.d(TAG, "Canceling vibration.");
                 }
-                long ident = Binder.clearCallingIdentity();
+                final long ident = Binder.clearCallingIdentity();
                 try {
-                    doCancelVibrateLocked();
+                    doCancelVibrateLocked(VibrationInfo.Status.CANCELLED);
                 } finally {
                     Binder.restoreCallingIdentity(ident);
                 }
@@ -908,7 +1081,7 @@
     }
 
     @GuardedBy("mLock")
-    private void doCancelVibrateLocked() {
+    private void doCancelVibrateLocked(VibrationInfo.Status status) {
         Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doCancelVibrateLocked");
         try {
@@ -917,12 +1090,13 @@
                 mThread = null;
             }
             if (mCurrentExternalVibration != null) {
-                mCurrentExternalVibration.mute();
+                endVibrationLocked(mCurrentExternalVibration, status);
+                mCurrentExternalVibration.externalVibration.mute();
                 mCurrentExternalVibration = null;
                 setVibratorUnderExternalControl(false);
             }
             doVibratorOff();
-            reportFinishVibrationLocked();
+            reportFinishVibrationLocked(status);
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
         }
@@ -936,7 +1110,7 @@
         synchronized (mLock) {
             // Make sure the vibration is really done. This also reports that the vibration is
             // finished.
-            doCancelVibrateLocked();
+            doCancelVibrateLocked(VibrationInfo.Status.FINISHED);
         }
     }
 
@@ -944,7 +1118,7 @@
     private void startVibrationLocked(final Vibration vib) {
         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationLocked");
         try {
-            final int intensity = getCurrentIntensityLocked(vib);
+            final int intensity = getCurrentIntensityLocked(vib.attrs.getUsage());
             if (!shouldVibrate(vib, intensity)) {
                 return;
             }
@@ -959,32 +1133,34 @@
     private void startVibrationInnerLocked(Vibration vib) {
         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationInnerLocked");
         try {
+            // Set current vibration before starting it, so callback will work.
             mCurrentVibration = vib;
             if (vib.effect instanceof VibrationEffect.OneShot) {
                 Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
-                VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) vib.effect;
-                doVibratorOn(oneShot.getDuration(), oneShot.getAmplitude(), vib);
+                doVibratorOn(vib);
             } else if (vib.effect instanceof VibrationEffect.Waveform) {
                 // mThread better be null here. doCancelVibrate should always be
                 // called before startNextVibrationLocked or startVibrationLocked.
-                VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) vib.effect;
-                mThread = new VibrateThread(waveform, vib.uid, vib.attrs);
+                mThread = new VibrateWaveformThread(vib);
                 mThread.start();
             } else if (vib.effect instanceof VibrationEffect.Prebaked) {
                 Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
                 doVibratorPrebakedEffectLocked(vib);
-            } else if (vib.effect instanceof  VibrationEffect.Composed) {
+            } else if (vib.effect instanceof VibrationEffect.Composed) {
                 Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
                 doVibratorComposedEffectLocked(vib);
             } else {
                 Slog.e(TAG, "Unknown vibration type, ignoring");
+                endVibrationLocked(vib, VibrationInfo.Status.IGNORED_UNKNOWN_VIBRATION);
+                // The set current vibration is not actually playing, so drop it.
+                mCurrentVibration = null;
             }
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
         }
     }
 
-    private boolean isAllowedToVibrateLocked(Vibration vib) {
+    private boolean shouldVibrateForPowerModeLocked(Vibration vib) {
         if (!mLowPowerMode) {
             return true;
         }
@@ -995,14 +1171,28 @@
                 || usage == VibrationAttributes.USAGE_COMMUNICATION_REQUEST;
     }
 
-    private int getCurrentIntensityLocked(Vibration vib) {
-        if (vib.isRingtone()) {
+    private int getCurrentIntensityLocked(int usageHint) {
+        if (isRingtone(usageHint)) {
             return mRingIntensity;
-        } else if (vib.isNotification()) {
+        } else if (isNotification(usageHint)) {
             return mNotificationIntensity;
-        } else if (vib.isHapticFeedback()) {
+        } else if (isHapticFeedback(usageHint)) {
             return mHapticFeedbackIntensity;
-        } else if (vib.isAlarm()) {
+        } else if (isAlarm(usageHint)) {
+            return Vibrator.VIBRATION_INTENSITY_HIGH;
+        } else {
+            return Vibrator.VIBRATION_INTENSITY_MEDIUM;
+        }
+    }
+
+    private int getDefaultIntensity(int usageHint) {
+        if (isRingtone(usageHint)) {
+            return mVibrator.getDefaultRingVibrationIntensity();
+        } else if (isNotification(usageHint)) {
+            return mVibrator.getDefaultNotificationVibrationIntensity();
+        } else if (isHapticFeedback(usageHint)) {
+            return mVibrator.getDefaultHapticFeedbackIntensity();
+        } else if (isAlarm(usageHint)) {
             return Vibrator.VIBRATION_INTENSITY_HIGH;
         } else {
             return Vibrator.VIBRATION_INTENSITY_MEDIUM;
@@ -1021,21 +1211,7 @@
             return;
         }
 
-        final int defaultIntensity;
-        if (vib.isRingtone()) {
-            defaultIntensity = mVibrator.getDefaultRingVibrationIntensity();
-        } else if (vib.isNotification()) {
-            defaultIntensity = mVibrator.getDefaultNotificationVibrationIntensity();
-        } else if (vib.isHapticFeedback()) {
-            defaultIntensity = mVibrator.getDefaultHapticFeedbackIntensity();
-        } else if (vib.isAlarm()) {
-            defaultIntensity = Vibrator.VIBRATION_INTENSITY_HIGH;
-        } else {
-            // If we don't know what kind of vibration we're playing then just skip scaling for
-            // now.
-            return;
-        }
-
+        final int defaultIntensity = getDefaultIntensity(vib.attrs.getUsage());
         final ScaleLevel scale = mScaleLevels.get(intensity - defaultIntensity);
         if (scale == null) {
             // We should have scaling levels for all cases, so not being able to scale because of a
@@ -1047,6 +1223,7 @@
 
         vib.originalEffect = vib.effect;
         vib.effect = vib.effect.resolve(mDefaultVibrationAmplitude).scale(scale.factor);
+        vib.scale = scale.factor;
     }
 
     private boolean shouldVibrateForRingtone() {
@@ -1070,7 +1247,7 @@
 
     private int getAppOpMode(int uid, String packageName, VibrationAttributes attrs) {
         int mode = mAppOps.checkAudioOpNoThrow(AppOpsManager.OP_VIBRATE,
-                attrs.getAudioAttributes().getUsage(), uid, packageName);
+                attrs.getAudioUsage(), uid, packageName);
         if (mode == AppOpsManager.MODE_ALLOWED) {
             mode = mAppOps.startOpNoThrow(AppOpsManager.OP_VIBRATE, uid, packageName);
         }
@@ -1086,11 +1263,13 @@
     }
 
     private boolean shouldVibrate(Vibration vib, int intensity) {
-        if (!isAllowedToVibrateLocked(vib)) {
+        if (!shouldVibrateForPowerModeLocked(vib)) {
+            endVibrationLocked(vib, VibrationInfo.Status.IGNORED_FOR_POWER);
             return false;
         }
 
         if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) {
+            endVibrationLocked(vib, VibrationInfo.Status.IGNORED_FOR_SETTINGS);
             return false;
         }
 
@@ -1098,6 +1277,7 @@
             if (DEBUG) {
                 Slog.e(TAG, "Vibrate ignored, not vibrating for ringtones");
             }
+            endVibrationLocked(vib, VibrationInfo.Status.IGNORED_RINGTONE);
             return false;
         }
 
@@ -1107,6 +1287,9 @@
                 // We might be getting calls from within system_server, so we don't actually
                 // want to throw a SecurityException here.
                 Slog.w(TAG, "Would be an error: vibrate from uid " + vib.uid);
+                endVibrationLocked(vib, VibrationInfo.Status.ERROR_APP_OPS);
+            } else {
+                endVibrationLocked(vib, VibrationInfo.Status.IGNORED_APP_OPS);
             }
             return false;
         }
@@ -1115,10 +1298,11 @@
     }
 
     @GuardedBy("mLock")
-    private void reportFinishVibrationLocked() {
+    private void reportFinishVibrationLocked(VibrationInfo.Status status) {
         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "reportFinishVibrationLocked");
         try {
             if (mCurrentVibration != null) {
+                endVibrationLocked(mCurrentVibration, status);
                 mAppOps.finishOp(AppOpsManager.OP_VIBRATE, mCurrentVibration.uid,
                         mCurrentVibration.opPkg);
                 unlinkVibration(mCurrentVibration);
@@ -1155,7 +1339,7 @@
 
             if (devicesUpdated || lowPowerModeUpdated) {
                 // If the state changes out from under us then just reset.
-                doCancelVibrateLocked();
+                doCancelVibrateLocked(VibrationInfo.Status.CANCELLED);
             }
 
             updateAlwaysOnLocked();
@@ -1226,7 +1410,7 @@
     }
 
     private void updateAlwaysOnLocked(int id, Vibration vib) {
-        final int intensity = getCurrentIntensityLocked(vib);
+        final int intensity = getCurrentIntensityLocked(vib.attrs.getUsage());
         if (!shouldVibrate(vib, intensity)) {
             mNativeWrapper.vibratorAlwaysOnDisable(id);
         } else {
@@ -1271,41 +1455,29 @@
         return mNativeWrapper.vibratorExists();
     }
 
-    /** Vibrates with native callback trigger for {@link #onVibrationComplete(long)}. */
-    private void doVibratorOn(long millis, int amplitude, Vibration vib) {
-        doVibratorOn(millis, amplitude, vib.uid, vib.attrs, vib.id);
-    }
-
-    /** Vibrates without native callback. */
-    private void doVibratorOn(long millis, int amplitude, int uid, VibrationAttributes attrs) {
-        doVibratorOn(millis, amplitude, uid, attrs, /* vibrationId= */ 0);
-    }
-
-    private void doVibratorOn(long millis, int amplitude, int uid, VibrationAttributes attrs,
-            long vibrationId) {
+    private void doVibratorOn(Vibration vib) {
         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOn");
         try {
             synchronized (mInputDeviceVibrators) {
-                if (amplitude == VibrationEffect.DEFAULT_AMPLITUDE) {
-                    amplitude = mDefaultVibrationAmplitude;
-                }
+                final VibrationEffect.OneShot oneShot =
+                        (VibrationEffect.OneShot) vib.effect.resolve(mDefaultVibrationAmplitude);
                 if (DEBUG) {
-                    Slog.d(TAG, "Turning vibrator on for " + millis + " ms" +
-                            " with amplitude " + amplitude + ".");
+                    Slog.d(TAG, "Turning vibrator on for " + oneShot.getDuration() + " ms"
+                            + " with amplitude " + oneShot.getAmplitude() + ".");
                 }
-                noteVibratorOnLocked(uid, millis);
+                noteVibratorOnLocked(vib.uid, oneShot.getDuration());
                 final int vibratorCount = mInputDeviceVibrators.size();
                 if (vibratorCount != 0) {
                     for (int i = 0; i < vibratorCount; i++) {
-                        Vibrator inputDeviceVibrator = mInputDeviceVibrators.get(i);
-                        inputDeviceVibrator.vibrate(millis, attrs.getAudioAttributes());
+                        mInputDeviceVibrators.get(i).vibrate(vib.uid, vib.opPkg, oneShot,
+                                vib.reason, vib.attrs);
                     }
                 } else {
                     // Note: ordering is important here! Many haptic drivers will reset their
                     // amplitude when enabled, so we always have to enable first, then set the
                     // amplitude.
-                    mNativeWrapper.vibratorOn(millis, vibrationId);
-                    doVibratorSetAmplitude(amplitude);
+                    mNativeWrapper.vibratorOn(oneShot.getDuration(), vib.id);
+                    doVibratorSetAmplitude(oneShot.getAmplitude());
                 }
             }
         } finally {
@@ -1359,6 +1531,10 @@
                     return;
                 }
             }
+            endVibrationLocked(vib, VibrationInfo.Status.IGNORED_UNSUPPORTED);
+            // The set current vibration is not actually playing, so drop it.
+            mCurrentVibration = null;
+
             if (!prebaked.shouldFallback()) {
                 return;
             }
@@ -1369,11 +1545,12 @@
             }
             Vibration fallbackVib = new Vibration(vib.token, effect, vib.attrs, vib.uid,
                     vib.opPkg, vib.reason + " (fallback)");
-            final int intensity = getCurrentIntensityLocked(fallbackVib);
+            // Set current vibration before starting it, so callback will work.
+            mCurrentVibration = fallbackVib;
+            final int intensity = getCurrentIntensityLocked(fallbackVib.attrs.getUsage());
             linkVibration(fallbackVib);
             applyVibrationIntensityScalingLocked(fallbackVib, intensity);
             startVibrationInnerLocked(fallbackVib);
-            return;
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
         }
@@ -1390,11 +1567,10 @@
                 usingInputDeviceVibrators = !mInputDeviceVibrators.isEmpty();
             }
             // Input devices don't support composed effect, so skip trying it with them.
-            if (usingInputDeviceVibrators) {
-                return;
-            }
-
-            if (!hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
+            if (usingInputDeviceVibrators || !hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
+                endVibrationLocked(vib, VibrationInfo.Status.IGNORED_UNSUPPORTED);
+                // The set current vibration is not actually playing, so drop it.
+                mCurrentVibration = null;
                 return;
             }
 
@@ -1503,15 +1679,25 @@
             } else {
                 pw.println("null");
             }
-            pw.print("  mCurrentExternalVibration=" + mCurrentExternalVibration);
+            pw.print("  mCurrentExternalVibration=");
+            if (mCurrentExternalVibration != null) {
+                pw.println(mCurrentExternalVibration.toInfo().toString());
+            } else {
+                pw.println("null");
+            }
             pw.println("  mVibratorUnderExternalControl=" + mVibratorUnderExternalControl);
             pw.println("  mIsVibrating=" + mIsVibrating);
-            pw.println("  mVibratorStateListeners Count=" +
-                            mVibratorStateListeners.getRegisteredCallbackCount());
+            pw.println("  mVibratorStateListeners Count="
+                    + mVibratorStateListeners.getRegisteredCallbackCount());
             pw.println("  mLowPowerMode=" + mLowPowerMode);
             pw.println("  mHapticFeedbackIntensity=" + mHapticFeedbackIntensity);
+            pw.println("  mHapticFeedbackDefaultIntensity="
+                    + mVibrator.getDefaultHapticFeedbackIntensity());
             pw.println("  mNotificationIntensity=" + mNotificationIntensity);
+            pw.println("  mNotificationDefaultIntensity="
+                    + mVibrator.getDefaultNotificationVibrationIntensity());
             pw.println("  mRingIntensity=" + mRingIntensity);
+            pw.println("  mRingDefaultIntensity=" + mVibrator.getDefaultRingVibrationIntensity());
             pw.println("  mSupportedEffects=" + mSupportedEffects);
             pw.println("  mSupportedPrimitives=" + mSupportedPrimitives);
             pw.println();
@@ -1537,8 +1723,8 @@
             }
 
             pw.println("  Previous external vibrations:");
-            for (ExternalVibration vib : mPreviousExternalVibrations) {
-                pw.println("    " + vib);
+            for (VibrationInfo info : mPreviousExternalVibrations) {
+                pw.println("    " + info);
             }
         }
     }
@@ -1549,54 +1735,62 @@
         synchronized (mLock) {
             if (mCurrentVibration != null) {
                 mCurrentVibration.toInfo().dumpProto(proto,
-                    VibratorServiceDumpProto.CURRENT_VIBRATION);
+                        VibratorServiceDumpProto.CURRENT_VIBRATION);
+            }
+            if (mCurrentExternalVibration != null) {
+                mCurrentExternalVibration.toInfo().dumpProto(proto,
+                        VibratorServiceDumpProto.CURRENT_EXTERNAL_VIBRATION);
             }
             proto.write(VibratorServiceDumpProto.IS_VIBRATING, mIsVibrating);
             proto.write(VibratorServiceDumpProto.VIBRATOR_UNDER_EXTERNAL_CONTROL,
-                mVibratorUnderExternalControl);
+                    mVibratorUnderExternalControl);
             proto.write(VibratorServiceDumpProto.LOW_POWER_MODE, mLowPowerMode);
             proto.write(VibratorServiceDumpProto.HAPTIC_FEEDBACK_INTENSITY,
-                mHapticFeedbackIntensity);
-            proto.write(VibratorServiceDumpProto.NOTIFICATION_INTENSITY,
-                mNotificationIntensity);
+                    mHapticFeedbackIntensity);
+            proto.write(VibratorServiceDumpProto.HAPTIC_FEEDBACK_DEFAULT_INTENSITY,
+                    mVibrator.getDefaultHapticFeedbackIntensity());
+            proto.write(VibratorServiceDumpProto.NOTIFICATION_INTENSITY, mNotificationIntensity);
+            proto.write(VibratorServiceDumpProto.NOTIFICATION_DEFAULT_INTENSITY,
+                    mVibrator.getDefaultNotificationVibrationIntensity());
             proto.write(VibratorServiceDumpProto.RING_INTENSITY, mRingIntensity);
+            proto.write(VibratorServiceDumpProto.RING_DEFAULT_INTENSITY,
+                    mVibrator.getDefaultRingVibrationIntensity());
 
             for (VibrationInfo info : mPreviousRingVibrations) {
-                info.dumpProto(proto,
-                    VibratorServiceDumpProto.PREVIOUS_RING_VIBRATIONS);
+                info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_RING_VIBRATIONS);
             }
 
             for (VibrationInfo info : mPreviousNotificationVibrations) {
-                info.dumpProto(proto,
-                    VibratorServiceDumpProto.PREVIOUS_NOTIFICATION_VIBRATIONS);
+                info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_NOTIFICATION_VIBRATIONS);
             }
 
             for (VibrationInfo info : mPreviousAlarmVibrations) {
-                info.dumpProto(proto,
-                    VibratorServiceDumpProto.PREVIOUS_ALARM_VIBRATIONS);
+                info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_ALARM_VIBRATIONS);
             }
 
             for (VibrationInfo info : mPreviousVibrations) {
-                info.dumpProto(proto,
-                    VibratorServiceDumpProto.PREVIOUS_VIBRATIONS);
+                info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_VIBRATIONS);
+            }
+
+            for (VibrationInfo info : mPreviousExternalVibrations) {
+                info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_EXTERNAL_VIBRATIONS);
             }
         }
         proto.flush();
     }
 
     /** Thread that plays a single {@link VibrationEffect.Waveform}. */
-    private class VibrateThread extends Thread {
+    private class VibrateWaveformThread extends Thread {
         private final VibrationEffect.Waveform mWaveform;
-        private final int mUid;
-        private final VibrationAttributes mAttrs;
+        private final Vibration mVibration;
 
         private boolean mForceStop;
 
-        VibrateThread(VibrationEffect.Waveform waveform, int uid, VibrationAttributes attrs) {
-            mWaveform = waveform;
-            mUid = uid;
-            mAttrs = attrs;
-            mTmpWorkSource.set(uid);
+        VibrateWaveformThread(Vibration vib) {
+            mWaveform = (VibrationEffect.Waveform) vib.effect;
+            mVibration = new Vibration(vib.token, /* effect= */ null, vib.attrs, vib.uid,
+                    vib.opPkg, vib.reason);
+            mTmpWorkSource.set(vib.uid);
             mWakeLock.setWorkSource(mTmpWorkSource);
         }
 
@@ -1670,7 +1864,9 @@
                                     // appropriate intervals.
                                     onDuration = getTotalOnDuration(timings, amplitudes, index - 1,
                                             repeat);
-                                    doVibratorOn(onDuration, amplitude, mUid, mAttrs);
+                                    mVibration.effect = VibrationEffect.createOneShot(
+                                            onDuration, amplitude);
+                                    doVibratorOn(mVibration);
                                 } else {
                                     doVibratorSetAmplitude(amplitude);
                                 }
@@ -1840,7 +2036,7 @@
                     if (mCurrentVibration != null
                             && !(mCurrentVibration.isHapticFeedback()
                                 && mCurrentVibration.isFromSystem())) {
-                        doCancelVibrateLocked();
+                        doCancelVibrateLocked(VibrationInfo.Status.CANCELLED);
                     }
                 }
             }
@@ -1895,63 +2091,54 @@
 
             int mode = getAppOpMode(vib.getUid(), vib.getPackage(), vib.getVibrationAttributes());
             if (mode != AppOpsManager.MODE_ALLOWED) {
+                ExternalVibrationHolder vibHolder = new ExternalVibrationHolder(vib);
+                vibHolder.scale = SCALE_MUTE;
                 if (mode == AppOpsManager.MODE_ERRORED) {
                     Slog.w(TAG, "Would be an error: external vibrate from uid " + vib.getUid());
+                    endVibrationLocked(vibHolder, VibrationInfo.Status.ERROR_APP_OPS);
+                } else {
+                    endVibrationLocked(vibHolder, VibrationInfo.Status.IGNORED_APP_OPS);
                 }
                 return SCALE_MUTE;
             }
 
             final int scaleLevel;
             synchronized (mLock) {
-                if (!vib.equals(mCurrentExternalVibration)) {
-                    if (mCurrentExternalVibration == null) {
-                        // If we're not under external control right now, then cancel any normal
-                        // vibration that may be playing and ready the vibrator for external
-                        // control.
-                        doCancelVibrateLocked();
-                        setVibratorUnderExternalControl(true);
-                    }
-                    // At this point we either have an externally controlled vibration playing, or
-                    // no vibration playing. Since the interface defines that only one externally
-                    // controlled vibration can play at a time, by returning something other than
-                    // SCALE_MUTE from this function we can be assured that if we are currently
-                    // playing vibration, it will be muted in favor of the new vibration.
-                    //
-                    // Note that this doesn't support multiple concurrent external controls, as we
-                    // would need to mute the old one still if it came from a different controller.
-                    mCurrentExternalVibration = vib;
-                    mCurrentExternalDeathRecipient = new ExternalVibrationDeathRecipient();
-                    mCurrentExternalVibration.linkToDeath(mCurrentExternalDeathRecipient);
-                    if (mPreviousExternalVibrations.size() > mPreviousVibrationsLimit) {
-                        mPreviousExternalVibrations.removeFirst();
-                    }
-                    mPreviousExternalVibrations.addLast(vib);
-                    if (DEBUG) {
-                        Slog.e(TAG, "Playing external vibration: " + vib);
-                    }
+                if (mCurrentExternalVibration != null
+                        && mCurrentExternalVibration.externalVibration.equals(vib)) {
+                    // We are already playing this external vibration, so we can return the same
+                    // scale calculated in the previous call to this method.
+                    return mCurrentExternalVibration.scale;
                 }
-                final int usage = vib.getVibrationAttributes().getUsage();
-                final int defaultIntensity;
-                final int currentIntensity;
-                if (isRingtone(usage)) {
-                    defaultIntensity = mVibrator.getDefaultRingVibrationIntensity();
-                    currentIntensity = mRingIntensity;
-                } else if (isNotification(usage)) {
-                    defaultIntensity = mVibrator.getDefaultNotificationVibrationIntensity();
-                    currentIntensity = mNotificationIntensity;
-                } else if (isHapticFeedback(usage)) {
-                    defaultIntensity = mVibrator.getDefaultHapticFeedbackIntensity();
-                    currentIntensity = mHapticFeedbackIntensity;
-                } else if (isAlarm(usage)) {
-                    defaultIntensity = Vibrator.VIBRATION_INTENSITY_HIGH;
-                    currentIntensity = Vibrator.VIBRATION_INTENSITY_HIGH;
+                if (mCurrentExternalVibration == null) {
+                    // If we're not under external control right now, then cancel any normal
+                    // vibration that may be playing and ready the vibrator for external control.
+                    doCancelVibrateLocked(VibrationInfo.Status.CANCELLED);
+                    setVibratorUnderExternalControl(true);
                 } else {
-                    defaultIntensity = 0;
-                    currentIntensity = 0;
+                    endVibrationLocked(mCurrentExternalVibration, VibrationInfo.Status.CANCELLED);
                 }
+                // At this point we either have an externally controlled vibration playing, or
+                // no vibration playing. Since the interface defines that only one externally
+                // controlled vibration can play at a time, by returning something other than
+                // SCALE_MUTE from this function we can be assured that if we are currently
+                // playing vibration, it will be muted in favor of the new vibration.
+                //
+                // Note that this doesn't support multiple concurrent external controls, as we
+                // would need to mute the old one still if it came from a different controller.
+                mCurrentExternalVibration = new ExternalVibrationHolder(vib);
+                mCurrentExternalDeathRecipient = new ExternalVibrationDeathRecipient();
+                vib.linkToDeath(mCurrentExternalDeathRecipient);
+                if (DEBUG) {
+                    Slog.e(TAG, "Playing external vibration: " + vib);
+                }
+                int usage = vib.getVibrationAttributes().getUsage();
+                int defaultIntensity = getDefaultIntensity(usage);
+                int currentIntensity = getCurrentIntensityLocked(usage);
                 scaleLevel = currentIntensity - defaultIntensity;
             }
             if (scaleLevel >= SCALE_VERY_LOW && scaleLevel <= SCALE_VERY_HIGH) {
+                mCurrentExternalVibration.scale = scaleLevel;
                 return scaleLevel;
             } else {
                 // Presumably we want to play this but something about our scaling has gone
@@ -1965,22 +2152,42 @@
         @Override
         public void onExternalVibrationStop(ExternalVibration vib) {
             synchronized (mLock) {
-                if (vib.equals(mCurrentExternalVibration)) {
-                    mCurrentExternalVibration.unlinkToDeath(mCurrentExternalDeathRecipient);
-                    mCurrentExternalDeathRecipient = null;
-                    mCurrentExternalVibration = null;
-                    setVibratorUnderExternalControl(false);
+                if (mCurrentExternalVibration != null
+                        && mCurrentExternalVibration.externalVibration.equals(vib)) {
                     if (DEBUG) {
                         Slog.e(TAG, "Stopping external vibration" + vib);
                     }
+                    doCancelExternalVibrateLocked(VibrationInfo.Status.FINISHED);
                 }
             }
         }
 
+        private void doCancelExternalVibrateLocked(VibrationInfo.Status status) {
+            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doCancelExternalVibrateLocked");
+            try {
+                if (mCurrentExternalVibration == null) {
+                    return;
+                }
+                endVibrationLocked(mCurrentExternalVibration, status);
+                mCurrentExternalVibration.externalVibration.unlinkToDeath(
+                        mCurrentExternalDeathRecipient);
+                mCurrentExternalDeathRecipient = null;
+                mCurrentExternalVibration = null;
+                setVibratorUnderExternalControl(false);
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+            }
+        }
+
         private class ExternalVibrationDeathRecipient implements IBinder.DeathRecipient {
             public void binderDied() {
                 synchronized (mLock) {
-                    onExternalVibrationStop(mCurrentExternalVibration);
+                    if (mCurrentExternalVibration != null) {
+                        if (DEBUG) {
+                            Slog.d(TAG, "External vibration finished because binder died");
+                        }
+                        doCancelExternalVibrateLocked(VibrationInfo.Status.CANCELLED);
+                    }
                 }
             }
         }
@@ -2242,5 +2449,4 @@
             }
         }
     }
-
 }
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 990a547..ba1eda9 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -23,7 +23,9 @@
 import android.content.IntentFilter;
 import android.hidl.manager.V1_0.IServiceManager;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Debug;
+import android.os.FileUtils;
 import android.os.Handler;
 import android.os.IPowerManager;
 import android.os.Looper;
@@ -31,10 +33,12 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
+import android.os.SystemProperties;
 import android.util.EventLog;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.sysprop.WatchdogProperties;
 
 import com.android.internal.os.ProcessCpuTracker;
 import com.android.internal.os.ZygoteConnectionConstants;
@@ -42,12 +46,16 @@
 import com.android.server.am.ActivityManagerService;
 import com.android.server.wm.SurfaceAnimationThread;
 
+import java.io.BufferedReader;
 import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
 import java.io.FileWriter;
 import java.io.IOException;
 import java.io.StringWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
 import java.util.HashSet;
 import java.util.List;
 
@@ -75,6 +83,12 @@
     private static final int WAITED_HALF = 2;
     private static final int OVERDUE = 3;
 
+    // Track watchdog timeout history and break the crash loop if there is.
+    private static final String TIMEOUT_HISTORY_FILE = "/data/system/watchdog-timeout-history.txt";
+    private static final String PROP_FATAL_LOOP_COUNT = "framework_watchdog.fatal_count";
+    private static final String PROP_FATAL_LOOP_WINDOWS_SECS =
+            "framework_watchdog.fatal_window.second";
+
     // Which native processes to dump into dropbox's stack traces
     public static final String[] NATIVE_STACKS_OF_INTEREST = new String[] {
         "/system/bin/audioserver",
@@ -90,6 +104,7 @@
         "media.metrics", // system/bin/mediametrics
         "media.codec", // vendor/bin/hw/android.hardware.media.omx@1.0-service
         "media.swcodec", // /apex/com.android.media.swcodec/bin/mediaswcodec
+        "media.transcoding", // Media transcoding service
         "com.android.bluetooth",  // Bluetooth service
         "/apex/com.android.os.statsd/bin/statsd",  // Stats daemon
     };
@@ -699,6 +714,10 @@
                 Slog.w(TAG, "*** WATCHDOG KILLING SYSTEM PROCESS: " + subject);
                 WatchdogDiagnostics.diagnoseCheckers(blockedCheckers);
                 Slog.w(TAG, "*** GOODBYE!");
+                if (!Build.IS_USER && isCrashLoopFound()
+                        && !WatchdogProperties.is_fatal_ignore().orElse(false)) {
+                    breakCrashLoop();
+                }
                 Process.killProcess(Process.myPid());
                 System.exit(10);
             }
@@ -716,4 +735,107 @@
             Slog.w(TAG, "Failed to write to /proc/sysrq-trigger", e);
         }
     }
+
+    private void resetTimeoutHistory() {
+        writeTimeoutHistory(new ArrayList<String>());
+    }
+
+    private void writeTimeoutHistory(Iterable<String> crashHistory) {
+        String data = String.join(",", crashHistory);
+
+        try (FileWriter writer = new FileWriter(TIMEOUT_HISTORY_FILE)) {
+            writer.write(SystemProperties.get("ro.boottime.zygote"));
+            writer.write(":");
+            writer.write(data);
+        } catch (IOException e) {
+            Slog.e(TAG, "Failed to write file " + TIMEOUT_HISTORY_FILE, e);
+        }
+    }
+
+    private String[] readTimeoutHistory() {
+        final String[] emptyStringArray = {};
+
+        try (BufferedReader reader = new BufferedReader(new FileReader(TIMEOUT_HISTORY_FILE))) {
+            String line = reader.readLine();
+            if (line == null) {
+                return emptyStringArray;
+            }
+
+            String[] data = line.trim().split(":");
+            String boottime = data.length >= 1 ? data[0] : "";
+            String history = data.length >= 2 ? data[1] : "";
+            if (SystemProperties.get("ro.boottime.zygote").equals(boottime) && !history.isEmpty()) {
+                return history.split(",");
+            } else {
+                return emptyStringArray;
+            }
+        } catch (FileNotFoundException e) {
+            return emptyStringArray;
+        } catch (IOException e) {
+            Slog.e(TAG, "Failed to read file " + TIMEOUT_HISTORY_FILE, e);
+            return emptyStringArray;
+        }
+    }
+
+    private boolean hasActiveUsbConnection() {
+        try {
+            final String state = FileUtils.readTextFile(
+                    new File("/sys/class/android_usb/android0/state"),
+                    128 /*max*/, null /*ellipsis*/).trim();
+            if ("CONFIGURED".equals(state)) {
+                return true;
+            }
+        } catch (IOException e) {
+            Slog.w(TAG, "Failed to determine if device was on USB", e);
+        }
+        return false;
+    }
+
+    private boolean isCrashLoopFound() {
+        int fatalCount = WatchdogProperties.fatal_count().orElse(0);
+        long fatalWindowMs = TimeUnit.SECONDS.toMillis(
+                WatchdogProperties.fatal_window_second().orElse(0));
+        if (fatalCount == 0 || fatalWindowMs == 0) {
+            if (fatalCount != fatalWindowMs) {
+                Slog.w(TAG, String.format("sysprops '%s' and '%s' should be set or unset together",
+                            PROP_FATAL_LOOP_COUNT, PROP_FATAL_LOOP_WINDOWS_SECS));
+            }
+            return false;
+        }
+
+        // new-history = [last (fatalCount - 1) items in old-history] + [nowMs].
+        long nowMs = SystemClock.elapsedRealtime(); // Time since boot including deep sleep.
+        String[] rawCrashHistory = readTimeoutHistory();
+        ArrayList<String> crashHistory = new ArrayList<String>(Arrays.asList(Arrays.copyOfRange(
+                        rawCrashHistory,
+                        Math.max(0, rawCrashHistory.length - fatalCount - 1),
+                        rawCrashHistory.length)));
+        // Something wrong here.
+        crashHistory.add(String.valueOf(nowMs));
+        writeTimeoutHistory(crashHistory);
+
+        // Returns false if the device has an active USB connection.
+        if (hasActiveUsbConnection()) {
+            return false;
+        }
+
+        long firstCrashMs;
+        try {
+            firstCrashMs = Long.parseLong(crashHistory.get(0));
+        } catch (NumberFormatException t) {
+            Slog.w(TAG, "Failed to parseLong " + crashHistory.get(0), t);
+            resetTimeoutHistory();
+            return false;
+        }
+        return crashHistory.size() >= fatalCount && nowMs - firstCrashMs < fatalWindowMs;
+    }
+
+    private void breakCrashLoop() {
+        try (FileWriter kmsg = new FileWriter("/dev/kmsg_debug", /* append= */ true)) {
+            kmsg.append("Fatal reset to escape the system_server crashing loop\n");
+        } catch (IOException e) {
+            Slog.w(TAG, "Failed to append to kmsg", e);
+        }
+        doSysRq('c');
+    }
 }
diff --git a/services/core/java/com/android/server/WiredAccessoryManager.java b/services/core/java/com/android/server/WiredAccessoryManager.java
index 8e5c73bf..7fa93c0 100644
--- a/services/core/java/com/android/server/WiredAccessoryManager.java
+++ b/services/core/java/com/android/server/WiredAccessoryManager.java
@@ -319,7 +319,7 @@
     }
 
     private String switchCodeToString(int switchValues, int switchMask) {
-        StringBuffer sb = new StringBuffer();
+        StringBuilder sb = new StringBuilder();
         if ((switchMask & SW_HEADPHONE_INSERT_BIT) != 0 &&
                 (switchValues & SW_HEADPHONE_INSERT_BIT) != 0) {
             sb.append("SW_HEADPHONE_INSERT ");
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 5a34283..9a94e4e 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -42,7 +42,6 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
-import android.app.admin.DeviceAdminInfo;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.content.BroadcastReceiver;
@@ -478,7 +477,7 @@
          * TODO: Only allow accounts that were shared to be added by a limited user.
          */
         // fails if the account already exists
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
             return addAccountInternal(accounts, account, password, extras, callingUid,
@@ -507,7 +506,7 @@
             managedTypes.add(accountType);
         }
 
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
             return getAccountsAndVisibilityForPackage(packageName, managedTypes, callingUid,
@@ -556,7 +555,7 @@
             throw new SecurityException(msg);
         }
 
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
             synchronized (accounts.dbLock) {
@@ -605,7 +604,7 @@
                     account.type);
             throw new SecurityException(msg);
         }
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
             if (AccountManager.PACKAGE_NAME_KEY_LEGACY_VISIBLE.equals(packageName)) {
@@ -665,7 +664,7 @@
         Objects.requireNonNull(packageName, "packageName cannot be null");
         int uid = -1;
         try {
-            long identityToken = clearCallingIdentity();
+            final long identityToken = clearCallingIdentity();
             try {
                 uid = mPackageManager.getPackageUidAsUser(packageName, accounts.userId);
             } finally {
@@ -737,7 +736,7 @@
      */
     private boolean isPreOApplication(String packageName) {
         try {
-            long identityToken = clearCallingIdentity();
+            final long identityToken = clearCallingIdentity();
             ApplicationInfo applicationInfo;
             try {
                 applicationInfo = mPackageManager.getApplicationInfo(packageName, 0);
@@ -770,7 +769,7 @@
                     account.type);
             throw new SecurityException(msg);
         }
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
             return setAccountVisibility(account, packageName, newVisibility, true /* notify */,
@@ -884,7 +883,7 @@
         mAppOpsManager.checkPackage(callingUid, opPackageName);
 
         int userId = UserHandle.getCallingUserId();
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
             registerAccountListener(accountTypes, opPackageName, accounts);
@@ -917,7 +916,7 @@
         int callingUid = Binder.getCallingUid();
         mAppOpsManager.checkPackage(callingUid, opPackageName);
         int userId = UserHandle.getCallingUserId();
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
             unregisterAccountListener(accountTypes, opPackageName, accounts);
@@ -1034,7 +1033,7 @@
 
     private boolean packageExistsForUser(String packageName, int userId) {
         try {
-            long identityToken = clearCallingIdentity();
+            final long identityToken = clearCallingIdentity();
             try {
                 mPackageManager.getPackageUidAsUser(packageName, userId);
                 return true;
@@ -1500,7 +1499,7 @@
                     account.type);
             throw new SecurityException(msg);
         }
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
             return readPasswordInternal(accounts, account);
@@ -1535,7 +1534,7 @@
         }
         Objects.requireNonNull(account, "account cannot be null");
         int userId = UserHandle.getCallingUserId();
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
             return readPreviousNameInternal(accounts, account);
@@ -1585,7 +1584,7 @@
             Log.w(TAG, "User " + userId + " data is locked. callingUid " + callingUid);
             return null;
         }
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
             if (!accountExistsCache(accounts, account)) {
@@ -1680,7 +1679,7 @@
 
         Slog.d(TAG, "Copying account " + account.toSafeString()
                 + " from user " + userFrom + " to user " + userTo);
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             new Session(fromAccounts, response, account.type, false,
                     false /* stripAuthTokenFromResult */, account.name,
@@ -1738,7 +1737,7 @@
             return false;
         }
 
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
             return updateLastAuthenticatedTime(account);
@@ -1760,7 +1759,7 @@
             final Bundle accountCredentials, final Account account, final UserAccounts targetUser,
             final int parentUserId){
         Bundle.setDefusable(accountCredentials, true);
-        long id = clearCallingIdentity();
+        final long id = clearCallingIdentity();
         try {
             new Session(targetUser, response, account.type, false,
                     false /* stripAuthTokenFromResult */, account.name,
@@ -1927,7 +1926,7 @@
         checkReadAccountsPermitted(callingUid, account.type, userId,
                 opPackageName);
 
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
             new TestFeaturesSession(accounts, response, account, features).bind();
@@ -2011,7 +2010,7 @@
                     accountToRename.type);
             throw new SecurityException(msg);
         }
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
             Account resultingAccount = renameAccountInternal(accounts, accountToRename, newName);
@@ -2194,7 +2193,7 @@
             }
             return;
         }
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         UserAccounts accounts = getUserAccounts(userId);
         cancelNotification(getSigninRequiredNotificationId(accounts, account), user);
         synchronized(accounts.credentialsPermissionNotificationIds) {
@@ -2251,7 +2250,7 @@
                 accountId,
                 accounts,
                 callingUid);
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             return removeAccountInternal(accounts, account, callingUid);
         } finally {
@@ -2368,7 +2367,7 @@
                 }
             }
         }
-        long id = Binder.clearCallingIdentity();
+        final long id = Binder.clearCallingIdentity();
         try {
             int parentUserId = accounts.userId;
             if (canHaveProfile(parentUserId)) {
@@ -2414,7 +2413,7 @@
                     + ", pid " + Binder.getCallingPid());
         }
         int userId = UserHandle.getCallingUserId();
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
             List<Pair<Account, String>> deletedTokens;
@@ -2538,7 +2537,7 @@
                     + callingUid);
             return null;
         }
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
             return readAuthTokenInternal(accounts, account, authTokenType);
@@ -2566,7 +2565,7 @@
                     account.type);
             throw new SecurityException(msg);
         }
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
             saveAuthTokenToDatabase(accounts, account, authTokenType, authToken);
@@ -2592,7 +2591,7 @@
                     account.type);
             throw new SecurityException(msg);
         }
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
             setPasswordInternal(accounts, account, password, callingUid);
@@ -2658,7 +2657,7 @@
                     account.type);
             throw new SecurityException(msg);
         }
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
             setPasswordInternal(accounts, account, null, callingUid);
@@ -2686,7 +2685,7 @@
                     account.type);
             throw new SecurityException(msg);
         }
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
             if (!accountExistsCache(accounts, account)) {
@@ -2772,7 +2771,7 @@
             throw new SecurityException("can only call from system");
         }
         int userId = UserHandle.getUserId(callingUid);
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
             new Session(accounts, response, accountType, false /* expectActivityLaunch */,
@@ -2844,7 +2843,7 @@
             return;
         }
         int userId = UserHandle.getCallingUserId();
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         final UserAccounts accounts;
         final RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
         try {
@@ -2866,11 +2865,11 @@
         // Get the calling package. We will use it for the purpose of caching.
         final String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
         List<String> callerOwnedPackageNames;
-        ident = Binder.clearCallingIdentity();
+        final long ident2 = Binder.clearCallingIdentity();
         try {
             callerOwnedPackageNames = Arrays.asList(mPackageManager.getPackagesForUid(callerUid));
         } finally {
-            Binder.restoreCallingIdentity(ident);
+            Binder.restoreCallingIdentity(ident2);
         }
         if (callerPkg == null || !callerOwnedPackageNames.contains(callerPkg)) {
             String msg = String.format(
@@ -2888,7 +2887,7 @@
             loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true);
         }
 
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             // Distill the caller's package signatures into a single digest.
             final byte[] callerPkgSigDigest = calculatePackageSignatureDigest(callerPkg);
@@ -3195,7 +3194,7 @@
         options.putInt(AccountManager.KEY_CALLER_PID, pid);
 
         int usrId = UserHandle.getCallingUserId();
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(usrId);
             logRecordWithUid(
@@ -3276,7 +3275,7 @@
         options.putInt(AccountManager.KEY_CALLER_UID, uid);
         options.putInt(AccountManager.KEY_CALLER_PID, pid);
 
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
             logRecordWithUid(
@@ -3359,7 +3358,7 @@
         boolean isPasswordForwardingAllowed = checkPermissionAndNote(
                 callerPkg, uid, Manifest.permission.GET_PASSWORD);
 
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
             logRecordWithUid(accounts, AccountsDb.DEBUG_ACTION_CALLED_START_ACCOUNT_ADD,
@@ -3600,7 +3599,7 @@
             return;
         }
 
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
             logRecordWithUid(
@@ -3649,7 +3648,7 @@
         if (intent == null) {
             intent = getDefaultCantAddAccountIntent(errorCode);
         }
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             mContext.startActivityAsUser(intent, new UserHandle(userId));
         } finally {
@@ -3693,7 +3692,7 @@
         }
         if (response == null) throw new IllegalArgumentException("response is null");
         if (account == null) throw new IllegalArgumentException("account is null");
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
             new Session(accounts, response, account.type, expectActivityLaunch,
@@ -3730,7 +3729,7 @@
         if (response == null) throw new IllegalArgumentException("response is null");
         if (account == null) throw new IllegalArgumentException("account is null");
         int userId = UserHandle.getCallingUserId();
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
             new Session(accounts, response, account.type, expectActivityLaunch,
@@ -3784,7 +3783,7 @@
         boolean isPasswordForwardingAllowed = checkPermissionAndNote(
                 callerPkg, uid, Manifest.permission.GET_PASSWORD);
 
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
             new StartAccountSession(
@@ -3840,7 +3839,7 @@
         }
 
         int usrId = UserHandle.getCallingUserId();
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(usrId);
             new Session(accounts, response, account.type, false /* expectActivityLaunch */,
@@ -3925,7 +3924,7 @@
                     accountType);
             throw new SecurityException(msg);
         }
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
             new Session(accounts, response, accountType, expectActivityLaunch,
@@ -4229,7 +4228,7 @@
         if (visibleAccountTypes.isEmpty()) {
             return EMPTY_ACCOUNT_ARRAY;
         }
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
             return getAccountsInternal(
@@ -4353,7 +4352,7 @@
         } // else aggregate all the visible accounts (it won't matter if the
           // list is empty).
 
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
             return getAccountsInternal(
@@ -4559,7 +4558,7 @@
 
         int userId = UserHandle.getCallingUserId();
 
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             UserAccounts userAccounts = getUserAccounts(userId);
             if (ArrayUtils.isEmpty(features)) {
@@ -4637,7 +4636,7 @@
             return;
         }
 
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             UserAccounts userAccounts = getUserAccounts(userId);
             if (features == null || features.length == 0) {
@@ -4782,7 +4781,7 @@
                     | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
                     | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
                     | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION));
-            long bid = Binder.clearCallingIdentity();
+            final long bid = Binder.clearCallingIdentity();
             try {
                 PackageManager pm = mContext.getPackageManager();
                 ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId);
@@ -5282,7 +5281,7 @@
 
     private void doNotification(UserAccounts accounts, Account account, CharSequence message,
             Intent intent, String packageName, final int userId) {
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             if (Log.isLoggable(TAG, Log.VERBOSE)) {
                 Log.v(TAG, "doNotification: " + message + " intent:" + intent);
@@ -5341,7 +5340,7 @@
     }
 
     private void cancelNotification(NotificationId id, String packageName, UserHandle user) {
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             INotificationManager service = mInjector.getNotificationManager();
             service.cancelNotificationWithTag(
@@ -5410,7 +5409,7 @@
 
     private boolean isPrivileged(int callingUid) {
         String[] packages;
-        long identityToken = Binder.clearCallingIdentity();
+        final long identityToken = Binder.clearCallingIdentity();
         try {
             packages = mPackageManager.getPackagesForUid(callingUid);
             if (packages == null) {
@@ -5502,7 +5501,7 @@
         if (accountType == null) {
             return false;
         }
-        long identityToken = Binder.clearCallingIdentity();
+        final long identityToken = Binder.clearCallingIdentity();
         Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> serviceInfos;
         try {
             serviceInfos = mAuthenticatorCache.getAllServices(userId);
@@ -5531,7 +5530,7 @@
             return SIGNATURE_CHECK_MISMATCH;
         }
 
-        long identityToken = Binder.clearCallingIdentity();
+        final long identityToken = Binder.clearCallingIdentity();
         Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> serviceInfos;
         try {
             serviceInfos = mAuthenticatorCache.getAllServices(userId);
@@ -5577,7 +5576,7 @@
     private List<String> getTypesForCaller(
             int callingUid, int userId, boolean isOtherwisePermitted) {
         List<String> managedAccountTypes = new ArrayList<>();
-        long identityToken = Binder.clearCallingIdentity();
+        final long identityToken = Binder.clearCallingIdentity();
         Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> serviceInfos;
         try {
             serviceInfos = mAuthenticatorCache.getAllServices(userId);
@@ -5660,7 +5659,7 @@
 
     private boolean isSystemUid(int callingUid) {
         String[] packages = null;
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             packages = mPackageManager.getPackagesForUid(callingUid);
             if (packages != null) {
@@ -5736,8 +5735,8 @@
     private boolean isProfileOwner(int uid) {
         final DevicePolicyManagerInternal dpmi =
                 LocalServices.getService(DevicePolicyManagerInternal.class);
-        return (dpmi != null)
-                && dpmi.isActiveAdminWithPolicy(uid, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+        //TODO(b/169395065) Figure out if this flow makes sense in Device Owner mode.
+        return (dpmi != null) && (dpmi.isActiveProfileOwner(uid) || dpmi.isActiveDeviceOwner(uid));
     }
 
     @Override
@@ -6189,7 +6188,7 @@
 
             final int uid;
             try {
-                long identityToken = clearCallingIdentity();
+                final long identityToken = clearCallingIdentity();
                 try {
                     uid = mPackageManager.getPackageUidAsUser(packageName, userId);
                 } finally {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index b2e021f..6e5c041 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -56,6 +56,8 @@
 import android.app.ServiceStartArgs;
 import android.app.admin.DevicePolicyEventLogger;
 import android.appwidget.AppWidgetManagerInternal;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
 import android.content.ComponentName;
 import android.content.ComponentName.WithComponentName;
 import android.content.Context;
@@ -79,6 +81,7 @@
 import android.os.Process;
 import android.os.RemoteCallback;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.TransactionTooLargeException;
@@ -100,6 +103,7 @@
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.procstats.ServiceState;
+import com.android.internal.compat.IPlatformCompat;
 import com.android.internal.messages.nano.SystemMessageProto;
 import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.os.BatteryStatsImpl;
@@ -234,6 +238,16 @@
     private static final SimpleDateFormat DATE_FORMATTER =
             new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
 
+    private final IPlatformCompat mPlatformCompat;
+
+    /**
+     * The BG-launch FGS restriction feature is going to be allowed only for apps targetSdkVersion
+     * is higher than R.
+     */
+    @ChangeId
+    @Disabled
+    static final long FGS_BG_START_RESTRICTION_CHANGE_ID = 170668199L;
+
     final Runnable mLastAnrDumpClearer = new Runnable() {
         @Override public void run() {
             synchronized (mAm) {
@@ -430,6 +444,9 @@
         }
         mMaxStartingBackground = maxBg > 0
                 ? maxBg : ActivityManager.isLowRamDeviceStatic() ? 1 : 8;
+
+        final IBinder b = ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE);
+        mPlatformCompat = IPlatformCompat.Stub.asInterface(b);
     }
 
     void systemServicesReady() {
@@ -557,12 +574,20 @@
                     r.mLoggedInfoAllowStartForeground = true;
                 }
                 if (r.mAllowStartForeground == FGS_FEATURE_DENIED
-                        && mAm.mConstants.mFlagFgsStartRestrictionEnabled) {
-                    Slog.w(TAG, "startForegroundService() not allowed due to "
-                                    + "mAllowStartForeground false: service "
-                                    + r.shortInstanceName);
-                    showFgsBgRestrictedNotificationLocked(r);
-                    forcedStandby = true;
+                        && (mAm.mConstants.mFlagFgsStartRestrictionEnabled
+                        || isChangeEnabled(FGS_BG_START_RESTRICTION_CHANGE_ID, r))) {
+                    if (mAm.mConstants.mFlagFgsStartTempAllowListEnabled
+                            && mAm.isOnDeviceIdleWhitelistLocked(r.appInfo.uid, false)) {
+                        // uid is on DeviceIdleController's allowlist.
+                        Slog.d(TAG, "startForegroundService() mAllowStartForeground false "
+                                + "but allowlist true: service " + r.shortInstanceName);
+                    } else {
+                        Slog.w(TAG, "startForegroundService() not allowed due to "
+                                + "mAllowStartForeground false: service "
+                                + r.shortInstanceName);
+                        showFgsBgRestrictedNotificationLocked(r);
+                        return null;
+                    }
                 }
             }
         }
@@ -1461,14 +1486,22 @@
                             r.mLoggedInfoAllowStartForeground = true;
                         }
                         if (r.mAllowStartForeground == FGS_FEATURE_DENIED
-                                && mAm.mConstants.mFlagFgsStartRestrictionEnabled) {
-                            Slog.w(TAG,
-                                    "Service.startForeground() not allowed due to "
-                                            + "mAllowStartForeground false: service "
-                                            + r.shortInstanceName);
-                            showFgsBgRestrictedNotificationLocked(r);
-                            updateServiceForegroundLocked(r.app, true);
-                            ignoreForeground = true;
+                                && (mAm.mConstants.mFlagFgsStartRestrictionEnabled
+                                || isChangeEnabled(FGS_BG_START_RESTRICTION_CHANGE_ID, r))) {
+                            if (mAm.mConstants.mFlagFgsStartTempAllowListEnabled
+                                    && mAm.isOnDeviceIdleWhitelistLocked(r.appInfo.uid, false)) {
+                                // uid is on DeviceIdleController's allowlist.
+                                Slog.d(TAG, "Service.startForeground() "
+                                        + "mAllowStartForeground false but allowlist true: service "
+                                        + r.shortInstanceName);
+                            } else {
+                                Slog.w(TAG, "Service.startForeground() not allowed due to "
+                                                + "mAllowStartForeground false: service "
+                                                + r.shortInstanceName);
+                                showFgsBgRestrictedNotificationLocked(r);
+                                updateServiceForegroundLocked(r.app, true);
+                                ignoreForeground = true;
+                            }
                         }
                     }
                 }
@@ -1907,7 +1940,9 @@
         ActivityServiceConnectionsHolder<ConnectionRecord> activity = null;
         if (token != null) {
             activity = mAm.mAtmInternal.getServiceConnectionsHolder(token);
-            if (activity == null) {
+            // TODO(b/171280916): Remove the check after we have another API get window context
+            //  token than getActivityToken.
+            if (activity == null && !mAm.mWindowManager.isWindowToken(token)) {
                 Slog.w(TAG, "Binding with unknown activity: " + token);
                 return 0;
             }
@@ -5087,4 +5122,12 @@
         context.getSystemService(NotificationManager.class).notifyAsUser(Long.toString(now),
                 NOTE_FOREGROUND_SERVICE_BG_LAUNCH, n.build(), UserHandle.ALL);
     }
+
+    private boolean isChangeEnabled(long changeId, ServiceRecord r) {
+        boolean enabled = false;
+        try {
+            enabled = mPlatformCompat.isChangeEnabled(changeId, r.appInfo);
+        } catch (RemoteException e) { }
+        return enabled;
+    }
 }
diff --git a/services/core/java/com/android/server/am/ActiveUids.java b/services/core/java/com/android/server/am/ActiveUids.java
index 4e1435e..c86c29f 100644
--- a/services/core/java/com/android/server/am/ActiveUids.java
+++ b/services/core/java/com/android/server/am/ActiveUids.java
@@ -16,7 +16,12 @@
 
 package com.android.server.am;
 
+import android.app.ActivityManager;
+import android.os.UserHandle;
 import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.PrintWriter;
 
 /** Class for tracking active uids for running processes. */
 final class ActiveUids {
@@ -71,4 +76,43 @@
     int indexOfKey(int uid) {
         return mActiveUids.indexOfKey(uid);
     }
+
+    boolean dump(PrintWriter pw, String dumpPackage, int dumpAppId,
+            String header, boolean needSep) {
+        boolean printed = false;
+        for (int i = 0; i < mActiveUids.size(); i++) {
+            final UidRecord uidRec = mActiveUids.valueAt(i);
+            if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != dumpAppId) {
+                continue;
+            }
+            if (!printed) {
+                printed = true;
+                if (needSep) {
+                    pw.println();
+                }
+                pw.print("  "); pw.println(header);
+            }
+            pw.print("    UID "); UserHandle.formatUid(pw, uidRec.uid);
+            pw.print(": "); pw.println(uidRec);
+            pw.print("      curProcState="); pw.print(uidRec.mCurProcState);
+            pw.print(" curCapability=");
+            ActivityManager.printCapabilitiesFull(pw, uidRec.curCapability);
+            pw.println();
+            for (int j = uidRec.procRecords.size() - 1; j >= 0; j--) {
+                pw.print("      proc=");
+                pw.println(uidRec.procRecords.valueAt(j));
+            }
+        }
+        return printed;
+    }
+
+    void dumpProto(ProtoOutputStream proto, String dumpPackage, int dumpAppId, long fieldId) {
+        for (int i = 0; i < mActiveUids.size(); i++) {
+            UidRecord uidRec = mActiveUids.valueAt(i);
+            if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != dumpAppId) {
+                continue;
+            }
+            uidRec.dumpDebug(proto, fieldId);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 48055b5..b54a917e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -161,6 +161,13 @@
     private static final String KEY_DEFAULT_FGS_STARTS_RESTRICTION_ENABLED =
             "default_fgs_starts_restriction_enabled";
 
+    /**
+     * Default value for mFlagFgsStartTempAllowListEnabled if not explicitly set in
+     * Settings.Global.
+     */
+    private static final String KEY_DEFAULT_FGS_STARTS_TEMP_ALLOWLIST_ENABLED =
+            "default_fgs_starts_temp_allowlist_enabled";
+
     // Maximum number of cached processes we will allow.
     public int MAX_CACHED_PROCESSES = DEFAULT_MAX_CACHED_PROCESSES;
 
@@ -321,6 +328,10 @@
     // at all.
     volatile boolean mFlagFgsStartRestrictionEnabled = false;
 
+    // When the foreground service background start restriction is enabled, if the app in
+    // DeviceIdleController's Temp AllowList is allowed to bypass the restriction.
+    volatile boolean mFlagFgsStartTempAllowListEnabled = false;
+
     private final ActivityManagerService mService;
     private ContentResolver mResolver;
     private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -472,6 +483,9 @@
                             case KEY_DEFAULT_FGS_STARTS_RESTRICTION_ENABLED:
                                 updateFgsStartsRestriction();
                                 break;
+                            case KEY_DEFAULT_FGS_STARTS_TEMP_ALLOWLIST_ENABLED:
+                                updateFgsStartsTempAllowList();
+                                break;
                             case KEY_OOMADJ_UPDATE_POLICY:
                                 updateOomAdjUpdatePolicy();
                                 break;
@@ -724,6 +738,13 @@
                 /*defaultValue*/ false);
     }
 
+    private void updateFgsStartsTempAllowList() {
+        mFlagFgsStartTempAllowListEnabled = DeviceConfig.getBoolean(
+                DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                KEY_DEFAULT_FGS_STARTS_TEMP_ALLOWLIST_ENABLED,
+                /*defaultValue*/ false);
+    }
+
     private void updateOomAdjUpdatePolicy() {
         OOMADJ_UPDATE_QUICK = DeviceConfig.getInt(
                 DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d3f4667..63f7d44 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1979,7 +1979,6 @@
         mConstants = hasHandlerThread
                 ? new ActivityManagerConstants(mContext, this, mHandler) : null;
         final ActiveUids activeUids = new ActiveUids(this, false /* postChangesToAtm */);
-        mUidObserverController = new UidObserverController(this);
         mPlatformCompat = null;
         mProcessList = injector.getProcessList(this);
         mProcessList.init(this, activeUids, mPlatformCompat);
@@ -1997,6 +1996,7 @@
         mServices = hasHandlerThread ? new ActiveServices(this) : null;
         mSystemThread = null;
         mUiHandler = injector.getUiHandler(null /* service */);
+        mUidObserverController = new UidObserverController(mUiHandler);
         mUserController = hasHandlerThread ? new UserController(this) : null;
         mPendingIntentController = hasHandlerThread
                 ? new PendingIntentController(handlerThread.getLooper(), mUserController,
@@ -2077,7 +2077,7 @@
         mCpHelper = new ContentProviderHelper(this, true);
         mPackageWatchdog = PackageWatchdog.getInstance(mUiContext);
         mAppErrors = new AppErrors(mUiContext, this, mPackageWatchdog);
-        mUidObserverController = new UidObserverController(this);
+        mUidObserverController = new UidObserverController(mUiHandler);
 
         final File systemDir = SystemServiceManager.ensureSystemDir();
 
@@ -2395,8 +2395,8 @@
     }
 
     @Override
-    public void setFocusedStack(int stackId) {
-        mActivityTaskManager.setFocusedStack(stackId);
+    public void setFocusedRootTask(int taskId) {
+        mActivityTaskManager.setFocusedRootTask(taskId);
     }
 
     /** Sets the task stack listener that gets callbacks when a task stack changes. */
@@ -3356,7 +3356,7 @@
         final ApplicationInfo appInfo;
         final boolean isInstantApp;
 
-        long callingId = Binder.clearCallingIdentity();
+        final long callingId = Binder.clearCallingIdentity();
         try {
             IPackageManager pm = AppGlobals.getPackageManager();
             synchronized(this) {
@@ -3497,7 +3497,7 @@
                 userId, true, ALLOW_FULL_ONLY, "killBackgroundProcesses", null);
         final int[] userIds = mUserController.expandUserId(userId);
 
-        long callingId = Binder.clearCallingIdentity();
+        final long callingId = Binder.clearCallingIdentity();
         try {
             IPackageManager pm = AppGlobals.getPackageManager();
             for (int targetUserId : userIds) {
@@ -3596,7 +3596,7 @@
         final int callingPid = Binder.getCallingPid();
         userId = mUserController.handleIncomingUser(callingPid, Binder.getCallingUid(),
                 userId, true, ALLOW_FULL_ONLY, "forceStopPackage", null);
-        long callingId = Binder.clearCallingIdentity();
+        final long callingId = Binder.clearCallingIdentity();
         try {
             IPackageManager pm = AppGlobals.getPackageManager();
             synchronized(this) {
@@ -5658,11 +5658,6 @@
     }
 
     @Override
-    public void removeStack(int stackId) {
-        mActivityTaskManager.removeStack(stackId);
-    }
-
-    @Override
     public boolean removeTask(int taskId) {
         return mActivityTaskManager.removeTask(taskId);
     }
@@ -5698,8 +5693,8 @@
     }
 
     @Override
-    public void moveTaskToStack(int taskId, int stackId, boolean toTop) {
-        mActivityTaskManager.moveTaskToStack(taskId, stackId, toTop);
+    public void moveTaskToRootTask(int taskId, int rootTaskId, boolean toTop) {
+        mActivityTaskManager.moveTaskToRootTask(taskId, rootTaskId, toTop);
     }
 
     @Override
@@ -5709,17 +5704,17 @@
     }
 
     /**
-     * Moves the top activity in the input stackId to the pinned stack.
+     * Moves the top activity in the input rootTaskId to the pinned root task.
      *
-     * @param stackId Id of stack to move the top activity to pinned stack.
-     * @param bounds Bounds to use for pinned stack.
+     * @param rootTaskId Id of root task to move the top activity to pinned root task.
+     * @param bounds Bounds to use for pinned root task.
      *
-     * @return True if the top activity of the input stack was successfully moved to the pinned
-     *          stack.
+     * @return True if the top activity of the input root task was successfully moved to the pinned
+     *          root task.
      */
     @Override
-    public boolean moveTopActivityToPinnedStack(int stackId, Rect bounds) {
-        return mActivityTaskManager.moveTopActivityToPinnedStack(stackId, bounds);
+    public boolean moveTopActivityToPinnedRootTask(int rootTaskId, Rect bounds) {
+        return mActivityTaskManager.moveTopActivityToPinnedRootTask(rootTaskId, bounds);
     }
 
     @Override
@@ -6160,7 +6155,7 @@
         enforceCallingPermission(android.Manifest.permission.SET_DEBUG_APP,
                 "setDebugApp()");
 
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             // Note that this is not really thread safe if there are multiple
             // callers into it at the same time, but that's not a situation we
@@ -6261,7 +6256,7 @@
         enforceCallingPermission(android.Manifest.permission.SET_ALWAYS_FINISH,
                 "setAlwaysFinish()");
 
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             Settings.Global.putInt(
                     mContext.getContentResolver(),
@@ -7027,7 +7022,7 @@
                     + android.Manifest.permission.FORCE_STOP_PACKAGES);
         }
         int callerUid = Binder.getCallingUid();
-        long iden = Binder.clearCallingIdentity();
+        final long iden = Binder.clearCallingIdentity();
         try {
             mProcessList.killProcessesWhenImperceptible(pids, reason, callerUid);
         } finally {
@@ -7366,7 +7361,7 @@
         final boolean bootingSystemUser = currentUserId == UserHandle.USER_SYSTEM;
 
         if (bootingSystemUser) {
-            mSystemServiceManager.startUser(t, currentUserId);
+            mSystemServiceManager.onUserStarting(t, currentUserId);
         }
 
         synchronized (this) {
@@ -7412,7 +7407,7 @@
                 t.traceBegin("sendUserStartBroadcast");
                 final int callingUid = Binder.getCallingUid();
                 final int callingPid = Binder.getCallingPid();
-                long ident = Binder.clearCallingIdentity();
+                final long ident = Binder.clearCallingIdentity();
                 try {
                     Intent intent = new Intent(Intent.ACTION_USER_STARTED);
                     intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
@@ -8176,7 +8171,7 @@
      */
     int enforceDumpPermissionForPackage(String packageName, int userId, int callingUid,
             String function) {
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         int uid = Process.INVALID_UID;
         try {
             uid = mPackageManagerInt.getPackageUid(packageName,
@@ -8184,12 +8179,10 @@
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
-        if (uid == Process.INVALID_UID) {
-            return Process.INVALID_UID;
-        }
+        // If the uid is Process.INVALID_UID, the below 'if' check will be always true
         if (UserHandle.getAppId(uid) != UserHandle.getAppId(callingUid)) {
             // Requires the DUMP permission if the target package doesn't belong
-            // to the caller.
+            // to the caller or it doesn't exist.
             enforceCallingPermission(android.Manifest.permission.DUMP, function);
         }
         return uid;
@@ -8438,7 +8431,7 @@
             }
         }
 
-        long origId = Binder.clearCallingIdentity();
+        final long origId = Binder.clearCallingIdentity();
 
         if (useProto) {
             final ProtoOutputStream proto = new ProtoOutputStream(fd);
@@ -8823,37 +8816,6 @@
         return -1;
     }
 
-    boolean dumpUids(PrintWriter pw, String dumpPackage, int dumpAppId, ActiveUids uids,
-                String header, boolean needSep) {
-        boolean printed = false;
-        for (int i=0; i<uids.size(); i++) {
-            UidRecord uidRec = uids.valueAt(i);
-            if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != dumpAppId) {
-                continue;
-            }
-            if (!printed) {
-                printed = true;
-                if (needSep) {
-                    pw.println();
-                }
-                pw.print("  ");
-                pw.println(header);
-                needSep = true;
-            }
-            pw.print("    UID "); UserHandle.formatUid(pw, uidRec.uid);
-            pw.print(": "); pw.println(uidRec);
-            pw.print("      curProcState="); pw.print(uidRec.mCurProcState);
-            pw.print(" curCapability=");
-            ActivityManager.printCapabilitiesFull(pw, uidRec.curCapability);
-            pw.println();
-            for (int j = uidRec.procRecords.size() - 1; j >= 0; j--) {
-                pw.print("      proc=");
-                pw.println(uidRec.procRecords.valueAt(j));
-            }
-        }
-        return printed;
-    }
-
     void dumpBinderProxyInterfaceCounts(PrintWriter pw, String header) {
         final BinderProxy.InterfaceCount[] proxyCounts = BinderProxy.getSortedInterfaceCounts(50);
 
@@ -9103,19 +9065,13 @@
         needSep = dumpProcessesToGc(pw, needSep, dumpPackage);
 
         if (mProcessList.mActiveUids.size() > 0) {
-            if (dumpUids(pw, dumpPackage, dumpAppId, mProcessList.mActiveUids,
-                    "UID states:", needSep)) {
-                needSep = true;
-            }
+            needSep |= mProcessList.mActiveUids.dump(pw, dumpPackage, dumpAppId,
+                    "UID states:", needSep);
         }
 
         if (dumpAll) {
-            if (mUidObserverController.mValidateUids.size() > 0) {
-                if (dumpUids(pw, dumpPackage, dumpAppId, mUidObserverController.mValidateUids,
-                                "UID validation:", needSep)) {
-                    needSep = true;
-                }
-            }
+            needSep |= mUidObserverController.dumpValidateUids(pw,
+                    dumpPackage, dumpAppId, "UID validation:", needSep);
         }
 
         if (needSep) {
@@ -9384,22 +9340,12 @@
                     ActivityManagerServiceDumpProcessesProto.ACTIVE_INSTRUMENTATIONS);
         }
 
-        int whichAppId = getAppId(dumpPackage);
-        for (int i = 0; i < mProcessList.mActiveUids.size(); i++) {
-            UidRecord uidRec = mProcessList.mActiveUids.valueAt(i);
-            if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != whichAppId) {
-                continue;
-            }
-            uidRec.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.ACTIVE_UIDS);
-        }
+        final int dumpAppId = getAppId(dumpPackage);
+        mProcessList.mActiveUids.dumpProto(proto, dumpPackage, dumpAppId,
+                ActivityManagerServiceDumpProcessesProto.ACTIVE_UIDS);
 
-        for (int i = 0; i < mUidObserverController.mValidateUids.size(); i++) {
-            UidRecord uidRec = mUidObserverController.mValidateUids.valueAt(i);
-            if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != whichAppId) {
-                continue;
-            }
-            uidRec.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.VALIDATE_UIDS);
-        }
+        mUidObserverController.dumpValidateUidsProto(proto, dumpPackage, dumpAppId,
+                ActivityManagerServiceDumpProcessesProto.VALIDATE_UIDS);
 
         if (mProcessList.getLruSizeLocked() > 0) {
             long lruToken = proto.start(ActivityManagerServiceDumpProcessesProto.LRU_PROCS);
@@ -12134,6 +12080,7 @@
         app.setHasClientActivities(false);
 
         mServices.killServicesLocked(app, allowRestart);
+        mPhantomProcessList.onAppDied(app.pid);
 
         boolean restart = false;
 
@@ -12706,7 +12653,7 @@
             }
         }
 
-        long oldIdent = Binder.clearCallingIdentity();
+        final long oldIdent = Binder.clearCallingIdentity();
         try {
             IBackupManager bm = IBackupManager.Stub.asInterface(
                     ServiceManager.getService(Context.BACKUP_SERVICE));
@@ -13670,6 +13617,11 @@
                 case Intent.ACTION_PRE_BOOT_COMPLETED:
                     timeoutExempt = true;
                     break;
+                case Intent.ACTION_PACKAGE_UNSTARTABLE:
+                    final String packageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME);
+                    forceStopPackageLocked(packageName, -1, false, true, true,
+                            false, false, userId, "package unstartable");
+                    break;
             }
 
             if (Intent.ACTION_PACKAGE_ADDED.equals(action) ||
@@ -14893,6 +14845,59 @@
         return false;
     }
 
+    private boolean isEphemeralLocked(int uid) {
+        final String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
+        if (packages == null || packages.length != 1) { // Ephemeral apps cannot share uid
+            return false;
+        }
+        return getPackageManagerInternalLocked().isPackageEphemeral(
+                UserHandle.getUserId(uid), packages[0]);
+    }
+
+    void enqueueUidChangeLocked(UidRecord uidRec, int uid, int change) {
+        uid = uidRec != null ? uidRec.uid : uid;
+        if (uid < 0) {
+            throw new IllegalArgumentException("No UidRecord or uid");
+        }
+
+        final int procState = uidRec != null
+                ? uidRec.setProcState : PROCESS_STATE_NONEXISTENT;
+        final long procStateSeq = uidRec != null ? uidRec.curProcStateSeq : 0;
+        final int capability = uidRec != null ? uidRec.setCapability : 0;
+        final boolean ephemeral = uidRec != null ? uidRec.ephemeral : isEphemeralLocked(uid);
+
+        if (uidRec != null && !uidRec.idle && (change & UidRecord.CHANGE_GONE) != 0) {
+            // If this uid is going away, and we haven't yet reported it is gone,
+            // then do so now.
+            change |= UidRecord.CHANGE_IDLE;
+        }
+        final int enqueuedChange = mUidObserverController.enqueueUidChange(
+                uidRec == null ? null : uidRec.pendingChange,
+                uid, change, procState, procStateSeq, capability, ephemeral);
+        if (uidRec != null) {
+            uidRec.lastReportedChange = enqueuedChange;
+            uidRec.updateLastDispatchedProcStateSeq(enqueuedChange);
+        }
+
+        // Directly update the power manager, since we sit on top of it and it is critical
+        // it be kept in sync (so wake locks will be held as soon as appropriate).
+        if (mLocalPowerManager != null) {
+            // TODO: dispatch cached/uncached changes here, so we don't need to report
+            // all proc state changes.
+            if ((enqueuedChange & UidRecord.CHANGE_ACTIVE) != 0) {
+                mLocalPowerManager.uidActive(uid);
+            }
+            if ((enqueuedChange & UidRecord.CHANGE_IDLE) != 0) {
+                mLocalPowerManager.uidIdle(uid);
+            }
+            if ((enqueuedChange & UidRecord.CHANGE_GONE) != 0) {
+                mLocalPowerManager.uidGone(uid);
+            } else {
+                mLocalPowerManager.updateUidProcState(uid, procState);
+            }
+        }
+    }
+
     final void setProcessTrackerStateLocked(ProcessRecord proc, int memFactor, long now) {
         synchronized (mProcessStats.mLock) {
             if (proc.thread != null && proc.baseProcessTracker != null) {
@@ -15067,7 +15072,7 @@
         final int callingPid = Binder.getCallingPid();
         userId = mUserController.handleIncomingUser(callingPid, Binder.getCallingUid(),
                 userId, true, ALLOW_FULL_ONLY, "makePackageIdle", null);
-        long callingId = Binder.clearCallingIdentity();
+        final long callingId = Binder.clearCallingIdentity();
         synchronized(this) {
             try {
                 IPackageManager pm = AppGlobals.getPackageManager();
@@ -15158,7 +15163,7 @@
     @GuardedBy("this")
     final void doStopUidLocked(int uid, final UidRecord uidRec) {
         mServices.stopInBackgroundLocked(uid);
-        mUidObserverController.enqueueUidChangeLocked(uidRec, uid, UidRecord.CHANGE_IDLE);
+        enqueueUidChangeLocked(uidRec, uid, UidRecord.CHANGE_IDLE);
     }
 
     /**
@@ -15791,6 +15796,11 @@
         }
 
         @Override
+        public int getPendingIntentFlags(IIntentSender target) {
+            return mPendingIntentController.getPendingIntentFlags(target);
+        }
+
+        @Override
         public void setPendingIntentAllowBgActivityStarts(IIntentSender target,
                 IBinder whitelistToken, int flags) {
             if (!(target instanceof PendingIntentRecord)) {
@@ -16703,7 +16713,7 @@
                             + totalTime + ". Uid: " + callingUid + " procStateSeq: "
                             + procStateSeq + " UidRec: " + record
                             + " validateUidRec: "
-                            + mUidObserverController.mValidateUids.get(callingUid));
+                            + mUidObserverController.getValidateUidRecord(callingUid));
                 }
             } catch (InterruptedException e) {
                 Thread.currentThread().interrupt();
@@ -16759,7 +16769,7 @@
                     "Cannot kill the dependents of a package without its name.");
         }
 
-        long callingId = Binder.clearCallingIdentity();
+        final long callingId = Binder.clearCallingIdentity();
         IPackageManager pm = AppGlobals.getPackageManager();
         int pkgUid = -1;
         try {
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index d346430..31e7106 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -2587,9 +2587,9 @@
             case "info":
                 return runRootTaskInfo(pw);
             case "move-top-activity-to-pinned-stack":
-                return runMoveTopActivityToPinnedStack(pw);
+                return runMoveTopActivityToPinnedRootTask(pw);
             case "remove":
-                return runStackRemove(pw);
+                return runRootTaskRemove(pw);
             default:
                 getErrPrintWriter().println("Error: unknown command '" + op + "'");
                 return -1;
@@ -2626,19 +2626,19 @@
     }
 
     int runDisplayMoveStack(PrintWriter pw) throws RemoteException {
-        String stackIdStr = getNextArgRequired();
-        int stackId = Integer.parseInt(stackIdStr);
+        String rootTaskIdStr = getNextArgRequired();
+        int rootTaskId = Integer.parseInt(rootTaskIdStr);
         String displayIdStr = getNextArgRequired();
         int displayId = Integer.parseInt(displayIdStr);
-        mTaskInterface.moveStackToDisplay(stackId, displayId);
+        mTaskInterface.moveRootTaskToDisplay(rootTaskId, displayId);
         return 0;
     }
 
     int runStackMoveTask(PrintWriter pw) throws RemoteException {
         String taskIdStr = getNextArgRequired();
         int taskId = Integer.parseInt(taskIdStr);
-        String stackIdStr = getNextArgRequired();
-        int stackId = Integer.parseInt(stackIdStr);
+        String rootTaskIdStr = getNextArgRequired();
+        int rootTaskId = Integer.parseInt(rootTaskIdStr);
         String toTopStr = getNextArgRequired();
         final boolean toTop;
         if ("true".equals(toTopStr)) {
@@ -2650,7 +2650,7 @@
             return -1;
         }
 
-        mTaskInterface.moveTaskToStack(taskId, stackId, toTop);
+        mTaskInterface.moveTaskToRootTask(taskId, rootTaskId, toTop);
         return 0;
     }
 
@@ -2670,22 +2670,22 @@
         return 0;
     }
 
-    int runStackRemove(PrintWriter pw) throws RemoteException {
-        String stackIdStr = getNextArgRequired();
-        int stackId = Integer.parseInt(stackIdStr);
-        mTaskInterface.removeStack(stackId);
+    int runRootTaskRemove(PrintWriter pw) throws RemoteException {
+        String taskIdStr = getNextArgRequired();
+        int taskId = Integer.parseInt(taskIdStr);
+        mTaskInterface.removeTask(taskId);
         return 0;
     }
 
-    int runMoveTopActivityToPinnedStack(PrintWriter pw) throws RemoteException {
-        int stackId = Integer.parseInt(getNextArgRequired());
+    int runMoveTopActivityToPinnedRootTask(PrintWriter pw) throws RemoteException {
+        int rootTaskId = Integer.parseInt(getNextArgRequired());
         final Rect bounds = getBounds();
         if (bounds == null) {
             getErrPrintWriter().println("Error: invalid input bounds");
             return -1;
         }
 
-        if (!mTaskInterface.moveTopActivityToPinnedStack(stackId, bounds)) {
+        if (!mTaskInterface.moveTopActivityToPinnedRootTask(rootTaskId, bounds)) {
             getErrPrintWriter().println("Didn't move top activity to pinned stack.");
             return -1;
         }
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 3f55bff..cf900ab 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -546,7 +546,7 @@
                 }
             }
             if (res == AppErrorDialog.FORCE_QUIT) {
-                long orig = Binder.clearCallingIdentity();
+                final long orig = Binder.clearCallingIdentity();
                 try {
                     // Kill it with fire!
                     mService.mAtmInternal.onHandleAppCrash(r.getWindowProcessController());
@@ -800,9 +800,7 @@
         // with a home activity running in the process to prevent a repeatedly crashing app
         // from blocking the user to manually clear the list.
         final WindowProcessController proc = app.getWindowProcessController();
-        final WindowProcessController homeProc = mService.mAtmInternal.getHomeProcess();
-        if (proc == homeProc && proc.hasActivities()
-                && (((ProcessRecord) homeProc.mOwner).info.flags & FLAG_SYSTEM) == 0) {
+        if (proc.isHomeProcess() && proc.hasActivities() && (app.info.flags & FLAG_SYSTEM) == 0) {
             proc.clearPackagePreferredForHomeActivities();
         }
 
diff --git a/services/core/java/com/android/server/am/AppExitInfoTracker.java b/services/core/java/com/android/server/am/AppExitInfoTracker.java
index 374c215..20cad18 100644
--- a/services/core/java/com/android/server/am/AppExitInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppExitInfoTracker.java
@@ -503,7 +503,7 @@
     @VisibleForTesting
     void getExitInfo(final String packageName, final int filterUid,
             final int filterPid, final int maxNum, final ArrayList<ApplicationExitInfo> results) {
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             synchronized (mLock) {
                 boolean emptyPackageName = TextUtils.isEmpty(packageName);
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 76125aa..6f706ac 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -651,12 +651,13 @@
     public void noteWakupAlarm(final String name, final int uid, final WorkSource workSource,
             final String tag) {
         enforceCallingPermission();
+        final WorkSource localWs = workSource != null ? new WorkSource(workSource) : null;
         synchronized (mLock) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
             mHandler.post(() -> {
                 synchronized (mStats) {
-                    mStats.noteWakupAlarmLocked(name, uid, workSource, tag,
+                    mStats.noteWakupAlarmLocked(name, uid, localWs, tag,
                             elapsedRealtime, uptime);
                 }
             });
@@ -665,12 +666,13 @@
 
     public void noteAlarmStart(final String name, final WorkSource workSource, final int uid) {
         enforceCallingPermission();
+        final WorkSource localWs = workSource != null ? new WorkSource(workSource) : null;
         synchronized (mLock) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
             mHandler.post(() -> {
                 synchronized (mStats) {
-                    mStats.noteAlarmStartLocked(name, workSource, uid, elapsedRealtime, uptime);
+                    mStats.noteAlarmStartLocked(name, localWs, uid, elapsedRealtime, uptime);
                 }
             });
         }
@@ -678,12 +680,13 @@
 
     public void noteAlarmFinish(final String name, final WorkSource workSource, final int uid) {
         enforceCallingPermission();
+        final WorkSource localWs = workSource != null ? new WorkSource(workSource) : null;
         synchronized (mLock) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
             mHandler.post(() -> {
                 synchronized (mStats) {
-                    mStats.noteAlarmFinishLocked(name, workSource, uid, elapsedRealtime, uptime);
+                    mStats.noteAlarmFinishLocked(name, localWs, uid, elapsedRealtime, uptime);
                 }
             });
         }
@@ -722,12 +725,13 @@
     public void noteStartWakelockFromSource(final WorkSource ws, final int pid, final String name,
             final String historyName, final int type, final boolean unimportantForLogging) {
         enforceCallingPermission();
+        final WorkSource localWs = ws != null ? new WorkSource(ws) : null;
         synchronized (mLock) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
             mHandler.post(() -> {
                 synchronized (mStats) {
-                    mStats.noteStartWakeFromSourceLocked(ws, pid, name, historyName,
+                    mStats.noteStartWakeFromSourceLocked(localWs, pid, name, historyName,
                             type, unimportantForLogging, elapsedRealtime, uptime);
                 }
             });
@@ -739,13 +743,15 @@
             final String newName, final String newHistoryName, final int newType,
             final boolean newUnimportantForLogging) {
         enforceCallingPermission();
+        final WorkSource localWs = ws != null ? new WorkSource(ws) : null;
+        final WorkSource localNewWs = newWs != null ? new WorkSource(newWs) : null;
         synchronized (mLock) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
             mHandler.post(() -> {
                 synchronized (mStats) {
-                    mStats.noteChangeWakelockFromSourceLocked(ws, pid, name, historyName, type,
-                            newWs, newPid, newName, newHistoryName, newType,
+                    mStats.noteChangeWakelockFromSourceLocked(localWs, pid, name, historyName, type,
+                            localNewWs, newPid, newName, newHistoryName, newType,
                             newUnimportantForLogging, elapsedRealtime, uptime);
                 }
             });
@@ -755,12 +761,13 @@
     public void noteStopWakelockFromSource(final WorkSource ws, final int pid, final String name,
             final String historyName, final int type) {
         enforceCallingPermission();
+        final WorkSource localWs = ws != null ? new WorkSource(ws) : null;
         synchronized (mLock) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
             mHandler.post(() -> {
                 synchronized (mStats) {
-                    mStats.noteStopWakeFromSourceLocked(ws, pid, name, historyName, type,
+                    mStats.noteStopWakeFromSourceLocked(localWs, pid, name, historyName, type,
                             elapsedRealtime, uptime);
                 }
             });
@@ -787,12 +794,13 @@
     public void noteLongPartialWakelockStartFromSource(final String name, final String historyName,
             final WorkSource workSource) {
         enforceCallingPermission();
+        final WorkSource localWs = workSource != null ? new WorkSource(workSource) : null;
         synchronized (mLock) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
             mHandler.post(() -> {
                 synchronized (mStats) {
-                    mStats.noteLongPartialWakelockStartFromSource(name, historyName, workSource,
+                    mStats.noteLongPartialWakelockStartFromSource(name, historyName, localWs,
                             elapsedRealtime, uptime);
                 }
             });
@@ -819,12 +827,13 @@
     public void noteLongPartialWakelockFinishFromSource(final String name, final String historyName,
             final WorkSource workSource) {
         enforceCallingPermission();
+        final WorkSource localWs = workSource != null ? new WorkSource(workSource) : null;
         synchronized (mLock) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
             mHandler.post(() -> {
                 synchronized (mStats) {
-                    mStats.noteLongPartialWakelockFinishFromSource(name, historyName, workSource,
+                    mStats.noteLongPartialWakelockFinishFromSource(name, historyName, localWs,
                             elapsedRealtime, uptime);
                 }
             });
@@ -890,12 +899,14 @@
     @Override
     public void noteGpsChanged(final WorkSource oldWs, final WorkSource newWs) {
         enforceCallingPermission();
+        final WorkSource localOldWs = oldWs != null ? new WorkSource(oldWs) : null;
+        final WorkSource localNewWs = newWs != null ? new WorkSource(newWs) : null;
         synchronized (mLock) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
             mHandler.post(() -> {
                 synchronized (mStats) {
-                    mStats.noteGpsChangedLocked(oldWs, newWs, elapsedRealtime, uptime);
+                    mStats.noteGpsChangedLocked(localOldWs, localNewWs, elapsedRealtime, uptime);
                 }
             });
         }
@@ -1322,12 +1333,13 @@
 
     public void noteWifiRunning(final WorkSource ws) {
         enforceCallingPermission();
+        final WorkSource localWs = ws != null ? new WorkSource(ws) : null;
         synchronized (mLock) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
             mHandler.post(() -> {
                 synchronized (mStats) {
-                    mStats.noteWifiRunningLocked(ws, elapsedRealtime, uptime);
+                    mStats.noteWifiRunningLocked(localWs, elapsedRealtime, uptime);
                 }
                 // TODO: Log WIFI_RUNNING_STATE_CHANGED in a better spot to include Hotspot too.
                 FrameworkStatsLog.write(FrameworkStatsLog.WIFI_RUNNING_STATE_CHANGED,
@@ -1338,12 +1350,15 @@
 
     public void noteWifiRunningChanged(final WorkSource oldWs, final WorkSource newWs) {
         enforceCallingPermission();
+        final WorkSource localOldWs = oldWs != null ? new WorkSource(oldWs) : null;
+        final WorkSource localNewWs = newWs != null ? new WorkSource(newWs) : null;
         synchronized (mLock) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
             mHandler.post(() -> {
                 synchronized (mStats) {
-                    mStats.noteWifiRunningChangedLocked(oldWs, newWs, elapsedRealtime, uptime);
+                    mStats.noteWifiRunningChangedLocked(
+                            localOldWs, localNewWs, elapsedRealtime, uptime);
                 }
                 FrameworkStatsLog.write(FrameworkStatsLog.WIFI_RUNNING_STATE_CHANGED,
                         newWs, FrameworkStatsLog.WIFI_RUNNING_STATE_CHANGED__STATE__ON);
@@ -1355,12 +1370,13 @@
 
     public void noteWifiStopped(final WorkSource ws) {
         enforceCallingPermission();
+        final WorkSource localWs = ws != null ? new WorkSource(ws) : ws;
         synchronized (mLock) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
             mHandler.post(() -> {
                 synchronized (mStats) {
-                    mStats.noteWifiStoppedLocked(ws, elapsedRealtime, uptime);
+                    mStats.noteWifiStoppedLocked(localWs, elapsedRealtime, uptime);
                 }
                 FrameworkStatsLog.write(FrameworkStatsLog.WIFI_RUNNING_STATE_CHANGED,
                         ws, FrameworkStatsLog.WIFI_RUNNING_STATE_CHANGED__STATE__OFF);
@@ -1487,12 +1503,14 @@
 
     public void noteFullWifiLockAcquiredFromSource(final WorkSource ws) {
         enforceCallingPermission();
+        final WorkSource localWs = ws != null ? new WorkSource(ws) : null;
         synchronized (mLock) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
             mHandler.post(() -> {
                 synchronized (mStats) {
-                    mStats.noteFullWifiLockAcquiredFromSourceLocked(ws, elapsedRealtime, uptime);
+                    mStats.noteFullWifiLockAcquiredFromSourceLocked(
+                            localWs, elapsedRealtime, uptime);
                 }
             });
         }
@@ -1500,12 +1518,14 @@
 
     public void noteFullWifiLockReleasedFromSource(final WorkSource ws) {
         enforceCallingPermission();
+        final WorkSource localWs = ws != null ? new WorkSource(ws) : null;
         synchronized (mLock) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
             mHandler.post(() -> {
                 synchronized (mStats) {
-                    mStats.noteFullWifiLockReleasedFromSourceLocked(ws, elapsedRealtime, uptime);
+                    mStats.noteFullWifiLockReleasedFromSourceLocked(
+                            localWs, elapsedRealtime, uptime);
                 }
             });
         }
@@ -1513,12 +1533,13 @@
 
     public void noteWifiScanStartedFromSource(final WorkSource ws) {
         enforceCallingPermission();
+        final WorkSource localWs = ws != null ? new WorkSource(ws) : null;
         synchronized (mLock) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
             mHandler.post(() -> {
                 synchronized (mStats) {
-                    mStats.noteWifiScanStartedFromSourceLocked(ws, elapsedRealtime, uptime);
+                    mStats.noteWifiScanStartedFromSourceLocked(localWs, elapsedRealtime, uptime);
                 }
             });
         }
@@ -1526,12 +1547,13 @@
 
     public void noteWifiScanStoppedFromSource(final WorkSource ws) {
         enforceCallingPermission();
+        final WorkSource localWs = ws != null ? new WorkSource(ws) : null;
         synchronized (mLock) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
             mHandler.post(() -> {
                 synchronized (mStats) {
-                    mStats.noteWifiScanStoppedFromSourceLocked(ws, elapsedRealtime, uptime);
+                    mStats.noteWifiScanStoppedFromSourceLocked(localWs, elapsedRealtime, uptime);
                 }
             });
         }
@@ -1539,12 +1561,13 @@
 
     public void noteWifiBatchedScanStartedFromSource(final WorkSource ws, final int csph) {
         enforceCallingPermission();
+        final WorkSource localWs = ws != null ? new WorkSource(ws) : null;
         synchronized (mLock) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
             mHandler.post(() -> {
                 synchronized (mStats) {
-                    mStats.noteWifiBatchedScanStartedFromSourceLocked(ws, csph,
+                    mStats.noteWifiBatchedScanStartedFromSourceLocked(localWs, csph,
                             elapsedRealtime, uptime);
                 }
             });
@@ -1553,12 +1576,14 @@
 
     public void noteWifiBatchedScanStoppedFromSource(final WorkSource ws) {
         enforceCallingPermission();
+        final WorkSource localWs = ws != null ? new WorkSource(ws) : null;
         synchronized (mLock) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
             mHandler.post(() -> {
                 synchronized (mStats) {
-                    mStats.noteWifiBatchedScanStoppedFromSourceLocked(ws, elapsedRealtime, uptime);
+                    mStats.noteWifiBatchedScanStoppedFromSourceLocked(
+                            localWs, elapsedRealtime, uptime);
                 }
             });
         }
@@ -1635,12 +1660,13 @@
     @Override
     public void noteBleScanStarted(final WorkSource ws, final boolean isUnoptimized) {
         enforceCallingPermission();
+        final WorkSource localWs = ws != null ? new WorkSource(ws) : null;
         synchronized (mLock) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
             mHandler.post(() -> {
                 synchronized (mStats) {
-                    mStats.noteBluetoothScanStartedFromSourceLocked(ws, isUnoptimized,
+                    mStats.noteBluetoothScanStartedFromSourceLocked(localWs, isUnoptimized,
                             elapsedRealtime, uptime);
                 }
             });
@@ -1650,12 +1676,13 @@
     @Override
     public void noteBleScanStopped(final WorkSource ws, final boolean isUnoptimized) {
         enforceCallingPermission();
+        final WorkSource localWs = ws != null ? new WorkSource(ws) : null;
         synchronized (mLock) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
             mHandler.post(() -> {
                 synchronized (mStats) {
-                    mStats.noteBluetoothScanStoppedFromSourceLocked(ws, isUnoptimized,
+                    mStats.noteBluetoothScanStoppedFromSourceLocked(localWs, isUnoptimized,
                             uptime, elapsedRealtime);
                 }
             });
@@ -1679,12 +1706,13 @@
     @Override
     public void noteBleScanResults(final WorkSource ws, final int numNewResults) {
         enforceCallingPermission();
+        final WorkSource localWs = ws != null ? new WorkSource(ws) : null;
         synchronized (mLock) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
             mHandler.post(() -> {
                 synchronized (mStats) {
-                    mStats.noteBluetoothScanResultsFromSourceLocked(ws, numNewResults,
+                    mStats.noteBluetoothScanResultsFromSourceLocked(localWs, numNewResults,
                             elapsedRealtime, uptime);
                 }
             });
@@ -2090,7 +2118,7 @@
             return;
         }
 
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             if (BatteryStatsHelper.checkWifiOnly(mContext)) {
                 flags |= BatteryStats.DUMP_DEVICE_WIFI_ONLY;
@@ -2250,7 +2278,7 @@
             mContext.enforceCallingOrSelfPermission(
                     android.Manifest.permission.BATTERY_STATS, null);
         }
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             // Wait for the completion of pending works if there is any
             awaitCompletion();
@@ -2277,7 +2305,7 @@
             mContext.enforceCallingOrSelfPermission(
                     android.Manifest.permission.BATTERY_STATS, null);
         }
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         int i=-1;
         try {
             // Wait for the completion of pending works if there is any
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 57f8112..f1c5915 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -18,6 +18,7 @@
 
 import static android.os.Process.ZYGOTE_POLICY_FLAG_EMPTY;
 import static android.os.Process.ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE;
+import static android.text.TextUtils.formatSimple;
 
 import static com.android.server.am.ActivityManagerDebugConfig.*;
 
@@ -1892,7 +1893,7 @@
     }
 
     private String createBroadcastTraceTitle(BroadcastRecord record, int state) {
-        return String.format("Broadcast %s from %s (%s) %s",
+        return formatSimple("Broadcast %s from %s (%s) %s",
                 state == BroadcastRecord.DELIVERY_PENDING ? "in queue" : "dispatched",
                 record.callerPackage == null ? "" : record.callerPackage,
                 record.callerApp == null ? "process unknown" : record.callerApp.toShortString(),
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 8112bb8..cd0d5b4 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -777,27 +777,27 @@
         long freezeTime = app.freezeUnfreezeTime;
 
         try {
+            freezeBinder(app.pid, false);
+        } catch (RuntimeException e) {
+            Slog.e(TAG_AM, "Unable to unfreeze binder for " + app.pid + " " + app.processName
+                    + ". Killing it");
+            app.kill("Unable to unfreeze",
+                    ApplicationExitInfo.REASON_OTHER,
+                    ApplicationExitInfo.SUBREASON_INVALID_STATE, true);
+            return;
+        }
+
+        try {
             Process.setProcessFrozen(app.pid, app.uid, false);
 
             app.freezeUnfreezeTime = SystemClock.uptimeMillis();
             app.frozen = false;
         } catch (Exception e) {
             Slog.e(TAG_AM, "Unable to unfreeze " + app.pid + " " + app.processName
-                    + ". Any related user experience might be hanged.");
+                    + ". This might cause inconsistency or UI hangs.");
         }
 
         if (!app.frozen) {
-            try {
-                freezeBinder(app.pid, false);
-            } catch (RuntimeException e) {
-                Slog.e(TAG_AM, "Unable to unfreeze binder for " + app.pid + " " + app.processName
-                        + ". Killing it");
-                app.kill("Unable to unfreeze",
-                        ApplicationExitInfo.REASON_OTHER,
-                        ApplicationExitInfo.SUBREASON_INVALID_STATE, true);
-                return;
-            }
-
             if (DEBUG_FREEZER) {
                 Slog.d(TAG_AM, "sync unfroze " + app.pid + " " + app.processName);
             }
@@ -1110,14 +1110,6 @@
                     return;
                 }
 
-                try {
-                    freezeBinder(pid, true);
-                } catch (RuntimeException e) {
-                    // TODO: it might be preferable to kill the target pid in this case
-                    Slog.e(TAG_AM, "Unable to freeze binder for " + pid + " " + name);
-                    return;
-                }
-
                 if (pid == 0 || proc.frozen) {
                     // Already frozen or not a real process, either one being
                     // launched or one being killed
@@ -1146,6 +1138,15 @@
 
                 EventLog.writeEvent(EventLogTags.AM_FREEZE, pid, name);
 
+                try {
+                    freezeBinder(pid, true);
+                } catch (RuntimeException e) {
+                    Slog.e(TAG_AM, "Unable to freeze binder for " + pid + " " + name);
+                    proc.kill("Unable to freeze binder interface",
+                            ApplicationExitInfo.REASON_OTHER,
+                            ApplicationExitInfo.SUBREASON_INVALID_STATE, true);
+                }
+
                 // See above for why we're not taking mPhenotypeFlagLock here
                 if (mRandom.nextFloat() < mFreezerStatsdSampleRate) {
                     FrameworkStatsLog.write(FrameworkStatsLog.APP_FREEZE_CHANGED,
diff --git a/services/core/java/com/android/server/am/ContentProviderConnection.java b/services/core/java/com/android/server/am/ContentProviderConnection.java
index df8bff2..be49ce4 100644
--- a/services/core/java/com/android/server/am/ContentProviderConnection.java
+++ b/services/core/java/com/android/server/am/ContentProviderConnection.java
@@ -265,4 +265,13 @@
             return mUnstableCount;
         }
     }
+
+    /**
+     * Returns the total number of stable and unstable references.
+     */
+    int totalRefCount() {
+        synchronized (mLock) {
+            return mStableCount + mUnstableCount;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index 1155569..34256a0 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -62,6 +62,7 @@
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.RescueParty;
 
@@ -261,7 +262,8 @@
                     // doesn't kill our process.
                     Slog.wtf(TAG, "Existing provider " + cpr.name.flattenToShortString()
                             + " is crashing; detaching " + r);
-                    boolean lastRef = decProviderCountLocked(conn, cpr, token, stable);
+                    boolean lastRef = decProviderCountLocked(conn, cpr, token, stable,
+                            false, false);
                     if (!lastRef) {
                         // This wasn't the last ref our process had on
                         // the provider...  we will be killed during cleaning up, bail.
@@ -682,7 +684,7 @@
      */
     void removeContentProvider(IBinder connection, boolean stable) {
         mService.enforceNotIsolatedCaller("removeContentProvider");
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mService) {
                 ContentProviderConnection conn;
@@ -697,10 +699,7 @@
                 if (conn == null) {
                     throw new NullPointerException("connection is null");
                 }
-                if (decProviderCountLocked(conn, null, null, stable)) {
-                    mService.updateOomAdjLocked(conn.provider.proc,
-                            OomAdjuster.OOM_ADJ_REASON_REMOVE_PROVIDER);
-                }
+                decProviderCountLocked(conn, null, null, stable, true, true);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -711,7 +710,7 @@
         mService.enforceCallingPermission(
                 android.Manifest.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY,
                 "Do not have permission in call removeContentProviderExternal()");
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             removeContentProviderExternalUnchecked(name, token, userId);
         } finally {
@@ -1275,30 +1274,56 @@
 
     @GuardedBy("mService")
     private boolean decProviderCountLocked(ContentProviderConnection conn,
-            ContentProviderRecord cpr, IBinder externalProcessToken, boolean stable) {
+            ContentProviderRecord cpr, IBinder externalProcessToken, boolean stable,
+            boolean enforceDelay, boolean updateOomAdj) {
         if (conn == null) {
             cpr.removeExternalProcessHandleLocked(externalProcessToken);
             return false;
         }
-        if (conn.decrementCount(stable) != 0) {
+
+        if (conn.totalRefCount() > 1) {
+            conn.decrementCount(stable);
             return false;
         }
+        if (enforceDelay) {
+            // delay the removal of the provider for 5 seconds - this optimizes for those cases
+            // where providers are released and then quickly re-acquired, causing lots of churn.
+            BackgroundThread.getHandler().postDelayed(() -> {
+                handleProviderRemoval(conn, stable, updateOomAdj);
+            }, 5 * 1000);
+        } else {
+            handleProviderRemoval(conn, stable, updateOomAdj);
+        }
+        return true;
+    }
 
-        cpr = conn.provider;
-        conn.stopAssociation();
-        cpr.connections.remove(conn);
-        conn.client.conProviders.remove(conn);
-        if (conn.client.setProcState < ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
-            // The client is more important than last activity -- note the time this
-            // is happening, so we keep the old provider process around a bit as last
-            // activity to avoid thrashing it.
-            if (cpr.proc != null) {
-                cpr.proc.lastProviderTime = SystemClock.uptimeMillis();
+    private void handleProviderRemoval(ContentProviderConnection conn, boolean stable,
+            boolean updateOomAdj) {
+        synchronized (mService) {
+            // if the proc was already killed or this is not the last reference, simply exit.
+            if (conn == null || conn.provider == null || conn.decrementCount(stable) != 0) {
+                return;
+            }
+
+            final ContentProviderRecord cpr = conn.provider;
+            conn.stopAssociation();
+            cpr.connections.remove(conn);
+            conn.client.conProviders.remove(conn);
+            if (conn.client.setProcState < ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
+                // The client is more important than last activity -- note the time this
+                // is happening, so we keep the old provider process around a bit as last
+                // activity to avoid thrashing it.
+                if (cpr.proc != null) {
+                    cpr.proc.lastProviderTime = SystemClock.uptimeMillis();
+                }
+            }
+            mService.stopAssociationLocked(conn.client.uid, conn.client.processName, cpr.uid,
+                    cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
+            if (updateOomAdj) {
+                mService.updateOomAdjLocked(conn.provider.proc,
+                        OomAdjuster.OOM_ADJ_REASON_REMOVE_PROVIDER);
             }
         }
-        mService.stopAssociationLocked(conn.client.uid, conn.client.processName, cpr.uid,
-                cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
-        return true;
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index 529c651..f32423f 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -85,19 +85,19 @@
         sGlobalSettingToTypeMap.put(
                 Settings.Global.DEBUG_VIEW_ATTRIBUTES_APPLICATION_PACKAGE, String.class);
         sGlobalSettingToTypeMap.put(
-                Settings.Global.GLOBAL_SETTINGS_ANGLE_DEBUG_PACKAGE, String.class);
+                Settings.Global.ANGLE_DEBUG_PACKAGE, String.class);
         sGlobalSettingToTypeMap.put(
-                Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE, int.class);
+                Settings.Global.ANGLE_GL_DRIVER_ALL_ANGLE, int.class);
         sGlobalSettingToTypeMap.put(
-                Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS, String.class);
+                Settings.Global.ANGLE_GL_DRIVER_SELECTION_PKGS, String.class);
         sGlobalSettingToTypeMap.put(
-                Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES, String.class);
+                Settings.Global.ANGLE_GL_DRIVER_SELECTION_VALUES, String.class);
         sGlobalSettingToTypeMap.put(
-                Settings.Global.GLOBAL_SETTINGS_ANGLE_ALLOWLIST, String.class);
+                Settings.Global.ANGLE_ALLOWLIST, String.class);
         sGlobalSettingToTypeMap.put(
                 Settings.Global.ANGLE_EGL_FEATURES, String.class);
         sGlobalSettingToTypeMap.put(
-                Settings.Global.GLOBAL_SETTINGS_SHOW_ANGLE_IN_USE_DIALOG_BOX, String.class);
+                Settings.Global.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);
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index b818ccf..68f2e35 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -104,6 +104,14 @@
 30076 uc_start_user_internal (userId|1|5)
 30077 uc_unlock_user (userId|1|5)
 30078 uc_finish_user_boot (userId|1|5)
-30079 uc_dispatch_user_switch (oldUserId|1|5) (newUserId|1|5)
-30080 uc_continue_user_switch (oldUserId|1|5) (newUserId|1|5)
-30081 uc_send_user_broadcast (userId|1|5),(IntentAction|3)
\ No newline at end of file
+30079 uc_dispatch_user_switch (oldUserId|1|5),(newUserId|1|5)
+30080 uc_continue_user_switch (oldUserId|1|5),(newUserId|1|5)
+30081 uc_send_user_broadcast (userId|1|5),(IntentAction|3)
+# Tags below are used by SystemServiceManager - although it's technically part of am, these are
+# also user switch events and useful to be analyzed together with events above.
+30082 ssm_user_starting (userId|1|5)
+30083 ssm_user_switching (oldUserId|1|5),(newUserId|1|5)
+30084 ssm_user_unlocking (userId|1|5)
+30085 ssm_user_unlocked (userId|1|5)
+30086 ssm_user_stopping (userId|1|5)
+30087 ssm_user_stopped (userId|1|5)
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 58ac2dc..9d49236 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1147,7 +1147,7 @@
                 uidRec.setWhitelist = uidRec.curWhitelist;
                 uidRec.setIdle = uidRec.idle;
                 mService.mAtmInternal.onUidProcStateChanged(uidRec.uid, uidRec.setProcState);
-                mService.mUidObserverController.enqueueUidChangeLocked(uidRec, -1, uidChange);
+                mService.enqueueUidChangeLocked(uidRec, -1, uidChange);
                 mService.noteUidProcessState(uidRec.uid, uidRec.getCurProcState(),
                         uidRec.curCapability);
                 if (uidRec.foregroundServices) {
diff --git a/services/core/java/com/android/server/am/PendingIntentController.java b/services/core/java/com/android/server/am/PendingIntentController.java
index c62df56..2ae3d35 100644
--- a/services/core/java/com/android/server/am/PendingIntentController.java
+++ b/services/core/java/com/android/server/am/PendingIntentController.java
@@ -311,6 +311,16 @@
         }
     }
 
+    int getPendingIntentFlags(IIntentSender target) {
+        if (!(target instanceof PendingIntentRecord)) {
+            Slog.w(TAG, "markAsSentFromNotification(): not a PendingIntentRecord: " + target);
+            return 0;
+        }
+        synchronized (mLock) {
+            return ((PendingIntentRecord) target).key.flags;
+        }
+    }
+
     private void makeIntentSenderCanceled(PendingIntentRecord rec) {
         rec.canceled = true;
         final RemoteCallbackList<IResultReceiver> callbacks = rec.detachCancelListenersLocked();
diff --git a/services/core/java/com/android/server/am/PhantomProcessList.java b/services/core/java/com/android/server/am/PhantomProcessList.java
index e2fcf08..5167c57 100644
--- a/services/core/java/com/android/server/am/PhantomProcessList.java
+++ b/services/core/java/com/android/server/am/PhantomProcessList.java
@@ -27,15 +27,22 @@
 import android.app.ApplicationExitInfo.SubReason;
 import android.os.Handler;
 import android.os.Process;
+import android.os.StrictMode;
 import android.util.Slog;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.ProcStatsUtil;
 import com.android.internal.os.ProcessCpuTracker;
 
 import libcore.io.IoUtils;
 
 import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -78,18 +85,178 @@
     @GuardedBy("mLock")
     private final ArrayList<PhantomProcessRecord> mTempPhantomProcesses = new ArrayList<>();
 
+    /**
+     * The mapping between a phantom process ID to its parent process (an app process)
+     */
+    @GuardedBy("mLock")
+    private final SparseArray<ProcessRecord> mPhantomToAppProcessMap = new SparseArray<>();
+
+    @GuardedBy("mLock")
+    private final SparseArray<InputStream> mCgroupProcsFds = new SparseArray<>();
+
+    @GuardedBy("mLock")
+    private final byte[] mDataBuffer = new byte[4096];
+
     @GuardedBy("mLock")
     private boolean mTrimPhantomProcessScheduled = false;
 
     @GuardedBy("mLock")
     int mUpdateSeq;
 
+    @VisibleForTesting
+    Injector mInjector;
+
     private final ActivityManagerService mService;
     private final Handler mKillHandler;
 
     PhantomProcessList(final ActivityManagerService service) {
         mService = service;
         mKillHandler = service.mProcessList.sKillHandler;
+        mInjector = new Injector();
+    }
+
+    @VisibleForTesting
+    @GuardedBy("mLock")
+    void lookForPhantomProcessesLocked() {
+        mPhantomToAppProcessMap.clear();
+        StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+        try {
+            synchronized (mService.mPidsSelfLocked) {
+                for (int i = mService.mPidsSelfLocked.size() - 1; i >= 0; i--) {
+                    final ProcessRecord app = mService.mPidsSelfLocked.valueAt(i);
+                    lookForPhantomProcessesLocked(app);
+                }
+            }
+        } finally {
+            StrictMode.setThreadPolicy(oldPolicy);
+        }
+    }
+
+    @GuardedBy({"mLock", "mService.mPidsSelfLocked"})
+    private void lookForPhantomProcessesLocked(ProcessRecord app) {
+        if (app.appZygote || app.killed || app.killedByAm) {
+            // process forked from app zygote doesn't have its own acct entry
+            return;
+        }
+        InputStream input = mCgroupProcsFds.get(app.pid);
+        if (input == null) {
+            final String path = getCgroupFilePath(app.info.uid, app.pid);
+            try {
+                input = mInjector.openCgroupProcs(path);
+            } catch (FileNotFoundException | SecurityException e) {
+                if (DEBUG_PROCESSES) {
+                    Slog.w(TAG, "Unable to open " + path, e);
+                }
+                return;
+            }
+            // Keep the FD open for better performance
+            mCgroupProcsFds.put(app.pid, input);
+        }
+        final byte[] buf = mDataBuffer;
+        try {
+            int read = 0;
+            int pid = 0;
+            long totalRead = 0;
+            do {
+                read = mInjector.readCgroupProcs(input, buf, 0, buf.length);
+                if (read == -1) {
+                    break;
+                }
+                totalRead += read;
+                for (int i = 0; i < read; i++) {
+                    final byte b = buf[i];
+                    if (b == '\n') {
+                        addChildPidLocked(app, pid);
+                        pid = 0;
+                    } else {
+                        pid = pid * 10 + (b - '0');
+                    }
+                }
+                if (read < buf.length) {
+                    // we may break from here safely as sysfs reading should return the whole page
+                    // if the remaining data is larger than a page
+                    break;
+                }
+            } while (true);
+            if (pid != 0) {
+                addChildPidLocked(app, pid);
+            }
+            // rewind the fd for the next read
+            input.skip(-totalRead);
+        } catch (IOException e) {
+            Slog.e(TAG, "Error in reading cgroup procs from " + app, e);
+            IoUtils.closeQuietly(input);
+            mCgroupProcsFds.delete(app.pid);
+        }
+    }
+
+    @VisibleForTesting
+    static String getCgroupFilePath(int uid, int pid) {
+        return "/acct/uid_" + uid + "/pid_" + pid + "/cgroup.procs";
+    }
+
+    static String getProcessName(int pid) {
+        String procName = ProcStatsUtil.readTerminatedProcFile(
+                "/proc/" + pid + "/cmdline", (byte) '\0');
+        if (procName == null) {
+            return null;
+        }
+        int l = procName.lastIndexOf('/');
+        if (l > 0 && l < procName.length() - 1) {
+            procName = procName.substring(l + 1);
+        }
+        return procName;
+    }
+
+    @GuardedBy({"mLock", "mService.mPidsSelfLocked"})
+    private void addChildPidLocked(final ProcessRecord app, final int pid) {
+        if (app.pid != pid) {
+            // That's something else...
+            final ProcessRecord r = mService.mPidsSelfLocked.get(pid);
+            if (r != null) {
+                // Is this a process forked via app zygote?
+                if (!r.appZygote) {
+                    // Unexpected...
+                    if (DEBUG_PROCESSES) {
+                        Slog.w(TAG, "Unexpected: " + r + " appears in the cgroup.procs of " + app);
+                    }
+                } else {
+                    // Just a child process of app zygote, no worries
+                }
+            } else {
+                final int index = mPhantomToAppProcessMap.indexOfKey(pid);
+                if (index >= 0) { // unlikely since we cleared the map at the beginning
+                    final ProcessRecord current = mPhantomToAppProcessMap.valueAt(index);
+                    if (app == current) {
+                        // Okay it's unchanged
+                        return;
+                    }
+                    mPhantomToAppProcessMap.setValueAt(index, app);
+                } else {
+                    mPhantomToAppProcessMap.put(pid, app);
+                }
+                // Its UID isn't necessarily to be the same as the app.info.uid, since it could be
+                // forked from child processes of app zygote
+                final int uid = Process.getUidForPid(pid);
+                String procName = mInjector.getProcessName(pid);
+                if (procName == null || uid < 0) {
+                    mPhantomToAppProcessMap.delete(pid);
+                    return;
+                }
+                getOrCreatePhantomProcessIfNeededLocked(procName, uid, pid, true);
+            }
+        }
+    }
+
+    void onAppDied(final int pid) {
+        synchronized (mLock) {
+            final int index = mCgroupProcsFds.indexOfKey(pid);
+            if (index >= 0) {
+                final InputStream inputStream = mCgroupProcsFds.valueAt(index);
+                mCgroupProcsFds.removeAt(index);
+                IoUtils.closeQuietly(inputStream);
+            }
+        }
     }
 
     /**
@@ -99,7 +266,7 @@
      */
     @GuardedBy("mLock")
     PhantomProcessRecord getOrCreatePhantomProcessIfNeededLocked(final String processName,
-            final int uid, final int pid) {
+            final int uid, final int pid, boolean createIfNeeded) {
         // First check if it's actually an app process we know
         if (isAppProcess(pid)) {
             return null;
@@ -123,56 +290,47 @@
                 if (proc.equals(processName, uid, pid)) {
                     return proc;
                 }
-                // Our zombie process information is outdated, let's remove this one, it shoud
+                // Our zombie process information is outdated, let's remove this one, it should
                 // have been gone.
                 mZombiePhantomProcesses.removeAt(idx);
             }
         }
 
-        int ppid = getParentPid(pid);
+        if (!createIfNeeded) {
+            return null;
+        }
 
-        // Walk through its parents and see if it could be traced back to an app process.
-        while (ppid > 1) {
-            if (isAppProcess(ppid)) {
-                // It's a phantom process, bookkeep it
-                try {
-                    final PhantomProcessRecord proc = new PhantomProcessRecord(
-                            processName, uid, pid, ppid, mService,
-                            this::onPhantomProcessKilledLocked);
-                    proc.mUpdateSeq = mUpdateSeq;
-                    mPhantomProcesses.put(pid, proc);
-                    SparseArray<PhantomProcessRecord> array = mAppPhantomProcessMap.get(ppid);
-                    if (array == null) {
-                        array = new SparseArray<>();
-                        mAppPhantomProcessMap.put(ppid, array);
-                    }
-                    array.put(pid, proc);
-                    if (proc.mPidFd != null) {
-                        mKillHandler.getLooper().getQueue().addOnFileDescriptorEventListener(
-                                proc.mPidFd, EVENT_INPUT | EVENT_ERROR,
-                                this::onPhantomProcessFdEvent);
-                        mPhantomProcessesPidFds.put(proc.mPidFd.getInt$(), proc);
-                    }
-                    scheduleTrimPhantomProcessesLocked();
-                    return proc;
-                } catch (IllegalStateException e) {
-                    return null;
+        final ProcessRecord r = mPhantomToAppProcessMap.get(pid);
+
+        if (r != null) {
+            // It's a phantom process, bookkeep it
+            try {
+                final PhantomProcessRecord proc = new PhantomProcessRecord(
+                        processName, uid, pid, r.pid, mService,
+                        this::onPhantomProcessKilledLocked);
+                proc.mUpdateSeq = mUpdateSeq;
+                mPhantomProcesses.put(pid, proc);
+                SparseArray<PhantomProcessRecord> array = mAppPhantomProcessMap.get(r.pid);
+                if (array == null) {
+                    array = new SparseArray<>();
+                    mAppPhantomProcessMap.put(r.pid, array);
                 }
+                array.put(pid, proc);
+                if (proc.mPidFd != null) {
+                    mKillHandler.getLooper().getQueue().addOnFileDescriptorEventListener(
+                            proc.mPidFd, EVENT_INPUT | EVENT_ERROR,
+                            this::onPhantomProcessFdEvent);
+                    mPhantomProcessesPidFds.put(proc.mPidFd.getInt$(), proc);
+                }
+                scheduleTrimPhantomProcessesLocked();
+                return proc;
+            } catch (IllegalStateException e) {
+                return null;
             }
-
-            ppid = getParentPid(ppid);
         }
         return null;
     }
 
-    private static int getParentPid(int pid) {
-        try {
-            return Process.getParentPid(pid);
-        } catch (Exception e) {
-        }
-        return -1;
-    }
-
     private boolean isAppProcess(int pid) {
         synchronized (mService.mPidsSelfLocked) {
             return mService.mPidsSelfLocked.get(pid) != null;
@@ -346,10 +504,14 @@
         synchronized (mLock) {
             // refresh the phantom process list with the latest cpu stats results.
             mUpdateSeq++;
+
+            // Scan app process's accounting procs
+            lookForPhantomProcessesLocked();
+
             for (int i = tracker.countStats() - 1; i >= 0; i--) {
                 final ProcessCpuTracker.Stats st = tracker.getStats(i);
                 final PhantomProcessRecord r =
-                        getOrCreatePhantomProcessIfNeededLocked(st.name, st.uid, st.pid);
+                        getOrCreatePhantomProcessIfNeededLocked(st.name, st.uid, st.pid, false);
                 if (r != null) {
                     r.mUpdateSeq = mUpdateSeq;
                     r.mCurrentCputime += st.rel_utime + st.rel_stime;
@@ -392,4 +554,19 @@
             proc.dump(pw, prefix + "    ");
         }
     }
+
+    @VisibleForTesting
+    static class Injector {
+        InputStream openCgroupProcs(String path) throws FileNotFoundException, SecurityException {
+            return new FileInputStream(path);
+        }
+
+        int readCgroupProcs(InputStream input, byte[] buf, int offset, int len) throws IOException {
+            return input.read(buf, offset, len);
+        }
+
+        String getProcessName(final int pid) {
+            return PhantomProcessList.getProcessName(pid);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/PreBootBroadcaster.java b/services/core/java/com/android/server/am/PreBootBroadcaster.java
index 747e8a8..be63dd4 100644
--- a/services/core/java/com/android/server/am/PreBootBroadcaster.java
+++ b/services/core/java/com/android/server/am/PreBootBroadcaster.java
@@ -144,7 +144,8 @@
 
                     final PendingIntent contentIntent;
                     if (context.getPackageManager().resolveActivity(intent, 0) != null) {
-                        contentIntent = PendingIntent.getActivity(context, 0, intent, 0);
+                        contentIntent = PendingIntent.getActivity(context, 0, intent,
+                                PendingIntent.FLAG_IMMUTABLE);
                     } else {
                         contentIntent = null;
                     }
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index ed47616d9..6f6cad0 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -2540,8 +2540,8 @@
                 app.hostingRecord.getName() != null ? app.hostingRecord.getName() : "");
 
         try {
-            AppGlobals.getPackageManager().logAppProcessStartIfNeeded(app.processName, app.uid,
-                    app.seInfo, app.info.sourceDir, pid);
+            AppGlobals.getPackageManager().logAppProcessStartIfNeeded(app.info.packageName,
+                    app.processName, app.uid, app.seInfo, app.info.sourceDir, pid);
         } catch (RemoteException ex) {
             // Ignore
         }
@@ -2948,7 +2948,7 @@
                 // No more processes using this uid, tell clients it is gone.
                 if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
                         "No more processes in " + uidRecord);
-                mService.mUidObserverController.enqueueUidChangeLocked(uidRecord, -1,
+                mService.enqueueUidChangeLocked(uidRecord, -1,
                         UidRecord.CHANGE_GONE);
                 EventLogTags.writeAmUidStopped(uid);
                 mActiveUids.remove(uid);
@@ -3561,7 +3561,7 @@
             int clientTargetSdk) {
         outInfo.pid = app.pid;
         outInfo.uid = app.info.uid;
-        if (mService.mAtmInternal.isHeavyWeightProcess(app.getWindowProcessController())) {
+        if (app.getWindowProcessController().isHeavyWeightProcess()) {
             outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_CANT_SAVE_STATE;
         }
         if (app.isPersistent()) {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index ebc5b59..53c6758 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -246,7 +246,7 @@
     long lastTopTime;           // The last time the process was in the TOP state or greater.
     boolean reportLowMemory;    // Set to true when waiting to report low mem
     boolean empty;              // Is this an empty background process?
-    private volatile boolean mCached;    // Is this a cached process?
+    private boolean mCached;    // Is this a cached process?
     String adjType;             // Debugging: primary thing impacting oom_adj.
     int adjTypeCode;            // Debugging: adj code to report to app.
     Object adjSource;           // Debugging: option dependent object.
@@ -794,10 +794,7 @@
     }
 
     void setCached(boolean cached) {
-        if (mCached != cached) {
-            mCached = cached;
-            mWindowProcessController.onProcCachedStateChanged(cached);
-        }
+        mCached = cached;
     }
 
     @Override
@@ -887,7 +884,7 @@
                     Slog.w(TAG, "scheduleCrash: trying to crash system process!");
                     return;
                 }
-                long ident = Binder.clearCallingIdentity();
+                final long ident = Binder.clearCallingIdentity();
                 try {
                     thread.scheduleCrash(message);
                 } catch (RemoteException e) {
@@ -1463,6 +1460,12 @@
             if (updateServiceConnectionActivities) {
                 mService.mServices.updateServiceConnectionActivitiesLocked(this);
             }
+            if (thread == null) {
+                // Only update lru and oom-adj if the process is alive. Because it may be called
+                // when cleaning up the last activity from handling process died, the dead process
+                // should not be added to lru list again.
+                return;
+            }
             mService.mProcessList.updateLruProcessLocked(this, activityChange, null /* client */);
             if (updateOomAdj) {
                 mService.updateOomAdjLocked(this, OomAdjuster.OOM_ADJ_REASON_ACTIVITY);
@@ -1850,8 +1853,8 @@
 
     boolean getCachedIsHeavyWeight() {
         if (mCachedIsHeavyWeight == VALUE_INVALID) {
-            mCachedIsHeavyWeight = mService.mAtmInternal.isHeavyWeightProcess(
-                    getWindowProcessController()) ? VALUE_TRUE : VALUE_FALSE;
+            mCachedIsHeavyWeight = getWindowProcessController().isHeavyWeightProcess()
+                    ? VALUE_TRUE : VALUE_FALSE;
         }
         return mCachedIsHeavyWeight == VALUE_TRUE;
     }
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index d925def..c10a078 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -855,7 +855,7 @@
         if (!com.android.internal.util.DumpUtils.checkDumpAndUsageStatsPermission(mAm.mContext,
                 TAG, pw)) return;
 
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             if (args.length > 0) {
                 if ("--proto".equals(args[0])) {
diff --git a/services/core/java/com/android/server/am/UidObserverController.java b/services/core/java/com/android/server/am/UidObserverController.java
index 4d9260a..b3150d1 100644
--- a/services/core/java/com/android/server/am/UidObserverController.java
+++ b/services/core/java/com/android/server/am/UidObserverController.java
@@ -20,9 +20,12 @@
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS;
 import static com.android.server.am.ActivityManagerService.TAG_UID_OBSERVERS;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityManagerProto;
 import android.app.IUidObserver;
+import android.os.Handler;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -40,158 +43,154 @@
 import java.util.ArrayList;
 
 public class UidObserverController {
-    private final ActivityManagerService mService;
-    final RemoteCallbackList<IUidObserver> mUidObservers = new RemoteCallbackList<>();
-
-    UidRecord.ChangeItem[] mActiveUidChanges = new UidRecord.ChangeItem[5];
-    final ArrayList<UidRecord.ChangeItem> mPendingUidChanges = new ArrayList<>();
-    final ArrayList<UidRecord.ChangeItem> mAvailUidChanges = new ArrayList<>();
-
-    /** Total # of UID change events dispatched, shown in dumpsys. */
-    int mUidChangeDispatchCount;
-
     /** If a UID observer takes more than this long, send a WTF. */
     private static final int SLOW_UID_OBSERVER_THRESHOLD_MS = 20;
 
+    private final Handler mHandler;
+
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
+    final RemoteCallbackList<IUidObserver> mUidObservers = new RemoteCallbackList<>();
+
+    @GuardedBy("mLock")
+    private final ArrayList<ChangeRecord> mPendingUidChanges = new ArrayList<>();
+    @GuardedBy("mLock")
+    private final ArrayList<ChangeRecord> mAvailUidChanges = new ArrayList<>();
+
+    private ChangeRecord[] mActiveUidChanges = new ChangeRecord[5];
+
+    /** Total # of UID change events dispatched, shown in dumpsys. */
+    @GuardedBy("mLock")
+    private int mUidChangeDispatchCount;
+
+    private final Runnable mDispatchRunnable = this::dispatchUidsChanged;
+
     /**
      * This is for verifying the UID report flow.
      */
-    static final boolean VALIDATE_UID_STATES = true;
-    final ActiveUids mValidateUids;
+    private static final boolean VALIDATE_UID_STATES = true;
+    private final ActiveUids mValidateUids;
 
-    UidObserverController(ActivityManagerService service) {
-        mService = service;
-        mValidateUids = new ActiveUids(mService, false /* postChangesToAtm */);
+    UidObserverController(@NonNull Handler handler) {
+        mHandler = handler;
+        mValidateUids = new ActiveUids(null /* service */, false /* postChangesToAtm */);
     }
 
-    @GuardedBy("mService")
-    void register(IUidObserver observer, int which, int cutpoint, String callingPackage,
-            int callingUid) {
-        mUidObservers.register(observer, new UidObserverRegistration(callingUid,
-                callingPackage, which, cutpoint));
+    void register(@NonNull IUidObserver observer, int which, int cutpoint,
+            @NonNull String callingPackage, int callingUid) {
+        synchronized (mLock) {
+            mUidObservers.register(observer, new UidObserverRegistration(callingUid,
+                    callingPackage, which, cutpoint));
+        }
     }
 
-    @GuardedBy("mService")
-    void unregister(IUidObserver observer) {
-        mUidObservers.unregister(observer);
+    void unregister(@NonNull IUidObserver observer) {
+        synchronized (mLock) {
+            mUidObservers.unregister(observer);
+        }
     }
 
-    @GuardedBy("mService")
-    final void enqueueUidChangeLocked(UidRecord uidRec, int uid, int change) {
-        final UidRecord.ChangeItem pendingChange;
-        if (uidRec == null || uidRec.pendingChange == null) {
+    int enqueueUidChange(@Nullable ChangeRecord currentRecord, int uid, int change, int procState,
+            long procStateSeq, int capability, boolean ephemeral) {
+        synchronized (mLock) {
             if (mPendingUidChanges.size() == 0) {
                 if (DEBUG_UID_OBSERVERS) {
                     Slog.i(TAG_UID_OBSERVERS, "*** Enqueueing dispatch uid changed!");
                 }
-                mService.mUiHandler.post(this::dispatchUidsChanged);
+                mHandler.post(mDispatchRunnable);
             }
-            final int size = mAvailUidChanges.size();
-            if (size > 0) {
-                pendingChange = mAvailUidChanges.remove(size - 1);
-                if (DEBUG_UID_OBSERVERS) {
-                    Slog.i(TAG_UID_OBSERVERS, "Retrieving available item: " + pendingChange);
-                }
-            } else {
-                pendingChange = new UidRecord.ChangeItem();
-                if (DEBUG_UID_OBSERVERS) {
-                    Slog.i(TAG_UID_OBSERVERS, "Allocating new item: " + pendingChange);
-                }
-            }
-            if (uidRec != null) {
-                uidRec.pendingChange = pendingChange;
-                if ((change & UidRecord.CHANGE_GONE) != 0 && !uidRec.idle) {
-                    // If this uid is going away, and we haven't yet reported it is gone,
-                    // then do so now.
-                    change |= UidRecord.CHANGE_IDLE;
-                }
-            } else if (uid < 0) {
-                throw new IllegalArgumentException("No UidRecord or uid");
-            }
-            pendingChange.uidRecord = uidRec;
-            pendingChange.uid = uidRec != null ? uidRec.uid : uid;
-            mPendingUidChanges.add(pendingChange);
-        } else {
-            pendingChange = uidRec.pendingChange;
-            // If there is no change in idle or active state, then keep whatever was pending.
-            if ((change & (UidRecord.CHANGE_IDLE | UidRecord.CHANGE_ACTIVE)) == 0) {
-                change |= (pendingChange.change & (UidRecord.CHANGE_IDLE
-                        | UidRecord.CHANGE_ACTIVE));
-            }
-            // If there is no change in cached or uncached state, then keep whatever was pending.
-            if ((change & (UidRecord.CHANGE_CACHED | UidRecord.CHANGE_UNCACHED)) == 0) {
-                change |= (pendingChange.change & (UidRecord.CHANGE_CACHED
-                        | UidRecord.CHANGE_UNCACHED));
-            }
-            // If this is a report of the UID being gone, then we shouldn't keep any previous
-            // report of it being active or cached.  (That is, a gone uid is never active,
-            // and never cached.)
-            if ((change & UidRecord.CHANGE_GONE) != 0) {
-                change &= ~(UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_CACHED);
-                if (!uidRec.idle) {
-                    // If this uid is going away, and we haven't yet reported it is gone,
-                    // then do so now.
-                    change |= UidRecord.CHANGE_IDLE;
-                }
-            }
-        }
-        pendingChange.change = change;
-        pendingChange.processState = uidRec != null
-                ? uidRec.setProcState : PROCESS_STATE_NONEXISTENT;
-        pendingChange.capability = uidRec != null ? uidRec.setCapability : 0;
-        pendingChange.ephemeral = uidRec != null ? uidRec.ephemeral : isEphemeralLocked(uid);
-        pendingChange.procStateSeq = uidRec != null ? uidRec.curProcStateSeq : 0;
-        if (uidRec != null) {
-            uidRec.lastReportedChange = change;
-            uidRec.updateLastDispatchedProcStateSeq(change);
-        }
 
-        // Directly update the power manager, since we sit on top of it and it is critical
-        // it be kept in sync (so wake locks will be held as soon as appropriate).
-        if (mService.mLocalPowerManager != null) {
-            // TO DO: dispatch cached/uncached changes here, so we don't need to report
-            // all proc state changes.
-            if ((change & UidRecord.CHANGE_ACTIVE) != 0) {
-                mService.mLocalPowerManager.uidActive(pendingChange.uid);
-            }
-            if ((change & UidRecord.CHANGE_IDLE) != 0) {
-                mService.mLocalPowerManager.uidIdle(pendingChange.uid);
-            }
-            if ((change & UidRecord.CHANGE_GONE) != 0) {
-                mService.mLocalPowerManager.uidGone(pendingChange.uid);
+            final ChangeRecord changeRecord = currentRecord != null
+                    ? currentRecord : getOrCreateChangeRecordLocked();
+            if (!changeRecord.isPending) {
+                changeRecord.isPending = true;
+                mPendingUidChanges.add(changeRecord);
             } else {
-                mService.mLocalPowerManager.updateUidProcState(pendingChange.uid,
-                        pendingChange.processState);
+                change = mergeWithPendingChange(change, changeRecord.change);
+            }
+
+            changeRecord.uid = uid;
+            changeRecord.change = change;
+            changeRecord.procState = procState;
+            changeRecord.procStateSeq = procStateSeq;
+            changeRecord.capability = capability;
+            changeRecord.ephemeral = ephemeral;
+
+            return changeRecord.change;
+        }
+    }
+
+    ArrayList<ChangeRecord> getPendingUidChangesForTest() {
+        return mPendingUidChanges;
+    }
+
+    ActiveUids getValidateUidsForTest() {
+        return mValidateUids;
+    }
+
+    @VisibleForTesting
+    static int mergeWithPendingChange(int currentChange, int pendingChange) {
+        // If there is no change in idle or active state, then keep whatever was pending.
+        if ((currentChange & (UidRecord.CHANGE_IDLE | UidRecord.CHANGE_ACTIVE)) == 0) {
+            currentChange |= (pendingChange & (UidRecord.CHANGE_IDLE
+                    | UidRecord.CHANGE_ACTIVE));
+        }
+        // If there is no change in cached or uncached state, then keep whatever was pending.
+        if ((currentChange & (UidRecord.CHANGE_CACHED | UidRecord.CHANGE_UNCACHED)) == 0) {
+            currentChange |= (pendingChange & (UidRecord.CHANGE_CACHED
+                    | UidRecord.CHANGE_UNCACHED));
+        }
+        // If this is a report of the UID being gone, then we shouldn't keep any previous
+        // report of it being active or cached.  (That is, a gone uid is never active,
+        // and never cached.)
+        if ((currentChange & UidRecord.CHANGE_GONE) != 0) {
+            currentChange &= ~(UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_CACHED);
+        }
+        return currentChange;
+    }
+
+    @GuardedBy("mLock")
+    private ChangeRecord getOrCreateChangeRecordLocked() {
+        final ChangeRecord changeRecord;
+        final int size = mAvailUidChanges.size();
+        if (size > 0) {
+            changeRecord = mAvailUidChanges.remove(size - 1);
+            if (DEBUG_UID_OBSERVERS) {
+                Slog.i(TAG_UID_OBSERVERS, "Retrieving available item: " + changeRecord);
+            }
+        } else {
+            changeRecord = new ChangeRecord();
+            if (DEBUG_UID_OBSERVERS) {
+                Slog.i(TAG_UID_OBSERVERS, "Allocating new item: " + changeRecord);
             }
         }
+        return changeRecord;
     }
 
     @VisibleForTesting
     void dispatchUidsChanged() {
-        int numUidChanges;
-        synchronized (mService) {
+        final int numUidChanges;
+        synchronized (mLock) {
             numUidChanges = mPendingUidChanges.size();
             if (mActiveUidChanges.length < numUidChanges) {
-                mActiveUidChanges = new UidRecord.ChangeItem[numUidChanges];
+                mActiveUidChanges = new ChangeRecord[numUidChanges];
             }
             for (int i = 0; i < numUidChanges; i++) {
-                final UidRecord.ChangeItem change = mPendingUidChanges.get(i);
-                mActiveUidChanges[i] = change;
-                if (change.uidRecord != null) {
-                    change.uidRecord.pendingChange = null;
-                    change.uidRecord = null;
-                }
+                final ChangeRecord changeRecord = mPendingUidChanges.get(i);
+                mActiveUidChanges[i] = getOrCreateChangeRecordLocked();
+                changeRecord.copyTo(mActiveUidChanges[i]);
+                changeRecord.isPending = false;
             }
             mPendingUidChanges.clear();
             if (DEBUG_UID_OBSERVERS) {
                 Slog.i(TAG_UID_OBSERVERS, "*** Delivering " + numUidChanges + " uid changes");
             }
+            mUidChangeDispatchCount += numUidChanges;
         }
 
-        mUidChangeDispatchCount += numUidChanges;
         int i = mUidObservers.beginBroadcast();
-        while (i > 0) {
-            i--;
+        while (i-- > 0) {
             dispatchUidsChangedForObserver(mUidObservers.getBroadcastItem(i),
                     (UidObserverRegistration) mUidObservers.getBroadcastCookie(i), numUidChanges);
         }
@@ -199,7 +198,7 @@
 
         if (VALIDATE_UID_STATES && mUidObservers.getRegisteredCallbackCount() > 0) {
             for (int j = 0; j < numUidChanges; ++j) {
-                final UidRecord.ChangeItem item = mActiveUidChanges[j];
+                final ChangeRecord item = mActiveUidChanges[j];
                 if ((item.change & UidRecord.CHANGE_GONE) != 0) {
                     mValidateUids.remove(item.uid);
                 } else {
@@ -213,28 +212,30 @@
                     } else if ((item.change & UidRecord.CHANGE_ACTIVE) != 0) {
                         validateUid.idle = false;
                     }
-                    validateUid.setCurProcState(validateUid.setProcState = item.processState);
+                    validateUid.setCurProcState(validateUid.setProcState = item.procState);
                     validateUid.curCapability = validateUid.setCapability = item.capability;
                     validateUid.lastDispatchedProcStateSeq = item.procStateSeq;
                 }
             }
         }
 
-        synchronized (mService) {
+        synchronized (mLock) {
             for (int j = 0; j < numUidChanges; j++) {
-                mAvailUidChanges.add(mActiveUidChanges[j]);
+                final ChangeRecord changeRecord = mActiveUidChanges[j];
+                changeRecord.isPending = false;
+                mAvailUidChanges.add(changeRecord);
             }
         }
     }
 
-    private void dispatchUidsChangedForObserver(IUidObserver observer,
-            UidObserverRegistration reg, int changesSize) {
+    private void dispatchUidsChangedForObserver(@NonNull IUidObserver observer,
+            @NonNull UidObserverRegistration reg, int changesSize) {
         if (observer == null) {
             return;
         }
         try {
             for (int j = 0; j < changesSize; j++) {
-                UidRecord.ChangeItem item = mActiveUidChanges[j];
+                final ChangeRecord item = mActiveUidChanges[j];
                 final int change = item.change;
                 if (change == UidRecord.CHANGE_PROCSTATE
                         && (reg.mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) == 0) {
@@ -285,7 +286,7 @@
                     if ((reg.mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) {
                         if (DEBUG_UID_OBSERVERS) {
                             Slog.i(TAG_UID_OBSERVERS, "UID CHANGED uid=" + item.uid
-                                    + ": " + item.processState + ": " + item.capability);
+                                    + ": " + item.procState + ": " + item.capability);
                         }
                         boolean doReport = true;
                         if (reg.mCutpoint >= ActivityManager.MIN_PROCESS_STATE) {
@@ -293,17 +294,17 @@
                                     ActivityManager.PROCESS_STATE_UNKNOWN);
                             if (lastState != ActivityManager.PROCESS_STATE_UNKNOWN) {
                                 final boolean lastAboveCut = lastState <= reg.mCutpoint;
-                                final boolean newAboveCut = item.processState <= reg.mCutpoint;
+                                final boolean newAboveCut = item.procState <= reg.mCutpoint;
                                 doReport = lastAboveCut != newAboveCut;
                             } else {
-                                doReport = item.processState != PROCESS_STATE_NONEXISTENT;
+                                doReport = item.procState != PROCESS_STATE_NONEXISTENT;
                             }
                         }
                         if (doReport) {
                             if (reg.mLastProcStates != null) {
-                                reg.mLastProcStates.put(item.uid, item.processState);
+                                reg.mLastProcStates.put(item.uid, item.procState);
                             }
-                            observer.onUidStateChanged(item.uid, item.processState,
+                            observer.onUidStateChanged(item.uid, item.procState,
                                     item.procStateSeq, item.capability);
                         }
                     }
@@ -320,94 +321,93 @@
         }
     }
 
-    private boolean isEphemeralLocked(int uid) {
-        final String[] packages = mService.mContext.getPackageManager().getPackagesForUid(uid);
-        if (packages == null || packages.length != 1) { // Ephemeral apps cannot share uid
-            return false;
-        }
-        return mService.getPackageManagerInternalLocked().isPackageEphemeral(
-                UserHandle.getUserId(uid), packages[0]);
+    UidRecord getValidateUidRecord(int uid) {
+        return mValidateUids.get(uid);
     }
 
-    @GuardedBy("mService")
-    void dump(PrintWriter pw, String dumpPackage) {
-        final int count = mUidObservers.getRegisteredCallbackCount();
-        boolean printed = false;
-        for (int i = 0; i < count; i++) {
-            final UidObserverRegistration reg = (UidObserverRegistration)
-                    mUidObservers.getRegisteredCallbackCookie(i);
-            if (dumpPackage == null || dumpPackage.equals(reg.mPkg)) {
-                if (!printed) {
-                    pw.println("  mUidObservers:");
-                    printed = true;
-                }
-                pw.print("    "); UserHandle.formatUid(pw, reg.mUid);
-                pw.print(" "); pw.print(reg.mPkg);
-                final IUidObserver observer = mUidObservers.getRegisteredCallbackItem(i);
-                pw.print(" "); pw.print(observer.getClass().getTypeName()); pw.print(":");
-                if ((reg.mWhich & ActivityManager.UID_OBSERVER_IDLE) != 0) {
-                    pw.print(" IDLE");
-                }
-                if ((reg.mWhich & ActivityManager.UID_OBSERVER_ACTIVE) != 0) {
-                    pw.print(" ACT");
-                }
-                if ((reg.mWhich & ActivityManager.UID_OBSERVER_GONE) != 0) {
-                    pw.print(" GONE");
-                }
-                if ((reg.mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) {
-                    pw.print(" STATE");
-                    pw.print(" (cut="); pw.print(reg.mCutpoint);
-                    pw.print(")");
-                }
-                pw.println();
-                if (reg.mLastProcStates != null) {
-                    final int size = reg.mLastProcStates.size();
-                    for (int j = 0; j < size; j++) {
-                        pw.print("      Last ");
-                        UserHandle.formatUid(pw, reg.mLastProcStates.keyAt(j));
-                        pw.print(": "); pw.println(reg.mLastProcStates.valueAt(j));
+    void dump(@NonNull PrintWriter pw, @Nullable String dumpPackage) {
+        synchronized (mLock) {
+            final int count = mUidObservers.getRegisteredCallbackCount();
+            boolean printed = false;
+            for (int i = 0; i < count; i++) {
+                final UidObserverRegistration reg = (UidObserverRegistration)
+                        mUidObservers.getRegisteredCallbackCookie(i);
+                if (dumpPackage == null || dumpPackage.equals(reg.mPkg)) {
+                    if (!printed) {
+                        pw.println("  mUidObservers:");
+                        printed = true;
                     }
+                    reg.dump(pw, mUidObservers.getRegisteredCallbackItem(i));
+                }
+            }
+
+            pw.println();
+            pw.print("  mUidChangeDispatchCount=");
+            pw.print(mUidChangeDispatchCount);
+            pw.println();
+            pw.println("  Slow UID dispatches:");
+            for (int i = 0; i < count; i++) {
+                final UidObserverRegistration reg = (UidObserverRegistration)
+                        mUidObservers.getRegisteredCallbackCookie(i);
+                pw.print("    ");
+                pw.print(mUidObservers.getRegisteredCallbackItem(i).getClass().getTypeName());
+                pw.print(": ");
+                pw.print(reg.mSlowDispatchCount);
+                pw.print(" / Max ");
+                pw.print(reg.mMaxDispatchTime);
+                pw.println("ms");
+            }
+        }
+    }
+
+    void dumpDebug(@NonNull ProtoOutputStream proto, @Nullable String dumpPackage) {
+        synchronized (mLock) {
+            final int count = mUidObservers.getRegisteredCallbackCount();
+            for (int i = 0; i < count; i++) {
+                final UidObserverRegistration reg = (UidObserverRegistration)
+                        mUidObservers.getRegisteredCallbackCookie(i);
+                if (dumpPackage == null || dumpPackage.equals(reg.mPkg)) {
+                    reg.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.UID_OBSERVERS);
                 }
             }
         }
-
-        pw.println();
-        pw.print("  mUidChangeDispatchCount=");
-        pw.print(mUidChangeDispatchCount);
-        pw.println();
-        pw.println("  Slow UID dispatches:");
-        final int size = mUidObservers.beginBroadcast();
-        for (int i = 0; i < size; i++) {
-            UidObserverRegistration r =
-                    (UidObserverRegistration) mUidObservers.getBroadcastCookie(i);
-            pw.print("    ");
-            pw.print(mUidObservers.getBroadcastItem(i).getClass().getTypeName());
-            pw.print(": ");
-            pw.print(r.mSlowDispatchCount);
-            pw.print(" / Max ");
-            pw.print(r.mMaxDispatchTime);
-            pw.println("ms");
-        }
-        mUidObservers.finishBroadcast();
     }
 
-    @GuardedBy("mService")
-    void dumpDebug(ProtoOutputStream proto, String dumpPackage) {
-        final int count = mUidObservers.getRegisteredCallbackCount();
-        for (int i = 0; i < count; i++) {
-            final UidObserverRegistration reg = (UidObserverRegistration)
-                    mUidObservers.getRegisteredCallbackCookie(i);
-            if (dumpPackage == null || dumpPackage.equals(reg.mPkg)) {
-                reg.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.UID_OBSERVERS);
-            }
+    boolean dumpValidateUids(@NonNull PrintWriter pw, @Nullable String dumpPackage, int dumpAppId,
+            @NonNull String header, boolean needSep) {
+        return mValidateUids.dump(pw, dumpPackage, dumpAppId, header, needSep);
+    }
+
+    void dumpValidateUidsProto(@NonNull ProtoOutputStream proto, @Nullable String dumpPackage,
+            int dumpAppId, long fieldId) {
+        mValidateUids.dumpProto(proto, dumpPackage, dumpAppId, fieldId);
+    }
+
+    static final class ChangeRecord {
+        public boolean isPending;
+        public int uid;
+        public int change;
+        public int procState;
+        public int capability;
+        public boolean ephemeral;
+        public long procStateSeq;
+
+        void copyTo(@NonNull ChangeRecord changeRecord) {
+            changeRecord.isPending = isPending;
+            changeRecord.uid = uid;
+            changeRecord.change = change;
+            changeRecord.procState = procState;
+            changeRecord.capability = capability;
+            changeRecord.ephemeral = ephemeral;
+            changeRecord.procStateSeq = procStateSeq;
         }
     }
 
     private static final class UidObserverRegistration {
-        final int mUid;
-        final String mPkg;
-        final int mWhich;
-        final int mCutpoint;
+        private final int mUid;
+        private final String mPkg;
+        private final int mWhich;
+        private final int mCutpoint;
 
         /**
          * Total # of callback calls that took more than {@link #SLOW_UID_OBSERVER_THRESHOLD_MS}.
@@ -434,19 +434,51 @@
                 ActivityManagerProto.UID_OBSERVER_FLAG_PROCSTATE,
         };
 
-        UidObserverRegistration(int uid, String pkg, int which, int cutpoint) {
+        UidObserverRegistration(int uid, @NonNull String pkg, int which, int cutpoint) {
             this.mUid = uid;
             this.mPkg = pkg;
             this.mWhich = which;
             this.mCutpoint = cutpoint;
-            if (cutpoint >= ActivityManager.MIN_PROCESS_STATE) {
-                mLastProcStates = new SparseIntArray();
-            } else {
-                mLastProcStates = null;
+            mLastProcStates = cutpoint >= ActivityManager.MIN_PROCESS_STATE
+                    ? new SparseIntArray() : null;
+        }
+
+        void dump(@NonNull PrintWriter pw, @NonNull IUidObserver observer) {
+            pw.print("    ");
+            UserHandle.formatUid(pw, mUid);
+            pw.print(" ");
+            pw.print(mPkg);
+            pw.print(" ");
+            pw.print(observer.getClass().getTypeName());
+            pw.print(":");
+            if ((mWhich & ActivityManager.UID_OBSERVER_IDLE) != 0) {
+                pw.print(" IDLE");
+            }
+            if ((mWhich & ActivityManager.UID_OBSERVER_ACTIVE) != 0) {
+                pw.print(" ACT");
+            }
+            if ((mWhich & ActivityManager.UID_OBSERVER_GONE) != 0) {
+                pw.print(" GONE");
+            }
+            if ((mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) {
+                pw.print(" STATE");
+                pw.print(" (cut=");
+                pw.print(mCutpoint);
+                pw.print(")");
+            }
+            pw.println();
+            if (mLastProcStates != null) {
+                final int size = mLastProcStates.size();
+                for (int j = 0; j < size; j++) {
+                    pw.print("      Last ");
+                    UserHandle.formatUid(pw, mLastProcStates.keyAt(j));
+                    pw.print(": ");
+                    pw.println(mLastProcStates.valueAt(j));
+                }
             }
         }
 
-        void dumpDebug(ProtoOutputStream proto, long fieldId) {
+        void dumpDebug(@NonNull ProtoOutputStream proto, long fieldId) {
             final long token = proto.start(fieldId);
             proto.write(UidObserverRegistrationProto.UID, mUid);
             proto.write(UidObserverRegistrationProto.PACKAGE, mPkg);
diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java
index c84ccb2..f1945ed 100644
--- a/services/core/java/com/android/server/am/UidRecord.java
+++ b/services/core/java/com/android/server/am/UidRecord.java
@@ -27,6 +27,7 @@
 import android.util.proto.ProtoUtils;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.server.am.UidObserverController.ChangeRecord;
 
 /**
  * Overall information about a uid that has actively running processes.
@@ -107,17 +108,9 @@
             UidRecordProto.CHANGE_UNCACHED,
     };
 
-    static final class ChangeItem {
-        UidRecord uidRecord;
-        int uid;
-        int change;
-        int processState;
-        int capability;
-        boolean ephemeral;
-        long procStateSeq;
-    }
+    // UidObserverController is the only thing that should modify this.
+    final ChangeRecord pendingChange = new ChangeRecord();
 
-    ChangeItem pendingChange;
     int lastReportedChange;
 
     public UidRecord(int _uid) {
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 6c08826..c29df6c 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -952,7 +952,7 @@
         mInjector.batteryStatsServiceNoteEvent(
                 BatteryStats.HistoryItem.EVENT_USER_RUNNING_FINISH,
                 Integer.toString(userId), userId);
-        mInjector.getSystemServiceManager().stopUser(userId);
+        mInjector.getSystemServiceManager().onUserStopping(userId);
 
         Runnable finishUserStoppedAsync = () ->
                 mHandler.post(() -> finishUserStopped(uss, allowDelayedLocking));
@@ -1028,7 +1028,7 @@
         }
 
         if (stopped) {
-            mInjector.systemServiceManagerCleanupUser(userId);
+            mInjector.systemServiceManagerOnUserStopped(userId);
             mInjector.stackSupervisorRemoveUser(userId);
 
             // Remove the user if it is ephemeral.
@@ -1307,7 +1307,7 @@
                 Slog.w(TAG, "No user info for user #" + userId);
                 return false;
             }
-            if (foreground && userInfo.isManagedProfile()) {
+            if (foreground && userInfo.isProfile()) {
                 Slog.w(TAG, "Cannot switch to User #" + userId + ": not a full user");
                 return false;
             }
@@ -1612,7 +1612,7 @@
             Slog.w(TAG, "Cannot switch to User #" + targetUserId + ": not supported");
             return false;
         }
-        if (targetUserInfo.isManagedProfile()) {
+        if (targetUserInfo.isProfile()) {
             Slog.w(TAG, "Cannot switch to User #" + targetUserId + ": not a full user");
             return false;
         }
@@ -1814,7 +1814,7 @@
     void sendUserSwitchBroadcasts(int oldUserId, int newUserId) {
         final int callingUid = Binder.getCallingUid();
         final int callingPid = Binder.getCallingPid();
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             Intent intent;
             if (oldUserId >= 0) {
@@ -2494,8 +2494,8 @@
                 logUserLifecycleEvent(msg.arg1, USER_LIFECYCLE_EVENT_START_USER,
                         USER_LIFECYCLE_EVENT_STATE_BEGIN);
 
-                mInjector.getSystemServiceManager().startUser(TimingsTraceAndSlog.newAsyncLog(),
-                        msg.arg1);
+                mInjector.getSystemServiceManager().onUserStarting(
+                        TimingsTraceAndSlog.newAsyncLog(), msg.arg1);
 
                 logUserLifecycleEvent(msg.arg1, USER_LIFECYCLE_EVENT_START_USER,
                         USER_LIFECYCLE_EVENT_STATE_FINISH);
@@ -2503,7 +2503,7 @@
                 break;
             case USER_UNLOCK_MSG:
                 final int userId = msg.arg1;
-                mInjector.getSystemServiceManager().unlockUser(userId);
+                mInjector.getSystemServiceManager().onUserUnlocking(userId);
                 // Loads recents on a worker thread that allows disk I/O
                 FgThread.getHandler().post(() -> {
                     mInjector.loadUserRecents(userId);
@@ -2528,7 +2528,7 @@
                         BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_START,
                         Integer.toString(msg.arg1), msg.arg1);
 
-                mInjector.getSystemServiceManager().switchUser(msg.arg2, msg.arg1);
+                mInjector.getSystemServiceManager().onUserSwitching(msg.arg2, msg.arg1);
                 break;
             case FOREGROUND_PROFILE_CHANGED_MSG:
                 dispatchForegroundProfileChanged(msg.arg1);
@@ -2781,8 +2781,8 @@
             LocalServices.getService(ActivityTaskManagerInternal.class).onUserStopped(userId);
         }
 
-        void systemServiceManagerCleanupUser(@UserIdInt int userId) {
-            mService.mSystemServiceManager.cleanupUser(userId);
+        void systemServiceManagerOnUserStopped(@UserIdInt int userId) {
+            mService.mSystemServiceManager.onUserStopped(userId);
         }
 
         protected UserManagerService getUserManager() {
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 7dbb39e..5379f32 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -29,6 +29,7 @@
 import static android.app.AppOpsManager.KEY_FG_SERVICE_STATE_SETTLE_TIME;
 import static android.app.AppOpsManager.KEY_TOP_STATE_SETTLE_TIME;
 import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_DEFAULT;
 import static android.app.AppOpsManager.MODE_FOREGROUND;
 import static android.app.AppOpsManager.MODE_IGNORED;
 import static android.app.AppOpsManager.NoteOpEvent;
@@ -132,6 +133,7 @@
 import android.util.KeyValueListParser;
 import android.util.LongSparseArray;
 import android.util.Pair;
+import android.util.Pools;
 import android.util.Pools.SimplePool;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -408,14 +410,26 @@
         }
 
         InProgressStartOpEvent acquire(long startTime, long elapsedTime, @NonNull IBinder clientId,
-                @NonNull Runnable onDeath, int uidState) throws RemoteException {
+                @NonNull Runnable onDeath, int proxyUid, @Nullable String proxyPackageName,
+                @Nullable String proxyAttributionTag, @AppOpsManager.UidState int uidState,
+                @OpFlags int flags) throws RemoteException {
+
             InProgressStartOpEvent recycled = acquire();
+
+            OpEventProxyInfo proxyInfo = null;
+            if (proxyUid != Process.INVALID_UID) {
+                proxyInfo = mOpEventProxyInfoPool.acquire(proxyUid, proxyPackageName,
+                        proxyAttributionTag);
+            }
+
             if (recycled != null) {
-                recycled.reinit(startTime, elapsedTime, clientId, onDeath, uidState);
+                recycled.reinit(startTime, elapsedTime, clientId, onDeath, uidState, flags,
+                        proxyInfo, mOpEventProxyInfoPool);
                 return recycled;
             }
 
-            return new InProgressStartOpEvent(startTime, elapsedTime, clientId, onDeath, uidState);
+            return new InProgressStartOpEvent(startTime, elapsedTime, clientId, onDeath, uidState,
+                    proxyInfo, flags);
         }
     }
 
@@ -670,6 +684,12 @@
         /** uidstate used when calling startOp */
         private @AppOpsManager.UidState int mUidState;
 
+        /** Proxy information of the startOp event */
+        private @Nullable OpEventProxyInfo mProxy;
+
+        /** Proxy flag information */
+        private @OpFlags int mFlags;
+
         /** How many times the op was started but not finished yet */
         int numUnfinishedStarts;
 
@@ -681,17 +701,22 @@
          * @param clientId The client id of the caller of {@link #startOperation}
          * @param onDeath The code to execute on client death
          * @param uidState The uidstate of the app {@link #startOperation} was called for
+         * @param proxy The proxy information, if {@link #startProxyOperation} was called
+         * @param flags The trusted/nontrusted/self flags.
          *
          * @throws RemoteException If the client is dying
          */
         private InProgressStartOpEvent(long startTime, long startElapsedTime,
-                @NonNull IBinder clientId, @NonNull Runnable onDeath, int uidState)
-                throws RemoteException {
+                @NonNull IBinder clientId, @NonNull Runnable onDeath,
+                @AppOpsManager.UidState int uidState, @Nullable OpEventProxyInfo proxy,
+                @OpFlags int flags) throws RemoteException {
             mStartTime = startTime;
             mStartElapsedTime = startElapsedTime;
             mClientId = clientId;
             mOnDeath = onDeath;
             mUidState = uidState;
+            mProxy = proxy;
+            mFlags = flags;
 
             clientId.linkToDeath(this, 0);
         }
@@ -714,16 +739,27 @@
          * @param clientId The client id of the caller of {@link #startOperation}
          * @param onDeath The code to execute on client death
          * @param uidState The uidstate of the app {@link #startOperation} was called for
+         * @param flags The flags relating to the proxy
+         * @param proxy The proxy information, if {@link #startProxyOperation} was called
+         * @param proxyPool The pool to release previous {@link OpEventProxyInfo} to
          *
          * @throws RemoteException If the client is dying
          */
         public void reinit(long startTime, long startElapsedTime, @NonNull IBinder clientId,
-                @NonNull Runnable onDeath, int uidState) throws RemoteException {
+                @NonNull Runnable onDeath, @AppOpsManager.UidState int uidState, @OpFlags int flags,
+                @Nullable OpEventProxyInfo proxy, @NonNull Pools.Pool<OpEventProxyInfo> proxyPool
+        ) throws RemoteException {
             mStartTime = startTime;
             mStartElapsedTime = startElapsedTime;
             mClientId = clientId;
             mOnDeath = onDeath;
             mUidState = uidState;
+            mFlags = flags;
+
+            if (mProxy != null) {
+                proxyPool.release(mProxy);
+            }
+            mProxy = proxy;
 
             clientId.linkToDeath(this, 0);
         }
@@ -744,9 +780,19 @@
         }
 
         /** @return uidstate used when calling startOp */
-        public int getUidState() {
+        public @AppOpsManager.UidState int getUidState() {
             return mUidState;
         }
+
+        /** @return proxy info for the access */
+        public @Nullable OpEventProxyInfo getProxy() {
+            return mProxy;
+        }
+
+        /** @return flags used for the access */
+        public @OpFlags int getFlags() {
+            return mFlags;
+        }
     }
 
     private final class AttributedOp {
@@ -876,14 +922,22 @@
          * Update state when start was called
          *
          * @param clientId Id of the startOp caller
+         * @param proxyUid The UID of the proxy app
+         * @param proxyPackageName The package name of the proxy app
+         * @param proxyAttributionTag The attribution tag of the proxy app
          * @param uidState UID state of the app startOp is called for
+         * @param flags The proxy flags
          */
-        public void started(@NonNull IBinder clientId, @AppOpsManager.UidState int uidState)
-                throws RemoteException {
-            started(clientId, uidState, true);
+        public void started(@NonNull IBinder clientId, int proxyUid,
+                @Nullable String proxyPackageName, @Nullable String proxyAttributionTag,
+                @AppOpsManager.UidState int uidState, @OpFlags int flags) throws RemoteException {
+            started(clientId, proxyUid, proxyPackageName, proxyAttributionTag, uidState, flags,
+                    true);
         }
 
-        private void started(@NonNull IBinder clientId, @AppOpsManager.UidState int uidState,
+        private void started(@NonNull IBinder clientId, int proxyUid,
+                @Nullable String proxyPackageName, @Nullable String proxyAttributionTag,
+                @AppOpsManager.UidState int uidState, @OpFlags int flags,
                 boolean triggerCallbackIfNeeded) throws RemoteException {
             if (triggerCallbackIfNeeded && !parent.isRunning()) {
                 scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
@@ -899,7 +953,7 @@
                 event = mInProgressStartOpEventPool.acquire(System.currentTimeMillis(),
                         SystemClock.elapsedRealtime(), clientId,
                         PooledLambda.obtainRunnable(AppOpsService::onClientDeath, this, clientId),
-                        uidState);
+                        proxyUid, proxyPackageName, proxyAttributionTag, uidState, flags);
                 mInProgressEvents.put(clientId, event);
             } else {
                 if (uidState != event.mUidState) {
@@ -909,9 +963,8 @@
 
             event.numUnfinishedStarts++;
 
-            // startOp events don't support proxy, hence use flags==SELF
             mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid, parent.packageName,
-                    tag, uidState, OP_FLAG_SELF);
+                    tag, uidState, flags);
         }
 
         /**
@@ -945,14 +998,17 @@
                     mAccessEvents = new LongSparseArray<>(1);
                 }
 
-                // startOp events don't support proxy, hence use flags==SELF
+                OpEventProxyInfo proxyCopy = event.getProxy() != null
+                        ? new OpEventProxyInfo(event.getProxy()) : null;
+
                 NoteOpEvent finishedEvent = new NoteOpEvent(event.getStartTime(),
-                        SystemClock.elapsedRealtime() - event.getStartElapsedTime(), null);
-                mAccessEvents.put(makeKey(event.getUidState(), OP_FLAG_SELF), finishedEvent);
+                        SystemClock.elapsedRealtime() - event.getStartElapsedTime(), proxyCopy);
+                mAccessEvents.put(makeKey(event.getUidState(), event.getFlags()),
+                        finishedEvent);
 
                 mHistoricalRegistry.increaseOpAccessDuration(parent.op, parent.uid,
                         parent.packageName, tag, event.getUidState(),
-                        AppOpsManager.OP_FLAG_SELF, finishedEvent.getDuration());
+                        event.getFlags(), finishedEvent.getDuration());
 
                 mInProgressStartOpEventPool.release(event);
 
@@ -1010,9 +1066,17 @@
                         event.numUnfinishedStarts = 1;
                         finished(event.getClientId(), false);
 
+                        OpEventProxyInfo proxy = event.getProxy();
+
                         // Call started() to add a new start event object and then add the
                         // previously removed unfinished start counts back
-                        started(event.getClientId(), newState, false);
+                        if (proxy != null) {
+                            started(event.getClientId(), proxy.getUid(), proxy.getPackageName(),
+                                    proxy.getAttributionTag(), newState, event.getFlags(), false);
+                        } else {
+                            started(event.getClientId(), Process.INVALID_UID, null, null, newState,
+                                    OP_FLAG_SELF, false);
+                        }
                         event.numUnfinishedStarts += numPreviousUnfinishedStarts - 1;
                     } catch (RemoteException e) {
                         if (DEBUG) Slog.e(TAG, "Cannot switch to new uidState " + newState);
@@ -1116,10 +1180,9 @@
                 for (int i = 0; i < numInProgressEvents; i++) {
                     InProgressStartOpEvent event = mInProgressEvents.valueAt(i);
 
-                    // startOp events don't support proxy
-                    accessEvents.append(makeKey(event.getUidState(), OP_FLAG_SELF),
+                    accessEvents.append(makeKey(event.getUidState(), event.getFlags()),
                             new NoteOpEvent(event.getStartTime(), now - event.getStartElapsedTime(),
-                                    null));
+                                    event.getProxy()));
                 }
             }
 
@@ -2182,14 +2245,14 @@
                 if (mode == defaultMode) {
                     return;
                 }
-                previousMode = AppOpsManager.MODE_DEFAULT;
+                previousMode = MODE_DEFAULT;
                 uidState = new UidState(uid);
                 uidState.opModes = new SparseIntArray();
                 uidState.opModes.put(code, mode);
                 mUidStates.put(uid, uidState);
                 scheduleWriteLocked();
             } else if (uidState.opModes == null) {
-                previousMode = AppOpsManager.MODE_DEFAULT;
+                previousMode = MODE_DEFAULT;
                 if (mode != defaultMode) {
                     uidState.opModes = new SparseIntArray();
                     uidState.opModes.put(code, mode);
@@ -2356,7 +2419,7 @@
                                 + permissionInfo.backgroundPermission);
                     }
 
-                    long identity = Binder.clearCallingIdentity();
+                    final long identity = Binder.clearCallingIdentity();
                     try {
                         packageManager.updatePermissionFlags(permissionInfo.backgroundPermission,
                                 packageName, PackageManager.FLAG_PERMISSION_REVOKED_COMPAT,
@@ -2380,7 +2443,7 @@
                         + switchCode + ", mode=" + mode + ", permission=" + permissionName);
             }
 
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 packageManager.updatePermissionFlags(permissionName, packageName,
                         PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, isRevokedCompat
@@ -2430,7 +2493,7 @@
             return;
         }
 
-        int previousMode = AppOpsManager.MODE_DEFAULT;
+        int previousMode = MODE_DEFAULT;
         synchronized (this) {
             UidState uidState = getUidStateLocked(uid, false);
             Op op = getOpLocked(code, uid, packageName, null, bypass, true);
@@ -3261,7 +3324,7 @@
 
         int callingUid = Binder.getCallingUid();
 
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
                 Pair<String, Integer> key = getAsyncNotedOpsKey(packageName, uid);
@@ -3409,7 +3472,75 @@
                 return result;
             }
         }
+        return startOperationUnchecked(clientId, code, uid, packageName, attributionTag,
+                Process.INVALID_UID, null, null, OP_FLAG_SELF, startIfModeDefault,
+                shouldCollectAsyncNotedOp, message, shouldCollectMessage, false);
 
+    }
+
+    @Override
+    public int startProxyOperation(IBinder clientId, int code, int proxiedUid,
+            String proxiedPackageName, @Nullable String proxiedAttributionTag, int proxyUid,
+            String proxyPackageName, @Nullable String proxyAttributionTag,
+            boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, String message,
+            boolean shouldCollectMessage) {
+        verifyIncomingUid(proxyUid);
+        verifyIncomingOp(code);
+        verifyIncomingPackage(proxyPackageName, UserHandle.getUserId(proxyUid));
+        verifyIncomingPackage(proxiedPackageName, UserHandle.getUserId(proxiedUid));
+
+        String resolvedProxyPackageName = resolvePackageName(proxyUid, proxyPackageName);
+        if (resolvedProxyPackageName == null) {
+            return AppOpsManager.MODE_IGNORED;
+        }
+
+        final boolean isProxyTrusted = mContext.checkPermission(
+                Manifest.permission.UPDATE_APP_OPS_STATS, -1, proxyUid)
+                == PackageManager.PERMISSION_GRANTED;
+
+        final int proxyFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXY
+                : AppOpsManager.OP_FLAG_UNTRUSTED_PROXY;
+
+        String resolvedProxiedPackageName = resolvePackageName(proxiedUid, proxiedPackageName);
+        if (resolvedProxiedPackageName == null) {
+            return AppOpsManager.MODE_IGNORED;
+        }
+        final int proxiedFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXIED
+                : AppOpsManager.OP_FLAG_UNTRUSTED_PROXIED;
+
+        // Test if the proxied operation will succeed before starting the proxy operation
+        final int testProxiedMode = startOperationUnchecked(clientId, code, proxiedUid,
+                resolvedProxiedPackageName, proxiedAttributionTag, proxyUid,
+                resolvedProxyPackageName, proxyAttributionTag, proxiedFlags, startIfModeDefault,
+                shouldCollectAsyncNotedOp, message, shouldCollectMessage, true);
+        if (!shouldStartForMode(testProxiedMode, startIfModeDefault)) {
+            return testProxiedMode;
+        }
+
+        final int proxyMode = startOperationUnchecked(clientId, code, proxyUid,
+                resolvedProxyPackageName, proxyAttributionTag, Process.INVALID_UID, null, null,
+                proxyFlags, startIfModeDefault, !isProxyTrusted, "proxy " + message,
+                shouldCollectMessage, false);
+        if (!shouldStartForMode(proxyMode, startIfModeDefault)
+                || Binder.getCallingUid() == proxiedUid) {
+            return proxyMode;
+        }
+
+        return startOperationUnchecked(clientId, code, proxiedUid, resolvedProxiedPackageName,
+                proxiedAttributionTag, proxyUid, resolvedProxyPackageName, proxyAttributionTag,
+                proxiedFlags, startIfModeDefault, shouldCollectAsyncNotedOp, message,
+                shouldCollectMessage, false);
+    }
+
+    private boolean shouldStartForMode(int mode, boolean startIfModeDefault) {
+        return (mode == MODE_ALLOWED || (mode == MODE_DEFAULT && startIfModeDefault));
+    }
+
+    private int startOperationUnchecked(IBinder clientId, int code, int uid,
+            @NonNull String packageName, @Nullable String attributionTag, int proxyUid,
+            String proxyPackageName, @Nullable String proxyAttributionTag, @OpFlags int flags,
+            boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, @Nullable String message,
+            boolean shouldCollectMessage, boolean dryRun) {
         RestrictionBypass bypass;
         try {
             bypass = verifyAndGetBypass(uid, packageName, attributionTag);
@@ -3419,19 +3550,25 @@
         }
 
         synchronized (this) {
-            final Ops ops = getOpsLocked(uid, resolvedPackageName, attributionTag, bypass,
-                    true /* edit */);
+            final Ops ops = getOpsLocked(uid, packageName, attributionTag, bypass, true /* edit */);
             if (ops == null) {
-                scheduleOpStartedIfNeededLocked(code, uid, packageName, AppOpsManager.MODE_IGNORED);
+                if (!dryRun) {
+                    scheduleOpStartedIfNeededLocked(code, uid, packageName,
+                            AppOpsManager.MODE_IGNORED);
+                }
                 if (DEBUG) Slog.d(TAG, "startOperation: no op for code " + code + " uid " + uid
-                        + " package " + resolvedPackageName);
+                        + " package " + packageName);
                 return AppOpsManager.MODE_ERRORED;
             }
             final Op op = getOpLocked(ops, code, uid, true);
-            if (isOpRestrictedLocked(uid, code, resolvedPackageName, bypass)) {
-                scheduleOpStartedIfNeededLocked(code, uid, packageName, AppOpsManager.MODE_IGNORED);
+            if (isOpRestrictedLocked(uid, code, packageName, bypass)) {
+                if (!dryRun) {
+                    scheduleOpStartedIfNeededLocked(code, uid, packageName,
+                            AppOpsManager.MODE_IGNORED);
+                }
                 return AppOpsManager.MODE_IGNORED;
             }
+
             final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag);
             final int switchCode = AppOpsManager.opToSwitch(code);
             final UidState uidState = ops.uidState;
@@ -3439,13 +3576,16 @@
             // non-default) it takes over, otherwise use the per package policy.
             if (uidState.opModes != null && uidState.opModes.indexOfKey(switchCode) >= 0) {
                 final int uidMode = uidState.evalMode(code, uidState.opModes.get(switchCode));
-                if (uidMode != AppOpsManager.MODE_ALLOWED
-                        && (!startIfModeDefault || uidMode != AppOpsManager.MODE_DEFAULT)) {
-                    if (DEBUG) Slog.d(TAG, "noteOperation: uid reject #" + uidMode + " for code "
-                            + switchCode + " (" + code + ") uid " + uid + " package "
-                            + resolvedPackageName);
-                    attributedOp.rejected(uidState.state, AppOpsManager.OP_FLAG_SELF);
-                    scheduleOpStartedIfNeededLocked(code, uid, packageName, uidMode);
+                if (!shouldStartForMode(uidMode, startIfModeDefault)) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "startOperation: uid reject #" + uidMode + " for code "
+                                + switchCode + " (" + code + ") uid " + uid + " package "
+                                + packageName);
+                    }
+                    if (!dryRun) {
+                        attributedOp.rejected(uidState.state, flags);
+                        scheduleOpStartedIfNeededLocked(code, uid, packageName, uidMode);
+                    }
                     return uidMode;
                 }
             } else {
@@ -3453,26 +3593,31 @@
                         : op;
                 final int mode = switchOp.evalMode();
                 if (mode != AppOpsManager.MODE_ALLOWED
-                        && (!startIfModeDefault || mode != AppOpsManager.MODE_DEFAULT)) {
+                        && (!startIfModeDefault || mode != MODE_DEFAULT)) {
                     if (DEBUG) Slog.d(TAG, "startOperation: reject #" + mode + " for code "
                             + switchCode + " (" + code + ") uid " + uid + " package "
-                            + resolvedPackageName);
-                    attributedOp.rejected(uidState.state, AppOpsManager.OP_FLAG_SELF);
-                    scheduleOpStartedIfNeededLocked(code, uid, packageName, mode);
+                            + packageName);
+                    if (!dryRun) {
+                        attributedOp.rejected(uidState.state, flags);
+                        scheduleOpStartedIfNeededLocked(code, uid, packageName, mode);
+                    }
                     return mode;
                 }
             }
             if (DEBUG) Slog.d(TAG, "startOperation: allowing code " + code + " uid " + uid
-                    + " package " + resolvedPackageName);
-            scheduleOpStartedIfNeededLocked(code, uid, packageName, AppOpsManager.MODE_ALLOWED);
-            try {
-                attributedOp.started(clientId, uidState.state);
-            } catch (RemoteException e) {
-                throw new RuntimeException(e);
+                    + " package " + packageName);
+            if (!dryRun) {
+                scheduleOpStartedIfNeededLocked(code, uid, packageName, AppOpsManager.MODE_ALLOWED);
+                try {
+                    attributedOp.started(clientId, proxyUid, proxyPackageName, proxyAttributionTag,
+                            uidState.state, flags);
+                } catch (RemoteException e) {
+                    throw new RuntimeException(e);
+                }
             }
         }
 
-        if (shouldCollectAsyncNotedOp) {
+        if (shouldCollectAsyncNotedOp && !dryRun) {
             collectAsyncNotedOp(uid, packageName, code, attributionTag, AppOpsManager.OP_FLAG_SELF,
                     message, shouldCollectMessage);
         }
@@ -3492,6 +3637,37 @@
             return;
         }
 
+        finishOperationUnchecked(clientId, code, uid, resolvedPackageName, attributionTag);
+    }
+
+    @Override
+    public void finishProxyOperation(IBinder clientId, int code, int proxiedUid,
+            String proxiedPackageName, @Nullable String proxiedAttributionTag, int proxyUid,
+            @Nullable String proxyPackageName, @Nullable String proxyAttributionTag) {
+        verifyIncomingUid(proxyUid);
+        verifyIncomingOp(code);
+        verifyIncomingPackage(proxyPackageName, UserHandle.getUserId(proxyUid));
+        verifyIncomingPackage(proxiedPackageName, UserHandle.getUserId(proxiedUid));
+
+        String resolvedProxyPackageName = resolvePackageName(proxyUid, proxyPackageName);
+        if (resolvedProxyPackageName == null) {
+            return;
+        }
+
+        finishOperationUnchecked(clientId, code, proxyUid, resolvedProxyPackageName,
+                proxyAttributionTag);
+
+        String resolvedProxiedPackageName = resolvePackageName(proxiedUid, proxiedPackageName);
+        if (resolvedProxiedPackageName == null) {
+            return;
+        }
+
+        finishOperationUnchecked(clientId, code, proxiedUid, resolvedProxiedPackageName,
+                proxiedAttributionTag);
+    }
+
+    private void finishOperationUnchecked(IBinder clientId, int code, int uid, String packageName,
+            String attributionTag) {
         RestrictionBypass bypass;
         try {
             bypass = verifyAndGetBypass(uid, packageName, attributionTag);
@@ -3501,7 +3677,7 @@
         }
 
         synchronized (this) {
-            Op op = getOpLocked(code, uid, resolvedPackageName, attributionTag, bypass, true);
+            Op op = getOpLocked(code, uid, packageName, attributionTag, bypass, true);
             if (op == null) {
                 Slog.e(TAG, "Operation not found: uid=" + uid + " pkg=" + packageName + "("
                         + attributionTag + ") op=" + AppOpsManager.opToName(code));
@@ -4937,7 +5113,7 @@
                 case "write-settings": {
                     shell.mInternal.enforceManageAppOpsModes(Binder.getCallingPid(),
                             Binder.getCallingUid(), -1);
-                    long token = Binder.clearCallingIdentity();
+                    final long token = Binder.clearCallingIdentity();
                     try {
                         synchronized (shell.mInternal) {
                             shell.mInternal.mHandler.removeCallbacks(shell.mInternal.mWriteRunner);
@@ -4952,7 +5128,7 @@
                 case "read-settings": {
                     shell.mInternal.enforceManageAppOpsModes(Binder.getCallingPid(),
                             Binder.getCallingUid(), -1);
-                    long token = Binder.clearCallingIdentity();
+                    final long token = Binder.clearCallingIdentity();
                     try {
                         shell.mInternal.readState();
                         pw.println("Last settings read.");
diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java
index e128d99..68cfc23 100644
--- a/services/core/java/com/android/server/attention/AttentionManagerService.java
+++ b/services/core/java/com/android/server/attention/AttentionManagerService.java
@@ -26,6 +26,7 @@
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityThread;
 import android.attention.AttentionManagerInternal;
 import android.attention.AttentionManagerInternal.AttentionCallbackInternal;
 import android.content.BroadcastReceiver;
@@ -68,6 +69,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * An attention service implementation that runs in System Server process.
@@ -82,11 +84,15 @@
     private static final long CONNECTION_TTL_MILLIS = 60_000;
 
     /** DeviceConfig flag name, if {@code true}, enables AttentionManagerService features. */
-    private static final String KEY_SERVICE_ENABLED = "service_enabled";
+    @VisibleForTesting
+    static final String KEY_SERVICE_ENABLED = "service_enabled";
 
     /** Default value in absence of {@link DeviceConfig} override. */
     private static final boolean DEFAULT_SERVICE_ENABLED = true;
 
+    @VisibleForTesting
+    boolean mIsServiceEnabled;
+
     /**
      * DeviceConfig flag name, describes how much time we consider a result fresh; if the check
      * attention called within that period - cached value will be returned.
@@ -98,6 +104,9 @@
     @VisibleForTesting
     static final long DEFAULT_STALE_AFTER_MILLIS = 1_000;
 
+    @VisibleForTesting
+    long mStaleAfterMillis;
+
     /** The size of the buffer that stores recent attention check results. */
     @VisibleForTesting
     protected static final int ATTENTION_CACHE_BUFFER_SIZE = 5;
@@ -144,6 +153,11 @@
         if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
             mContext.registerReceiver(new ScreenStateReceiver(),
                     new IntentFilter(Intent.ACTION_SCREEN_OFF));
+
+            readValuesFromDeviceConfig();
+            DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                    ActivityThread.currentApplication().getMainExecutor(),
+                    (properties) -> onDeviceConfigChange(properties.getKeyset()));
         }
     }
 
@@ -167,15 +181,7 @@
         return mComponentName != null;
     }
 
-    /**
-     * Returns {@code true} if attention service is supported on this device.
-     */
-    @VisibleForTesting
-    protected boolean isAttentionServiceSupported() {
-        return isServiceEnabled() && isServiceConfigured(mContext);
-    }
-
-    private boolean isServiceEnabled() {
+    private boolean getIsServiceEnabled() {
         return DeviceConfig.getBoolean(NAMESPACE_ATTENTION_MANAGER_SERVICE, KEY_SERVICE_ENABLED,
                 DEFAULT_SERVICE_ENABLED);
     }
@@ -198,6 +204,28 @@
         return millis;
     }
 
+    private void onDeviceConfigChange(@NonNull Set<String> keys) {
+        for (String key : keys) {
+            switch (key) {
+                case KEY_SERVICE_ENABLED:
+                case KEY_STALE_AFTER_MILLIS:
+                    readValuesFromDeviceConfig();
+                    return;
+                default:
+                    Slog.i(LOG_TAG, "Ignoring change on " + key);
+            }
+        }
+    }
+
+    private void readValuesFromDeviceConfig() {
+        mIsServiceEnabled = getIsServiceEnabled();
+        mStaleAfterMillis = getStaleAfterMillis();
+
+        Slog.i(LOG_TAG, "readValuesFromDeviceConfig():"
+                + "\nmIsServiceEnabled=" + mIsServiceEnabled
+                + "\nmStaleAfterMillis=" + mStaleAfterMillis);
+    }
+
     /**
      * Checks whether user attention is at the screen and calls in the provided callback.
      *
@@ -211,7 +239,7 @@
     boolean checkAttention(long timeout, AttentionCallbackInternal callbackInternal) {
         Objects.requireNonNull(callbackInternal);
 
-        if (!isAttentionServiceSupported()) {
+        if (!mIsServiceEnabled) {
             Slog.w(LOG_TAG, "Trying to call checkAttention() on an unsupported device.");
             return false;
         }
@@ -237,7 +265,7 @@
             // throttle frequent requests
             final AttentionCheckCache cache = mAttentionCheckCacheBuffer == null ? null
                     : mAttentionCheckCacheBuffer.getLast();
-            if (cache != null && now < cache.mLastComputed + getStaleAfterMillis()) {
+            if (cache != null && now < cache.mLastComputed + mStaleAfterMillis) {
                 callbackInternal.onSuccess(cache.mResult, cache.mTimestamp);
                 return true;
             }
@@ -344,7 +372,8 @@
 
     private void dumpInternal(IndentingPrintWriter ipw) {
         ipw.println("Attention Manager Service (dumpsys attention) state:\n");
-        ipw.println("isServiceEnabled=" + isServiceEnabled());
+        ipw.println("isServiceEnabled=" + mIsServiceEnabled);
+        ipw.println("mStaleAfterMillis=" + mStaleAfterMillis);
         ipw.println("AttentionServicePackageName=" + getServiceConfigPackage(mContext));
         ipw.println("Resolved component:");
         if (mComponentName != null) {
@@ -368,7 +397,7 @@
     private final class LocalService extends AttentionManagerInternal {
         @Override
         public boolean isAttentionServiceSupported() {
-            return AttentionManagerService.this.isAttentionServiceSupported();
+            return AttentionManagerService.this.mIsServiceEnabled;
         }
 
         @Override
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 1615998..3b407f1 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -25,6 +25,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.media.AudioDeviceAttributes;
+import android.media.AudioDeviceInfo;
+import android.media.AudioManager;
 import android.media.AudioRoutesInfo;
 import android.media.AudioSystem;
 import android.media.IAudioRoutesObserver;
@@ -39,6 +41,7 @@
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.PrintWriterPrinter;
@@ -46,8 +49,8 @@
 import com.android.internal.annotations.GuardedBy;
 
 import java.io.PrintWriter;
-import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.NoSuchElementException;
 import java.util.Set;
@@ -73,10 +76,6 @@
 
     /** Forced device usage for communications sent to AudioSystem */
     private int mForcedUseForComm;
-    /**
-     * Externally reported force device usage state returned by getters: always consistent
-     * with requests by setters */
-    private int mForcedUseForCommExt;
 
     // Manages all connected devices, only ever accessed on the message loop
     private final AudioDeviceInventory mDeviceInventory;
@@ -136,7 +135,6 @@
         setupMessaging(mContext);
 
         mForcedUseForComm = AudioSystem.FORCE_NONE;
-        mForcedUseForCommExt = mForcedUseForComm;
     }
 
     /*package*/ Context getContext() {
@@ -159,15 +157,6 @@
     }
 
     /*package*/ void onAudioServerDied() {
-        // Restore forced usage for communications and record
-        synchronized (mDeviceStateLock) {
-            AudioSystem.setParameters(
-                    "BT_SCO=" + (mForcedUseForComm == AudioSystem.FORCE_BT_SCO ? "on" : "off"));
-            onSetForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm,
-                          false /*fromA2dp*/, "onAudioServerDied");
-            onSetForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm,
-                          false /*fromA2dp*/, "onAudioServerDied");
-        }
         // restore devices
         sendMsgNoDelay(MSG_RESTORE_DEVICES, SENDMSG_REPLACE);
     }
@@ -219,85 +208,156 @@
      * Turns speakerphone on/off
      * @param on
      * @param eventSource for logging purposes
-     * @return true if speakerphone state changed
      */
-    /*package*/ boolean setSpeakerphoneOn(IBinder cb, int pid, boolean on, String eventSource) {
-        synchronized (mDeviceStateLock) {
-            if (!addSpeakerphoneClient(cb, pid, on)) {
-                return false;
+    /*package*/ void setSpeakerphoneOn(IBinder cb, int pid, boolean on, String eventSource) {
+
+        if (AudioService.DEBUG_COMM_RTE) {
+            Log.v(TAG, "setSpeakerphoneOn, on: " + on + " pid: " + pid);
+        }
+
+        synchronized (mSetModeLock) {
+            synchronized (mDeviceStateLock) {
+                AudioDeviceAttributes device = null;
+                if (on) {
+                    device = new AudioDeviceAttributes(AudioDeviceAttributes.ROLE_OUTPUT,
+                            AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, "");
+                } else {
+                    CommunicationRouteClient client = getCommunicationRouteClientForPid(pid);
+                    if (client == null || !client.requestsSpeakerphone()) {
+                        return;
+                    }
+                }
+                setCommunicationRouteForClient(
+                        cb, pid, device, BtHelper.SCO_MODE_UNDEFINED, eventSource);
             }
-            if (on) {
-                // Cancel BT SCO ON request by this same client: speakerphone and BT SCO routes
-                // are mutually exclusive.
-                // See symmetrical operation for startBluetoothScoForClient_Sync().
-                mBtHelper.stopBluetoothScoForPid(pid);
-            }
-            final boolean wasOn = isSpeakerphoneOn();
-            updateSpeakerphoneOn(eventSource);
-            return (wasOn != isSpeakerphoneOn());
         }
     }
 
-    /**
-     * Turns speakerphone off for a given pid and update speakerphone state.
-     * @param pid
-     */
     @GuardedBy("mDeviceStateLock")
-    private void setSpeakerphoneOffForPid(int pid) {
-        SpeakerphoneClient client = getSpeakerphoneClientForPid(pid);
+    /*package*/ void setCommunicationRouteForClient(
+                            IBinder cb, int pid, AudioDeviceAttributes device,
+                            int scoAudioMode, String eventSource) {
+
+        if (AudioService.DEBUG_COMM_RTE) {
+            Log.v(TAG, "setCommunicationRouteForClient: device: " + device);
+        }
+
+        final boolean wasBtScoRequested = isBluetoothScoRequested();
+        final boolean wasSpeakerphoneRequested = isSpeakerphoneRequested();
+        CommunicationRouteClient client;
+
+
+        // Save previous client route in case of failure to start BT SCO audio
+        AudioDeviceAttributes prevClientDevice = null;
+        client = getCommunicationRouteClientForPid(pid);
+        if (client != null) {
+            prevClientDevice = client.getDevice();
+        }
+
+        if (device != null) {
+            client = addCommunicationRouteClient(cb, pid, device);
+            if (client == null) {
+                Log.w(TAG, "setCommunicationRouteForClient: could not add client for pid: "
+                        + pid + " and device: " + device);
+            }
+        } else {
+            client = removeCommunicationRouteClient(cb, true);
+        }
         if (client == null) {
             return;
         }
-        client.unregisterDeathRecipient();
-        mSpeakerphoneClients.remove(client);
-        final String eventSource = new StringBuilder("setSpeakerphoneOffForPid(")
-                .append(pid).append(")").toString();
-        updateSpeakerphoneOn(eventSource);
-    }
 
-    @GuardedBy("mDeviceStateLock")
-    private void updateSpeakerphoneOn(String eventSource) {
-        if (isSpeakerphoneOnRequested()) {
-            if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
-                setForceUse_Async(AudioSystem.FOR_RECORD, AudioSystem.FORCE_NONE, eventSource);
+        boolean isBtScoRequested = isBluetoothScoRequested();
+        if (isBtScoRequested && !wasBtScoRequested) {
+            if (!mBtHelper.startBluetoothSco(scoAudioMode, eventSource)) {
+                Log.w(TAG, "setCommunicationRouteForClient: failure to start BT SCO for pid: "
+                        + pid);
+                // clean up or restore previous client selection
+                if (prevClientDevice != null) {
+                    addCommunicationRouteClient(cb, pid, prevClientDevice);
+                } else {
+                    removeCommunicationRouteClient(cb, true);
+                }
             }
-            mForcedUseForComm = AudioSystem.FORCE_SPEAKER;
-        } else if (mForcedUseForComm == AudioSystem.FORCE_SPEAKER) {
-            if (mBtHelper.isBluetoothScoOn()) {
-                mForcedUseForComm = AudioSystem.FORCE_BT_SCO;
-                setForceUse_Async(
-                        AudioSystem.FOR_RECORD, AudioSystem.FORCE_BT_SCO, eventSource);
-            } else {
-                mForcedUseForComm = AudioSystem.FORCE_NONE;
+        } else if (!isBtScoRequested && wasBtScoRequested) {
+            mBtHelper.stopBluetoothSco(eventSource);
+        }
+
+        if (wasSpeakerphoneRequested != isSpeakerphoneRequested()) {
+            try {
+                mContext.sendBroadcastAsUser(
+                        new Intent(AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED)
+                                .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL);
+            } catch (Exception e) {
+                Log.w(TAG, "failed to broadcast ACTION_SPEAKERPHONE_STATE_CHANGED: " + e);
             }
         }
-        mForcedUseForCommExt = mForcedUseForComm;
-        setForceUse_Async(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource);
+
+        sendLMsgNoDelay(MSG_L_UPDATE_COMMUNICATION_ROUTE, SENDMSG_QUEUE, eventSource);
     }
 
     /**
-     * Returns if speakerphone is requested ON or OFF.
-     * If the current audio mode owner is in the speakerphone client list, use this preference.
+     * Returns the device currently requested for communication use case.
+     * If the current audio mode owner is in the communication route client list,
+     * use this preference.
      * Otherwise use first client's preference (first client corresponds to latest request).
-     * Speakerphone is requested OFF if no client is in the list.
-     * @return true if speakerphone is requested ON, false otherwise
+     * null is returned if no client is in the list.
+     * @return AudioDeviceAttributes the requested device for communication.
      */
+
     @GuardedBy("mDeviceStateLock")
-    private boolean isSpeakerphoneOnRequested() {
-        if (mSpeakerphoneClients.isEmpty()) {
-            return false;
-        }
-        for (SpeakerphoneClient cl : mSpeakerphoneClients) {
+    private AudioDeviceAttributes requestedCommunicationDevice() {
+        AudioDeviceAttributes device = null;
+        for (CommunicationRouteClient cl : mCommunicationRouteClients) {
             if (cl.getPid() == mModeOwnerPid) {
-                return cl.isOn();
+                device = cl.getDevice();
             }
         }
-        return mSpeakerphoneClients.get(0).isOn();
+        if (!mCommunicationRouteClients.isEmpty() && mModeOwnerPid == 0) {
+            device = mCommunicationRouteClients.get(0).getDevice();
+        }
+
+        if (AudioService.DEBUG_COMM_RTE) {
+            Log.v(TAG, "requestedCommunicationDevice, device: "
+                    + device + " mode owner pid: " + mModeOwnerPid);
+        }
+        return device;
     }
 
-    /*package*/ boolean isSpeakerphoneOn() {
+    /**
+     * Helper method on top of requestedCommunicationDevice() indicating if
+     * speakerphone ON is currently requested or not.
+     * @return true if speakerphone ON requested, false otherwise.
+     */
+
+    private boolean isSpeakerphoneRequested() {
         synchronized (mDeviceStateLock) {
-            return (mForcedUseForCommExt == AudioSystem.FORCE_SPEAKER);
+            AudioDeviceAttributes device = requestedCommunicationDevice();
+            return device != null
+                    && device.getType()
+                        == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
+        }
+    }
+
+    /**
+     * Indicates if active route selection for communication is speakerphone.
+     * @return true if speakerphone is active, false otherwise.
+     */
+    /*package*/ boolean isSpeakerphoneOn() {
+        return getForcedUseForComm() == AudioSystem.FORCE_SPEAKER;
+    }
+
+    /**
+     * Helper method on top of requestedCommunicationDevice() indicating if
+     * Bluetooth SCO ON is currently requested or not.
+     * @return true if Bluetooth SCO ON is requested, false otherwise.
+     */
+    /*package*/ boolean isBluetoothScoRequested() {
+        synchronized (mDeviceStateLock) {
+            AudioDeviceAttributes device = requestedCommunicationDevice();
+            return device != null
+                    && device.getType()
+                        == AudioDeviceInfo.TYPE_BLUETOOTH_SCO;
         }
     }
 
@@ -348,7 +408,6 @@
         }
     }
 
-
     /*package*/ void postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
             @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
             int profile, boolean suppressNoisyIntent, int a2dpVolume) {
@@ -431,42 +490,28 @@
         sendLMsgNoDelay(MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info);
     }
 
-    // never called by system components
-    /*package*/ void setBluetoothScoOnByApp(boolean on) {
-        synchronized (mDeviceStateLock) {
-            mForcedUseForCommExt = on ? AudioSystem.FORCE_BT_SCO : AudioSystem.FORCE_NONE;
-        }
-    }
 
-    /*package*/ boolean isBluetoothScoOnForApp() {
-        synchronized (mDeviceStateLock) {
-            return mForcedUseForCommExt == AudioSystem.FORCE_BT_SCO;
-        }
-    }
+    /**
+     * Current Bluetooth SCO audio active state indicated by BtHelper via setBluetoothScoOn().
+     */
+    private boolean mBluetoothScoOn;
 
     /*package*/ void setBluetoothScoOn(boolean on, String eventSource) {
-        //Log.i(TAG, "setBluetoothScoOn: " + on + " " + eventSource);
-        synchronized (mDeviceStateLock) {
-            if (on) {
-                // do not accept SCO ON if SCO audio is not connected
-                if (!mBtHelper.isBluetoothScoOn()) {
-                    mForcedUseForCommExt = AudioSystem.FORCE_BT_SCO;
-                    return;
-                }
-                mForcedUseForComm = AudioSystem.FORCE_BT_SCO;
-            } else if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
-                mForcedUseForComm = isSpeakerphoneOnRequested()
-                        ? AudioSystem.FORCE_SPEAKER : AudioSystem.FORCE_NONE;
-            }
-            mForcedUseForCommExt = mForcedUseForComm;
-            AudioSystem.setParameters("BT_SCO=" + (on ? "on" : "off"));
-            sendIILMsgNoDelay(MSG_IIL_SET_FORCE_USE, SENDMSG_QUEUE,
-                    AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource);
-            sendIILMsgNoDelay(MSG_IIL_SET_FORCE_USE, SENDMSG_QUEUE,
-                    AudioSystem.FOR_RECORD, mForcedUseForComm, eventSource);
+        if (AudioService.DEBUG_COMM_RTE) {
+            Log.v(TAG, "setBluetoothScoOn: " + on + " " + eventSource);
         }
-        // Un-mute ringtone stream volume
-        mAudioService.postUpdateRingerModeServiceInt();
+        synchronized (mDeviceStateLock) {
+            mBluetoothScoOn = on;
+            sendLMsgNoDelay(MSG_L_UPDATE_COMMUNICATION_ROUTE, SENDMSG_QUEUE, eventSource);
+        }
+    }
+
+    /**
+     * Indicates if active route selection for communication is Bluetooth SCO.
+     * @return true if Bluetooth SCO is active , false otherwise.
+     */
+    /*package*/ boolean isBluetoothScoOn() {
+        return getForcedUseForComm() == AudioSystem.FORCE_BT_SCO;
     }
 
     /*package*/ AudioRoutesInfo startWatchingRoutes(IAudioRoutesObserver observer) {
@@ -509,22 +554,43 @@
         sendLMsgNoDelay(MSG_L_A2DP_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, device);
     }
 
-    @GuardedBy("mSetModeLock")
-    /*package*/ void startBluetoothScoForClient_Sync(IBinder cb, int scoAudioMode,
+    /*package*/ void startBluetoothScoForClient(IBinder cb, int pid, int scoAudioMode,
                 @NonNull String eventSource) {
-        synchronized (mDeviceStateLock) {
-            // Cancel speakerphone ON request by this same client: speakerphone and BT SCO routes
-            // are mutually exclusive.
-            // See symmetrical operation for setSpeakerphoneOn(true).
-            setSpeakerphoneOffForPid(Binder.getCallingPid());
-            mBtHelper.startBluetoothScoForClient(cb, scoAudioMode, eventSource);
+
+        if (AudioService.DEBUG_COMM_RTE) {
+            Log.v(TAG, "startBluetoothScoForClient_Sync, pid: " + pid);
+        }
+
+        synchronized (mSetModeLock) {
+            synchronized (mDeviceStateLock) {
+                AudioDeviceAttributes device = new AudioDeviceAttributes(
+                        AudioDeviceAttributes.ROLE_OUTPUT, AudioDeviceInfo.TYPE_BLUETOOTH_SCO, "");
+                setCommunicationRouteForClient(cb, pid, device, scoAudioMode, eventSource);
+                if (!isBluetoothScoRequested()) {
+                    Log.w(TAG, "startBluetoothScoForClient_Sync: rejected for pid: "
+                            + pid + " mode owner pid: " + mModeOwnerPid);
+                    postBroadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+                }
+            }
         }
     }
 
-    @GuardedBy("mSetModeLock")
-    /*package*/ void stopBluetoothScoForClient_Sync(IBinder cb, @NonNull String eventSource) {
-        synchronized (mDeviceStateLock) {
-            mBtHelper.stopBluetoothScoForClient(cb, eventSource);
+    /*package*/ void stopBluetoothScoForClient(
+                        IBinder cb, int pid, @NonNull String eventSource) {
+
+        if (AudioService.DEBUG_COMM_RTE) {
+            Log.v(TAG, "stopBluetoothScoForClient_Sync, pid: " + pid);
+        }
+
+        synchronized (mSetModeLock) {
+            synchronized (mDeviceStateLock) {
+                CommunicationRouteClient client = getCommunicationRouteClientForPid(pid);
+                if (client == null || !client.requestsBluetoothSco()) {
+                    return;
+                }
+                setCommunicationRouteForClient(
+                        cb, pid, null, BtHelper.SCO_MODE_UNDEFINED, eventSource);
+            }
         }
     }
 
@@ -696,12 +762,8 @@
                 hearingAidProfile);
     }
 
-    /*package*/ void postScoClientDied(Object obj) {
-        sendLMsgNoDelay(MSG_L_SCOCLIENT_DIED, SENDMSG_QUEUE, obj);
-    }
-
-    /*package*/ void postSpeakerphoneClientDied(Object obj) {
-        sendLMsgNoDelay(MSG_L_SPEAKERPHONE_CLIENT_DIED, SENDMSG_QUEUE, obj);
+    /*package*/ void postCommunicationRouteClientDied(CommunicationRouteClient client) {
+        sendLMsgNoDelay(MSG_L_COMMUNICATION_ROUTE_CLIENT_DIED, SENDMSG_QUEUE, client);
     }
 
     /*package*/ void postSaveSetPreferredDevicesForStrategy(int strategy,
@@ -821,15 +883,14 @@
 
         mDeviceInventory.dump(pw, prefix);
 
+        pw.println("\n" + prefix + "Communication route clients:");
+        mCommunicationRouteClients.forEach((cl) -> {
+            pw.println("  " + prefix + "pid: " + cl.getPid() + " device: "
+                        + cl.getDevice() + " cb: " + cl.getBinder()); });
+
         pw.println("\n" + prefix + "mForcedUseForComm: "
                 +  AudioSystem.forceUseConfigToString(mForcedUseForComm));
-        pw.println(prefix + "mForcedUseForCommExt: "
-                + AudioSystem.forceUseConfigToString(mForcedUseForCommExt));
         pw.println(prefix + "mModeOwnerPid: " + mModeOwnerPid);
-        pw.println(prefix + "Speakerphone clients:");
-        mSpeakerphoneClients.forEach((cl) -> {
-            pw.println("  " + prefix + "pid: " + cl.getPid() + " on: "
-                        + cl.isOn() + " cb: " + cl.getBinder()); });
 
         mBtHelper.dump(pw, prefix);
     }
@@ -852,6 +913,11 @@
                 .set(MediaMetrics.Property.FORCE_USE_MODE,
                         AudioSystem.forceUseConfigToString(config))
                 .record();
+
+        if (AudioService.DEBUG_COMM_RTE) {
+            Log.v(TAG, "onSetForceUse(useCase<" + useCase + ">, config<" + config + ">, fromA2dp<"
+                    + fromA2dp + ">, eventSource<" + eventSource + ">)");
+        }
         AudioSystem.setForceUse(useCase, config);
     }
 
@@ -917,9 +983,12 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case MSG_RESTORE_DEVICES:
-                    synchronized (mDeviceStateLock) {
-                        mDeviceInventory.onRestoreDevices();
-                        mBtHelper.onAudioServerDiedRestoreA2dp();
+                    synchronized (mSetModeLock) {
+                        synchronized (mDeviceStateLock) {
+                            mDeviceInventory.onRestoreDevices();
+                            mBtHelper.onAudioServerDiedRestoreA2dp();
+                            onUpdateCommunicationRoute("MSG_RESTORE_DEVICES");
+                        }
                     }
                     break;
                 case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
@@ -1009,25 +1078,24 @@
                             if (mModeOwnerPid != msg.arg1) {
                                 mModeOwnerPid = msg.arg1;
                                 if (msg.arg2 != AudioSystem.MODE_RINGTONE) {
-                                    updateSpeakerphoneOn("setNewModeOwner");
-                                }
-                                if (mModeOwnerPid != 0) {
-                                    mBtHelper.disconnectBluetoothSco(mModeOwnerPid);
+                                    onUpdateCommunicationRoute("setNewModeOwner");
                                 }
                             }
                         }
                     }
                     break;
-                case MSG_L_SCOCLIENT_DIED:
+                case MSG_L_COMMUNICATION_ROUTE_CLIENT_DIED:
                     synchronized (mSetModeLock) {
                         synchronized (mDeviceStateLock) {
-                            mBtHelper.scoClientDied(msg.obj);
+                            onCommunicationRouteClientDied((CommunicationRouteClient) msg.obj);
                         }
                     }
                     break;
-                case MSG_L_SPEAKERPHONE_CLIENT_DIED:
-                    synchronized (mDeviceStateLock) {
-                        speakerphoneClientDied(msg.obj);
+                case MSG_L_UPDATE_COMMUNICATION_ROUTE:
+                    synchronized (mSetModeLock) {
+                        synchronized (mDeviceStateLock) {
+                            onUpdateCommunicationRoute((String) msg.obj);
+                        }
                     }
                     break;
                 case MSG_TOGGLE_HDMI:
@@ -1207,17 +1275,17 @@
     // process external command to (dis)connect a hearing aid device
     private static final int MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT = 31;
 
-    // a ScoClient died in BtHelper
-    private static final int MSG_L_SCOCLIENT_DIED = 32;
-    private static final int MSG_IL_SAVE_PREF_DEVICES_FOR_STRATEGY = 33;
-    private static final int MSG_I_SAVE_REMOVE_PREF_DEVICES_FOR_STRATEGY = 34;
+    private static final int MSG_IL_SAVE_PREF_DEVICES_FOR_STRATEGY = 32;
+    private static final int MSG_I_SAVE_REMOVE_PREF_DEVICES_FOR_STRATEGY = 33;
 
-    private static final int MSG_L_SPEAKERPHONE_CLIENT_DIED = 35;
-    private static final int MSG_CHECK_MUTE_MUSIC = 36;
-    private static final int MSG_REPORT_NEW_ROUTES_A2DP = 37;
+    private static final int MSG_L_COMMUNICATION_ROUTE_CLIENT_DIED = 34;
+    private static final int MSG_CHECK_MUTE_MUSIC = 35;
+    private static final int MSG_REPORT_NEW_ROUTES_A2DP = 36;
 
-    private static final int MSG_IL_SAVE_PREF_DEVICES_FOR_CAPTURE_PRESET = 38;
-    private static final int MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET = 39;
+    private static final int MSG_IL_SAVE_PREF_DEVICES_FOR_CAPTURE_PRESET = 37;
+    private static final int MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET = 38;
+
+    private static final int MSG_L_UPDATE_COMMUNICATION_ROUTE = 39;
 
 
     private static boolean isMessageHandledUnderWakelock(int msgId) {
@@ -1372,14 +1440,20 @@
         }
     }
 
-    private class SpeakerphoneClient implements IBinder.DeathRecipient {
+    // List of applications requesting a specific route for communication.
+    @GuardedBy("mDeviceStateLock")
+    private final @NonNull LinkedList<CommunicationRouteClient> mCommunicationRouteClients =
+            new LinkedList<CommunicationRouteClient>();
+
+    private class CommunicationRouteClient implements IBinder.DeathRecipient {
         private final IBinder mCb;
         private final int mPid;
-        private final boolean mOn;
-        SpeakerphoneClient(IBinder cb, int pid, boolean on) {
+        private AudioDeviceAttributes mDevice;
+
+        CommunicationRouteClient(IBinder cb, int pid, AudioDeviceAttributes device) {
             mCb = cb;
             mPid = pid;
-            mOn = on;
+            mDevice = device;
         }
 
         public boolean registerDeathRecipient() {
@@ -1388,7 +1462,7 @@
                 mCb.linkToDeath(this, 0);
                 status = true;
             } catch (RemoteException e) {
-                Log.w(TAG, "SpeakerphoneClient could not link to " + mCb + " binder death");
+                Log.w(TAG, "CommunicationRouteClient could not link to " + mCb + " binder death");
             }
             return status;
         }
@@ -1397,13 +1471,13 @@
             try {
                 mCb.unlinkToDeath(this, 0);
             } catch (NoSuchElementException e) {
-                Log.w(TAG, "SpeakerphoneClient could not not unregistered to binder");
+                Log.w(TAG, "CommunicationRouteClient could not not unregistered to binder");
             }
         }
 
         @Override
         public void binderDied() {
-            postSpeakerphoneClientDied(this);
+            postCommunicationRouteClientDied(this);
         }
 
         IBinder getBinder() {
@@ -1414,29 +1488,99 @@
             return mPid;
         }
 
-        boolean isOn() {
-            return mOn;
+        AudioDeviceAttributes getDevice() {
+            return mDevice;
+        }
+
+        boolean requestsBluetoothSco() {
+            return mDevice != null
+                    && mDevice.getType()
+                        == AudioDeviceInfo.TYPE_BLUETOOTH_SCO;
+        }
+
+        boolean requestsSpeakerphone() {
+            return mDevice != null
+                    && mDevice.getType()
+                        == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
         }
     }
 
+    // @GuardedBy("mSetModeLock")
     @GuardedBy("mDeviceStateLock")
-    private void speakerphoneClientDied(Object obj) {
-        if (obj == null) {
+    private void onCommunicationRouteClientDied(CommunicationRouteClient client) {
+        if (client == null) {
             return;
         }
         Log.w(TAG, "Speaker client died");
-        if (removeSpeakerphoneClient(((SpeakerphoneClient) obj).getBinder(), false) != null) {
-            updateSpeakerphoneOn("speakerphoneClientDied");
+        if (removeCommunicationRouteClient(client.getBinder(), false)
+                != null) {
+            onUpdateCommunicationRoute("onCommunicationRouteClientDied");
         }
     }
 
-    private SpeakerphoneClient removeSpeakerphoneClient(IBinder cb, boolean unregister) {
-        for (SpeakerphoneClient cl : mSpeakerphoneClients) {
+    /**
+     * Determines which forced usage for communication should be sent to audio policy manager
+     * as a function of current SCO audio activation state and active communication route requests.
+     * SCO audio state has the highest priority as it can result from external activation by
+     * telephony service.
+     * @return selected forced usage for communication.
+     */
+    @GuardedBy("mDeviceStateLock")
+    private int getForcedUseForComm() {
+        boolean btSCoOn = mBluetoothScoOn && mBtHelper.isBluetoothScoOn();
+
+        if (btSCoOn) {
+            return AudioSystem.FORCE_BT_SCO;
+        }
+        if (isSpeakerphoneRequested()) {
+            return AudioSystem.FORCE_SPEAKER;
+        }
+        return AudioSystem.FORCE_NONE;
+    }
+
+    /**
+     * Configures audio policy manager and audio HAL according to active communication route.
+     * Always called from message Handler.
+     */
+    // @GuardedBy("mSetModeLock")
+    @GuardedBy("mDeviceStateLock")
+    private void onUpdateCommunicationRoute(String eventSource) {
+        mForcedUseForComm = getForcedUseForComm();
+
+        if (AudioService.DEBUG_COMM_RTE) {
+            Log.v(TAG, "onUpdateCommunicationRoute, mForcedUseForComm: " + mForcedUseForComm
+                    + " eventSource: " + eventSource);
+        }
+
+        if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
+            AudioSystem.setParameters("BT_SCO=on");
+            setForceUse_Async(
+                    AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_BT_SCO, eventSource);
+            setForceUse_Async(
+                    AudioSystem.FOR_RECORD, AudioSystem.FORCE_BT_SCO, eventSource);
+        } else {
+            AudioSystem.setParameters("BT_SCO=off");
+            setForceUse_Async(
+                    AudioSystem.FOR_RECORD, AudioSystem.FORCE_NONE, eventSource);
+            if (mForcedUseForComm == AudioSystem.FORCE_SPEAKER) {
+                setForceUse_Async(
+                        AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_SPEAKER, eventSource);
+            } else {
+                setForceUse_Async(
+                        AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_NONE, eventSource);
+            }
+        }
+        mAudioService.postUpdateRingerModeServiceInt();
+    }
+
+    private CommunicationRouteClient removeCommunicationRouteClient(
+                    IBinder cb, boolean unregister) {
+        for (CommunicationRouteClient cl : mCommunicationRouteClients) {
             if (cl.getBinder() == cb) {
                 if (unregister) {
                     cl.unregisterDeathRecipient();
                 }
-                mSpeakerphoneClients.remove(cl);
+                mCommunicationRouteClients.remove(cl);
                 return cl;
             }
         }
@@ -1444,30 +1588,25 @@
     }
 
     @GuardedBy("mDeviceStateLock")
-    private boolean addSpeakerphoneClient(IBinder cb, int pid, boolean on) {
+    private CommunicationRouteClient addCommunicationRouteClient(
+                    IBinder cb, int pid, AudioDeviceAttributes device) {
         // always insert new request at first position
-        removeSpeakerphoneClient(cb, true);
-        SpeakerphoneClient client = new SpeakerphoneClient(cb, pid, on);
+        removeCommunicationRouteClient(cb, true);
+        CommunicationRouteClient client = new CommunicationRouteClient(cb, pid, device);
         if (client.registerDeathRecipient()) {
-            mSpeakerphoneClients.add(0, client);
-            return true;
+            mCommunicationRouteClients.add(0, client);
+            return client;
         }
-        return false;
+        return null;
     }
 
     @GuardedBy("mDeviceStateLock")
-    private SpeakerphoneClient getSpeakerphoneClientForPid(int pid) {
-        for (SpeakerphoneClient cl : mSpeakerphoneClients) {
+    private CommunicationRouteClient getCommunicationRouteClientForPid(int pid) {
+        for (CommunicationRouteClient cl : mCommunicationRouteClients) {
             if (cl.getPid() == pid) {
                 return cl;
             }
         }
         return null;
     }
-
-    // List of clients requesting speakerPhone ON
-    @GuardedBy("mDeviceStateLock")
-    private final @NonNull ArrayList<SpeakerphoneClient> mSpeakerphoneClients =
-            new ArrayList<SpeakerphoneClient>();
-
 }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 7ad0f21..cfc58a5 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -207,6 +207,9 @@
     /** debug calls to devices APIs */
     protected static final boolean DEBUG_DEVICES = false;
 
+    /** Debug communication route */
+    protected static final boolean DEBUG_COMM_RTE = false;
+
     /** How long to delay before persisting a change in volume/ringer mode. */
     private static final int PERSIST_DELAY = 500;
 
@@ -1075,7 +1078,7 @@
 
         // Restore call state
         synchronized (mDeviceBroker.mSetModeLock) {
-            if (AudioSystem.setPhoneState(mMode, getModeOwnerUid())
+            if (mAudioSystem.setPhoneState(mMode, getModeOwnerUid())
                     ==  AudioSystem.AUDIO_STATUS_OK) {
                 mModeLogger.log(new AudioEventLogger.StringEvent(
                         "onAudioServerDied causes setPhoneState(" + AudioSystem.modeToString(mMode)
@@ -1162,7 +1165,7 @@
             HashMap<Integer, Integer> allowedCapturePolicies =
                     mPlaybackMonitor.getAllAllowedCapturePolicies();
             for (HashMap.Entry<Integer, Integer> entry : allowedCapturePolicies.entrySet()) {
-                int result = AudioSystem.setAllowedCapturePolicy(
+                int result = mAudioSystem.setAllowedCapturePolicy(
                         entry.getKey(),
                         AudioAttributes.capturePolicyToFlags(entry.getValue(), 0x0));
                 if (result != AudioSystem.AUDIO_STATUS_OK) {
@@ -2109,7 +2112,7 @@
     protected @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributesInt(
             @NonNull AudioAttributes attributes) {
         Objects.requireNonNull(attributes);
-        return AudioSystem.getDevicesForAttributes(attributes);
+        return mAudioSystem.getDevicesForAttributes(attributes);
     }
 
     /** Indicates no special treatment in the handling of the volume adjustement */
@@ -2216,7 +2219,7 @@
                         || maybeActiveStreamType == AudioSystem.STREAM_NOTIFICATION) {
                     activeForReal = wasStreamActiveRecently(maybeActiveStreamType, 0);
                 } else {
-                    activeForReal = AudioSystem.isStreamActive(maybeActiveStreamType, 0);
+                    activeForReal = mAudioSystem.isStreamActive(maybeActiveStreamType, 0);
                 }
                 if (activeForReal || mVolumeControlStream == -1) {
                     streamType = maybeActiveStreamType;
@@ -2913,7 +2916,7 @@
         int streamType = getHearingAidStreamType(newMode);
 
         final Set<Integer> deviceTypes = AudioSystem.generateAudioDeviceTypesSet(
-                AudioSystem.getDevicesForStream(streamType));
+                mAudioSystem.getDevicesForStream(streamType));
         final Set<Integer> absVolumeMultiModeCaseDevices = AudioSystem.intersectionAudioDeviceTypes(
                 mAbsVolumeMultiModeCaseDevices, deviceTypes);
         if (absVolumeMultiModeCaseDevices.isEmpty()) {
@@ -3765,7 +3768,7 @@
         final boolean ringerModeMute = ringerMode == AudioManager.RINGER_MODE_VIBRATE
                 || ringerMode == AudioManager.RINGER_MODE_SILENT;
         final boolean shouldRingSco = ringerMode == AudioManager.RINGER_MODE_VIBRATE
-                && isBluetoothScoOn();
+                && mDeviceBroker.isBluetoothScoOn();
         // Ask audio policy engine to force use Bluetooth SCO channel if needed
         final String eventSource = "muteRingerModeStreams() from u/pid:" + Binder.getCallingUid()
                 + "/" + Binder.getCallingPid();
@@ -4120,7 +4123,7 @@
 
             if (actualMode != mMode) {
                 final long identity = Binder.clearCallingIdentity();
-                status = AudioSystem.setPhoneState(actualMode, getModeOwnerUid());
+                status = mAudioSystem.setPhoneState(actualMode, getModeOwnerUid());
                 Binder.restoreCallingIdentity(identity);
                 if (status == AudioSystem.AUDIO_STATUS_OK) {
                     if (DEBUG_MODE) { Log.v(TAG, " mode successfully set to " + actualMode); }
@@ -4406,10 +4409,10 @@
         // for logging only
         final int uid = Binder.getCallingUid();
         final int pid = Binder.getCallingPid();
+
         final String eventSource = new StringBuilder("setSpeakerphoneOn(").append(on)
                 .append(") from u/pid:").append(uid).append("/")
                 .append(pid).toString();
-        final boolean stateChanged = mDeviceBroker.setSpeakerphoneOn(cb, pid, on, eventSource);
         new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE
                 + MediaMetrics.SEPARATOR + "setSpeakerphoneOn")
                 .setUid(uid)
@@ -4417,17 +4420,9 @@
                 .set(MediaMetrics.Property.STATE, on
                         ? MediaMetrics.Value.ON : MediaMetrics.Value.OFF)
                 .record();
-
-        if (stateChanged) {
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                mContext.sendBroadcastAsUser(
-                        new Intent(AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED)
-                                .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
+        final long ident = Binder.clearCallingIdentity();
+        mDeviceBroker.setSpeakerphoneOn(cb, pid, on, eventSource);
+        Binder.restoreCallingIdentity(ident);
     }
 
     /** @see AudioManager#isSpeakerphoneOn() */
@@ -4435,6 +4430,11 @@
         return mDeviceBroker.isSpeakerphoneOn();
     }
 
+
+    /** BT SCO audio state seen by apps using the deprecated API setBluetoothScoOn().
+     * @see isBluetoothScoOn() */
+    private boolean mBtScoOnByApp;
+
     /** @see AudioManager#setBluetoothScoOn(boolean) */
     public void setBluetoothScoOn(boolean on) {
         if (!checkAudioSettingsPermission("setBluetoothScoOn()")) {
@@ -4443,7 +4443,7 @@
 
         // Only enable calls from system components
         if (UserHandle.getCallingAppId() >= FIRST_APPLICATION_UID) {
-            mDeviceBroker.setBluetoothScoOnByApp(on);
+            mBtScoOnByApp = on;
             return;
         }
 
@@ -4469,7 +4469,7 @@
      * Note that it doesn't report internal state, but state seen by apps (which may have
      * called setBluetoothScoOn() */
     public boolean isBluetoothScoOn() {
-        return mDeviceBroker.isBluetoothScoOnForApp();
+        return mBtScoOnByApp || mDeviceBroker.isBluetoothScoOn();
     }
 
     // TODO investigate internal users due to deprecation of SDK API
@@ -4516,7 +4516,7 @@
                 .set(MediaMetrics.Property.SCO_AUDIO_MODE,
                         BtHelper.scoAudioModeToString(scoAudioMode))
                 .record();
-        startBluetoothScoInt(cb, scoAudioMode, eventSource);
+        startBluetoothScoInt(cb, pid, scoAudioMode, eventSource);
 
     }
 
@@ -4535,10 +4535,10 @@
                 .set(MediaMetrics.Property.SCO_AUDIO_MODE,
                         BtHelper.scoAudioModeToString(BtHelper.SCO_MODE_VIRTUAL_CALL))
                 .record();
-        startBluetoothScoInt(cb, BtHelper.SCO_MODE_VIRTUAL_CALL, eventSource);
+        startBluetoothScoInt(cb, pid, BtHelper.SCO_MODE_VIRTUAL_CALL, eventSource);
     }
 
-    void startBluetoothScoInt(IBinder cb, int scoAudioMode, @NonNull String eventSource) {
+    void startBluetoothScoInt(IBinder cb, int pid, int scoAudioMode, @NonNull String eventSource) {
         MediaMetrics.Item mmi = new MediaMetrics.Item(MediaMetrics.Name.AUDIO_BLUETOOTH)
                 .set(MediaMetrics.Property.EVENT, "startBluetoothScoInt")
                 .set(MediaMetrics.Property.SCO_AUDIO_MODE,
@@ -4549,9 +4549,9 @@
             mmi.set(MediaMetrics.Property.EARLY_RETURN, "permission or systemReady").record();
             return;
         }
-        synchronized (mDeviceBroker.mSetModeLock) {
-            mDeviceBroker.startBluetoothScoForClient_Sync(cb, scoAudioMode, eventSource);
-        }
+        final long ident = Binder.clearCallingIdentity();
+        mDeviceBroker.startBluetoothScoForClient(cb, pid, scoAudioMode, eventSource);
+        Binder.restoreCallingIdentity(ident);
         mmi.record();
     }
 
@@ -4566,9 +4566,9 @@
         final String eventSource =  new StringBuilder("stopBluetoothSco()")
                 .append(") from u/pid:").append(uid).append("/")
                 .append(pid).toString();
-        synchronized (mDeviceBroker.mSetModeLock) {
-            mDeviceBroker.stopBluetoothScoForClient_Sync(cb, eventSource);
-        }
+        final long ident = Binder.clearCallingIdentity();
+        mDeviceBroker.stopBluetoothScoForClient(cb, pid, eventSource);
+        Binder.restoreCallingIdentity(ident);
         new MediaMetrics.Item(MediaMetrics.Name.AUDIO_BLUETOOTH)
                 .setUid(uid)
                 .setPid(pid)
@@ -4597,7 +4597,7 @@
                             caller,
                             MUSIC_ACTIVE_POLL_PERIOD_MS);
                     int index = mStreamStates[AudioSystem.STREAM_MUSIC].getIndex(device);
-                    if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)
+                    if (mAudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)
                             && (index > safeMediaVolumeIndex(device))) {
                         // Approximate cumulative active music time
                         mMusicActiveMs += MUSIC_ACTIVE_POLL_PERIOD_MS;
@@ -4981,8 +4981,8 @@
      *     in the last "delay_ms" ms.
      */
     private boolean wasStreamActiveRecently(int stream, int delay_ms) {
-        return AudioSystem.isStreamActive(stream, delay_ms)
-                || AudioSystem.isStreamActiveRemotely(stream, delay_ms);
+        return mAudioSystem.isStreamActive(stream, delay_ms)
+                || mAudioSystem.isStreamActiveRemotely(stream, delay_ms);
     }
 
     private int getActiveStreamType(int suggestedStreamType) {
@@ -4994,7 +4994,7 @@
         switch (mPlatformType) {
         case AudioSystem.PLATFORM_VOICE:
             if (isInCommunication()) {
-                if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
+                if (mAudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
                         == AudioSystem.FORCE_BT_SCO) {
                     // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
                     return AudioSystem.STREAM_BLUETOOTH_SCO;
@@ -5031,7 +5031,7 @@
             }
         default:
             if (isInCommunication()) {
-                if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
+                if (mAudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
                         == AudioSystem.FORCE_BT_SCO) {
                     if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO");
                     return AudioSystem.STREAM_BLUETOOTH_SCO;
@@ -5039,30 +5039,30 @@
                     if (DEBUG_VOL)  Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL");
                     return AudioSystem.STREAM_VOICE_CALL;
                 }
-            } else if (AudioSystem.isStreamActive(
+            } else if (mAudioSystem.isStreamActive(
                     AudioSystem.STREAM_NOTIFICATION, sStreamOverrideDelayMs)) {
                 if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION");
                 return AudioSystem.STREAM_NOTIFICATION;
-            } else if (AudioSystem.isStreamActive(
+            } else if (mAudioSystem.isStreamActive(
                     AudioSystem.STREAM_RING, sStreamOverrideDelayMs)) {
                 if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING");
                 return AudioSystem.STREAM_RING;
             } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
-                if (AudioSystem.isStreamActive(
+                if (mAudioSystem.isStreamActive(
                         AudioSystem.STREAM_NOTIFICATION, sStreamOverrideDelayMs)) {
                     if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION");
                     return AudioSystem.STREAM_NOTIFICATION;
-                } else if (AudioSystem.isStreamActive(
+                }
+                if (mAudioSystem.isStreamActive(
                         AudioSystem.STREAM_RING, sStreamOverrideDelayMs)) {
                     if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING");
                     return AudioSystem.STREAM_RING;
-                } else {
-                    if (DEBUG_VOL) {
-                        Log.v(TAG, "getActiveStreamType: Forcing DEFAULT_VOL_STREAM_NO_PLAYBACK("
-                                + DEFAULT_VOL_STREAM_NO_PLAYBACK + ") b/c default");
-                    }
-                    return DEFAULT_VOL_STREAM_NO_PLAYBACK;
                 }
+                if (DEBUG_VOL) {
+                    Log.v(TAG, "getActiveStreamType: Forcing DEFAULT_VOL_STREAM_NO_PLAYBACK("
+                            + DEFAULT_VOL_STREAM_NO_PLAYBACK + ") b/c default");
+                }
+                return DEFAULT_VOL_STREAM_NO_PLAYBACK;
             }
             break;
         }
@@ -5477,7 +5477,7 @@
                 && DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.contains(newDevice)
                 && mStreamStates[AudioSystem.STREAM_MUSIC].mIsMuted
                 && mStreamStates[AudioSystem.STREAM_MUSIC].getIndex(newDevice) != 0
-                && (newDevice & AudioSystem.getDevicesForStream(AudioSystem.STREAM_MUSIC)) != 0) {
+                && (newDevice & mAudioSystem.getDevicesForStream(AudioSystem.STREAM_MUSIC)) != 0) {
             if (DEBUG_VOL) {
                 Log.i(TAG, String.format("onAccessoryPlugMediaUnmute unmuting device=%d [%s]",
                         newDevice, AudioSystem.getOutputDeviceName(newDevice)));
@@ -5945,7 +5945,7 @@
             if (!mSystemServer.isPrivileged()) {
                 return AudioSystem.DEVICE_NONE;
             }
-            final int devices = AudioSystem.getDevicesForStream(mStreamType);
+            final int devices = mAudioSystem.getDevicesForStream(mStreamType);
             if (devices == mObservedDevices) {
                 return devices;
             }
@@ -6670,7 +6670,7 @@
                             .record();
                     sForceUseLogger.log(
                             new AudioServiceEvents.ForceUseEvent(useCase, config, eventSource));
-                    AudioSystem.setForceUse(useCase, config);
+                    mAudioSystem.setForceUse(useCase, config);
                 }
                     break;
 
@@ -7848,6 +7848,7 @@
         pw.print("  mHasVibrator="); pw.println(mHasVibrator);
         pw.print("  mVolumePolicy="); pw.println(mVolumePolicy);
         pw.print("  mAvrcpAbsVolSupported="); pw.println(mAvrcpAbsVolSupported);
+        pw.print("  mBtScoOnByApp="); pw.println(mBtScoOnByApp);
         pw.print("  mIsSingleVolume="); pw.println(mIsSingleVolume);
         pw.print("  mUseFixedVolume="); pw.println(mUseFixedVolume);
         pw.print("  mFixedVolumeDevices="); pw.println(dumpDeviceTypes(mFixedVolumeDevices));
@@ -7992,7 +7993,7 @@
         }
     }
 
-    public static class VolumeController {
+    public class VolumeController {
         private static final String TAG = "VolumeController";
 
         private IVolumeController mController;
@@ -8032,7 +8033,7 @@
             if (resolvedStream == DEFAULT_VOL_STREAM_NO_PLAYBACK && mController != null) {
                 // never suppress media vol adjustement during media playback
                 if (resolvedStream == AudioSystem.STREAM_MUSIC
-                        && AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, mLongPressTimeout))
+                        && mAudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, mLongPressTimeout))
                 {
                     // media is playing, adjust the volume right away
                     return false;
@@ -8070,7 +8071,7 @@
             return binder(mController);
         }
 
-        private static IBinder binder(IVolumeController controller) {
+        private IBinder binder(IVolumeController controller) {
             return controller == null ? null : controller.asBinder();
         }
 
@@ -8799,7 +8800,7 @@
         int flags = AudioAttributes.capturePolicyToFlags(capturePolicy, 0x0);
         final long identity = Binder.clearCallingIdentity();
         synchronized (mPlaybackMonitor) {
-            int result = AudioSystem.setAllowedCapturePolicy(callingUid, flags);
+            int result = mAudioSystem.setAllowedCapturePolicy(callingUid, flags);
             if (result == AudioSystem.AUDIO_STATUS_OK) {
                 mPlaybackMonitor.setAllowedCapturePolicy(callingUid, capturePolicy);
             }
@@ -8942,7 +8943,7 @@
                 }
             }
             final long identity = Binder.clearCallingIdentity();
-            AudioSystem.registerPolicyMixes(mMixes, false);
+            mAudioSystem.registerPolicyMixes(mMixes, false);
             Binder.restoreCallingIdentity(identity);
             synchronized (mAudioPolicies) {
                 mAudioPolicies.remove(mPolicyCallback.asBinder());
@@ -8985,24 +8986,24 @@
         int addMixes(@NonNull ArrayList<AudioMix> mixes) {
             // TODO optimize to not have to unregister the mixes already in place
             synchronized (mMixes) {
-                AudioSystem.registerPolicyMixes(mMixes, false);
+                mAudioSystem.registerPolicyMixes(mMixes, false);
                 this.add(mixes);
-                return AudioSystem.registerPolicyMixes(mMixes, true);
+                return mAudioSystem.registerPolicyMixes(mMixes, true);
             }
         }
 
         int removeMixes(@NonNull ArrayList<AudioMix> mixes) {
             // TODO optimize to not have to unregister the mixes already in place
             synchronized (mMixes) {
-                AudioSystem.registerPolicyMixes(mMixes, false);
+                mAudioSystem.registerPolicyMixes(mMixes, false);
                 this.remove(mixes);
-                return AudioSystem.registerPolicyMixes(mMixes, true);
+                return mAudioSystem.registerPolicyMixes(mMixes, true);
             }
         }
 
         @AudioSystem.AudioSystemError int connectMixes() {
             final long identity = Binder.clearCallingIdentity();
-            int status = AudioSystem.registerPolicyMixes(mMixes, true);
+            int status = mAudioSystem.registerPolicyMixes(mMixes, true);
             Binder.restoreCallingIdentity(identity);
             return status;
         }
@@ -9038,7 +9039,7 @@
         @AudioSystem.AudioSystemError private int removeUidDeviceAffinitiesFromSystem(int uid) {
             final long identity = Binder.clearCallingIdentity();
             try {
-                return AudioSystem.removeUidDeviceAffinities(uid);
+                return mAudioSystem.removeUidDeviceAffinities(uid);
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -9048,7 +9049,7 @@
                 AudioDeviceArray deviceArray) {
             final long identity = Binder.clearCallingIdentity();
             try {
-                return AudioSystem.setUidDeviceAffinities(uid, deviceArray.mDeviceTypes,
+                return mAudioSystem.setUidDeviceAffinities(uid, deviceArray.mDeviceTypes,
                         deviceArray.mDeviceAddresses);
             } finally {
                 Binder.restoreCallingIdentity(identity);
@@ -9090,7 +9091,7 @@
                 @UserIdInt int userId) {
             final long identity = Binder.clearCallingIdentity();
             try {
-                return AudioSystem.removeUserIdDeviceAffinities(userId);
+                return mAudioSystem.removeUserIdDeviceAffinities(userId);
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -9100,7 +9101,7 @@
                 @UserIdInt int userId, AudioDeviceArray deviceArray) {
             final long identity = Binder.clearCallingIdentity();
             try {
-                return AudioSystem.setUserIdDeviceAffinities(userId, deviceArray.mDeviceTypes,
+                return mAudioSystem.setUserIdDeviceAffinities(userId, deviceArray.mDeviceTypes,
                         deviceArray.mDeviceAddresses);
             } finally {
                 Binder.restoreCallingIdentity(identity);
@@ -9384,7 +9385,7 @@
         if (DEBUG_VOL) {
             Log.d(TAG, "Persisting Volume Behavior for DeviceType: " + deviceType);
         }
-        long callingIdentity = Binder.clearCallingIdentity();
+        final long callingIdentity = Binder.clearCallingIdentity();
         try {
             System.putIntForUser(mContentResolver,
                     getSettingsNameForDeviceVolumeBehavior(deviceType),
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index ae64990..891c713 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -17,9 +17,12 @@
 package com.android.server.audio;
 
 import android.annotation.NonNull;
+import android.media.AudioAttributes;
 import android.media.AudioDeviceAttributes;
 import android.media.AudioSystem;
+import android.media.audiopolicy.AudioMix;
 
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -40,6 +43,25 @@
     }
 
     /**
+     * Same as {@link AudioSystem#getDevicesForStream(int)}
+     * @param stream a valid stream type
+     * @return a mask of device types
+     */
+    public int getDevicesForStream(int stream) {
+        return AudioSystem.getDevicesForStream(stream);
+    }
+
+    /**
+     * Same as {@link AudioSystem#getDevicesForAttributes(AudioAttributes)}
+     * @param attributes the attributes for which the routing is queried
+     * @return the devices that the stream with the given attributes would be routed to
+     */
+    public @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributes(
+            @NonNull AudioAttributes attributes) {
+        return AudioSystem.getDevicesForAttributes(attributes);
+    }
+
+    /**
      * Same as {@link AudioSystem#setDeviceConnectionState(int, int, String, String, int)}
      * @param device
      * @param state
@@ -178,4 +200,104 @@
     public boolean isStreamActive(int stream, int inPastMs) {
         return AudioSystem.isStreamActive(stream, inPastMs);
     }
+
+    /**
+     * Same as {@link AudioSystem#isStreamActiveRemotely(int, int)}
+     * @param stream
+     * @param inPastMs
+     * @return
+     */
+    public boolean isStreamActiveRemotely(int stream, int inPastMs) {
+        return AudioSystem.isStreamActiveRemotely(stream, inPastMs);
+    }
+
+    /**
+     * Same as {@link AudioSystem#setPhoneState(int, int)}
+     * @param state
+     * @param uid
+     * @return
+     */
+    public int setPhoneState(int state, int uid) {
+        return AudioSystem.setPhoneState(state, uid);
+    }
+
+    /**
+     * Same as {@link AudioSystem#setAllowedCapturePolicy(int, int)}
+     * @param uid
+     * @param flags
+     * @return
+     */
+    public int setAllowedCapturePolicy(int uid, int flags) {
+        return AudioSystem.setAllowedCapturePolicy(uid, flags);
+    }
+
+    /**
+     * Same as {@link AudioSystem#setForceUse(int, int)}
+     * @param usage
+     * @param config
+     * @return
+     */
+    public int setForceUse(int usage, int config) {
+        return AudioSystem.setForceUse(usage, config);
+    }
+
+    /**
+     * Same as {@link AudioSystem#getForceUse(int)}
+     * @param usage
+     * @return
+     */
+    public int getForceUse(int usage) {
+        return AudioSystem.getForceUse(usage);
+    }
+
+    /**
+     * Same as {@link AudioSystem#registerPolicyMixes(ArrayList, boolean)}
+     * @param mixes
+     * @param register
+     * @return
+     */
+    public int registerPolicyMixes(ArrayList<AudioMix> mixes, boolean register) {
+        return AudioSystem.registerPolicyMixes(mixes, register);
+    }
+
+    /**
+     * Same as {@link AudioSystem#setUidDeviceAffinities(int, int[], String[])}
+     * @param uid
+     * @param types
+     * @param addresses
+     * @return
+     */
+    public int setUidDeviceAffinities(int uid, @NonNull int[] types,  @NonNull String[] addresses) {
+        return AudioSystem.setUidDeviceAffinities(uid, types, addresses);
+    }
+
+    /**
+     * Same as {@link AudioSystem#removeUidDeviceAffinities(int)}
+     * @param uid
+     * @return
+     */
+    public int removeUidDeviceAffinities(int uid) {
+        return AudioSystem.removeUidDeviceAffinities(uid);
+    }
+
+    /**
+     * Same as {@link AudioSystem#setUserIdDeviceAffinities(int, int[], String[])}
+     * @param userId
+     * @param types
+     * @param addresses
+     * @return
+     */
+    public int setUserIdDeviceAffinities(int userId, @NonNull int[] types,
+            @NonNull String[] addresses) {
+        return AudioSystem.setUserIdDeviceAffinities(userId, types, addresses);
+    }
+
+    /**
+     * Same as {@link AudioSystem#removeUserIdDeviceAffinities(int)}
+     * @param userId
+     * @return
+     */
+    public int removeUserIdDeviceAffinities(int userId) {
+        return AudioSystem.removeUserIdDeviceAffinities(userId);
+    }
 }
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 8de31d9..ae0e805 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -30,8 +30,6 @@
 import android.media.AudioManager;
 import android.media.AudioSystem;
 import android.os.Binder;
-import android.os.IBinder;
-import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Log;
@@ -39,9 +37,7 @@
 import com.android.internal.annotations.GuardedBy;
 
 import java.io.PrintWriter;
-import java.util.ArrayList;
 import java.util.List;
-import java.util.NoSuchElementException;
 import java.util.Objects;
 
 /**
@@ -58,10 +54,6 @@
         mDeviceBroker = broker;
     }
 
-    // List of clients having issued a SCO start request
-    @GuardedBy("BtHelper.this")
-    private final @NonNull ArrayList<ScoClient> mScoClients = new ArrayList<ScoClient>();
-
     // BluetoothHeadset API to control SCO connection
     private @Nullable BluetoothHeadset mBluetoothHeadset;
 
@@ -301,6 +293,8 @@
     @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
     /*package*/ synchronized void receiveBtEvent(Intent intent) {
         final String action = intent.getAction();
+
+        Log.i(TAG, "receiveBtEvent action: " + action + " mScoAudioState: " + mScoAudioState);
         if (action.equals(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) {
             BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
             setBtScoActiveDevice(btDevice);
@@ -308,20 +302,16 @@
             boolean broadcast = false;
             int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR;
             int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
-            // broadcast intent if the connection was initated by AudioService
-            if (!mScoClients.isEmpty()
-                    && (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL
-                    || mScoAudioState == SCO_STATE_ACTIVATE_REQ
-                    || mScoAudioState == SCO_STATE_DEACTIVATE_REQ
-                    || mScoAudioState == SCO_STATE_DEACTIVATING)) {
-                broadcast = true;
-            }
+            Log.i(TAG, "receiveBtEvent ACTION_AUDIO_STATE_CHANGED: " + btState);
             switch (btState) {
                 case BluetoothHeadset.STATE_AUDIO_CONNECTED:
                     scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
                     if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL
                             && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
                         mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
+                    } else if (mDeviceBroker.isBluetoothScoRequested()) {
+                        // broadcast intent if the connection was initated by AudioService
+                        broadcast = true;
                     }
                     mDeviceBroker.setBluetoothScoOn(true, "BtHelper.receiveBtEvent");
                     break;
@@ -333,21 +323,21 @@
                     // notified by requestScoState() setting state to SCO_STATE_ACTIVATE_REQ.
                     // 2) If audio was connected then disconnected via Bluetooth APIs and
                     // we still have pending activation requests by apps: this is indicated by
-                    // state SCO_STATE_ACTIVE_EXTERNAL and the mScoClients list not empty.
+                    // state SCO_STATE_ACTIVE_EXTERNAL and BT SCO is requested.
                     if (mScoAudioState == SCO_STATE_ACTIVATE_REQ
                             || (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL
-                                    && !mScoClients.isEmpty())) {
+                                    && mDeviceBroker.isBluetoothScoRequested())) {
                         if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null
                                 && connectBluetoothScoAudioHelper(mBluetoothHeadset,
                                 mBluetoothHeadsetDevice, mScoAudioMode)) {
                             mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
-                            broadcast = false;
+                            scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTING;
+                            broadcast = true;
                             break;
                         }
                     }
-                    // Tear down SCO if disconnected from external
-                    if (mScoAudioState == SCO_STATE_DEACTIVATING) {
-                        clearAllScoClients(0, false);
+                    if (mScoAudioState != SCO_STATE_ACTIVE_EXTERNAL) {
+                        broadcast = true;
                     }
                     mScoAudioState = SCO_STATE_INACTIVE;
                     break;
@@ -356,11 +346,8 @@
                             && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
                         mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
                     }
-                    broadcast = false;
                     break;
                 default:
-                    // do not broadcast CONNECTING or invalid state
-                    broadcast = false;
                     break;
             }
             if (broadcast) {
@@ -386,81 +373,19 @@
                 == BluetoothHeadset.STATE_AUDIO_CONNECTED;
     }
 
-    /**
-     * Disconnect all SCO connections started by {@link AudioManager} except those started by
-     * {@param exceptPid}
-     *
-     * @param exceptPid pid whose SCO connections through {@link AudioManager} should be kept
-     */
     // @GuardedBy("AudioDeviceBroker.mSetModeLock")
     @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
-    /*package*/ synchronized void disconnectBluetoothSco(int exceptPid) {
-        checkScoAudioState();
-        if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) {
-            return;
-        }
-        clearAllScoClients(exceptPid, true);
-    }
-
-    // @GuardedBy("AudioDeviceBroker.mSetModeLock")
-    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
-    /*package*/ synchronized void startBluetoothScoForClient(IBinder cb, int scoAudioMode,
+    /*package*/ synchronized boolean startBluetoothSco(int scoAudioMode,
                 @NonNull String eventSource) {
-        ScoClient client = getScoClient(cb, true);
-        // The calling identity must be cleared before calling ScoClient.incCount().
-        // inCount() calls requestScoState() which in turn can call BluetoothHeadset APIs
-        // and this must be done on behalf of system server to make sure permissions are granted.
-        // The caller identity must be cleared after getScoClient() because it is needed if a new
-        // client is created.
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource));
-            client.requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode);
-        } catch (NullPointerException e) {
-            Log.e(TAG, "Null ScoClient", e);
-        }
-        Binder.restoreCallingIdentity(ident);
-    }
-
-    // @GuardedBy("AudioDeviceBroker.mSetModeLock")
-    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
-    /*package*/ synchronized void stopBluetoothScoForClient(IBinder cb,
-            @NonNull String eventSource) {
-        ScoClient client = getScoClient(cb, false);
-        // The calling identity must be cleared before calling ScoClient.decCount().
-        // decCount() calls requestScoState() which in turn can call BluetoothHeadset APIs
-        // and this must be done on behalf of system server to make sure permissions are granted.
-        final long ident = Binder.clearCallingIdentity();
-        if (client != null) {
-            stopAndRemoveClient(client, eventSource);
-        }
-        Binder.restoreCallingIdentity(ident);
-    }
-
-    // @GuardedBy("AudioDeviceBroker.mSetModeLock")
-    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
-    /*package*/ synchronized void stopBluetoothScoForPid(int pid) {
-        ScoClient client = getScoClientForPid(pid);
-        if (client == null) {
-            return;
-        }
-        final String eventSource = new StringBuilder("stopBluetoothScoForPid(")
-                .append(pid).append(")").toString();
-        stopAndRemoveClient(client, eventSource);
-    }
-
-    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
-    // @GuardedBy("BtHelper.this")
-    private void stopAndRemoveClient(ScoClient client, @NonNull String eventSource) {
         AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource));
-        client.requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
-                SCO_MODE_VIRTUAL_CALL);
-        // If a disconnection is pending, the client will be removed when clearAllScoClients()
-        // is called form receiveBtEvent()
-        if (mScoAudioState != SCO_STATE_DEACTIVATE_REQ
-                && mScoAudioState != SCO_STATE_DEACTIVATING) {
-            client.remove(false /*stop */, true /*unregister*/);
-        }
+        return requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode);
+    }
+
+    // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+    /*package*/ synchronized boolean stopBluetoothSco(@NonNull String eventSource) {
+        AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource));
+        return requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, SCO_MODE_VIRTUAL_CALL);
     }
 
     /*package*/ synchronized void setHearingAidVolume(int index, int streamType) {
@@ -507,7 +432,6 @@
     // @GuardedBy("AudioDeviceBroker.mSetModeLock")
     @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
     /*package*/ synchronized void resetBluetoothSco() {
-        clearAllScoClients(0, false);
         mScoAudioState = SCO_STATE_INACTIVE;
         broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
         AudioSystem.setParameters("A2dpSuspended=false");
@@ -740,196 +664,122 @@
             };
 
     //----------------------------------------------------------------------
+
     // @GuardedBy("AudioDeviceBroker.mSetModeLock")
-    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
-    /*package*/ synchronized void scoClientDied(Object obj) {
-        final ScoClient client = (ScoClient) obj;
-        client.remove(true /*stop*/, false /*unregister*/);
-        Log.w(TAG, "SCO client died");
-    }
-
-    private class ScoClient implements IBinder.DeathRecipient {
-        private IBinder mCb; // To be notified of client's death
-        private int mCreatorPid;
-
-        ScoClient(IBinder cb) {
-            mCb = cb;
-            mCreatorPid = Binder.getCallingPid();
-        }
-
-        public void registerDeathRecipient() {
-            try {
-                mCb.linkToDeath(this, 0);
-            } catch (RemoteException e) {
-                Log.w(TAG, "ScoClient could not link to " + mCb + " binder death");
-            }
-        }
-
-        public void unregisterDeathRecipient() {
-            try {
-                mCb.unlinkToDeath(this, 0);
-            } catch (NoSuchElementException e) {
-                Log.w(TAG, "ScoClient could not not unregistered to binder");
-            }
-        }
-
-        @Override
-        public void binderDied() {
-            // process this from DeviceBroker's message queue to take the right locks since
-            // this event can impact SCO mode and requires querying audio mode stack
-            mDeviceBroker.postScoClientDied(this);
-        }
-
-        IBinder getBinder() {
-            return mCb;
-        }
-
-        int getPid() {
-            return mCreatorPid;
-        }
-
-        // @GuardedBy("AudioDeviceBroker.mSetModeLock")
-        //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
-        @GuardedBy("BtHelper.this")
-        private boolean requestScoState(int state, int scoAudioMode) {
-            checkScoAudioState();
-            if (mScoClients.size() != 1) {
-                Log.i(TAG, "requestScoState: state=" + state + ", scoAudioMode=" + scoAudioMode
-                        + ", num SCO clients=" + mScoClients.size());
-                return true;
-            }
-            if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
-                // Make sure that the state transitions to CONNECTING even if we cannot initiate
-                // the connection.
-                broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
-                // Accept SCO audio activation only in NORMAL audio mode or if the mode is
-                // currently controlled by the same client process.
-                final int modeOwnerPid =  mDeviceBroker.getModeOwnerPid();
-                if (modeOwnerPid != 0 && (modeOwnerPid != mCreatorPid)) {
-                    Log.w(TAG, "requestScoState: audio mode is not NORMAL and modeOwnerPid "
-                            + modeOwnerPid + " != creatorPid " + mCreatorPid);
+    //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+    @GuardedBy("BtHelper.this")
+    private boolean requestScoState(int state, int scoAudioMode) {
+        checkScoAudioState();
+        if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
+            // Make sure that the state transitions to CONNECTING even if we cannot initiate
+            // the connection.
+            broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
+            switch (mScoAudioState) {
+                case SCO_STATE_INACTIVE:
+                    mScoAudioMode = scoAudioMode;
+                    if (scoAudioMode == SCO_MODE_UNDEFINED) {
+                        mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
+                        if (mBluetoothHeadsetDevice != null) {
+                            mScoAudioMode = Settings.Global.getInt(
+                                    mDeviceBroker.getContentResolver(),
+                                    "bluetooth_sco_channel_"
+                                            + mBluetoothHeadsetDevice.getAddress(),
+                                    SCO_MODE_VIRTUAL_CALL);
+                            if (mScoAudioMode > SCO_MODE_MAX || mScoAudioMode < 0) {
+                                mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
+                            }
+                        }
+                    }
+                    if (mBluetoothHeadset == null) {
+                        if (getBluetoothHeadset()) {
+                            mScoAudioState = SCO_STATE_ACTIVATE_REQ;
+                        } else {
+                            Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
+                                    + " connection, mScoAudioMode=" + mScoAudioMode);
+                            broadcastScoConnectionState(
+                                    AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+                            return false;
+                        }
+                        break;
+                    }
+                    if (mBluetoothHeadsetDevice == null) {
+                        Log.w(TAG, "requestScoState: no active device while connecting,"
+                                + " mScoAudioMode=" + mScoAudioMode);
+                        broadcastScoConnectionState(
+                                AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+                        return false;
+                    }
+                    if (connectBluetoothScoAudioHelper(mBluetoothHeadset,
+                            mBluetoothHeadsetDevice, mScoAudioMode)) {
+                        mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
+                    } else {
+                        Log.w(TAG, "requestScoState: connect to "
+                                + getAnonymizedAddress(mBluetoothHeadsetDevice)
+                                + " failed, mScoAudioMode=" + mScoAudioMode);
+                        broadcastScoConnectionState(
+                                AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+                        return false;
+                    }
+                    break;
+                case SCO_STATE_DEACTIVATING:
+                    mScoAudioState = SCO_STATE_ACTIVATE_REQ;
+                    break;
+                case SCO_STATE_DEACTIVATE_REQ:
+                    mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
+                    broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
+                    break;
+                case SCO_STATE_ACTIVE_INTERNAL:
+                    Log.w(TAG, "requestScoState: already in ACTIVE mode, simply return");
+                    break;
+                default:
+                    Log.w(TAG, "requestScoState: failed to connect in state "
+                            + mScoAudioState + ", scoAudioMode=" + scoAudioMode);
                     broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
                     return false;
-                }
-                switch (mScoAudioState) {
-                    case SCO_STATE_INACTIVE:
-                        mScoAudioMode = scoAudioMode;
-                        if (scoAudioMode == SCO_MODE_UNDEFINED) {
-                            mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
-                            if (mBluetoothHeadsetDevice != null) {
-                                mScoAudioMode = Settings.Global.getInt(
-                                        mDeviceBroker.getContentResolver(),
-                                        "bluetooth_sco_channel_"
-                                                + mBluetoothHeadsetDevice.getAddress(),
-                                        SCO_MODE_VIRTUAL_CALL);
-                                if (mScoAudioMode > SCO_MODE_MAX || mScoAudioMode < 0) {
-                                    mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
-                                }
-                            }
-                        }
-                        if (mBluetoothHeadset == null) {
-                            if (getBluetoothHeadset()) {
-                                mScoAudioState = SCO_STATE_ACTIVATE_REQ;
-                            } else {
-                                Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
-                                        + " connection, mScoAudioMode=" + mScoAudioMode);
-                                broadcastScoConnectionState(
-                                        AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
-                                return false;
-                            }
-                            break;
-                        }
-                        if (mBluetoothHeadsetDevice == null) {
-                            Log.w(TAG, "requestScoState: no active device while connecting,"
-                                    + " mScoAudioMode=" + mScoAudioMode);
-                            broadcastScoConnectionState(
-                                    AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
-                            return false;
-                        }
-                        if (connectBluetoothScoAudioHelper(mBluetoothHeadset,
-                                mBluetoothHeadsetDevice, mScoAudioMode)) {
-                            mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
+            }
+        } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
+            switch (mScoAudioState) {
+                case SCO_STATE_ACTIVE_INTERNAL:
+                    if (mBluetoothHeadset == null) {
+                        if (getBluetoothHeadset()) {
+                            mScoAudioState = SCO_STATE_DEACTIVATE_REQ;
                         } else {
-                            Log.w(TAG, "requestScoState: connect to "
-                                    + getAnonymizedAddress(mBluetoothHeadsetDevice)
-                                    + " failed, mScoAudioMode=" + mScoAudioMode);
+                            Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
+                                    + " disconnection, mScoAudioMode=" + mScoAudioMode);
+                            mScoAudioState = SCO_STATE_INACTIVE;
                             broadcastScoConnectionState(
                                     AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
                             return false;
                         }
                         break;
-                    case SCO_STATE_DEACTIVATING:
-                        mScoAudioState = SCO_STATE_ACTIVATE_REQ;
-                        break;
-                    case SCO_STATE_DEACTIVATE_REQ:
-                        mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
-                        broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
-                        break;
-                    case SCO_STATE_ACTIVE_INTERNAL:
-                        Log.w(TAG, "requestScoState: already in ACTIVE mode, simply return");
-                        break;
-                    default:
-                        Log.w(TAG, "requestScoState: failed to connect in state "
-                                + mScoAudioState + ", scoAudioMode=" + scoAudioMode);
-                        broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
-                        return false;
-                }
-            } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
-                switch (mScoAudioState) {
-                    case SCO_STATE_ACTIVE_INTERNAL:
-                        if (mBluetoothHeadset == null) {
-                            if (getBluetoothHeadset()) {
-                                mScoAudioState = SCO_STATE_DEACTIVATE_REQ;
-                            } else {
-                                Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
-                                        + " disconnection, mScoAudioMode=" + mScoAudioMode);
-                                mScoAudioState = SCO_STATE_INACTIVE;
-                                broadcastScoConnectionState(
-                                        AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
-                                return false;
-                            }
-                            break;
-                        }
-                        if (mBluetoothHeadsetDevice == null) {
-                            mScoAudioState = SCO_STATE_INACTIVE;
-                            broadcastScoConnectionState(
-                                    AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
-                            break;
-                        }
-                        if (disconnectBluetoothScoAudioHelper(mBluetoothHeadset,
-                                mBluetoothHeadsetDevice, mScoAudioMode)) {
-                            mScoAudioState = SCO_STATE_DEACTIVATING;
-                        } else {
-                            mScoAudioState = SCO_STATE_INACTIVE;
-                            broadcastScoConnectionState(
-                                    AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
-                        }
-                        break;
-                    case SCO_STATE_ACTIVATE_REQ:
+                    }
+                    if (mBluetoothHeadsetDevice == null) {
                         mScoAudioState = SCO_STATE_INACTIVE;
-                        broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+                        broadcastScoConnectionState(
+                                AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
                         break;
-                    default:
-                        Log.w(TAG, "requestScoState: failed to disconnect in state "
-                                + mScoAudioState + ", scoAudioMode=" + scoAudioMode);
-                        broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
-                        return false;
-                }
+                    }
+                    if (disconnectBluetoothScoAudioHelper(mBluetoothHeadset,
+                            mBluetoothHeadsetDevice, mScoAudioMode)) {
+                        mScoAudioState = SCO_STATE_DEACTIVATING;
+                    } else {
+                        mScoAudioState = SCO_STATE_INACTIVE;
+                        broadcastScoConnectionState(
+                                AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+                    }
+                    break;
+                case SCO_STATE_ACTIVATE_REQ:
+                    mScoAudioState = SCO_STATE_INACTIVE;
+                    broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+                    break;
+                default:
+                    Log.w(TAG, "requestScoState: failed to disconnect in state "
+                            + mScoAudioState + ", scoAudioMode=" + scoAudioMode);
+                    broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+                    return false;
             }
-            return true;
         }
-
-        @GuardedBy("BtHelper.this")
-        void remove(boolean stop, boolean unregister) {
-            if (unregister) {
-                unregisterDeathRecipient();
-            }
-            if (stop) {
-                requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
-                        SCO_MODE_VIRTUAL_CALL);
-            }
-            mScoClients.remove(this);
-        }
+        return true;
     }
 
     //-----------------------------------------------------
@@ -982,49 +832,6 @@
         }
     }
 
-
-    @GuardedBy("BtHelper.this")
-    private ScoClient getScoClient(IBinder cb, boolean create) {
-        for (ScoClient existingClient : mScoClients) {
-            if (existingClient.getBinder() == cb) {
-                return existingClient;
-            }
-        }
-        if (create) {
-            ScoClient newClient = new ScoClient(cb);
-            newClient.registerDeathRecipient();
-            mScoClients.add(newClient);
-            return newClient;
-        }
-        return null;
-    }
-
-    @GuardedBy("BtHelper.this")
-    private ScoClient getScoClientForPid(int pid) {
-        for (ScoClient cl : mScoClients) {
-            if (cl.getPid() == pid) {
-                return cl;
-            }
-        }
-        return null;
-    }
-
-    // @GuardedBy("AudioDeviceBroker.mSetModeLock")
-    //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
-    @GuardedBy("BtHelper.this")
-    private void clearAllScoClients(int exceptPid, boolean stopSco) {
-        final ArrayList<ScoClient> clients = new ArrayList<ScoClient>();
-        for (ScoClient cl : mScoClients) {
-            if (cl.getPid() != exceptPid) {
-                clients.add(cl);
-            }
-        }
-        for (ScoClient cl : clients) {
-            cl.remove(stopSco, true /*unregister*/);
-        }
-
-    }
-
     private boolean getBluetoothHeadset() {
         boolean result = false;
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
@@ -1070,10 +877,6 @@
         pw.println(prefix + "mBluetoothHeadsetDevice: " + mBluetoothHeadsetDevice);
         pw.println(prefix + "mScoAudioState: " + scoAudioStateToString(mScoAudioState));
         pw.println(prefix + "mScoAudioMode: " + scoAudioModeToString(mScoAudioMode));
-        pw.println(prefix + "Sco clients:");
-        mScoClients.forEach((cl) -> {
-            pw.println("  " + prefix + "pid: " + cl.getPid() + " cb: " + cl.getBinder()); });
-
         pw.println("\n" + prefix + "mHearingAid: " + mHearingAid);
         pw.println(prefix + "mA2dp: " + mA2dp);
         pw.println(prefix + "mAvrcpAbsVolSupported: " + mAvrcpAbsVolSupported);
diff --git a/services/core/java/com/android/server/biometrics/HardwareAuthTokenUtils.java b/services/core/java/com/android/server/biometrics/HardwareAuthTokenUtils.java
new file mode 100644
index 0000000..eff4da3
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/HardwareAuthTokenUtils.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.biometrics;
+
+import static java.nio.ByteOrder.LITTLE_ENDIAN;
+
+import android.hardware.keymaster.HardwareAuthToken;
+import android.hardware.keymaster.Timestamp;
+
+import java.nio.ByteOrder;
+
+/**
+ * Utilities for converting between old and new HardwareAuthToken types. See
+ * {@link HardwareAuthToken}.
+ */
+public class HardwareAuthTokenUtils {
+    public static byte[] toByteArray(HardwareAuthToken hat) {
+        final byte[] array = new byte[69];
+
+        // Version, first byte. Used in hw_auth_token.h but not HardwareAuthToken
+        array[0] = 0;
+
+        // Challenge, 1:8.
+        writeLong(hat.challenge, array, 1 /* offset */);
+
+        // UserId, 9:16.
+        writeLong(hat.userId, array, 9 /* offset */);
+
+        // AuthenticatorId, 17:24.
+        writeLong(hat.authenticatorId, array, 17 /* offset */);
+
+        // AuthenticatorType, 25:28.
+        writeInt(flipIfNativelyLittle(hat.authenticatorType), array, 25 /* offset */);
+
+        // Timestamp, 29:36.
+        writeLong(flipIfNativelyLittle(hat.timestamp.milliSeconds), array, 29 /* offset */);
+
+        // MAC, 37:69. Byte array.
+        System.arraycopy(hat.mac, 0 /* srcPos */, array, 37 /* destPos */, hat.mac.length);
+
+        return array;
+    }
+
+    public static HardwareAuthToken toHardwareAuthToken(byte[] array) {
+        final HardwareAuthToken hardwareAuthToken = new HardwareAuthToken();
+
+        // First byte is version, which doesn't not exist in HardwareAuthToken anymore
+        // Next 8 bytes is the challenge.
+        hardwareAuthToken.challenge = getLong(array, 1 /* offset */);
+
+        // Next 8 bytes is the userId
+        hardwareAuthToken.userId = getLong(array, 9 /* offset */);
+
+        // Next 8 bytes is the authenticatorId.
+        hardwareAuthToken.authenticatorId = getLong(array, 17 /* offset */);
+
+        // Next 4 bytes is the authenticatorType.
+        hardwareAuthToken.authenticatorType = flipIfNativelyLittle(getInt(array, 25 /* offset */));
+
+        // Next 8 bytes is the timestamp.
+        final Timestamp timestamp = new Timestamp();
+        timestamp.milliSeconds = flipIfNativelyLittle(getLong(array, 29 /* offset */));
+        hardwareAuthToken.timestamp = timestamp;
+
+        // Last 32 bytes is the mac, 37:69
+        hardwareAuthToken.mac = new byte[32];
+        System.arraycopy(array, 37 /* srcPos */,
+                hardwareAuthToken.mac,
+                0 /* destPos */,
+                32 /* length */);
+
+        return hardwareAuthToken;
+    }
+
+    private static long flipIfNativelyLittle(long l) {
+        if (LITTLE_ENDIAN == ByteOrder.nativeOrder()) {
+            return Long.reverseBytes(l);
+        }
+        return l;
+    }
+
+    private static int flipIfNativelyLittle(int i) {
+        if (LITTLE_ENDIAN == ByteOrder.nativeOrder()) {
+            return Integer.reverseBytes(i);
+        }
+        return i;
+    }
+
+    private static void writeLong(long l, byte[] dest, int offset) {
+        dest[offset + 0] = (byte) l;
+        dest[offset + 1] = (byte) (l >> 8);
+        dest[offset + 2] = (byte) (l >> 16);
+        dest[offset + 3] = (byte) (l >> 24);
+        dest[offset + 4] = (byte) (l >> 32);
+        dest[offset + 5] = (byte) (l >> 40);
+        dest[offset + 6] = (byte) (l >> 48);
+        dest[offset + 7] = (byte) (l >> 56);
+    }
+
+    private static void writeInt(int i, byte[] dest, int offset) {
+        dest[offset + 0] = (byte) i;
+        dest[offset + 1] = (byte) (i >> 8);
+        dest[offset + 2] = (byte) (i >> 16);
+        dest[offset + 3] = (byte) (i >> 24);
+    }
+
+    private static long getLong(byte[] array, int offset) {
+        long result = 0;
+        // Lowest bit is LSB
+        for (int i = 0; i < 8; i++) {
+            result += (long) ((array[i + offset] & 0xffL) << (8 * i));
+        }
+        return result;
+    }
+
+    private static int getInt(byte[] array, int offset) {
+        int result = 0;
+        // Lowest bit is LSB
+        for (int i = 0; i < 4; i++) {
+            result += (int) (((int) array[i + offset] & 0xff) << (8 * i));
+        }
+        return result;
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java b/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java
index 5b18349..a8250ac 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java
@@ -16,6 +16,7 @@
 
 package com.android.server.biometrics.sensors;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.os.AsyncTask;
@@ -62,11 +63,6 @@
     protected abstract String getBiometricsTag();
 
     /**
-     * @return The file where the biometric metadata should be stored.
-     */
-    protected abstract String getBiometricFile();
-
-    /**
      * @return The resource for the name template, this is used to generate the default name.
      */
     protected abstract int getNameTemplateResource();
@@ -88,8 +84,8 @@
             throws IOException, XmlPullParserException;
 
 
-    public BiometricUserState(Context context, int userId) {
-        mFile = getFileForUser(userId);
+    public BiometricUserState(Context context, int userId, @NonNull String fileName) {
+        mFile = getFileForUser(userId, fileName);
         mContext = context;
         synchronized (this) {
             readStateSyncLocked();
@@ -159,8 +155,8 @@
         return true;
     }
 
-    private File getFileForUser(int userId) {
-        return new File(Environment.getUserSystemDirectory(userId), getBiometricFile());
+    private File getFileForUser(int userId, @NonNull String fileName) {
+        return new File(Environment.getUserSystemDirectory(userId), fileName);
     }
 
     private void scheduleWriteStateLocked() {
diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
index 3c9dddd..0dee816 100644
--- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
@@ -73,7 +73,7 @@
         T getDaemon();
     }
 
-    private final int mSequentialId;
+    protected final int mSequentialId;
     @NonNull private final Context mContext;
     @NonNull protected final LazyDaemon<T> mLazyDaemon;
     private final int mTargetUserId;
diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
index cb7db92..c87f62f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
+++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
@@ -89,7 +89,8 @@
         }
     }
 
-    void onError(int sensorId, int cookie, int error, int vendorCode) throws RemoteException {
+    public void onError(int sensorId, int cookie, int error, int vendorCode)
+            throws RemoteException {
         if (mSensorReceiver != null) {
             mSensorReceiver.onError(sensorId, cookie, error, vendorCode);
         } else if (mFaceServiceReceiver != null) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
index 5c08bce..e738d17 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
@@ -57,7 +57,7 @@
     }
 
     private final ArrayList<UserTemplate> mUnknownHALTemplates = new ArrayList<>();
-    private final BiometricUtils mBiometricUtils;
+    private final BiometricUtils<S> mBiometricUtils;
     private final Map<Integer, Long> mAuthenticatorIds;
     private final List<S> mEnrolledList;
     private ClientMonitor<T> mCurrentTask;
@@ -95,15 +95,15 @@
 
     protected abstract InternalEnumerateClient<T> getEnumerateClient(Context context,
             LazyDaemon<T> lazyDaemon, IBinder token, int userId, String owner,
-            List<S> enrolledList, BiometricUtils utils, int sensorId);
+            List<S> enrolledList, BiometricUtils<S> utils, int sensorId);
 
-    protected abstract RemovalClient<T> getRemovalClient(Context context, LazyDaemon<T> lazyDaemon,
-            IBinder token, int biometricId, int userId, String owner, BiometricUtils utils,
-            int sensorId, Map<Integer, Long> authenticatorIds);
+    protected abstract RemovalClient<S, T> getRemovalClient(Context context,
+            LazyDaemon<T> lazyDaemon, IBinder token, int biometricId, int userId, String owner,
+            BiometricUtils<S> utils, int sensorId, Map<Integer, Long> authenticatorIds);
 
     protected InternalCleanupClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
             int userId, @NonNull String owner, int sensorId, int statsModality,
-            @NonNull List<S> enrolledList, @NonNull BiometricUtils utils,
+            @NonNull List<S> enrolledList, @NonNull BiometricUtils<S> utils,
             @NonNull Map<Integer, Long> authenticatorIds) {
         super(context, lazyDaemon, null /* token */, null /* ClientMonitorCallbackConverter */,
                 userId, owner, 0 /* cookie */, sensorId, statsModality,
@@ -153,7 +153,7 @@
                     + mCurrentTask.getClass().getSimpleName());
             return;
         }
-        ((RemovalClient<T>) mCurrentTask).onRemoved(identifier, remaining);
+        ((RemovalClient<S, T>) mCurrentTask).onRemoved(identifier, remaining);
     }
 
     @Override
diff --git a/media/java/android/media/tv/TvChannelInfo.aidl b/services/core/java/com/android/server/biometrics/sensors/LockoutConsumer.java
similarity index 64%
copy from media/java/android/media/tv/TvChannelInfo.aidl
copy to services/core/java/com/android/server/biometrics/sensors/LockoutConsumer.java
index 71cd0a7..153bd46 100644
--- a/media/java/android/media/tv/TvChannelInfo.aidl
+++ b/services/core/java/com/android/server/biometrics/sensors/LockoutConsumer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open 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,6 +14,12 @@
  * limitations under the License.
  */
 
-package android.media.tv;
+package com.android.server.biometrics.sensors;
 
-parcelable TvChannelInfo;
+/**
+ * Interface that clients interested/eligible for lockout events should implement.
+ */
+public interface LockoutConsumer {
+    void onLockoutTimed(long durationMillis);
+    void onLockoutPermanent();
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
index 1348f79..f79abd5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
@@ -17,6 +17,7 @@
 package com.android.server.biometrics.sensors;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricsProtoEnums;
@@ -29,17 +30,18 @@
 /**
  * A class to keep track of the remove state for a given client.
  */
-public abstract class RemovalClient<T> extends ClientMonitor<T> implements RemovalConsumer {
+public abstract class RemovalClient<S extends BiometricAuthenticator.Identifier, T>
+        extends ClientMonitor<T> implements RemovalConsumer {
 
     private static final String TAG = "Biometrics/RemovalClient";
 
     protected final int mBiometricId;
-    private final BiometricUtils mBiometricUtils;
+    private final BiometricUtils<S> mBiometricUtils;
     private final Map<Integer, Long> mAuthenticatorIds;
 
     public RemovalClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
             @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
-            int biometricId, int userId, @NonNull String owner, @NonNull BiometricUtils utils,
+            int biometricId, int userId, @NonNull String owner, @NonNull BiometricUtils<S> utils,
             int sensorId, @NonNull Map<Integer, Long> authenticatorIds, int statsModality) {
         super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
                 statsModality, BiometricsProtoEnums.ACTION_REMOVE,
@@ -63,8 +65,8 @@
     }
 
     @Override
-    public void onRemoved(BiometricAuthenticator.Identifier identifier, int remaining) {
-        if (identifier.getBiometricId() != 0) {
+    public void onRemoved(@Nullable BiometricAuthenticator.Identifier identifier, int remaining) {
+        if (identifier != null) {
             mBiometricUtils.removeBiometricForUser(getContext(), getTargetUserId(),
                     identifier.getBiometricId());
         }
diff --git a/services/core/java/com/android/server/biometrics/sensors/RemovalConsumer.java b/services/core/java/com/android/server/biometrics/sensors/RemovalConsumer.java
index 3d7988c..0aba7e4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RemovalConsumer.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RemovalConsumer.java
@@ -16,6 +16,7 @@
 
 package com.android.server.biometrics.sensors;
 
+import android.annotation.Nullable;
 import android.hardware.biometrics.BiometricAuthenticator;
 
 /**
@@ -28,5 +29,5 @@
      * @param remaining number of templates that still need to be removed before the operation in
      *                  the HAL is complete (e.g. when removing all templates).
      */
-    void onRemoved(BiometricAuthenticator.Identifier identifier, int remaining);
+    void onRemoved(@Nullable BiometricAuthenticator.Identifier identifier, int remaining);
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/Face10.java
index c2d4c15..f7998ee 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/Face10.java
@@ -219,8 +219,7 @@
                         removalConsumer.onRemoved(face, remaining);
                     }
                 } else {
-                    final Face face = new Face("", 0 /* identifier */, deviceId);
-                    removalConsumer.onRemoved(face, 0 /* remaining */);
+                    removalConsumer.onRemoved(null, 0 /* remaining */);
                 }
 
                 Settings.Secure.putIntForUser(mContext.getContentResolver(),
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java
index 3e843ca..989b5c9 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java
@@ -23,6 +23,7 @@
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.face.V1_0.IBiometricsFace;
 import android.hardware.biometrics.face.V1_0.Status;
+import android.hardware.face.Face;
 import android.hardware.face.FaceManager;
 import android.os.IBinder;
 import android.os.NativeHandle;
@@ -53,9 +54,9 @@
 
     FaceEnrollClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFace> lazyDaemon,
             @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
-            @NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils utils,
-            @NonNull int[] disabledFeatures, int timeoutSec, @Nullable NativeHandle surfaceHandle,
-            int sensorId) {
+            @NonNull byte[] hardwareAuthToken, @NonNull String owner,
+            @NonNull BiometricUtils<Face> utils, @NonNull int[] disabledFeatures, int timeoutSec,
+            @Nullable NativeHandle surfaceHandle, int sensorId) {
         super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
                 timeoutSec, BiometricsProtoEnums.MODALITY_FACE, sensorId,
                 false /* shouldVibrate */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalCleanupClient.java
index 93f35f4..7626587 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalCleanupClient.java
@@ -40,7 +40,7 @@
 
     FaceInternalCleanupClient(@NonNull Context context,
             @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, int userId, @NonNull String owner,
-            int sensorId, @NonNull List<Face> enrolledList, @NonNull BiometricUtils utils,
+            int sensorId, @NonNull List<Face> enrolledList, @NonNull BiometricUtils<Face> utils,
             @NonNull Map<Integer, Long> authenticatorIds) {
         super(context, lazyDaemon, userId, owner, sensorId, BiometricsProtoEnums.MODALITY_FACE,
                 enrolledList, utils, authenticatorIds);
@@ -49,15 +49,15 @@
     @Override
     protected InternalEnumerateClient<IBiometricsFace> getEnumerateClient(Context context,
             LazyDaemon<IBiometricsFace> lazyDaemon, IBinder token, int userId, String owner,
-            List<Face> enrolledList, BiometricUtils utils, int sensorId) {
+            List<Face> enrolledList, BiometricUtils<Face> utils, int sensorId) {
         return new FaceInternalEnumerateClient(context, lazyDaemon, token, userId, owner,
                 enrolledList, utils, sensorId);
     }
 
     @Override
-    protected RemovalClient<IBiometricsFace> getRemovalClient(Context context,
+    protected RemovalClient<Face, IBiometricsFace> getRemovalClient(Context context,
             LazyDaemon<IBiometricsFace> lazyDaemon, IBinder token,
-            int biometricId, int userId, String owner, BiometricUtils utils, int sensorId,
+            int biometricId, int userId, String owner, BiometricUtils<Face> utils, int sensorId,
             Map<Integer, Long> authenticatorIds) {
         // Internal remove does not need to send results to anyone. Cleanup (enumerate + remove)
         // is all done internally.
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java
index 32d4832..4166dff 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java
@@ -40,8 +40,8 @@
 
     FaceInternalEnumerateClient(@NonNull Context context,
             @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, @NonNull IBinder token, int userId,
-            @NonNull String owner, @NonNull List<Face> enrolledList, @NonNull BiometricUtils utils,
-            int sensorId) {
+            @NonNull String owner, @NonNull List<Face> enrolledList,
+            @NonNull BiometricUtils<Face> utils, int sensorId) {
         super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId,
                 BiometricsProtoEnums.MODALITY_FACE);
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java
index dde5aba..31ae3a3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.face.Face;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Slog;
@@ -34,12 +35,12 @@
  * Face-specific removal client supporting the {@link android.hardware.biometrics.face.V1_0}
  * and {@link android.hardware.biometrics.face.V1_1} HIDL interfaces.
  */
-class FaceRemovalClient extends RemovalClient<IBiometricsFace> {
+class FaceRemovalClient extends RemovalClient<Face, IBiometricsFace> {
     private static final String TAG = "FaceRemovalClient";
 
     FaceRemovalClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFace> lazyDaemon,
             @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
-            int biometricId, int userId, @NonNull String owner, @NonNull BiometricUtils utils,
+            int biometricId, int userId, @NonNull String owner, @NonNull BiometricUtils<Face> utils,
             int sensorId, @NonNull Map<Integer, Long> authenticatorIds) {
         super(context, lazyDaemon, token, listener, biometricId, userId, owner, utils, sensorId,
                 authenticatorIds, BiometricsProtoEnums.MODALITY_FACE);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java
index 42c7d16..d30c3c8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java
@@ -17,7 +17,6 @@
 package com.android.server.biometrics.sensors.face;
 
 import android.content.Context;
-import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.face.Face;
 import android.util.AtomicFile;
 import android.util.Slog;
@@ -41,10 +40,9 @@
  * Class managing the set of faces per user across device reboots.
  * @hide
  */
-public class FaceUserState extends BiometricUserState {
+public class FaceUserState extends BiometricUserState<Face> {
 
     private static final String TAG = "FaceState";
-    private static final String FACE_FILE = "settings_face.xml";
 
     private static final String TAG_FACES = "faces";
     private static final String TAG_FACE = "face";
@@ -52,8 +50,8 @@
     private static final String ATTR_FACE_ID = "faceId";
     private static final String ATTR_DEVICE_ID = "deviceId";
 
-    public FaceUserState(Context ctx, int userId) {
-        super(ctx, userId);
+    public FaceUserState(Context ctx, int userId, String fileName) {
+        super(ctx, userId, fileName);
     }
 
     @Override
@@ -62,29 +60,14 @@
     }
 
     @Override
-    protected String getBiometricFile() {
-        return FACE_FILE;
-    }
-
-    @Override
     protected int getNameTemplateResource() {
         return com.android.internal.R.string.face_name_template;
     }
 
     @Override
-    public void addBiometric(BiometricAuthenticator.Identifier identifier) {
-        if (identifier instanceof Face) {
-            super.addBiometric(identifier);
-        } else {
-            Slog.w(TAG, "Attempted to add non-face identifier");
-        }
-    }
-
-    @Override
-    protected ArrayList getCopy(ArrayList array) {
-        ArrayList<Face> result = new ArrayList<>(array.size());
-        for (int i = 0; i < array.size(); i++) {
-            Face f = (Face) array.get(i);
+    protected ArrayList<Face> getCopy(ArrayList<Face> array) {
+        final ArrayList<Face> result = new ArrayList<>();
+        for (Face f : array) {
             result.add(new Face(f.getName(), f.getBiometricId(), f.getDeviceId()));
         }
         return result;
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceUtils.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceUtils.java
index 0197028..a0ffe58 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceUtils.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceUtils.java
@@ -16,6 +16,7 @@
 
 package com.android.server.biometrics.sensors.face;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.face.Face;
@@ -30,24 +31,59 @@
 /**
  * Utility class for dealing with faces and face settings.
  */
-public class FaceUtils implements BiometricUtils {
+public class FaceUtils implements BiometricUtils<Face> {
 
     private static final Object sInstanceLock = new Object();
-    private static FaceUtils sInstance;
+    // Map<SensorId, FaceUtils>
+    private static SparseArray<FaceUtils> sInstances;
+    private static final String LEGACY_FACE_FILE = "settings_face.xml";
 
     @GuardedBy("this")
-    private final SparseArray<FaceUserState> mUsers = new SparseArray<>();
+    private final SparseArray<FaceUserState> mUserStates;
+    private final String mFileName;
 
-    public static FaceUtils getInstance() {
-        synchronized (sInstanceLock) {
-            if (sInstance == null) {
-                sInstance = new FaceUtils();
-            }
-        }
-        return sInstance;
+    public static FaceUtils getInstance(int sensorId) {
+        // Specify a null fileName to use an auto-generated sensorId-specific filename.
+        return getInstance(sensorId, null /* fileName */);
     }
 
-    private FaceUtils() {
+    /**
+     * Retrieves an instance for the specified sensorId. If the fileName is null, a default
+     * filename (e.g. settings_face_<sensorId>.xml will be generated.
+     *
+     * Specifying an explicit fileName allows for backward compatibility with legacy devices,
+     * where everything is stored in settings_face.xml.
+     */
+    private static FaceUtils getInstance(int sensorId, @Nullable String fileName) {
+        final FaceUtils utils;
+        synchronized (sInstanceLock) {
+            if (sInstances == null) {
+                sInstances = new SparseArray<>();
+            }
+            if (sInstances.get(sensorId) == null) {
+                if (fileName == null) {
+                    fileName = "settings_face_" + sensorId + ".xml";
+                }
+                sInstances.put(sensorId, new FaceUtils(fileName));
+            }
+            utils = sInstances.get(sensorId);
+        }
+        return utils;
+    }
+
+    /**
+     * Legacy getter for {@link android.hardware.biometrics.face.V1_0} and its extended subclasses,
+     * which do not support a well defined sensorId from the HAL.
+     */
+    public static FaceUtils getInstance() {
+        // Note that sensorId for legacy services can be hard-coded to 0 since it's only used
+        // to index into the sensor states map.
+        return getInstance(0 /* sensorId */, LEGACY_FACE_FILE);
+    }
+
+    private FaceUtils(String fileName) {
+        mUserStates = new SparseArray<>();
+        mFileName = fileName;
     }
 
     @Override
@@ -56,9 +92,8 @@
     }
 
     @Override
-    public void addBiometricForUser(Context ctx, int userId,
-            BiometricAuthenticator.Identifier identifier) {
-        getStateForUser(ctx, userId).addBiometric(identifier);
+    public void addBiometricForUser(Context ctx, int userId, Face face) {
+        getStateForUser(ctx, userId).addBiometric(face);
     }
 
     @Override
@@ -82,10 +117,10 @@
 
     private FaceUserState getStateForUser(Context ctx, int userId) {
         synchronized (this) {
-            FaceUserState state = mUsers.get(userId);
+            FaceUserState state = mUserStates.get(userId);
             if (state == null) {
-                state = new FaceUserState(ctx, userId);
-                mUsers.put(userId, state);
+                state = new FaceUserState(ctx, userId, mFileName);
+                mUserStates.put(userId, state);
             }
             return state;
         }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index d353994..5dcadee 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -20,6 +20,7 @@
 import static android.Manifest.permission.MANAGE_BIOMETRIC;
 import static android.Manifest.permission.MANAGE_FINGERPRINT;
 import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT;
+import static android.Manifest.permission.TEST_BIOMETRIC;
 import static android.Manifest.permission.USE_BIOMETRIC;
 import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
 import static android.Manifest.permission.USE_FINGERPRINT;
@@ -32,6 +33,9 @@
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.IBiometricSensorReceiver;
 import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.hardware.biometrics.ITestSession;
+import android.hardware.biometrics.fingerprint.IFingerprint;
+import android.hardware.biometrics.fingerprint.SensorProps;
 import android.hardware.fingerprint.Fingerprint;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.hardware.fingerprint.IFingerprintClientActiveCallback;
@@ -40,11 +44,16 @@
 import android.hardware.fingerprint.IUdfpsOverlayController;
 import android.os.Binder;
 import android.os.Build;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.NativeHandle;
 import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.EventLog;
 import android.util.Pair;
 import android.util.Slog;
@@ -53,11 +62,13 @@
 import com.android.internal.R;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.widget.LockPatternUtils;
+import com.android.server.ServiceThread;
 import com.android.server.SystemService;
 import com.android.server.biometrics.Utils;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.LockoutResetDispatcher;
 import com.android.server.biometrics.sensors.LockoutTracker;
+import com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintProvider;
 import com.android.server.biometrics.sensors.fingerprint.hidl.Fingerprint21;
 import com.android.server.biometrics.sensors.fingerprint.hidl.Fingerprint21UdfpsMock;
 
@@ -81,11 +92,74 @@
     private final GestureAvailabilityDispatcher mGestureAvailabilityDispatcher;
     private final LockPatternUtils mLockPatternUtils;
     @NonNull private List<ServiceProvider> mServiceProviders;
+    @NonNull private final ArrayMap<Integer, TestSession> mTestSessions;
+
+    private final class TestSession extends ITestSession.Stub {
+        private final int mSensorId;
+
+        TestSession(int sensorId) {
+            mSensorId = sensorId;
+        }
+
+        @Override
+        public void enableTestHal(boolean enableTestHal) {
+            Utils.checkPermission(getContext(), TEST_BIOMETRIC);
+        }
+
+        @Override
+        public void startEnroll(int userId) {
+            Utils.checkPermission(getContext(), TEST_BIOMETRIC);
+        }
+
+        @Override
+        public void finishEnroll(int userId) {
+            Utils.checkPermission(getContext(), TEST_BIOMETRIC);
+        }
+
+        @Override
+        public void acceptAuthentication(int userId)  {
+            Utils.checkPermission(getContext(), TEST_BIOMETRIC);
+        }
+
+        @Override
+        public void rejectAuthentication(int userId)  {
+            Utils.checkPermission(getContext(), TEST_BIOMETRIC);
+        }
+
+        @Override
+        public void notifyAcquired(int userId, int acquireInfo)  {
+            Utils.checkPermission(getContext(), TEST_BIOMETRIC);
+        }
+
+        @Override
+        public void notifyError(int userId, int errorCode)  {
+            Utils.checkPermission(getContext(), TEST_BIOMETRIC);
+        }
+
+        @Override
+        public void cleanupInternalState(int userId)  {
+            Utils.checkPermission(getContext(), TEST_BIOMETRIC);
+        }
+    }
 
     /**
      * Receives the incoming binder calls from FingerprintManager.
      */
     private final class FingerprintServiceWrapper extends IFingerprintService.Stub {
+        @Override
+        public ITestSession createTestSession(int sensorId, String opPackageName) {
+            Utils.checkPermission(getContext(), TEST_BIOMETRIC);
+
+            final TestSession session;
+            synchronized (mTestSessions) {
+                if (!mTestSessions.containsKey(sensorId)) {
+                    mTestSessions.put(sensorId, new TestSession(sensorId));
+                }
+                session = mTestSessions.get(sensorId);
+            }
+            return session;
+        }
+
         @Override // Binder call
         public List<FingerprintSensorPropertiesInternal> getSensorPropertiesInternal(
                 String opPackageName) {
@@ -100,7 +174,7 @@
         }
 
         @Override // Binder call
-        public void generateChallenge(IBinder token, int sensorId,
+        public void generateChallenge(IBinder token, int sensorId, int userId,
                 IFingerprintServiceReceiver receiver, String opPackageName) {
             Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
 
@@ -110,20 +184,22 @@
                 return;
             }
 
-            provider.scheduleGenerateChallenge(sensorId, token, receiver, opPackageName);
+            provider.scheduleGenerateChallenge(sensorId, userId, token, receiver, opPackageName);
         }
 
         @Override // Binder call
-        public void revokeChallenge(IBinder token, String opPackageName) {
+        public void revokeChallenge(IBinder token, int sensorId, int userId, String opPackageName,
+                long challenge) {
             Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
 
-            final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+            final ServiceProvider provider = getProviderForSensor(sensorId);
             if (provider == null) {
-                Slog.w(TAG, "Null provider for revokeChallenge");
+                Slog.w(TAG, "No matching sensor for revokeChallenge, sensorId: " + sensorId);
                 return;
             }
 
-            provider.second.scheduleRevokeChallenge(provider.first, token, opPackageName);
+            provider.scheduleRevokeChallenge(sensorId, userId, token, opPackageName,
+                    challenge);
         }
 
         @Override // Binder call
@@ -506,7 +582,7 @@
         }
 
         @Override
-        public void onFingerDown(int sensorId, int x, int y, float minor, float major) {
+        public void onPointerDown(int sensorId, int x, int y, float minor, float major) {
             Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
 
             final ServiceProvider provider = getProviderForSensor(sensorId);
@@ -514,11 +590,11 @@
                 Slog.w(TAG, "No matching provider for onFingerDown, sensorId: " + sensorId);
                 return;
             }
-            provider.onFingerDown(sensorId, x, y, minor, major);
+            provider.onPointerDown(sensorId, x, y, minor, major);
         }
 
         @Override
-        public void onFingerUp(int sensorId) {
+        public void onPointerUp(int sensorId) {
             Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
 
             final ServiceProvider provider = getProviderForSensor(sensorId);
@@ -526,7 +602,7 @@
                 Slog.w(TAG, "No matching provider for onFingerUp, sensorId: " + sensorId);
                 return;
             }
-            provider.onFingerUp(sensorId);
+            provider.onPointerUp(sensorId);
         }
 
         @Override
@@ -546,6 +622,40 @@
         mLockoutResetDispatcher = new LockoutResetDispatcher(context);
         mLockPatternUtils = new LockPatternUtils(context);
         mServiceProviders = new ArrayList<>();
+        mTestSessions = new ArrayMap<>();
+
+        initializeAidlHals();
+    }
+
+    private void initializeAidlHals() {
+        final String[] instances = ServiceManager.getDeclaredInstances(IFingerprint.DESCRIPTOR);
+        if (instances == null || instances.length == 0) {
+            return;
+        }
+
+        // If for some reason the HAL is not started before the system service, do not block
+        // the rest of system server. Put this on a background thread.
+        final ServiceThread thread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND,
+                true /* allowIo */);
+        thread.start();
+        final Handler handler = new Handler(thread.getLooper());
+
+        handler.post(() -> {
+            for (String instance : instances) {
+                final String fqName = IFingerprint.DESCRIPTOR + "/" + instance;
+                final IFingerprint fp = IFingerprint.Stub.asInterface(
+                        ServiceManager.waitForDeclaredService(fqName));
+                try {
+                    final SensorProps[] props = fp.getSensorProps();
+                    final FingerprintProvider provider =
+                            new FingerprintProvider(getContext(), props, fqName,
+                                    mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
+                    mServiceProviders.add(provider);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Remote exception when initializing instance: " + fqName);
+                }
+            }
+        });
     }
 
     @Override
@@ -571,6 +681,7 @@
     private Pair<Integer, ServiceProvider> getSingleProvider() {
         final List<FingerprintSensorPropertiesInternal> properties = getSensorProperties();
         if (properties.size() != 1) {
+            Slog.e(TAG, "Multiple sensors found: " + properties.size());
             return null;
         }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUserState.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUserState.java
index 56312bc..e56c8d5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUserState.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUserState.java
@@ -17,7 +17,6 @@
 package com.android.server.biometrics.sensors.fingerprint;
 
 import android.content.Context;
-import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.fingerprint.Fingerprint;
 import android.util.AtomicFile;
 import android.util.Slog;
@@ -40,10 +39,9 @@
  * Class managing the set of fingerprint per user across device reboots.
  * @hide
  */
-public class FingerprintUserState extends BiometricUserState {
+public class FingerprintUserState extends BiometricUserState<Fingerprint> {
 
     private static final String TAG = "FingerprintState";
-    private static final String FINGERPRINT_FILE = "settings_fingerprint.xml";
 
     private static final String TAG_FINGERPRINTS = "fingerprints";
     private static final String TAG_FINGERPRINT = "fingerprint";
@@ -52,8 +50,8 @@
     private static final String ATTR_FINGER_ID = "fingerId";
     private static final String ATTR_DEVICE_ID = "deviceId";
 
-    public FingerprintUserState(Context context, int userId) {
-        super(context, userId);
+    public FingerprintUserState(Context context, int userId, String fileName) {
+        super(context, userId, fileName);
     }
 
     @Override
@@ -62,29 +60,14 @@
     }
 
     @Override
-    protected String getBiometricFile() {
-        return FINGERPRINT_FILE;
-    }
-
-    @Override
     protected int getNameTemplateResource() {
         return com.android.internal.R.string.fingerprint_name_template;
     }
 
     @Override
-    public void addBiometric(BiometricAuthenticator.Identifier identifier) {
-        if (identifier instanceof Fingerprint) {
-            super.addBiometric(identifier);
-        } else {
-            Slog.w(TAG, "Attempted to add non-fingerprint identifier");
-        }
-    }
-
-    @Override
-    protected ArrayList getCopy(ArrayList array) {
-        ArrayList<Fingerprint> result = new ArrayList<>();
-        for (int i = 0; i < array.size(); i++) {
-            Fingerprint fp = (Fingerprint) array.get(i);
+    protected ArrayList<Fingerprint> getCopy(ArrayList<Fingerprint> array) {
+        final ArrayList<Fingerprint> result = new ArrayList<>();
+        for (Fingerprint fp : array) {
             result.add(new Fingerprint(fp.getName(), fp.getGroupId(), fp.getBiometricId(),
                     fp.getDeviceId()));
         }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java
index f0bfe12..6da8650 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java
@@ -16,8 +16,8 @@
 
 package com.android.server.biometrics.sensors.fingerprint;
 
+import android.annotation.Nullable;
 import android.content.Context;
-import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.fingerprint.Fingerprint;
 import android.text.TextUtils;
 import android.util.SparseArray;
@@ -30,24 +30,62 @@
 /**
  * Utility class for dealing with fingerprints and fingerprint settings.
  */
-public class FingerprintUtils implements BiometricUtils {
+public class FingerprintUtils implements BiometricUtils<Fingerprint> {
 
     private static final Object sInstanceLock = new Object();
-    private static FingerprintUtils sInstance;
+    // Map<SensorId, FingerprintUtils>
+    private static SparseArray<FingerprintUtils> sInstances;
+    private static final String LEGACY_FINGERPRINT_FILE = "settings_fingerprint.xml";
 
     @GuardedBy("this")
-    private final SparseArray<FingerprintUserState> mUsers = new SparseArray<>();
+    private final SparseArray<FingerprintUserState> mUserStates;
+    private final String mFileName;
 
-    public static FingerprintUtils getInstance() {
-        synchronized (sInstanceLock) {
-            if (sInstance == null) {
-                sInstance = new FingerprintUtils();
-            }
-        }
-        return sInstance;
+    /**
+     * Retrieves an instance for the specified sensorId.
+     */
+    public static FingerprintUtils getInstance(int sensorId) {
+        // Specify a null fileName to use an auto-generated sensorId-specific filename.
+        return getInstance(sensorId, null /* fileName */);
     }
 
-    private FingerprintUtils() {
+    /**
+     * Retrieves an instance for the specified sensorId. If the fileName is null, a default
+     * filename (e.g. settings_fingerprint_<sensorId>.xml will be generated.
+     *
+     * Specifying an explicit fileName allows for backward compatibility with legacy devices,
+     * where everything is stored in settings_fingerprint.xml.
+     */
+    private static FingerprintUtils getInstance(int sensorId, @Nullable String fileName) {
+        final FingerprintUtils utils;
+        synchronized (sInstanceLock) {
+            if (sInstances == null) {
+                sInstances = new SparseArray<>();
+            }
+            if (sInstances.get(sensorId) == null) {
+                if (fileName == null) {
+                    fileName = "settings_fingerprint_" + sensorId + ".xml";
+                }
+                sInstances.put(sensorId, new FingerprintUtils(fileName));
+            }
+            utils = sInstances.get(sensorId);
+        }
+        return utils;
+    }
+
+    /**
+     * Legacy getter for {@link android.hardware.biometrics.fingerprint.V2_1} ands its extended
+     * subclasses, which do not support a well defined sensorId from the HAL.
+     */
+    public static FingerprintUtils getInstance() {
+        // Note that sensorId for legacy services can be hard-coded to 0 since it's only used
+        // to index into the sensor states map.
+        return getInstance(0 /* sensorId */, LEGACY_FINGERPRINT_FILE);
+    }
+
+    private FingerprintUtils(String fileName) {
+        mUserStates = new SparseArray<>();
+        mFileName = fileName;
     }
 
     @Override
@@ -56,9 +94,8 @@
     }
 
     @Override
-    public void addBiometricForUser(Context context, int userId,
-            BiometricAuthenticator.Identifier identifier) {
-        getStateForUser(context, userId).addBiometric(identifier);
+    public void addBiometricForUser(Context context, int userId, Fingerprint fingerprint) {
+        getStateForUser(context, userId).addBiometric(fingerprint);
     }
 
     @Override
@@ -83,10 +120,10 @@
 
     private FingerprintUserState getStateForUser(Context ctx, int userId) {
         synchronized (this) {
-            FingerprintUserState state = mUsers.get(userId);
+            FingerprintUserState state = mUserStates.get(userId);
             if (state == null) {
-                state = new FingerprintUserState(ctx, userId);
-                mUsers.put(userId, state);
+                state = new FingerprintUserState(ctx, userId, mFileName);
+                mUserStates.put(userId, state);
             }
             return state;
         }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
index d7338a0..c2315fd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
@@ -64,11 +64,11 @@
 
     void scheduleResetLockout(int sensorId, int userId, @Nullable byte[] hardwareAuthToken);
 
-    void scheduleGenerateChallenge(int sensorId, @NonNull IBinder token,
+    void scheduleGenerateChallenge(int sensorId, int userId, @NonNull IBinder token,
             @NonNull IFingerprintServiceReceiver receiver, String opPackageName);
 
-    void scheduleRevokeChallenge(int sensorId, @NonNull IBinder token,
-            @NonNull String opPackageName);
+    void scheduleRevokeChallenge(int sensorId, int userId, @NonNull IBinder token,
+            @NonNull String opPackageName, long challenge);
 
     void scheduleEnroll(int sensorId, @NonNull IBinder token, byte[] hardwareAuthToken, int userId,
             @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
@@ -92,6 +92,8 @@
             @NonNull IFingerprintServiceReceiver receiver, int fingerId, int userId,
             @NonNull String opPackageName);
 
+    void scheduleInternalCleanup(int sensorId, int userId);
+
     boolean isHardwareDetected(int sensorId);
 
     void rename(int sensorId, int fingerId, int userId, @NonNull String name);
@@ -102,9 +104,9 @@
 
     long getAuthenticatorId(int sensorId, int userId);
 
-    void onFingerDown(int sensorId, int x, int y, float minor, float major);
+    void onPointerDown(int sensorId, int x, int y, float minor, float major);
 
-    void onFingerUp(int sensorId);
+    void onPointerUp(int sensorId);
 
     void setUdfpsOverlayController(@NonNull IUdfpsOverlayController controller);
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Udfps.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java
similarity index 85%
rename from services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Udfps.java
rename to services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java
index 74cae02..0bf107a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Udfps.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.biometrics.sensors.fingerprint.hidl;
+package com.android.server.biometrics.sensors.fingerprint;
 
 /**
  * Interface for under-display fingerprint sensors.
@@ -22,6 +22,6 @@
  * finger position (e.g. enroll, authenticate) should implement this.
  */
 public interface Udfps {
-    void onFingerDown(int x, int y, float minor, float major);
-    void onFingerUp();
+    void onPointerDown(int x, int y, float minor, float major);
+    void onPointerUp();
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/UdfpsHelper.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java
similarity index 89%
rename from services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/UdfpsHelper.java
rename to services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java
index 0f1d6b4..a2b871e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/UdfpsHelper.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.biometrics.sensors.fingerprint.hidl;
+package com.android.server.biometrics.sensors.fingerprint;
 
 import android.annotation.Nullable;
 import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
@@ -29,7 +29,7 @@
 
     private static final String TAG = "UdfpsHelper";
 
-    static void onFingerDown(IBiometricsFingerprint daemon, int x, int y, float minor,
+    public static void onFingerDown(IBiometricsFingerprint daemon, int x, int y, float minor,
             float major) {
         android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint extension =
                 android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint.castFrom(
@@ -46,7 +46,7 @@
         }
     }
 
-    static void onFingerUp(IBiometricsFingerprint daemon) {
+    public static void onFingerUp(IBiometricsFingerprint daemon) {
         android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint extension =
                 android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint.castFrom(
                         daemon);
@@ -62,7 +62,7 @@
         }
     }
 
-    static void showUdfpsOverlay(int sensorId,
+    public static void showUdfpsOverlay(int sensorId,
             @Nullable IUdfpsOverlayController udfpsOverlayController) {
         if (udfpsOverlayController == null) {
             return;
@@ -74,7 +74,7 @@
         }
     }
 
-    static void hideUdfpsOverlay(int sensorId,
+    public static void hideUdfpsOverlay(int sensorId,
             @Nullable IUdfpsOverlayController udfpsOverlayController) {
         if (udfpsOverlayController == null) {
             return;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
new file mode 100644
index 0000000..8acf4f5
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.biometrics.sensors.fingerprint.aidl;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.TaskStackListener;
+import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricFingerprintConstants;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.common.ICancellationSignal;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.AuthenticationClient;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.LockoutConsumer;
+import com.android.server.biometrics.sensors.LockoutTracker;
+import com.android.server.biometrics.sensors.fingerprint.Udfps;
+import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
+
+import java.util.ArrayList;
+
+/**
+ * Fingerprint-specific authentication client supporting the
+ * {@link android.hardware.biometrics.fingerprint.IFingerprint} AIDL interface.
+ */
+class FingerprintAuthenticationClient extends AuthenticationClient<ISession> implements
+        Udfps, LockoutConsumer {
+    private static final String TAG = "FingerprintAuthenticationClient";
+
+    @NonNull private final LockoutCache mLockoutCache;
+    @Nullable private final IUdfpsOverlayController mUdfpsOverlayController;
+    @Nullable private ICancellationSignal mCancellationSignal;
+
+    FingerprintAuthenticationClient(@NonNull Context context,
+            @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token,
+            @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
+            boolean restricted, @NonNull String owner, int cookie, boolean requireConfirmation,
+            int sensorId, boolean isStrongBiometric, int statsClient,
+            @Nullable TaskStackListener taskStackListener, @NonNull LockoutCache lockoutCache,
+            @Nullable IUdfpsOverlayController udfpsOverlayController) {
+        super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted, owner,
+                cookie, requireConfirmation, sensorId, isStrongBiometric,
+                BiometricsProtoEnums.MODALITY_FINGERPRINT, statsClient, taskStackListener,
+                lockoutCache);
+        mLockoutCache = lockoutCache;
+        mUdfpsOverlayController = udfpsOverlayController;
+    }
+
+    @Override
+    public void onAuthenticated(BiometricAuthenticator.Identifier identifier,
+            boolean authenticated, ArrayList<Byte> token) {
+        super.onAuthenticated(identifier, authenticated, token);
+
+        if (authenticated) {
+            UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+            mCallback.onClientFinished(this, true /* success */);
+        }
+    }
+
+    @Override
+    protected void startHalOperation() {
+        UdfpsHelper.showUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+        try {
+            mCancellationSignal = getFreshDaemon().authenticate(mSequentialId, mOperationId);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Remote exception", e);
+            onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
+                    0 /* vendorCode */);
+            UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+            mCallback.onClientFinished(this, false /* success */);
+        }
+    }
+
+    @Override
+    protected void stopHalOperation() {
+        UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+        try {
+            mCancellationSignal.cancel();
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Remote exception", e);
+            onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
+                    0 /* vendorCode */);
+            mCallback.onClientFinished(this, false /* success */);
+        }
+    }
+
+    @Override
+    public void onPointerDown(int x, int y, float minor, float major) {
+        try {
+            getFreshDaemon().onPointerDown(0 /* pointerId */, x, y, minor, major);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Remote exception", e);
+        }
+    }
+
+    @Override
+    public void onPointerUp() {
+        try {
+            getFreshDaemon().onPointerUp(0 /* pointerId */);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Remote exception", e);
+        }
+    }
+
+    @Override
+    public void onLockoutTimed(long durationMillis) {
+        mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_TIMED);
+        // Lockout metrics are logged as an error code.
+        final int error = BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT;
+        logOnError(getContext(), error, 0 /* vendorCode */, getTargetUserId());
+
+        try {
+            getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Remote exception", e);
+        }
+    }
+
+    @Override
+    public void onLockoutPermanent() {
+        mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_PERMANENT);
+        // Lockout metrics are logged as an error code.
+        final int error = BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT;
+        logOnError(getContext(), error, 0 /* vendorCode */, getTargetUserId());
+
+        try {
+            getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Remote exception", e);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
new file mode 100644
index 0000000..3398323
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.biometrics.sensors.fingerprint.aidl;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.common.ICancellationSignal;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.AcquisitionClient;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
+
+/**
+ * Performs fingerprint detection without exposing any matching information (e.g. accept/reject
+ * have the same haptic, lockout counter is not increased).
+ */
+class FingerprintDetectClient extends AcquisitionClient<ISession> {
+
+    private static final String TAG = "FingerprintDetectClient";
+
+    private final boolean mIsStrongBiometric;
+    @Nullable private final IUdfpsOverlayController mUdfpsOverlayController;
+
+    @Nullable private ICancellationSignal mCancellationSignal;
+
+    FingerprintDetectClient(@NonNull Context context, @NonNull LazyDaemon<ISession> lazyDaemon,
+            @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
+            @NonNull String owner, int sensorId,
+            @Nullable IUdfpsOverlayController udfpsOverlayController, boolean isStrongBiometric,
+            int statsClient) {
+        super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
+                BiometricsProtoEnums.MODALITY_FINGERPRINT, BiometricsProtoEnums.ACTION_AUTHENTICATE,
+                statsClient);
+        mIsStrongBiometric = isStrongBiometric;
+        mUdfpsOverlayController = udfpsOverlayController;
+    }
+
+    @Override
+    protected void stopHalOperation() {
+        UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+        try {
+            mCancellationSignal.cancel();
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Remote exception", e);
+            mCallback.onClientFinished(this, false /* success */);
+        }
+    }
+
+    @Override
+    protected void startHalOperation() {
+        UdfpsHelper.showUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+        try {
+            mCancellationSignal = getFreshDaemon().detectInteraction(mSequentialId);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Remote exception when requesting finger detect", e);
+            UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+            mCallback.onClientFinished(this, false /* success */);
+        }
+    }
+
+    void onInteractionDetected() {
+        vibrateSuccess();
+
+        try {
+            getListener().onDetected(getSensorId(), getTargetUserId(), mIsStrongBiometric);
+            mCallback.onClientFinished(this, true /* success */);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Remote exception when sending onDetected", e);
+            mCallback.onClientFinished(this, false /* success */);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
new file mode 100644
index 0000000..437ecd7
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.biometrics.sensors.fingerprint.aidl;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.biometrics.BiometricFaceConstants;
+import android.hardware.biometrics.BiometricFingerprintConstants;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.common.ICancellationSignal;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.EnrollClient;
+import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
+import com.android.server.biometrics.sensors.fingerprint.Udfps;
+import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
+
+class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps {
+
+    private static final String TAG = "FingerprintEnrollClient";
+
+    @Nullable private final IUdfpsOverlayController mUdfpsOverlayController;
+    @Nullable private ICancellationSignal mCancellationSignal;
+    private final int mMaxTemplatesPerUser;
+
+    FingerprintEnrollClient(@NonNull Context context,
+            @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token,
+            @NonNull ClientMonitorCallbackConverter listener, int userId,
+            @NonNull byte[] hardwareAuthToken, @NonNull String owner,
+            @NonNull BiometricUtils<Fingerprint> utils, int sensorId,
+            @Nullable IUdfpsOverlayController udfpsOvelayController, int maxTemplatesPerUser) {
+        super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
+                0 /* timeoutSec */, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId,
+                true /* shouldVibrate */);
+        mUdfpsOverlayController = udfpsOvelayController;
+        mMaxTemplatesPerUser = maxTemplatesPerUser;
+    }
+
+    @Override
+    protected boolean hasReachedEnrollmentLimit() {
+        return FingerprintUtils.getInstance(getSensorId())
+                .getBiometricsForUser(getContext(), getTargetUserId()).size()
+                >= mMaxTemplatesPerUser;
+    }
+
+    @Override
+    protected void stopHalOperation() {
+        UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+        if (mCancellationSignal != null) {
+            try {
+                mCancellationSignal.cancel();
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Remote exception when requesting cancel", e);
+                onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
+                        0 /* vendorCode */);
+                mCallback.onClientFinished(this, false /* success */);
+            }
+        }
+    }
+
+    @Override
+    protected void startHalOperation() {
+        UdfpsHelper.showUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+        try {
+            mCancellationSignal = getFreshDaemon().enroll(mSequentialId,
+                    HardwareAuthTokenUtils.toHardwareAuthToken(mHardwareAuthToken));
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Remote exception when requesting enroll", e);
+            onError(BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS, 0 /* vendorCode */);
+            mCallback.onClientFinished(this, false /* success */);
+        }
+    }
+
+    @Override
+    public void onPointerDown(int x, int y, float minor, float major) {
+        try {
+            getFreshDaemon().onPointerDown(0 /* pointerId */, x, y, minor, major);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Unable to send pointer down", e);
+        }
+    }
+
+    @Override
+    public void onPointerUp() {
+        try {
+            getFreshDaemon().onPointerUp(0 /* pointerId */);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Unable to send pointer up", e);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
new file mode 100644
index 0000000..402886b
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.biometrics.sensors.fingerprint.aidl;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.fingerprint.IFingerprint;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.GenerateChallengeClient;
+
+/**
+ * Fingerprint-specific generateChallenge client for the {@link IFingerprint} AIDL HAL interface.
+ */
+class FingerprintGenerateChallengeClient extends GenerateChallengeClient<ISession> {
+    private static final String TAG = "FingerprintGenerateChallengeClient";
+    private static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes
+
+    FingerprintGenerateChallengeClient(@NonNull Context context,
+            @NonNull LazyDaemon<ISession> lazyDaemon,
+            @NonNull IBinder token,
+            @NonNull ClientMonitorCallbackConverter listener,
+            @NonNull String owner, int sensorId) {
+        super(context, lazyDaemon, token, listener, owner, sensorId);
+    }
+
+    @Override
+    protected void startHalOperation() {
+        try {
+            getFreshDaemon().generateChallenge(mSequentialId, CHALLENGE_TIMEOUT_SEC);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Unable to generateChallenge", e);
+        }
+    }
+
+    void onChallengeGenerated(int sensorId, int userId, long challenge) {
+        try {
+            getListener().onChallengeGenerated(sensorId, challenge);
+            mCallback.onClientFinished(FingerprintGenerateChallengeClient.this,
+                    true /* success */);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Unable to send challenge", e);
+            mCallback.onClientFinished(FingerprintGenerateChallengeClient.this,
+                    false /* success */);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java
new file mode 100644
index 0000000..fec3cff
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.biometrics.sensors.fingerprint.aidl;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.ClientMonitor;
+
+import java.util.Map;
+
+class FingerprintGetAuthenticatorIdClient extends ClientMonitor<ISession> {
+
+    private static final String TAG = "FingerprintGetAuthenticatorIdClient";
+
+    private final Map<Integer, Long> mAuthenticatorIds;
+
+    FingerprintGetAuthenticatorIdClient(@NonNull Context context,
+            @NonNull LazyDaemon<ISession> lazyDaemon, int userId, @NonNull String owner,
+            int sensorId, Map<Integer, Long> authenticatorIds) {
+        super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
+                0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_FINGERPRINT,
+                BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
+        mAuthenticatorIds = authenticatorIds;
+    }
+
+    @Override
+    public void unableToStart() {
+        // Nothing to do here
+    }
+
+    @Override
+    protected void startHalOperation() {
+        try {
+            getFreshDaemon().getAuthenticatorId(mSequentialId);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Remote exception", e);
+        }
+    }
+
+    void onAuthenticatorIdRetrieved(long authenticatorId) {
+        mAuthenticatorIds.put(getTargetUserId(), authenticatorId);
+        mCallback.onClientFinished(this, true /* success */);
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
new file mode 100644
index 0000000..2a0e984
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.biometrics.sensors.fingerprint.aidl;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.fingerprint.Fingerprint;
+import android.os.IBinder;
+
+import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.InternalCleanupClient;
+import com.android.server.biometrics.sensors.InternalEnumerateClient;
+import com.android.server.biometrics.sensors.RemovalClient;
+import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Fingerprint-specific internal cleanup client supporting the
+ * {@link android.hardware.biometrics.fingerprint.IFingerprint} AIDL interface.
+ */
+class FingerprintInternalCleanupClient extends InternalCleanupClient<Fingerprint, ISession> {
+
+    FingerprintInternalCleanupClient(@NonNull Context context,
+            @NonNull LazyDaemon<ISession> lazyDaemon, int userId, @NonNull String owner,
+            int sensorId, @NonNull List<Fingerprint> enrolledList,
+            @NonNull FingerprintUtils utils, @NonNull Map<Integer, Long> authenticatorIds) {
+        super(context, lazyDaemon, userId, owner, sensorId,
+                BiometricsProtoEnums.MODALITY_FINGERPRINT, enrolledList, utils, authenticatorIds);
+    }
+
+    @Override
+    protected InternalEnumerateClient<ISession> getEnumerateClient(Context context,
+            LazyDaemon<ISession> lazyDaemon, IBinder token, int userId, String owner,
+            List<Fingerprint> enrolledList, BiometricUtils<Fingerprint> utils, int sensorId) {
+        return new FingerprintInternalEnumerateClient(context, lazyDaemon, token, userId, owner,
+                enrolledList, utils, sensorId);
+    }
+
+    @Override
+    protected RemovalClient<Fingerprint, ISession> getRemovalClient(Context context,
+            LazyDaemon<ISession> lazyDaemon, IBinder token, int biometricId, int userId,
+            String owner, BiometricUtils<Fingerprint> utils, int sensorId,
+            Map<Integer, Long> authenticatorIds) {
+        return new FingerprintRemovalClient(context, lazyDaemon, token,
+                null /* ClientMonitorCallbackConverter */, biometricId, userId, owner, utils,
+                sensorId, authenticatorIds);
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClient.java
new file mode 100644
index 0000000..c930360
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClient.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.biometrics.sensors.fingerprint.aidl;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.fingerprint.Fingerprint;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.InternalEnumerateClient;
+
+import java.util.List;
+
+/**
+ * Fingerprint-specific internal client supporting the
+ * {@link android.hardware.biometrics.fingerprint.IFingerprint} AIDL interface.
+ */
+class FingerprintInternalEnumerateClient extends InternalEnumerateClient<ISession> {
+    private static final String TAG = "FingerprintInternalEnumerateClient";
+
+    protected FingerprintInternalEnumerateClient(@NonNull Context context,
+            @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token, int userId,
+            @NonNull String owner, @NonNull List<Fingerprint> enrolledList,
+            @NonNull BiometricUtils<Fingerprint> utils, int sensorId) {
+        super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId,
+                BiometricsProtoEnums.MODALITY_FINGERPRINT);
+    }
+
+    @Override
+    protected void startHalOperation() {
+        try {
+            getFreshDaemon().enumerateEnrollments(mSequentialId);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Remote exception when requesting enumerate", e);
+            mCallback.onClientFinished(this, false /* success */);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
new file mode 100644
index 0000000..d713f98
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -0,0 +1,585 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.biometrics.sensors.fingerprint.aidl;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.IActivityTaskManager;
+import android.app.TaskStackListener;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.hardware.biometrics.fingerprint.IFingerprint;
+import android.hardware.biometrics.fingerprint.SensorProps;
+import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.hardware.fingerprint.IFingerprintServiceReceiver;
+import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserManager;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.Surface;
+
+import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.sensors.AuthenticationClient;
+import com.android.server.biometrics.sensors.ClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.PerformanceTracker;
+import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
+import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
+import com.android.server.biometrics.sensors.fingerprint.ServiceProvider;
+import com.android.server.biometrics.sensors.fingerprint.Udfps;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Provider for a single instance of the {@link IFingerprint} HAL.
+ */
+@SuppressWarnings("deprecation")
+public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvider {
+
+    @NonNull private final Context mContext;
+    @NonNull private final String mHalInstanceName;
+    @NonNull private final SparseArray<Sensor> mSensors; // Map of sensors that this HAL supports
+    @NonNull private final ClientMonitor.LazyDaemon<IFingerprint> mLazyDaemon;
+    @NonNull private final Handler mHandler;
+    @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher;
+    @NonNull private final IActivityTaskManager mActivityTaskManager;
+    @NonNull private final BiometricTaskStackListener mTaskStackListener;
+
+    @Nullable private IFingerprint mDaemon;
+    @Nullable private IUdfpsOverlayController mUdfpsOverlayController;
+
+    private final class BiometricTaskStackListener extends TaskStackListener {
+        @Override
+        public void onTaskStackChanged() {
+            mHandler.post(() -> {
+                for (int i = 0; i < mSensors.size(); i++) {
+                    final ClientMonitor<?> client = mSensors.get(i).getScheduler()
+                            .getCurrentClient();
+                    if (!(client instanceof AuthenticationClient)) {
+                        Slog.e(getTag(), "Task stack changed for client: " + client);
+                        continue;
+                    }
+                    if (Utils.isKeyguard(mContext, client.getOwnerString())) {
+                        continue; // Keyguard is always allowed
+                    }
+
+                    try {
+                        final List<ActivityManager.RunningTaskInfo> runningTasks =
+                                mActivityTaskManager.getTasks(1);
+                        if (!runningTasks.isEmpty()) {
+                            final String topPackage =
+                                    runningTasks.get(0).topActivity.getPackageName();
+                            if (!topPackage.contentEquals(client.getOwnerString())
+                                    && !client.isAlreadyDone()) {
+                                Slog.e(getTag(), "Stopping background authentication, top: "
+                                        + topPackage + " currentClient: " + client);
+                                mSensors.get(i).getScheduler()
+                                        .cancelAuthentication(client.getToken());
+                            }
+                        }
+                    } catch (RemoteException e) {
+                        Slog.e(getTag(), "Unable to get running tasks", e);
+                    }
+                }
+            });
+        }
+    }
+
+    public FingerprintProvider(@NonNull Context context, @NonNull SensorProps[] props,
+            @NonNull String halInstanceName, @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+            @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
+        mContext = context;
+        mHalInstanceName = halInstanceName;
+        mSensors = new SparseArray<>();
+        mLazyDaemon = this::getHalInstance;
+        mHandler = new Handler(Looper.getMainLooper());
+        mLockoutResetDispatcher = lockoutResetDispatcher;
+        mActivityTaskManager = ActivityTaskManager.getService();
+        mTaskStackListener = new BiometricTaskStackListener();
+
+        for (SensorProps prop : props) {
+            final int sensorId = prop.commonProps.sensorId;
+
+            final FingerprintSensorPropertiesInternal internalProp =
+                    new FingerprintSensorPropertiesInternal(prop.commonProps.sensorId,
+                            prop.commonProps.sensorStrength,
+                            prop.commonProps.maxEnrollmentsPerUser,
+                            prop.sensorType,
+                            true /* resetLockoutRequiresHardwareAuthToken */);
+            final Sensor sensor = new Sensor(getTag() + "/" + sensorId, mContext, mHandler,
+                    internalProp, gestureAvailabilityDispatcher);
+
+            mSensors.put(sensorId, sensor);
+            Slog.d(getTag(), "Added: " + internalProp);
+        }
+    }
+
+    private String getTag() {
+        return "FingerprintProvider/" + mHalInstanceName;
+    }
+
+    @Nullable
+    private synchronized IFingerprint getHalInstance() {
+        if (mDaemon != null) {
+            return mDaemon;
+        }
+
+        Slog.d(getTag(), "Daemon was null, reconnecting");
+
+        mDaemon = IFingerprint.Stub.asInterface(
+                ServiceManager.waitForDeclaredService(mHalInstanceName));
+        if (mDaemon == null) {
+            Slog.e(getTag(), "Unable to get daemon");
+            return null;
+        }
+
+        try {
+            mDaemon.asBinder().linkToDeath(this, 0 /* flags */);
+        } catch (RemoteException e) {
+            Slog.e(getTag(), "Unable to linkToDeath", e);
+        }
+
+        for (int i = 0; i < mSensors.size(); i++) {
+            final int sensorId = mSensors.keyAt(i);
+            scheduleLoadAuthenticatorIds(sensorId);
+            scheduleInternalCleanup(sensorId, ActivityManager.getCurrentUser());
+        }
+
+        return mDaemon;
+    }
+
+    private void scheduleForSensor(int sensorId, @NonNull ClientMonitor<?> client) {
+        if (!mSensors.contains(sensorId)) {
+            throw new IllegalStateException("Unable to schedule client: " + client
+                    + " for sensor: " + sensorId);
+        }
+        mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client);
+    }
+
+    private void scheduleForSensor(int sensorId, @NonNull ClientMonitor<?> client,
+            ClientMonitor.Callback callback) {
+        if (!mSensors.contains(sensorId)) {
+            throw new IllegalStateException("Unable to schedule client: " + client
+                    + " for sensor: " + sensorId);
+        }
+        mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client, callback);
+    }
+
+    private void createNewSessionWithoutHandler(@NonNull IFingerprint daemon, int sensorId,
+            int userId) throws RemoteException {
+        // Note that per IFingerprint createSession contract, this method will block until all
+        // existing operations are canceled/finished. However, also note that this is fine, since
+        // this method "withoutHandler" means it should only ever be invoked from the worker thread,
+        // so callers will never be blocked.
+        mSensors.get(sensorId).createNewSession(daemon, sensorId, userId);
+    }
+
+    @Override
+    public boolean containsSensor(int sensorId) {
+        return mSensors.contains(sensorId);
+    }
+
+    @NonNull
+    @Override
+    public List<FingerprintSensorPropertiesInternal> getSensorProperties() {
+        final List<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
+        for (int i = 0; i < mSensors.size(); i++) {
+            props.add(mSensors.valueAt(i).getSensorProperties());
+        }
+        return props;
+    }
+
+    private void scheduleLoadAuthenticatorIds(int sensorId) {
+        for (UserInfo user : UserManager.get(mContext).getAliveUsers()) {
+            scheduleLoadAuthenticatorIdsForUser(sensorId, user.id);
+        }
+    }
+
+    private void scheduleLoadAuthenticatorIdsForUser(int sensorId, int userId) {
+        mHandler.post(() -> {
+            final IFingerprint daemon = getHalInstance();
+            if (daemon == null) {
+                Slog.e(getTag(), "Null daemon during loadAuthenticatorIds, sensorId: " + sensorId);
+                return;
+            }
+
+            try {
+                if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
+                    createNewSessionWithoutHandler(daemon, sensorId, userId);
+                }
+
+                final FingerprintGetAuthenticatorIdClient client =
+                        new FingerprintGetAuthenticatorIdClient(mContext,
+                                mSensors.get(sensorId).getLazySession(), userId,
+                                mContext.getOpPackageName(), sensorId,
+                                mSensors.get(sensorId).getAuthenticatorIds());
+                mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client);
+            } catch (RemoteException e) {
+                Slog.e(getTag(), "Remote exception when scheduling loadAuthenticatorId"
+                        + ", sensorId: " + sensorId
+                        + ", userId: " + userId, e);
+            }
+        });
+    }
+
+    @Override
+    public void scheduleResetLockout(int sensorId, int userId, @Nullable byte[] hardwareAuthToken) {
+        mHandler.post(() -> {
+            final IFingerprint daemon = getHalInstance();
+            if (daemon == null) {
+                Slog.e(getTag(), "Null daemon during resetLockout, sensorId: " + sensorId);
+                return;
+            }
+
+            try {
+                if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
+                    createNewSessionWithoutHandler(daemon, sensorId, userId);
+                }
+
+                final FingerprintResetLockoutClient client = new FingerprintResetLockoutClient(
+                        mContext, mSensors.get(sensorId).getLazySession(), userId,
+                        mContext.getOpPackageName(), sensorId, hardwareAuthToken,
+                        mSensors.get(sensorId).getLockoutCache(), mLockoutResetDispatcher);
+                scheduleForSensor(sensorId, client);
+            } catch (RemoteException e) {
+                Slog.e(getTag(), "Remote exception when scheduling resetLockout", e);
+            }
+        });
+    }
+
+    @Override
+    public void scheduleGenerateChallenge(int sensorId, int userId, @NonNull IBinder token,
+            @NonNull IFingerprintServiceReceiver receiver, String opPackageName) {
+        mHandler.post(() -> {
+            final IFingerprint daemon = getHalInstance();
+            if (daemon == null) {
+                Slog.e(getTag(), "Null daemon during generateChallenge, sensorId: " + sensorId);
+                return;
+            }
+
+            try {
+                if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
+                    createNewSessionWithoutHandler(daemon, sensorId, userId);
+                }
+
+                final FingerprintGenerateChallengeClient client =
+                        new FingerprintGenerateChallengeClient(mContext,
+                                mSensors.get(sensorId).getLazySession(), token,
+                                new ClientMonitorCallbackConverter(receiver), opPackageName,
+                                sensorId);
+                scheduleForSensor(sensorId, client);
+            } catch (RemoteException e) {
+                Slog.e(getTag(), "Remote exception when scheduling generateChallenge", e);
+            }
+        });
+    }
+
+    @Override
+    public void scheduleRevokeChallenge(int sensorId, int userId, @NonNull IBinder token,
+            @NonNull String opPackageName, long challenge) {
+        mHandler.post(() -> {
+            final IFingerprint daemon = getHalInstance();
+            if (daemon == null) {
+                Slog.e(getTag(), "Null daemon during revokeChallenge, sensorId: " + sensorId);
+                return;
+            }
+
+            try {
+                if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
+                    createNewSessionWithoutHandler(daemon, sensorId, userId);
+                }
+
+                final FingerprintRevokeChallengeClient client =
+                        new FingerprintRevokeChallengeClient(mContext,
+                                mSensors.get(sensorId).getLazySession(), token,
+                                opPackageName, sensorId, challenge);
+                scheduleForSensor(sensorId, client);
+            } catch (RemoteException e) {
+                Slog.e(getTag(), "Remote exception when scheduling revokeChallenge", e);
+            }
+        });
+    }
+
+    @Override
+    public void scheduleEnroll(int sensorId, @NonNull IBinder token, byte[] hardwareAuthToken,
+            int userId, @NonNull IFingerprintServiceReceiver receiver,
+            @NonNull String opPackageName, @Nullable Surface surface) {
+        mHandler.post(() -> {
+            final IFingerprint daemon = getHalInstance();
+            if (daemon == null) {
+                Slog.e(getTag(), "Null daemon during enroll, sensorId: " + sensorId);
+                // If this happens, we need to send HW_UNAVAILABLE after the scheduler gets to
+                // this operation. We should not send the callback yet, since the scheduler may
+                // be processing something else.
+                return;
+            }
+
+            try {
+                if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
+                    createNewSessionWithoutHandler(daemon, sensorId, userId);
+                }
+
+                final int maxTemplatesPerUser = mSensors.get(sensorId).getSensorProperties()
+                        .maxEnrollmentsPerUser;
+                final FingerprintEnrollClient client = new FingerprintEnrollClient(mContext,
+                        mSensors.get(sensorId).getLazySession(), token,
+                        new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
+                        opPackageName, FingerprintUtils.getInstance(sensorId), sensorId,
+                        mUdfpsOverlayController, maxTemplatesPerUser);
+                scheduleForSensor(sensorId, client, new ClientMonitor.Callback() {
+                    @Override
+                    public void onClientFinished(@NonNull ClientMonitor<?> clientMonitor,
+                            boolean success) {
+                        if (success) {
+                            scheduleLoadAuthenticatorIdsForUser(sensorId, userId);
+                        }
+                    }
+                });
+            } catch (RemoteException e) {
+                Slog.e(getTag(), "Remote exception when scheduling enroll", e);
+            }
+        });
+    }
+
+    @Override
+    public void cancelEnrollment(int sensorId, @NonNull IBinder token) {
+        mHandler.post(() -> mSensors.get(sensorId).getScheduler().cancelEnrollment(token));
+    }
+
+    @Override
+    public void scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
+            @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName,
+            @Nullable Surface surface, int statsClient) {
+        mHandler.post(() -> {
+            final IFingerprint daemon = getHalInstance();
+            if (daemon == null) {
+                Slog.e(getTag(), "Null daemon during finger detect, sensorId: " + sensorId);
+                // If this happens, we need to send HW_UNAVAILABLE after the scheduler gets to
+                // this operation. We should not send the callback yet, since the scheduler may
+                // be processing something else.
+                return;
+            }
+
+            try {
+                if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
+                    createNewSessionWithoutHandler(daemon, sensorId, userId);
+                }
+
+                final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId);
+                final FingerprintDetectClient client = new FingerprintDetectClient(mContext,
+                        mSensors.get(sensorId).getLazySession(), token, callback, userId,
+                        opPackageName, sensorId, mUdfpsOverlayController, isStrongBiometric,
+                        statsClient);
+                mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client);
+            } catch (RemoteException e) {
+                Slog.e(getTag(), "Remote exception when scheduling finger detect", e);
+            }
+        });
+    }
+
+    @Override
+    public void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
+            int userId, int cookie, @NonNull ClientMonitorCallbackConverter callback,
+            @NonNull String opPackageName, boolean restricted, int statsClient,
+            boolean isKeyguard) {
+        mHandler.post(() -> {
+            final IFingerprint daemon = getHalInstance();
+            if (daemon == null) {
+                Slog.e(getTag(), "Null daemon during authenticate, sensorId: " + sensorId);
+                // If this happens, we need to send HW_UNAVAILABLE after the scheduler gets to
+                // this operation. We should not send the callback yet, since the scheduler may
+                // be processing something else.
+                return;
+            }
+
+            try {
+                if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
+                    createNewSessionWithoutHandler(daemon, sensorId, userId);
+                }
+
+                final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId);
+                final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient(
+                        mContext, mSensors.get(sensorId).getLazySession(), token, callback, userId,
+                        operationId, restricted, opPackageName, cookie,
+                        false /* requireConfirmation */, sensorId, isStrongBiometric, statsClient,
+                        mTaskStackListener, mSensors.get(sensorId).getLockoutCache(),
+                        mUdfpsOverlayController);
+                mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client);
+            } catch (RemoteException e) {
+                Slog.e(getTag(), "Remote exception when scheduling authenticate", e);
+            }
+        });
+    }
+
+    @Override
+    public void startPreparedClient(int sensorId, int cookie) {
+        mHandler.post(() -> mSensors.get(sensorId).getScheduler().startPreparedClient(cookie));
+    }
+
+    @Override
+    public void cancelAuthentication(int sensorId, @NonNull IBinder token) {
+        mHandler.post(() -> mSensors.get(sensorId).getScheduler().cancelAuthentication(token));
+    }
+
+    @Override
+    public void scheduleRemove(int sensorId, @NonNull IBinder token,
+            @NonNull IFingerprintServiceReceiver receiver, int fingerId, int userId,
+            @NonNull String opPackageName) {
+        mHandler.post(() -> {
+            final IFingerprint daemon = getHalInstance();
+            if (daemon == null) {
+                Slog.e(getTag(), "Null daemon during remove, sensorId: " + sensorId);
+                // If this happens, we need to send HW_UNAVAILABLE after the scheduler gets to
+                // this operation. We should not send the callback yet, since the scheduler may
+                // be processing something else.
+                return;
+            }
+
+            try {
+                if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
+                    createNewSessionWithoutHandler(daemon, sensorId, userId);
+                }
+
+                final FingerprintRemovalClient client = new FingerprintRemovalClient(mContext,
+                        mSensors.get(sensorId).getLazySession(), token,
+                        new ClientMonitorCallbackConverter(receiver), fingerId, userId,
+                        opPackageName, FingerprintUtils.getInstance(sensorId), sensorId,
+                        mSensors.get(sensorId).getAuthenticatorIds());
+                mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client);
+            } catch (RemoteException e) {
+                Slog.e(getTag(), "Remote exception when scheduling remove", e);
+            }
+        });
+    }
+
+    @Override
+    public void scheduleInternalCleanup(int sensorId, int userId) {
+        mHandler.post(() -> {
+            final IFingerprint daemon = getHalInstance();
+            if (daemon == null) {
+                Slog.e(getTag(), "Null daemon during internal cleanup, sensorId: " + sensorId);
+                return;
+            }
+
+            try {
+                if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
+                    createNewSessionWithoutHandler(daemon, sensorId, userId);
+                }
+
+                final List<Fingerprint> enrolledList = getEnrolledFingerprints(sensorId, userId);
+                final FingerprintInternalCleanupClient client =
+                        new FingerprintInternalCleanupClient(mContext,
+                                mSensors.get(sensorId).getLazySession(), userId,
+                                mContext.getOpPackageName(), sensorId, enrolledList,
+                                FingerprintUtils.getInstance(sensorId),
+                                mSensors.get(sensorId).getAuthenticatorIds());
+                mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client);
+            } catch (RemoteException e) {
+                Slog.e(getTag(), "Remote exception when scheduling internal cleanup", e);
+            }
+        });
+    }
+
+    @Override
+    public boolean isHardwareDetected(int sensorId) {
+        return getHalInstance() != null;
+    }
+
+    @Override
+    public void rename(int sensorId, int fingerId, int userId, @NonNull String name) {
+        FingerprintUtils.getInstance(sensorId)
+                .renameBiometricForUser(mContext, userId, fingerId, name);
+    }
+
+    @NonNull
+    @Override
+    public List<Fingerprint> getEnrolledFingerprints(int sensorId, int userId) {
+        return FingerprintUtils.getInstance(sensorId).getBiometricsForUser(mContext, userId);
+    }
+
+    @Override
+    public int getLockoutModeForUser(int sensorId, int userId) {
+        return mSensors.get(sensorId).getLockoutCache().getLockoutModeForUser(userId);
+    }
+
+    @Override
+    public long getAuthenticatorId(int sensorId, int userId) {
+        return mSensors.get(sensorId).getAuthenticatorIds().getOrDefault(userId, 0L);
+    }
+
+    @Override
+    public void onPointerDown(int sensorId, int x, int y, float minor, float major) {
+        final ClientMonitor<?> client = mSensors.get(sensorId).getScheduler().getCurrentClient();
+        if (!(client instanceof Udfps)) {
+            Slog.e(getTag(), "onPointerDown received during client: " + client);
+            return;
+        }
+        final Udfps udfps = (Udfps) client;
+        udfps.onPointerDown(x, y, minor, major);
+    }
+
+    @Override
+    public void onPointerUp(int sensorId) {
+        final ClientMonitor<?> client = mSensors.get(sensorId).getScheduler().getCurrentClient();
+        if (!(client instanceof Udfps)) {
+            Slog.e(getTag(), "onPointerUp received during client: " + client);
+            return;
+        }
+        final Udfps udfps = (Udfps) client;
+        udfps.onPointerUp();
+    }
+
+    @Override
+    public void setUdfpsOverlayController(@NonNull IUdfpsOverlayController controller) {
+        mUdfpsOverlayController = controller;
+    }
+
+    @Override
+    public void dumpProto(int sensorId, @NonNull FileDescriptor fd) {
+
+    }
+
+    @Override
+    public void dumpInternal(int sensorId, @NonNull PrintWriter pw) {
+
+    }
+
+    @Override
+    public void binderDied() {
+        Slog.e(getTag(), "HAL died");
+        mHandler.post(() -> {
+            mDaemon = null;
+
+            for (int i = 0; i < mSensors.size(); i++) {
+                final int sensorId = mSensors.keyAt(i);
+                PerformanceTracker.getInstanceForSensorId(sensorId).incrementHALDeathCount();
+            }
+        });
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java
new file mode 100644
index 0000000..4a99a7b
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.biometrics.sensors.fingerprint.aidl;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.fingerprint.Fingerprint;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.RemovalClient;
+
+import java.util.Map;
+
+/**
+ * Fingerprint-specific removal client supporting the
+ * {@link android.hardware.biometrics.fingerprint.IFingerprint} interface.
+ */
+class FingerprintRemovalClient extends RemovalClient<Fingerprint, ISession> {
+    private static final String TAG = "FingerprintRemovalClient";
+
+    FingerprintRemovalClient(@NonNull Context context,
+            @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token,
+            @Nullable ClientMonitorCallbackConverter listener, int biometricId, int userId,
+            @NonNull String owner, @NonNull BiometricUtils<Fingerprint> utils, int sensorId,
+            @NonNull Map<Integer, Long> authenticatorIds) {
+        super(context, lazyDaemon, token, listener, biometricId, userId, owner, utils, sensorId,
+                authenticatorIds, BiometricsProtoEnums.MODALITY_FINGERPRINT);
+    }
+
+    @Override
+    protected void startHalOperation() {
+        try {
+            final int[] ids = new int[] {mBiometricId};
+            getFreshDaemon().removeEnrollments(mSequentialId, ids);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Remote exception when requesting remove", e);
+            mCallback.onClientFinished(this, false /* success */);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
new file mode 100644
index 0000000..1718126
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.biometrics.sensors.fingerprint.aidl;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.fingerprint.IFingerprint;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.keymaster.HardwareAuthToken;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.sensors.ClientMonitor;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.LockoutTracker;
+
+/**
+ * Fingerprint-specific resetLockout client for the {@link IFingerprint} AIDL HAL interface.
+ * Updates the framework's lockout cache and notifies clients such as Keyguard when lockout is
+ * cleared.
+ */
+class FingerprintResetLockoutClient extends ClientMonitor<ISession> {
+
+    private static final String TAG = "FingerprintResetLockoutClient";
+
+    private final HardwareAuthToken mHardwareAuthToken;
+    private final LockoutCache mLockoutCache;
+    private final LockoutResetDispatcher mLockoutResetDispatcher;
+
+    FingerprintResetLockoutClient(@NonNull Context context,
+            @NonNull LazyDaemon<ISession> lazyDaemon, int userId, String owner, int sensorId,
+            @NonNull byte[] hardwareAuthToken, @NonNull LockoutCache lockoutTracker,
+            @NonNull LockoutResetDispatcher lockoutResetDispatcher) {
+        super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
+                0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
+                BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
+        mHardwareAuthToken = HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken);
+        mLockoutCache = lockoutTracker;
+        mLockoutResetDispatcher = lockoutResetDispatcher;
+    }
+
+    @Override
+    public void unableToStart() {
+        // Nothing to do here
+    }
+
+    @Override
+    protected void startHalOperation() {
+        try {
+            getFreshDaemon().resetLockout(mSequentialId, mHardwareAuthToken);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Unable to reset lockout", e);
+            mCallback.onClientFinished(this, false /* success */);
+        }
+    }
+
+    void onLockoutCleared() {
+        mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_NONE);
+        mLockoutResetDispatcher.notifyLockoutResetCallbacks(getSensorId());
+        mCallback.onClientFinished(this, true /* success */);
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java
new file mode 100644
index 0000000..ebb4fe6
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.biometrics.sensors.fingerprint.aidl;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.fingerprint.IFingerprint;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.RevokeChallengeClient;
+
+/**
+ * Fingerprint-specific revokeChallenge client for the {@link IFingerprint} AIDL HAL interface.
+ */
+class FingerprintRevokeChallengeClient extends RevokeChallengeClient<ISession> {
+
+    private static final String TAG = "FingerpirntRevokeChallengeClient";
+
+    private final long mChallenge;
+
+    FingerprintRevokeChallengeClient(@NonNull Context context,
+            @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token,
+            @NonNull String owner, int sensorId, long challenge) {
+        super(context, lazyDaemon, token, owner, sensorId);
+        mChallenge = challenge;
+    }
+
+    @Override
+    protected void startHalOperation() {
+        try {
+            getFreshDaemon().revokeChallenge(mSequentialId, mChallenge);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Unable to revokeChallenge", e);
+        }
+    }
+
+    void onChallengeRevoked(int sensorId, int userId, long challenge) {
+        final boolean success = challenge == mChallenge;
+        mCallback.onClientFinished(FingerprintRevokeChallengeClient.this, success);
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/LockoutCache.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/LockoutCache.java
new file mode 100644
index 0000000..2fae1f3
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/LockoutCache.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.biometrics.sensors.fingerprint.aidl;
+
+import android.util.SparseIntArray;
+
+import com.android.server.biometrics.sensors.LockoutTracker;
+
+/**
+ * For a single sensor, caches lockout states for all users.
+ */
+class LockoutCache implements LockoutTracker {
+
+    // Map of userId to LockoutMode
+    private final SparseIntArray mUserLockoutStates;
+
+    LockoutCache() {
+        mUserLockoutStates = new SparseIntArray();
+    }
+
+    public void setLockoutModeForUser(int userId, @LockoutMode int mode) {
+        synchronized (this) {
+            mUserLockoutStates.put(userId, mode);
+        }
+    }
+
+    @Override
+    public int getLockoutModeForUser(int userId) {
+        synchronized (this) {
+            return mUserLockoutStates.get(userId, LOCKOUT_NONE);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
new file mode 100644
index 0000000..d4ce896
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.biometrics.sensors.fingerprint.aidl;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.fingerprint.Error;
+import android.hardware.biometrics.fingerprint.IFingerprint;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.biometrics.fingerprint.ISessionCallback;
+import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.hardware.keymaster.HardwareAuthToken;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.sensors.AcquisitionClient;
+import com.android.server.biometrics.sensors.AuthenticationConsumer;
+import com.android.server.biometrics.sensors.BiometricScheduler;
+import com.android.server.biometrics.sensors.ClientMonitor;
+import com.android.server.biometrics.sensors.EnumerateConsumer;
+import com.android.server.biometrics.sensors.Interruptable;
+import com.android.server.biometrics.sensors.LockoutConsumer;
+import com.android.server.biometrics.sensors.RemovalConsumer;
+import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
+import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Maintains the state of a single sensor within an instance of the
+ * {@link android.hardware.biometrics.fingerprint.IFingerprint} HAL.
+ */
+@SuppressWarnings("deprecation")
+class Sensor implements IBinder.DeathRecipient {
+    @NonNull private final String mTag;
+    @NonNull private final Context mContext;
+    @NonNull private final Handler mHandler;
+    @NonNull private final FingerprintSensorPropertiesInternal mSensorProperties;
+    @NonNull private final BiometricScheduler mScheduler;
+    @NonNull private final LockoutCache mLockoutCache;
+    @NonNull private final Map<Integer, Long> mAuthenticatorIds;
+
+    @Nullable private Session mCurrentSession;
+    @NonNull private final ClientMonitor.LazyDaemon<ISession> mLazySession;
+
+    @Override
+    public void binderDied() {
+        Slog.e(mTag, "Binder died");
+        mHandler.post(() -> {
+            final ClientMonitor<?> client = mScheduler.getCurrentClient();
+            if (client instanceof Interruptable) {
+                Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client);
+                final Interruptable interruptable = (Interruptable) client;
+                interruptable.onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE,
+                        0 /* vendorCode */);
+
+                mScheduler.recordCrashState();
+
+                FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
+                        BiometricsProtoEnums.MODALITY_FINGERPRINT,
+                        BiometricsProtoEnums.ISSUE_HAL_DEATH);
+                mCurrentSession = null;
+            }
+        });
+    }
+
+    private static class Session {
+        @NonNull private final String mTag;
+        @NonNull private final ISession mSession;
+        private final int mUserId;
+        private final ISessionCallback mSessionCallback;
+
+        Session(@NonNull String tag, @NonNull ISession session, int userId,
+                @NonNull ISessionCallback sessionCallback) {
+            mTag = tag;
+            mSession = session;
+            mUserId = userId;
+            mSessionCallback = sessionCallback;
+            Slog.d(mTag, "New session created for user: " + userId);
+        }
+    }
+
+    Sensor(@NonNull String tag, @NonNull Context context, @NonNull Handler handler,
+            @NonNull FingerprintSensorPropertiesInternal sensorProperties,
+            @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
+        mTag = tag;
+        mContext = context;
+        mHandler = handler;
+        mSensorProperties = sensorProperties;
+        mScheduler = new BiometricScheduler(tag, gestureAvailabilityDispatcher);
+        mLockoutCache = new LockoutCache();
+        mAuthenticatorIds = new HashMap<>();
+        mLazySession = () -> mCurrentSession != null ? mCurrentSession.mSession : null;
+    }
+
+    @NonNull ClientMonitor.LazyDaemon<ISession> getLazySession() {
+        return mLazySession;
+    }
+
+    @NonNull FingerprintSensorPropertiesInternal getSensorProperties() {
+        return mSensorProperties;
+    }
+
+    @SuppressWarnings("BooleanMethodIsAlwaysInverted")
+    boolean hasSessionForUser(int userId) {
+        return mCurrentSession != null && mCurrentSession.mUserId == userId;
+    }
+
+    void createNewSession(@NonNull IFingerprint daemon, int sensorId, int userId)
+            throws RemoteException {
+        final ISessionCallback callback = new ISessionCallback.Stub() {
+            @Override
+            public void onStateChanged(int cookie, byte state) {
+                // TODO(b/162973174)
+            }
+
+            @Override
+            public void onChallengeGenerated(long challenge) {
+                mHandler.post(() -> {
+                    final ClientMonitor<?> client = mScheduler.getCurrentClient();
+                    if (!(client instanceof FingerprintGenerateChallengeClient)) {
+                        Slog.e(mTag, "onChallengeGenerated for wrong client: "
+                                + Utils.getClientName(client));
+                        return;
+                    }
+
+                    final FingerprintGenerateChallengeClient generateChallengeClient =
+                            (FingerprintGenerateChallengeClient) client;
+                    generateChallengeClient.onChallengeGenerated(sensorId, userId, challenge);
+                });
+            }
+
+            @Override
+            public void onChallengeRevoked(long challenge) {
+                mHandler.post(() -> {
+                    final ClientMonitor<?> client = mScheduler.getCurrentClient();
+                    if (!(client instanceof FingerprintRevokeChallengeClient)) {
+                        Slog.e(mTag, "onChallengeRevoked for wrong client: "
+                                + Utils.getClientName(client));
+                        return;
+                    }
+
+                    final FingerprintRevokeChallengeClient revokeChallengeClient =
+                            (FingerprintRevokeChallengeClient) client;
+                    revokeChallengeClient.onChallengeRevoked(sensorId, userId, challenge);
+                });
+            }
+
+            @Override
+            public void onAcquired(byte info, int vendorCode) {
+                mHandler.post(() -> {
+                    final ClientMonitor<?> client = mScheduler.getCurrentClient();
+                    if (!(client instanceof AcquisitionClient)) {
+                        Slog.e(mTag, "onAcquired for non-acquisition client: "
+                                + Utils.getClientName(client));
+                        return;
+                    }
+
+                    final AcquisitionClient<?> acquisitionClient = (AcquisitionClient<?>) client;
+                    acquisitionClient.onAcquired(info, vendorCode);
+                });
+            }
+
+            @Override
+            public void onError(byte error, int vendorCode) {
+                mHandler.post(() -> {
+                    final ClientMonitor<?> client = mScheduler.getCurrentClient();
+                    Slog.d(mTag, "onError"
+                            + ", client: " + Utils.getClientName(client)
+                            + ", error: " + error
+                            + ", vendorCode: " + vendorCode);
+                    if (!(client instanceof Interruptable)) {
+                        Slog.e(mTag, "onError for non-error consumer: "
+                                + Utils.getClientName(client));
+                        return;
+                    }
+
+                    final Interruptable interruptable = (Interruptable) client;
+                    interruptable.onError(error, vendorCode);
+
+                    if (error == Error.HW_UNAVAILABLE) {
+                        Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE");
+                        mCurrentSession = null;
+                    }
+                });
+            }
+
+            @Override
+            public void onEnrollmentProgress(int enrollmentId, int remaining) {
+                mHandler.post(() -> {
+                    final ClientMonitor<?> client = mScheduler.getCurrentClient();
+                    if (!(client instanceof FingerprintEnrollClient)) {
+                        Slog.e(mTag, "onEnrollmentProgress for non-enroll client: "
+                                + Utils.getClientName(client));
+                        return;
+                    }
+
+                    final int currentUserId = client.getTargetUserId();
+                    final CharSequence name = FingerprintUtils.getInstance(sensorId)
+                            .getUniqueName(mContext, currentUserId);
+                    final Fingerprint fingerprint = new Fingerprint(name, enrollmentId, sensorId);
+
+                    final FingerprintEnrollClient enrollClient = (FingerprintEnrollClient) client;
+                    enrollClient.onEnrollResult(fingerprint, remaining);
+                });
+            }
+
+            @Override
+            public void onAuthenticationSucceeded(int enrollmentId, HardwareAuthToken hat) {
+                mHandler.post(() -> {
+                    final ClientMonitor<?> client = mScheduler.getCurrentClient();
+                    if (!(client instanceof AuthenticationConsumer)) {
+                        Slog.e(mTag, "onAuthenticationSucceeded for non-authentication consumer: "
+                                + Utils.getClientName(client));
+                        return;
+                    }
+
+                    final AuthenticationConsumer authenticationConsumer =
+                            (AuthenticationConsumer) client;
+                    final Fingerprint fp = new Fingerprint("", enrollmentId, sensorId);
+                    final byte[] byteArray = HardwareAuthTokenUtils.toByteArray(hat);
+                    final ArrayList<Byte> byteList = new ArrayList<>();
+                    for (byte b : byteArray) {
+                        byteList.add(b);
+                    }
+
+                    authenticationConsumer.onAuthenticated(fp, true /* authenticated */, byteList);
+                });
+            }
+
+            @Override
+            public void onAuthenticationFailed() {
+                mHandler.post(() -> {
+                    final ClientMonitor<?> client = mScheduler.getCurrentClient();
+                    if (!(client instanceof AuthenticationConsumer)) {
+                        Slog.e(mTag, "onAuthenticationFailed for non-authentication consumer: "
+                                + Utils.getClientName(client));
+                        return;
+                    }
+
+                    final AuthenticationConsumer authenticationConsumer =
+                            (AuthenticationConsumer) client;
+                    final Fingerprint fp = new Fingerprint("", 0 /* enrollmentId */, sensorId);
+                    authenticationConsumer
+                            .onAuthenticated(fp, false /* authenticated */, null /* hat */);
+                });
+            }
+
+            @Override
+            public void onLockoutTimed(long durationMillis) {
+                mHandler.post(() -> {
+                    final ClientMonitor<?> client = mScheduler.getCurrentClient();
+                    if (!(client instanceof LockoutConsumer)) {
+                        Slog.e(mTag, "onLockoutTimed for non-lockout consumer: "
+                                + Utils.getClientName(client));
+                        return;
+                    }
+
+                    final LockoutConsumer lockoutConsumer = (LockoutConsumer) client;
+                    lockoutConsumer.onLockoutTimed(durationMillis);
+                });
+            }
+
+            @Override
+            public void onLockoutPermanent() {
+                mHandler.post(() -> {
+                    final ClientMonitor<?> client = mScheduler.getCurrentClient();
+                    if (!(client instanceof LockoutConsumer)) {
+                        Slog.e(mTag, "onLockoutPermanent for non-lockout consumer: "
+                                + Utils.getClientName(client));
+                        return;
+                    }
+
+                    final LockoutConsumer lockoutConsumer = (LockoutConsumer) client;
+                    lockoutConsumer.onLockoutPermanent();
+                });
+            }
+
+            @Override
+            public void onLockoutCleared() {
+                mHandler.post(() -> {
+                    final ClientMonitor<?> client = mScheduler.getCurrentClient();
+                    if (!(client instanceof FingerprintResetLockoutClient)) {
+                        Slog.e(mTag, "onLockoutCleared for non-resetLockout client: "
+                                + Utils.getClientName(client));
+                        return;
+                    }
+
+                    final FingerprintResetLockoutClient resetLockoutClient =
+                            (FingerprintResetLockoutClient) client;
+                    resetLockoutClient.onLockoutCleared();
+                });
+            }
+
+            @Override
+            public void onInteractionDetected() {
+                mHandler.post(() -> {
+                    final ClientMonitor<?> client = mScheduler.getCurrentClient();
+                    if (!(client instanceof FingerprintDetectClient)) {
+                        Slog.e(mTag, "onInteractionDetected for non-detect client: "
+                                + Utils.getClientName(client));
+                        return;
+                    }
+
+                    final FingerprintDetectClient fingerprintDetectClient =
+                            (FingerprintDetectClient) client;
+                    fingerprintDetectClient.onInteractionDetected();
+                });
+            }
+
+            @Override
+            public void onEnrollmentsEnumerated(int[] enrollmentIds) {
+                mHandler.post(() -> {
+                    final ClientMonitor<?> client = mScheduler.getCurrentClient();
+                    if (!(client instanceof EnumerateConsumer)) {
+                        Slog.e(mTag, "onEnrollmentsEnumerated for non-enumerate consumer: "
+                                + Utils.getClientName(client));
+                        return;
+                    }
+
+                    final EnumerateConsumer enumerateConsumer =
+                            (EnumerateConsumer) client;
+                    if (enrollmentIds.length > 0) {
+                        for (int i = 0; i < enrollmentIds.length; i++) {
+                            final Fingerprint fp = new Fingerprint("", enrollmentIds[i], sensorId);
+                            enumerateConsumer.onEnumerationResult(fp, enrollmentIds.length - i - 1);
+                        }
+                    } else {
+                        enumerateConsumer.onEnumerationResult(null /* identifier */, 0);
+                    }
+                });
+            }
+
+            @Override
+            public void onEnrollmentsRemoved(int[] enrollmentIds) {
+                mHandler.post(() -> {
+                    final ClientMonitor<?> client = mScheduler.getCurrentClient();
+                    if (!(client instanceof RemovalConsumer)) {
+                        Slog.e(mTag, "onRemoved for non-removal consumer: "
+                                + Utils.getClientName(client));
+                        return;
+                    }
+
+                    final RemovalConsumer removalConsumer = (RemovalConsumer) client;
+                    if (enrollmentIds.length > 0) {
+                        for (int i  = 0; i < enrollmentIds.length; i++) {
+                            final Fingerprint fp = new Fingerprint("", enrollmentIds[i], sensorId);
+                            removalConsumer.onRemoved(fp, enrollmentIds.length - i - 1);
+                        }
+                    } else {
+                        removalConsumer.onRemoved(null, 0);
+                    }
+                });
+            }
+
+            @Override
+            public void onAuthenticatorIdRetrieved(long authenticatorId) {
+                mHandler.post(() -> {
+                    final ClientMonitor<?> client = mScheduler.getCurrentClient();
+                    if (!(client instanceof FingerprintGetAuthenticatorIdClient)) {
+                        Slog.e(mTag, "onAuthenticatorIdRetrieved for wrong consumer: "
+                                + Utils.getClientName(client));
+                        return;
+                    }
+
+                    final FingerprintGetAuthenticatorIdClient getAuthenticatorIdClient =
+                            (FingerprintGetAuthenticatorIdClient) client;
+                    getAuthenticatorIdClient.onAuthenticatorIdRetrieved(authenticatorId);
+                });
+            }
+
+            @Override
+            public void onAuthenticatorIdInvalidated() {
+                // TODO(159667191)
+            }
+        };
+
+        final ISession newSession = daemon.createSession(sensorId, userId, callback);
+        newSession.asBinder().linkToDeath(this, 0 /* flags */);
+        mCurrentSession = new Session(mTag, newSession, userId, callback);
+    }
+
+    @NonNull BiometricScheduler getScheduler() {
+        return mScheduler;
+    }
+
+    @NonNull LockoutCache getLockoutCache() {
+        return mLockoutCache;
+    }
+
+    @NonNull Map<Integer, Long> getAuthenticatorIds() {
+        return mAuthenticatorIds;
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index f890f57..ab4427c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -68,6 +68,7 @@
 import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
 import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
 import com.android.server.biometrics.sensors.fingerprint.ServiceProvider;
+import com.android.server.biometrics.sensors.fingerprint.Udfps;
 
 import org.json.JSONArray;
 import org.json.JSONException;
@@ -245,7 +246,7 @@
             mHandler.post(() -> {
                 final ClientMonitor<?> client = mScheduler.getCurrentClient();
                 Slog.d(TAG, "handleError"
-                        + ", client: " + (client != null ? client.getOwnerString() : null)
+                        + ", client: " + Utils.getClientName(client)
                         + ", error: " + error
                         + ", vendorCode: " + vendorCode);
                 if (!(client instanceof Interruptable)) {
@@ -504,7 +505,7 @@
     }
 
     @Override
-    public void scheduleGenerateChallenge(int sensorId, @NonNull IBinder token,
+    public void scheduleGenerateChallenge(int sensorId, int userId, @NonNull IBinder token,
             @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName) {
         mHandler.post(() -> {
             final FingerprintGenerateChallengeClient client =
@@ -516,8 +517,8 @@
     }
 
     @Override
-    public void scheduleRevokeChallenge(int sensorId, @NonNull IBinder token,
-            @NonNull String opPackageName) {
+    public void scheduleRevokeChallenge(int sensorId, int userId, @NonNull IBinder token,
+            @NonNull String opPackageName, long challenge) {
         mHandler.post(() -> {
             final FingerprintRevokeChallengeClient client = new FingerprintRevokeChallengeClient(
                     mContext, mLazyDaemon, token, opPackageName, mSensorProperties.sensorId);
@@ -593,16 +594,12 @@
 
     @Override
     public void startPreparedClient(int sensorId, int cookie) {
-        mHandler.post(() -> {
-            mScheduler.startPreparedClient(cookie);
-        });
+        mHandler.post(() -> mScheduler.startPreparedClient(cookie));
     }
 
     @Override
     public void cancelAuthentication(int sensorId, @NonNull IBinder token) {
-        mHandler.post(() -> {
-            mScheduler.cancelAuthentication(token);
-        });
+        mHandler.post(() -> mScheduler.cancelAuthentication(token));
     }
 
     @Override
@@ -635,6 +632,11 @@
     }
 
     @Override
+    public void scheduleInternalCleanup(int sensorId, int userId) {
+        scheduleInternalCleanup(userId);
+    }
+
+    @Override
     public boolean isHardwareDetected(int sensorId) {
         final IBiometricsFingerprint daemon = getDaemon();
         return daemon != null;
@@ -664,25 +666,25 @@
     }
 
     @Override
-    public void onFingerDown(int sensorId, int x, int y, float minor, float major) {
+    public void onPointerDown(int sensorId, int x, int y, float minor, float major) {
         final ClientMonitor<?> client = mScheduler.getCurrentClient();
         if (!(client instanceof Udfps)) {
             Slog.w(TAG, "onFingerDown received during client: " + client);
             return;
         }
         final Udfps udfps = (Udfps) client;
-        udfps.onFingerDown(x, y, minor, major);
+        udfps.onPointerDown(x, y, minor, major);
     }
 
     @Override
-    public void onFingerUp(int sensorId) {
+    public void onPointerUp(int sensorId) {
         final ClientMonitor<?> client = mScheduler.getCurrentClient();
         if (!(client instanceof Udfps)) {
             Slog.w(TAG, "onFingerDown received during client: " + client);
             return;
         }
         final Udfps udfps = (Udfps) client;
-        udfps.onFingerUp();
+        udfps.onPointerUp();
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
index 5dda5a8..e4933e4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
@@ -461,7 +461,7 @@
     }
 
     @Override
-    public void onFingerDown(int sensorId, int x, int y, float minor, float major) {
+    public void onPointerDown(int sensorId, int x, int y, float minor, float major) {
         mHandler.post(() -> {
             Slog.d(TAG, "onFingerDown");
             final AuthenticationConsumer lastAuthenticatedConsumer =
@@ -508,7 +508,7 @@
     }
 
     @Override
-    public void onFingerUp(int sensorId) {
+    public void onPointerUp(int sensorId) {
         mHandler.post(() -> {
             Slog.d(TAG, "onFingerUp");
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
index 0658f95..46605d1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
@@ -33,6 +33,8 @@
 import com.android.server.biometrics.sensors.AuthenticationClient;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.LockoutTracker;
+import com.android.server.biometrics.sensors.fingerprint.Udfps;
+import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
 
 import java.util.ArrayList;
 
@@ -138,12 +140,12 @@
     }
 
     @Override
-    public void onFingerDown(int x, int y, float minor, float major) {
+    public void onPointerDown(int x, int y, float minor, float major) {
         UdfpsHelper.onFingerDown(getFreshDaemon(), x, y, minor, major);
     }
 
     @Override
-    public void onFingerUp() {
+    public void onPointerUp() {
         UdfpsHelper.onFingerUp(getFreshDaemon());
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
index cad2214..4747488 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
@@ -32,6 +32,8 @@
 import com.android.server.biometrics.sensors.AuthenticationConsumer;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.PerformanceTracker;
+import com.android.server.biometrics.sensors.fingerprint.Udfps;
+import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
 
 import java.util.ArrayList;
 
@@ -93,12 +95,12 @@
     }
 
     @Override
-    public void onFingerDown(int x, int y, float minor, float major) {
+    public void onPointerDown(int x, int y, float minor, float major) {
         UdfpsHelper.onFingerDown(getFreshDaemon(), x, y, minor, major);
     }
 
     @Override
-    public void onFingerUp() {
+    public void onPointerUp() {
         UdfpsHelper.onFingerUp(getFreshDaemon());
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
index b1030bf..c750b90 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
@@ -22,6 +22,7 @@
 import android.hardware.biometrics.BiometricFingerprintConstants;
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.hardware.fingerprint.Fingerprint;
 import android.hardware.fingerprint.IUdfpsOverlayController;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -30,6 +31,8 @@
 import com.android.server.biometrics.sensors.BiometricUtils;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.EnrollClient;
+import com.android.server.biometrics.sensors.fingerprint.Udfps;
+import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
 
 /**
  * Fingerprint-specific enroll client supporting the
@@ -46,8 +49,8 @@
     FingerprintEnrollClient(@NonNull Context context,
             @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
             @NonNull ClientMonitorCallbackConverter listener, int userId,
-            @NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils utils,
-            int timeoutSec, int sensorId,
+            @NonNull byte[] hardwareAuthToken, @NonNull String owner,
+            @NonNull BiometricUtils<Fingerprint> utils, int timeoutSec, int sensorId,
             @Nullable IUdfpsOverlayController udfpsOverlayController) {
         super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
                 timeoutSec, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId,
@@ -97,12 +100,12 @@
     }
 
     @Override
-    public void onFingerDown(int x, int y, float minor, float major) {
+    public void onPointerDown(int x, int y, float minor, float major) {
         UdfpsHelper.onFingerDown(getFreshDaemon(), x, y, minor, major);
     }
 
     @Override
-    public void onFingerUp() {
+    public void onPointerUp() {
         UdfpsHelper.onFingerUp(getFreshDaemon());
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java
index e061112..a42a8ae 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java
@@ -42,7 +42,8 @@
     FingerprintInternalCleanupClient(@NonNull Context context,
             @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, int userId,
             @NonNull String owner, int sensorId, @NonNull List<Fingerprint> enrolledList,
-            @NonNull BiometricUtils utils, @NonNull Map<Integer, Long> authenticatorIds) {
+            @NonNull BiometricUtils<Fingerprint> utils,
+            @NonNull Map<Integer, Long> authenticatorIds) {
         super(context, lazyDaemon, userId, owner, sensorId,
                 BiometricsProtoEnums.MODALITY_FINGERPRINT, enrolledList, utils, authenticatorIds);
     }
@@ -50,18 +51,17 @@
     @Override
     protected InternalEnumerateClient<IBiometricsFingerprint> getEnumerateClient(
             Context context, LazyDaemon<IBiometricsFingerprint> lazyDaemon, IBinder token,
-            int userId, String owner,
-            List<Fingerprint> enrolledList, BiometricUtils utils,
-            int sensorId) {
+            int userId, String owner, List<Fingerprint> enrolledList,
+            BiometricUtils<Fingerprint> utils, int sensorId) {
         return new FingerprintInternalEnumerateClient(context, lazyDaemon, token, userId, owner,
                 enrolledList, utils, sensorId);
     }
 
     @Override
-    protected RemovalClient<IBiometricsFingerprint> getRemovalClient(Context context,
+    protected RemovalClient<Fingerprint, IBiometricsFingerprint> getRemovalClient(Context context,
             LazyDaemon<IBiometricsFingerprint> lazyDaemon, IBinder token,
-            int biometricId, int userId, String owner, BiometricUtils utils, int sensorId,
-            Map<Integer, Long> authenticatorIds) {
+            int biometricId, int userId, String owner, BiometricUtils<Fingerprint> utils,
+            int sensorId, Map<Integer, Long> authenticatorIds) {
         // Internal remove does not need to send results to anyone. Cleanup (enumerate + remove)
         // is all done internally.
         return new FingerprintRemovalClient(context, lazyDaemon, token,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalEnumerateClient.java
index 5fd1d1e..7117cf3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalEnumerateClient.java
@@ -41,7 +41,7 @@
     FingerprintInternalEnumerateClient(@NonNull Context context,
             @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
             int userId, @NonNull String owner, @NonNull List<Fingerprint> enrolledList,
-            @NonNull BiometricUtils utils, int sensorId) {
+            @NonNull BiometricUtils<Fingerprint> utils, int sensorId) {
         super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId,
                 BiometricsProtoEnums.MODALITY_FINGERPRINT);
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java
index 4bbb7ef..f6a22f5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.hardware.fingerprint.Fingerprint;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Slog;
@@ -35,13 +36,13 @@
  * {@link android.hardware.biometrics.fingerprint.V2_1} and
  * {@link android.hardware.biometrics.fingerprint.V2_2} HIDL interfaces.
  */
-class FingerprintRemovalClient extends RemovalClient<IBiometricsFingerprint> {
+class FingerprintRemovalClient extends RemovalClient<Fingerprint, IBiometricsFingerprint> {
     private static final String TAG = "FingerprintRemovalClient";
 
     FingerprintRemovalClient(@NonNull Context context,
             @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
             @NonNull ClientMonitorCallbackConverter listener, int biometricId, int userId,
-            @NonNull String owner, @NonNull BiometricUtils utils, int sensorId,
+            @NonNull String owner, @NonNull BiometricUtils<Fingerprint> utils, int sensorId,
             @NonNull Map<Integer, Long> authenticatorIds) {
         super(context, lazyDaemon, token, listener, biometricId, userId, owner, utils, sensorId,
                 authenticatorIds, BiometricsProtoEnums.MODALITY_FINGERPRINT);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java
index 4fc1545..dc5dace9 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java
@@ -135,6 +135,6 @@
     private PendingIntent getLockoutResetIntentForUser(int userId) {
         return PendingIntent.getBroadcast(mContext, userId,
                 new Intent(ACTION_LOCKOUT_RESET).putExtra(KEY_LOCKOUT_RESET_USER, userId),
-                PendingIntent.FLAG_UPDATE_CURRENT);
+                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
     }
 }
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index 2e9818d..bc3bff1 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -63,7 +63,7 @@
     private Map<String, Boolean> mPackageOverrides;
 
     public CompatChange(long changeId) {
-        this(changeId, null, -1, false, false, null);
+        this(changeId, null, -1, -1, false, false, null);
     }
 
     /**
@@ -71,11 +71,14 @@
      * @param name Short descriptive name.
      * @param enableAfterTargetSdk {@code targetSdkVersion} restriction. See {@link EnabledAfter};
      *                             -1 if the change is always enabled.
+     * @param enableSinceTargetSdk {@code targetSdkVersion} restriction. See {@link EnabledSince};
+     *                             -1 if the change is always enabled.
      * @param disabled If {@code true}, overrides any {@code enableAfterTargetSdk} set.
      */
     public CompatChange(long changeId, @Nullable String name, int enableAfterTargetSdk,
-            boolean disabled, boolean loggingOnly, String description) {
-        super(changeId, name, enableAfterTargetSdk, disabled, loggingOnly, description);
+            int enableSinceTargetSdk, boolean disabled, boolean loggingOnly, String description) {
+        super(changeId, name, enableAfterTargetSdk, enableSinceTargetSdk, disabled, loggingOnly,
+              description);
     }
 
     /**
@@ -83,7 +86,8 @@
      */
     public CompatChange(Change change) {
         super(change.getId(), change.getName(), change.getEnableAfterTargetSdk(),
-                change.getDisabled(), change.getLoggingOnly(), change.getDescription());
+                change.getEnableSinceTargetSdk(), change.getDisabled(), change.getLoggingOnly(),
+                change.getDescription());
     }
 
     void registerListener(ChangeListener listener) {
@@ -145,8 +149,8 @@
         if (getDisabled()) {
             return false;
         }
-        if (getEnableAfterTargetSdk() != -1) {
-            return app.targetSdkVersion > getEnableAfterTargetSdk();
+        if (getEnableSinceTargetSdk() != -1) {
+            return app.targetSdkVersion >= getEnableSinceTargetSdk();
         }
         return true;
     }
@@ -167,8 +171,8 @@
         if (getName() != null) {
             sb.append("; name=").append(getName());
         }
-        if (getEnableAfterTargetSdk() != -1) {
-            sb.append("; enableAfterTargetSdk=").append(getEnableAfterTargetSdk());
+        if (getEnableSinceTargetSdk() != -1) {
+            sb.append("; enableSinceTargetSdk=").append(getEnableSinceTargetSdk());
         }
         if (getDisabled()) {
             sb.append("; disabled");
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index aeaa1fe..d80c58b 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -192,16 +192,19 @@
     }
 
     /**
-     * Returns the minimum sdk version for which this change should be enabled (or 0 if it is not
+     * Returns the maximum sdk version for which this change can be opted in (or -1 if it is not
      * target sdk gated).
      */
-    int minTargetSdkForChangeId(long changeId) {
+    int maxTargetSdkForChangeIdOptIn(long changeId) {
         synchronized (mChanges) {
             CompatChange c = mChanges.get(changeId);
             if (c == null) {
-                return 0;
+                return -1;
             }
-            return c.getEnableAfterTargetSdk();
+            if (c.getEnableSinceTargetSdk() != -1) {
+                return c.getEnableSinceTargetSdk() - 1;
+            }
+            return -1;
         }
     }
 
@@ -318,7 +321,7 @@
         }
     }
 
-    private long[] getAllowedChangesAfterTargetSdkForPackage(String packageName,
+    private long[] getAllowedChangesSinceTargetSdkForPackage(String packageName,
                                                              int targetSdkVersion)
                     throws RemoteException {
         LongArray allowed = new LongArray();
@@ -326,7 +329,7 @@
             for (int i = 0; i < mChanges.size(); ++i) {
                 try {
                     CompatChange change = mChanges.valueAt(i);
-                    if (change.getEnableAfterTargetSdk() != targetSdkVersion) {
+                    if (change.getEnableSinceTargetSdk() != targetSdkVersion) {
                         continue;
                     }
                     OverrideAllowedState allowedState =
@@ -345,14 +348,14 @@
     }
 
     /**
-     * Enables all changes with enabledAfterTargetSdk == {@param targetSdkVersion} for
+     * Enables all changes with enabledSinceTargetSdk == {@param targetSdkVersion} for
      * {@param packageName}.
      *
      * @return The number of changes that were toggled.
      */
     int enableTargetSdkChangesForPackage(String packageName, int targetSdkVersion)
             throws RemoteException {
-        long[] changes = getAllowedChangesAfterTargetSdkForPackage(packageName, targetSdkVersion);
+        long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion);
         for (long changeId : changes) {
             addOverride(changeId, packageName, true);
         }
@@ -361,14 +364,14 @@
 
 
     /**
-     * Disables all changes with enabledAfterTargetSdk == {@param targetSdkVersion} for
+     * Disables all changes with enabledSinceTargetSdk == {@param targetSdkVersion} for
      * {@param packageName}.
      *
      * @return The number of changes that were toggled.
      */
     int disableTargetSdkChangesForPackage(String packageName, int targetSdkVersion)
             throws RemoteException {
-        long[] changes = getAllowedChangesAfterTargetSdkForPackage(packageName, targetSdkVersion);
+        long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion);
         for (long changeId : changes) {
             addOverride(changeId, packageName, false);
         }
@@ -448,12 +451,7 @@
             CompatibilityChangeInfo[] changeInfos = new CompatibilityChangeInfo[mChanges.size()];
             for (int i = 0; i < mChanges.size(); ++i) {
                 CompatChange change = mChanges.valueAt(i);
-                changeInfos[i] = new CompatibilityChangeInfo(change.getId(),
-                        change.getName(),
-                        change.getEnableAfterTargetSdk(),
-                        change.getDisabled(),
-                        change.getLoggingOnly(),
-                        change.getDescription());
+                changeInfos[i] = new CompatibilityChangeInfo(change);
             }
             return changeInfos;
         }
diff --git a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
index 08d2664..79a13ca 100644
--- a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
+++ b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
@@ -58,7 +58,7 @@
 
         boolean debuggableBuild = mAndroidBuildClassifier.isDebuggableBuild();
         boolean finalBuild = mAndroidBuildClassifier.isFinalBuild();
-        int minTargetSdk = mCompatConfig.minTargetSdkForChangeId(changeId);
+        int maxTargetSdk = mCompatConfig.maxTargetSdkForChangeIdOptIn(changeId);
         boolean disabled = mCompatConfig.isDisabled(changeId);
 
         // Allow any override for userdebug or eng builds.
@@ -82,16 +82,16 @@
         }
         // Allow overriding any change for debuggable apps on non-final builds.
         if (!finalBuild) {
-            return new OverrideAllowedState(ALLOWED, appTargetSdk, minTargetSdk);
+            return new OverrideAllowedState(ALLOWED, appTargetSdk, maxTargetSdk);
         }
         // Do not allow overriding default enabled changes on user builds
-        if (minTargetSdk == -1 && !disabled) {
-            return new OverrideAllowedState(DISABLED_NON_TARGET_SDK, appTargetSdk, minTargetSdk);
+        if (maxTargetSdk == -1 && !disabled) {
+            return new OverrideAllowedState(DISABLED_NON_TARGET_SDK, appTargetSdk, maxTargetSdk);
         }
         // Only allow to opt-in for a targetSdk gated change.
-        if (disabled || appTargetSdk <= minTargetSdk) {
-            return new OverrideAllowedState(ALLOWED, appTargetSdk, minTargetSdk);
+        if (disabled || appTargetSdk <= maxTargetSdk) {
+            return new OverrideAllowedState(ALLOWED, appTargetSdk, maxTargetSdk);
         }
-        return new OverrideAllowedState(DISABLED_TARGET_SDK_TOO_HIGH, appTargetSdk, minTargetSdk);
+        return new OverrideAllowedState(DISABLED_TARGET_SDK_TOO_HIGH, appTargetSdk, maxTargetSdk);
     }
 }
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index c7de8d3..e4f52f1 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -59,8 +59,8 @@
     private final ChangeReporter mChangeReporter;
     private final CompatConfig mCompatConfig;
 
-    private static int sMinTargetSdk = Build.VERSION_CODES.P;
-    private static int sMaxTargetSdk = Build.VERSION_CODES.Q;
+    private static int sMinTargetSdk = Build.VERSION_CODES.Q;
+    private static int sMaxTargetSdk = Build.VERSION_CODES.R;
 
     public PlatformCompat(Context context) {
         mContext = context;
@@ -381,9 +381,9 @@
         if (change.getLoggingOnly()) {
             return false;
         }
-        if (change.getEnableAfterTargetSdk() > 0) {
-            if (change.getEnableAfterTargetSdk() < sMinTargetSdk
-                    || change.getEnableAfterTargetSdk() > sMaxTargetSdk) {
+        if (change.getEnableSinceTargetSdk() > 0) {
+            if (change.getEnableSinceTargetSdk() < sMinTargetSdk
+                    || change.getEnableSinceTargetSdk() > sMaxTargetSdk) {
                 return false;
             }
         }
diff --git a/services/core/java/com/android/server/connectivity/DataConnectionStats.java b/services/core/java/com/android/server/connectivity/DataConnectionStats.java
index 0304cdc..15f43a0 100644
--- a/services/core/java/com/android/server/connectivity/DataConnectionStats.java
+++ b/services/core/java/com/android/server/connectivity/DataConnectionStats.java
@@ -19,12 +19,12 @@
 import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
 import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
 
+import android.annotation.NonNull;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.Handler;
-import android.os.Looper;
 import android.os.RemoteException;
 import android.telephony.NetworkRegistrationInfo;
 import android.telephony.PhoneStateListener;
@@ -36,6 +36,9 @@
 import com.android.internal.app.IBatteryStats;
 import com.android.server.am.BatteryStatsService;
 
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
+
 public class DataConnectionStats extends BroadcastReceiver {
     private static final String TAG = "DataConnectionStats";
     private static final boolean DEBUG = false;
@@ -55,7 +58,8 @@
         mContext = context;
         mBatteryStats = BatteryStatsService.getService();
         mListenerHandler = listenerHandler;
-        mPhoneStateListener = new PhoneStateListenerImpl(listenerHandler.getLooper());
+        mPhoneStateListener =
+                new PhoneStateListenerImpl(new PhoneStateListenerExecutor(listenerHandler));
     }
 
     public void startMonitoring() {
@@ -140,9 +144,24 @@
                 && mServiceState.getState() != ServiceState.STATE_POWER_OFF;
     }
 
+    private static class PhoneStateListenerExecutor implements Executor {
+        @NonNull
+        private final Handler mHandler;
+
+        PhoneStateListenerExecutor(@NonNull Handler handler) {
+            mHandler = handler;
+        }
+        @Override
+        public void execute(Runnable command) {
+            if (!mHandler.post(command)) {
+                throw new RejectedExecutionException(mHandler + " is shutting down");
+            }
+        }
+    }
+
     private class PhoneStateListenerImpl extends PhoneStateListener {
-        PhoneStateListenerImpl(Looper looper) {
-            super(looper);
+        PhoneStateListenerImpl(Executor executor) {
+            super(executor);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java
index cf6a7f6..c789186 100644
--- a/services/core/java/com/android/server/connectivity/DnsManager.java
+++ b/services/core/java/com/android/server/connectivity/DnsManager.java
@@ -50,7 +50,6 @@
 
 import java.net.InetAddress;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -238,24 +237,21 @@
     private final Context mContext;
     private final ContentResolver mContentResolver;
     private final IDnsResolver mDnsResolver;
-    private final MockableSystemProperties mSystemProperties;
     private final ConcurrentHashMap<Integer, PrivateDnsConfig> mPrivateDnsMap;
     // TODO: Replace the Map with SparseArrays.
     private final Map<Integer, PrivateDnsValidationStatuses> mPrivateDnsValidationMap;
     private final Map<Integer, LinkProperties> mLinkPropertiesMap;
     private final Map<Integer, int[]> mTransportsMap;
 
-    private int mNumDnsEntries;
     private int mSampleValidity;
     private int mSuccessThreshold;
     private int mMinSamples;
     private int mMaxSamples;
 
-    public DnsManager(Context ctx, IDnsResolver dnsResolver, MockableSystemProperties sp) {
+    public DnsManager(Context ctx, IDnsResolver dnsResolver) {
         mContext = ctx;
         mContentResolver = mContext.getContentResolver();
         mDnsResolver = dnsResolver;
-        mSystemProperties = sp;
         mPrivateDnsMap = new ConcurrentHashMap<>();
         mPrivateDnsValidationMap = new HashMap<>();
         mLinkPropertiesMap = new HashMap<>();
@@ -409,18 +405,6 @@
         }
     }
 
-    public void setDefaultDnsSystemProperties(Collection<InetAddress> dnses) {
-        int last = 0;
-        for (InetAddress dns : dnses) {
-            ++last;
-            setNetDnsProperty(last, dns.getHostAddress());
-        }
-        for (int i = last + 1; i <= mNumDnsEntries; ++i) {
-            setNetDnsProperty(i, "");
-        }
-        mNumDnsEntries = last;
-    }
-
     /**
      * Flush DNS caches and events work before boot has completed.
      */
@@ -476,16 +460,6 @@
         return Settings.Global.getInt(mContentResolver, which, dflt);
     }
 
-    private void setNetDnsProperty(int which, String value) {
-        final String key = "net.dns" + which;
-        // Log and forget errors setting unsupported properties.
-        try {
-            mSystemProperties.set(key, value);
-        } catch (Exception e) {
-            Slog.e(TAG, "Error setting unsupported net.dns property: ", e);
-        }
-    }
-
     private static String getPrivateDnsMode(ContentResolver cr) {
         String mode = getStringSetting(cr, PRIVATE_DNS_MODE);
         if (TextUtils.isEmpty(mode)) mode = getStringSetting(cr, PRIVATE_DNS_DEFAULT_MODE);
diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
index 01fa9e7..8625a6f 100644
--- a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
+++ b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
@@ -47,7 +47,6 @@
 import android.net.NetworkUtils;
 import android.net.SocketKeepalive.InvalidSocketException;
 import android.net.TcpKeepalivePacketData;
-import android.net.util.IpUtils;
 import android.net.util.KeepaliveUtils;
 import android.os.Binder;
 import android.os.Handler;
@@ -63,6 +62,7 @@
 import com.android.internal.R;
 import com.android.internal.util.HexDump;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.net.module.util.IpUtils;
 
 import java.io.FileDescriptor;
 import java.net.InetAddress;
diff --git a/services/core/java/com/android/server/connectivity/LingerMonitor.java b/services/core/java/com/android/server/connectivity/LingerMonitor.java
index 04c000f..7fdc7a0 100644
--- a/services/core/java/com/android/server/connectivity/LingerMonitor.java
+++ b/services/core/java/com/android/server/connectivity/LingerMonitor.java
@@ -159,8 +159,9 @@
 
     @VisibleForTesting
     protected PendingIntent createNotificationIntent() {
-        return PendingIntent.getActivityAsUser(mContext, 0, CELLULAR_SETTINGS,
-                PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
+        return PendingIntent.getActivityAsUser(mContext, 0 /* requestCode */, CELLULAR_SETTINGS,
+                PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE,
+                null /* options */, UserHandle.CURRENT);
     }
 
     // Removes any notification that was put up as a result of switching to nai.
diff --git a/services/core/java/com/android/server/connectivity/MockableSystemProperties.java b/services/core/java/com/android/server/connectivity/MockableSystemProperties.java
index 77b86d8..ef76734 100644
--- a/services/core/java/com/android/server/connectivity/MockableSystemProperties.java
+++ b/services/core/java/com/android/server/connectivity/MockableSystemProperties.java
@@ -17,6 +17,7 @@
 package com.android.server.connectivity;
 
 import android.os.SystemProperties;
+import android.sysprop.NetworkProperties;
 
 public class MockableSystemProperties {
 
@@ -31,8 +32,10 @@
     public boolean getBoolean(String key, boolean def) {
         return SystemProperties.getBoolean(key, def);
     }
-
-    public void set(String key, String value) {
-        SystemProperties.set(key, value);
+    /**
+     * Set net.tcp_def_init_rwnd to the tcp initial receive window size.
+     */
+    public void setTcpInitRwnd(int value) {
+        NetworkProperties.tcp_init_rwnd(value);
     }
 }
diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
index 34b0aa2..26356b4 100644
--- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
@@ -325,7 +325,8 @@
     public void setProvNotificationVisible(boolean visible, int id, String action) {
         if (visible) {
             Intent intent = new Intent(action);
-            PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+            PendingIntent pendingIntent = PendingIntent.getBroadcast(
+                    mContext, 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE);
             showNotification(id, NotificationType.SIGN_IN, null, null, pendingIntent, false);
         } else {
             clearNotification(id);
diff --git a/services/core/java/com/android/server/connectivity/PacManager.java b/services/core/java/com/android/server/connectivity/PacManager.java
index de302fc..198de78 100644
--- a/services/core/java/com/android/server/connectivity/PacManager.java
+++ b/services/core/java/com/android/server/connectivity/PacManager.java
@@ -154,7 +154,7 @@
         mNetThreadHandler = new Handler(netThread.getLooper());
 
         mPacRefreshIntent = PendingIntent.getBroadcast(
-                context, 0, new Intent(ACTION_PAC_REFRESH), 0);
+                context, 0, new Intent(ACTION_PAC_REFRESH), PendingIntent.FLAG_IMMUTABLE);
         context.registerReceiver(new PacRefreshIntentReceiver(),
                 new IntentFilter(ACTION_PAC_REFRESH));
         mConnectivityHandler = handler;
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 14b3478..e218793 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -38,6 +38,7 @@
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -380,8 +381,8 @@
             }
         }
 
-        public boolean checkInterfacePresent(final Vpn vpn, final String iface) {
-            return vpn.jniCheck(iface) == 0;
+        public boolean isInterfacePresent(final Vpn vpn, final String iface) {
+            return vpn.jniCheck(iface) != 0;
         }
     }
 
@@ -966,7 +967,7 @@
     /** Prepare the VPN for the given package. Does not perform permission checks. */
     @GuardedBy("this")
     private void prepareInternal(String newPackage) {
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             // Reset the interface.
             if (mInterface != null) {
@@ -1130,7 +1131,13 @@
         return mNetworkInfo;
     }
 
-    public int getNetId() {
+    /**
+     * Return netId of current running VPN network.
+     *
+     * @return a netId if there is a running VPN network or NETID_UNSET if there is no running VPN
+     *         network or network is null.
+     */
+    public synchronized int getNetId() {
         final NetworkAgent agent = mNetworkAgent;
         if (null == agent) return NETID_UNSET;
         final Network network = agent.getNetwork();
@@ -1245,7 +1252,7 @@
         mNetworkCapabilities.setAdministratorUids(new int[] {mOwnerUID});
         mNetworkCapabilities.setUids(createUserAndRestrictedProfilesRanges(mUserId,
                 mConfig.allowedApplications, mConfig.disallowedApplications));
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             mNetworkAgent = new NetworkAgent(mLooper, mContext, NETWORKTYPE /* logtag */,
                     mNetworkInfo, mNetworkCapabilities, lp,
@@ -1264,7 +1271,7 @@
     }
 
     private boolean canHaveRestrictedProfile(int userId) {
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             return UserManager.get(mContext).canHaveRestrictedProfile(userId);
         } finally {
@@ -1311,7 +1318,7 @@
         // Check if the service is properly declared.
         Intent intent = new Intent(VpnConfig.SERVICE_INTERFACE);
         intent.setClassName(mPackage, config.user);
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             // Restricted users are not allowed to create VPNs, they are tied to Owner
             enforceNotRestrictedUser();
@@ -1602,7 +1609,7 @@
      */
     public synchronized void onUserStopped() {
         // Switch off networking lockdown (if it was enabled)
-        setLockdown(false);
+        setVpnForcedLocked(false);
         mAlwaysOn = false;
 
         // Quit any active connections
@@ -1708,7 +1715,7 @@
     /**
      * Return the configuration of the currently running VPN.
      */
-    public VpnConfig getVpnConfig() {
+    public synchronized VpnConfig getVpnConfig() {
         enforceControlPermission();
         return mConfig;
     }
@@ -1970,28 +1977,33 @@
          * @see Settings.Secure#putStringForUser
          */
         public void settingsSecurePutStringForUser(String key, String value, int userId) {
-            Settings.Secure.putStringForUser(mContext.getContentResolver(), key, value, userId);
+            Settings.Secure.putString(getContentResolverAsUser(userId), key, value);
         }
 
         /**
          * @see Settings.Secure#putIntForUser
          */
         public void settingsSecurePutIntForUser(String key, int value, int userId) {
-            Settings.Secure.putIntForUser(mContext.getContentResolver(), key, value, userId);
+            Settings.Secure.putInt(getContentResolverAsUser(userId), key, value);
         }
 
         /**
          * @see Settings.Secure#getStringForUser
          */
         public String settingsSecureGetStringForUser(String key, int userId) {
-            return Settings.Secure.getStringForUser(mContext.getContentResolver(), key, userId);
+            return Settings.Secure.getString(getContentResolverAsUser(userId), key);
         }
 
         /**
          * @see Settings.Secure#getIntForUser
          */
         public int settingsSecureGetIntForUser(String key, int def, int userId) {
-            return Settings.Secure.getIntForUser(mContext.getContentResolver(), key, def, userId);
+            return Settings.Secure.getInt(getContentResolverAsUser(userId), key, def);
+        }
+
+        private ContentResolver getContentResolverAsUser(int userId) {
+            return mContext.createContextAsUser(
+                    UserHandle.of(userId), 0 /* flags */).getContentResolver();
         }
 
         public boolean isCallerSystem() {
@@ -2039,7 +2051,7 @@
      */
     public void startLegacyVpn(VpnProfile profile, KeyStore keyStore, LinkProperties egress) {
         enforceControlPermission();
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             startLegacyVpnPrivileged(profile, keyStore, egress);
         } finally {
@@ -2146,7 +2158,11 @@
                 break;
         }
 
-        // Prepare arguments for mtpd.
+        // Prepare arguments for mtpd. MTU/MRU calculated conservatively. Only IPv4 supported
+        // because LegacyVpn.
+        // 1500 - 60 (Carrier-internal IPv6 + UDP + GTP) - 10 (PPP) - 16 (L2TP) - 8 (UDP)
+        //   - 77 (IPsec w/ SHA-2 512, 256b trunc-len, AES-CBC) - 8 (UDP encap) - 20 (IPv4)
+        //   - 28 (464xlat)
         String[] mtpd = null;
         switch (profile.type) {
             case VpnProfile.TYPE_PPTP:
@@ -2154,7 +2170,7 @@
                     iface, "pptp", profile.server, "1723",
                     "name", profile.username, "password", profile.password,
                     "linkname", "vpn", "refuse-eap", "nodefaultroute",
-                    "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
+                    "usepeerdns", "idle", "1800", "mtu", "1270", "mru", "1270",
                     (profile.mppe ? "+mppe" : "nomppe"),
                 };
                 break;
@@ -2164,7 +2180,7 @@
                     iface, "l2tp", profile.server, "1701", profile.l2tpSecret,
                     "name", profile.username, "password", profile.password,
                     "linkname", "vpn", "refuse-eap", "nodefaultroute",
-                    "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
+                    "usepeerdns", "idle", "1800", "mtu", "1270", "mru", "1270",
                 };
                 break;
         }
@@ -2755,7 +2771,10 @@
                     final LinkProperties lp = cm.getLinkProperties(network);
                     if (lp != null && lp.getAllInterfaceNames().contains(mOuterInterface)) {
                         final NetworkInfo networkInfo = cm.getNetworkInfo(network);
-                        if (networkInfo != null) mOuterConnection.set(networkInfo.getType());
+                        if (networkInfo != null) {
+                            mOuterConnection.set(networkInfo.getType());
+                            break;
+                        }
                     }
                 }
             }
@@ -2986,7 +3005,7 @@
                     checkInterruptAndDelay(false);
 
                     // Check if the interface is gone while we are waiting.
-                    if (mDeps.checkInterfacePresent(Vpn.this, mConfig.interfaze)) {
+                    if (!mDeps.isInterfacePresent(Vpn.this, mConfig.interfaze)) {
                         throw new IllegalStateException(mConfig.interfaze + " is gone");
                     }
 
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index 1294e90..a2c427b8 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -596,7 +596,7 @@
 
         // This makes it so that future permission checks will be in the context of this
         // process rather than the caller's process. We will restore this before returning.
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             SyncManager syncManager = getSyncManager();
             if (syncManager != null) {
@@ -650,7 +650,7 @@
 
         // This makes it so that future permission checks will be in the context of this
         // process rather than the caller's process. We will restore this before returning.
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             SyncManager syncManager = getSyncManager();
             if (syncManager == null) {
@@ -718,7 +718,7 @@
                 "no permission to modify the sync settings for user " + userId);
         // This makes it so that future permission checks will be in the context of this
         // process rather than the caller's process. We will restore this before returning.
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         if (cname != null) {
             Slog.e(TAG, "cname not null.");
             return;
@@ -751,7 +751,7 @@
         Bundle extras = new Bundle(request.getBundle());
         validateExtras(callingUid, extras);
 
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             SyncStorageEngine.EndPoint info;
 
@@ -834,7 +834,7 @@
         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
                 "no permission to read the sync settings");
 
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             SyncManager syncManager = getSyncManager();
             if (syncManager != null) {
@@ -866,7 +866,7 @@
         final int callingPid = Binder.getCallingPid();
         final int syncExemptionFlag = getSyncExemptionForCaller(callingUid);
 
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             SyncManager syncManager = getSyncManager();
             if (syncManager != null) {
@@ -899,7 +899,7 @@
         pollFrequency = clampPeriod(pollFrequency);
         long defaultFlex = SyncStorageEngine.calculateDefaultFlexTime(pollFrequency);
 
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             SyncStorageEngine.EndPoint info =
                     new SyncStorageEngine.EndPoint(account, authority, userId);
@@ -927,7 +927,7 @@
         final int callingUid = Binder.getCallingUid();
 
         int userId = UserHandle.getCallingUserId();
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             getSyncManager()
                     .removePeriodicSync(
@@ -951,7 +951,7 @@
                 "no permission to read the sync settings");
 
         int userId = UserHandle.getCallingUserId();
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             return getSyncManager().getPeriodicSyncs(
                     new SyncStorageEngine.EndPoint(account, providerName, userId));
@@ -976,7 +976,7 @@
         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
                 "no permission to read the sync settings");
 
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             SyncManager syncManager = getSyncManager();
             if (syncManager != null) {
@@ -1012,7 +1012,7 @@
         final int callingUid = Binder.getCallingUid();
         final int callingPid = Binder.getCallingPid();
 
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             SyncManager syncManager = getSyncManager();
             if (syncManager != null) {
@@ -1040,7 +1040,7 @@
         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
                 "no permission to read the sync settings");
 
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             SyncManager syncManager = getSyncManager();
             if (syncManager != null) {
@@ -1067,7 +1067,7 @@
         final int callingUid = Binder.getCallingUid();
         final int callingPid = Binder.getCallingPid();
 
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             SyncManager syncManager = getSyncManager();
             if (syncManager != null) {
@@ -1084,7 +1084,7 @@
         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
                 "no permission to read the sync stats");
         int userId = UserHandle.getCallingUserId();
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             SyncManager syncManager = getSyncManager();
             if (syncManager == null) {
@@ -1116,7 +1116,7 @@
         final boolean canAccessAccounts =
             mContext.checkCallingOrSelfPermission(Manifest.permission.GET_ACCOUNTS)
                 == PackageManager.PERMISSION_GRANTED;
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             return getSyncManager().getSyncStorageEngine()
                 .getCurrentSyncsCopy(userId, canAccessAccounts);
@@ -1146,7 +1146,7 @@
         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
                 "no permission to read the sync stats");
 
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             SyncManager syncManager = getSyncManager();
             if (syncManager == null) {
@@ -1176,7 +1176,7 @@
                 "no permission to read the sync stats");
         enforceCrossUserPermission(userId,
                 "no permission to retrieve the sync settings for user " + userId);
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         SyncManager syncManager = getSyncManager();
         if (syncManager == null) return false;
 
@@ -1196,7 +1196,7 @@
     @Override
     public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
         final int callingUid = Binder.getCallingUid();
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             SyncManager syncManager = getSyncManager();
             if (syncManager != null && callback != null) {
@@ -1210,7 +1210,7 @@
 
     @Override
     public void removeStatusChangeListener(ISyncStatusObserver callback) {
-        long identityToken = clearCallingIdentity();
+        final long identityToken = clearCallingIdentity();
         try {
             SyncManager syncManager = getSyncManager();
             if (syncManager != null && callback != null) {
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 91fa15a..c686ba4 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -2773,7 +2773,7 @@
         }
 
         private void onReady() {
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 mOnReadyCallback.onReady();
             } finally {
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
new file mode 100644
index 0000000..7e3c1ab
--- /dev/null
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.devicestate;
+
+import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.devicestate.IDeviceStateManager;
+import android.util.IntArray;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.SystemService;
+import com.android.server.policy.DeviceStatePolicyImpl;
+
+import java.util.Arrays;
+
+/**
+ * A system service that manages the state of a device with user-configurable hardware like a
+ * foldable phone.
+ * <p>
+ * Device state is an abstract concept that allows mapping the current state of the device to the
+ * state of the system. For example, system services (like
+ * {@link com.android.server.display.DisplayManagerService display manager} and
+ * {@link com.android.server.wm.WindowManagerService window manager}) and system UI may have
+ * different behaviors depending on the physical state of the device. This is useful for
+ * variable-state devices, like foldable or rollable devices, that can be configured by users into
+ * differing hardware states, which each may have a different expected use case.
+ * </p>
+ * <p>
+ * The {@link DeviceStateManagerService} is responsible for receiving state change requests from
+ * the {@link DeviceStateProvider} to modify the current device state and communicating with the
+ * {@link DeviceStatePolicy policy} to ensure the system is configured to match the requested state.
+ * </p>
+ *
+ * @see DeviceStatePolicy
+ */
+public final class DeviceStateManagerService extends SystemService {
+    private static final String TAG = "DeviceStateManagerService";
+    private static final boolean DEBUG = false;
+
+    private final Object mLock = new Object();
+    @NonNull
+    private final DeviceStatePolicy mDeviceStatePolicy;
+
+    @GuardedBy("mLock")
+    private IntArray mSupportedDeviceStates;
+
+    // The current committed device state.
+    @GuardedBy("mLock")
+    private int mCommittedState = INVALID_DEVICE_STATE;
+    // The device state that is currently pending callback from the policy to be committed.
+    @GuardedBy("mLock")
+    private int mPendingState = INVALID_DEVICE_STATE;
+    // Whether or not the policy is currently waiting to be notified of the current pending state.
+    @GuardedBy("mLock")
+    private boolean mIsPolicyWaitingForState = false;
+    // The device state that is currently requested and is next to be configured and committed.
+    @GuardedBy("mLock")
+    private int mRequestedState = INVALID_DEVICE_STATE;
+
+    public DeviceStateManagerService(@NonNull Context context) {
+        this(context, new DeviceStatePolicyImpl());
+    }
+
+    @VisibleForTesting
+    DeviceStateManagerService(@NonNull Context context, @NonNull DeviceStatePolicy policy) {
+        super(context);
+        mDeviceStatePolicy = policy;
+    }
+
+    @Override
+    public void onStart() {
+        mDeviceStatePolicy.getDeviceStateProvider().setListener(new DeviceStateProviderListener());
+        publishBinderService(Context.DEVICE_STATE_SERVICE, new BinderService());
+    }
+
+    /**
+     * Returns the current state the system is in. Note that the system may be in the process of
+     * configuring a different state.
+     *
+     * @see #getPendingState()
+     */
+    @VisibleForTesting
+    int getCommittedState() {
+        synchronized (mLock) {
+            return mCommittedState;
+        }
+    }
+
+    /**
+     * Returns the state the system is currently configuring, or {@link #INVALID_DEVICE_STATE} if
+     * the system is not in the process of configuring a state.
+     */
+    @VisibleForTesting
+    int getPendingState() {
+        synchronized (mLock) {
+            return mPendingState;
+        }
+    }
+
+    /**
+     * Returns the requested state. The service will configure the device to match the requested
+     * state when possible.
+     */
+    @VisibleForTesting
+    int getRequestedState() {
+        synchronized (mLock) {
+            return mRequestedState;
+        }
+    }
+
+    private void updateSupportedStates(int[] supportedDeviceStates) {
+        // Must ensure sorted as isSupportedStateLocked() impl uses binary search.
+        Arrays.sort(supportedDeviceStates, 0, supportedDeviceStates.length);
+        synchronized (mLock) {
+            mSupportedDeviceStates = IntArray.wrap(supportedDeviceStates);
+
+            if (mRequestedState != INVALID_DEVICE_STATE
+                    && !isSupportedStateLocked(mRequestedState)) {
+                // The current requested state is no longer valid. We'll clear it here, though
+                // we won't actually update the current state with a call to
+                // updatePendingStateLocked() as doing so will not have any effect.
+                mRequestedState = INVALID_DEVICE_STATE;
+            }
+        }
+    }
+
+    /**
+     * Returns {@code true} if the provided state is supported. Requires that
+     * {@link #mSupportedDeviceStates} is sorted prior to calling.
+     */
+    private boolean isSupportedStateLocked(int state) {
+        return mSupportedDeviceStates.binarySearch(state) >= 0;
+    }
+
+    /**
+     * Requests that the system enter the provided {@code state}. The request may not be honored
+     * under certain conditions, for example if the provided state is not supported.
+     *
+     * @see #isSupportedStateLocked(int)
+     */
+    private void requestState(int state) {
+        synchronized (mLock) {
+            if (isSupportedStateLocked(state)) {
+                mRequestedState = state;
+            }
+            updatePendingStateLocked();
+        }
+
+        notifyPolicyIfNeeded();
+    }
+
+    /**
+     * Tries to update the current configuring state with the current requested state. Must call
+     * {@link #notifyPolicyIfNeeded()} to actually notify the policy that the state is being
+     * changed.
+     */
+    private void updatePendingStateLocked() {
+        if (mRequestedState == INVALID_DEVICE_STATE) {
+            // No currently requested state.
+            return;
+        }
+
+        if (mPendingState != INVALID_DEVICE_STATE) {
+            // Have pending state, can not configure a new state until the state is committed.
+            return;
+        }
+
+        if (mRequestedState == mCommittedState) {
+            // No need to notify the policy as the committed state matches the requested state.
+            return;
+        }
+
+        mPendingState = mRequestedState;
+        mIsPolicyWaitingForState = true;
+    }
+
+    /**
+     * Notifies the policy to configure the supplied state. Should not be called with {@link #mLock}
+     * held.
+     */
+    private void notifyPolicyIfNeeded() {
+        if (Thread.holdsLock(mLock)) {
+            Throwable error = new Throwable("Attempting to notify DeviceStatePolicy with service"
+                    + " lock held");
+            error.fillInStackTrace();
+            Slog.w(TAG, error);
+        }
+        int state;
+        synchronized (mLock) {
+            if (!mIsPolicyWaitingForState) {
+                return;
+            }
+            mIsPolicyWaitingForState = false;
+            state = mPendingState;
+        }
+
+        if (DEBUG) {
+            Slog.d(TAG, "Notifying policy to configure state: " + state);
+        }
+        mDeviceStatePolicy.configureDeviceForState(state, this::commitPendingState);
+    }
+
+    /**
+     * Commits the current pending state after a callback from the {@link DeviceStatePolicy}.
+     *
+     * <pre>
+     *              -------------    -----------              -------------
+     * Provider ->  | Requested | -> | Pending | -> Policy -> | Committed |
+     *              -------------    -----------              -------------
+     * </pre>
+     * <p>
+     * When a new state is requested it immediately enters the requested state. Once the policy is
+     * available to accept a new state, which could also be immediately if there is no current
+     * pending state at the point of request, the policy is notified and a callback is provided to
+     * trigger the state to be committed.
+     * </p>
+     */
+    private void commitPendingState() {
+        synchronized (mLock) {
+            if (DEBUG) {
+                Slog.d(TAG, "Committing state: " + mPendingState);
+            }
+            mCommittedState = mPendingState;
+            mPendingState = INVALID_DEVICE_STATE;
+            updatePendingStateLocked();
+        }
+
+        notifyPolicyIfNeeded();
+    }
+
+    private final class DeviceStateProviderListener implements DeviceStateProvider.Listener {
+        @Override
+        public void onSupportedDeviceStatesChanged(int[] newDeviceStates) {
+            for (int i = 0; i < newDeviceStates.length; i++) {
+                if (newDeviceStates[i] < 0) {
+                    throw new IllegalArgumentException("Supported device states includes invalid"
+                            + " value: " + newDeviceStates[i]);
+                }
+            }
+
+            updateSupportedStates(newDeviceStates);
+        }
+
+        @Override
+        public void onStateChanged(int state) {
+            if (state < 0) {
+                throw new IllegalArgumentException("Invalid state: " + state);
+            }
+
+            requestState(state);
+        }
+    }
+
+    /** Implementation of {@link IDeviceStateManager} published as a binder service. */
+    private final class BinderService extends IDeviceStateManager.Stub {
+
+    }
+}
diff --git a/services/core/java/com/android/server/devicestate/DeviceStatePolicy.java b/services/core/java/com/android/server/devicestate/DeviceStatePolicy.java
new file mode 100644
index 0000000..274b8e5
--- /dev/null
+++ b/services/core/java/com/android/server/devicestate/DeviceStatePolicy.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.devicestate;
+
+import android.annotation.NonNull;
+
+/**
+ * Interface for the component responsible for supplying the current device state as well as
+ * configuring the state of the system in response to device state changes.
+ *
+ * @see DeviceStateManagerService
+ */
+public interface DeviceStatePolicy {
+    /** Returns the provider of device states. */
+    DeviceStateProvider getDeviceStateProvider();
+
+    /**
+     * Configures the system into the provided state. Guaranteed not to be called again until the
+     * {@code onComplete} callback has been executed.
+     *
+     * @param state the state the system should be configured for.
+     * @param onComplete a callback that must be triggered once the system has been properly
+     *                   configured to match the supplied state.
+     */
+    void configureDeviceForState(int state, @NonNull Runnable onComplete);
+}
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateProvider.java b/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
new file mode 100644
index 0000000..0e8bf9b
--- /dev/null
+++ b/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.devicestate;
+
+/**
+ * Responsible for providing the set of currently supported device states and well as the current
+ * device state.
+ *
+ * @see DeviceStatePolicy
+ */
+public interface DeviceStateProvider {
+    /**
+     * Registers a listener for changes in provider state.
+     * <p>
+     * It is <b>required</b> that {@link Listener#onSupportedDeviceStatesChanged(int[])} be called
+     * followed by {@link Listener#onStateChanged(int)} with the initial values on successful
+     * registration of the listener.
+     */
+    void setListener(Listener listener);
+
+    /** Callback for changes in {@link DeviceStateProvider} state. */
+    interface Listener {
+        /**
+         * Called to notify the listener of a change in supported device states. Required to be
+         * called once on successful registration of the listener and then once on every
+         * subsequent change in supported device states.
+         * <p>
+         * The set of device states can change based on the current hardware state of the device.
+         * For example, if a device state depends on a particular peripheral device (display, etc)
+         * it would only be reported as supported when the device is plugged. Otherwise, it should
+         * not be included in the set of supported states.
+         * <p>
+         * All values provided must be greater than or equal to zero and there must always be at
+         * least one supported device state.
+         *
+         * @param newDeviceStates array of supported device states.
+         *
+         * @throws IllegalArgumentException if the list of device states is empty or if one of the
+         * provided states is less than 0.
+         */
+        void onSupportedDeviceStatesChanged(int[] newDeviceStates);
+
+        /**
+         * Called to notify the listener of a change in current device state. Required to be called
+         * once on successful registration of the listener and then once on every subsequent change
+         * in device state. Value must have been included in the set of supported device states
+         * provided in the most recent call to {@link #onSupportedDeviceStatesChanged(int[])}.
+         *
+         * @param state the new device state.
+         *
+         * @throws IllegalArgumentException if the state is less than 0.
+         */
+        void onStateChanged(int state);
+    }
+}
diff --git a/services/core/java/com/android/server/devicestate/OWNERS b/services/core/java/com/android/server/devicestate/OWNERS
new file mode 100644
index 0000000..ae79fc0
--- /dev/null
+++ b/services/core/java/com/android/server/devicestate/OWNERS
@@ -0,0 +1,3 @@
+ogunwale@google.com
+akulian@google.com
+darryljohnson@google.com
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index eb2c7e6..eb61a1c 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -208,7 +208,6 @@
     private PackageManager mPackageManager;
     private Context mContext;
 
-    private DisplayDeviceConfig mDisplayDeviceConfig;
     private final Injector mInjector;
 
     AutomaticBrightnessController(Callbacks callbacks, Looper looper,
@@ -217,14 +216,13 @@
             float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
             long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
             boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
-            HysteresisLevels screenBrightnessThresholds, Context context, DisplayDeviceConfig
-            displayDeviceConfig) {
+            HysteresisLevels screenBrightnessThresholds, Context context) {
         this(new Injector(), callbacks, looper, sensorManager, lightSensor, mapper,
                 lightSensorWarmUpTime, brightnessMin, brightnessMax, dozeScaleFactor,
                 lightSensorRate, initialLightSensorRate, brighteningLightDebounceConfig,
                 darkeningLightDebounceConfig, resetAmbientLuxAfterWarmUpConfig,
-                ambientBrightnessThresholds, screenBrightnessThresholds, context,
-                displayDeviceConfig);
+                ambientBrightnessThresholds, screenBrightnessThresholds, context
+        );
     }
 
     @VisibleForTesting
@@ -234,8 +232,7 @@
             float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
             long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
             boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
-            HysteresisLevels screenBrightnessThresholds, Context context, DisplayDeviceConfig
-            displayDeviceConfig) {
+            HysteresisLevels screenBrightnessThresholds, Context context) {
         mInjector = injector;
         mContext = context;
         mCallbacks = callbacks;
@@ -257,7 +254,6 @@
         mScreenBrightnessThresholds = screenBrightnessThresholds;
         mShortTermModelValid = true;
         mShortTermModelAnchor = -1;
-        mDisplayDeviceConfig = displayDeviceConfig;
         mHandler = new AutomaticBrightnessHandler(looper);
         mAmbientLightRingBuffer =
             new AmbientLightRingBuffer(mNormalLightSensorRate, mAmbientLightHorizon);
@@ -1055,7 +1051,7 @@
 
         @Override
         public String toString() {
-            StringBuffer buf = new StringBuffer();
+            StringBuilder buf = new StringBuilder();
             buf.append('[');
             for (int i = 0; i < mCount; i++) {
                 final long next = i + 1 < mCount ? getTime(i + 1) : SystemClock.uptimeMillis();
diff --git a/services/core/java/com/android/server/display/DisplayBlanker.java b/services/core/java/com/android/server/display/DisplayBlanker.java
index d294898..e2129ba 100644
--- a/services/core/java/com/android/server/display/DisplayBlanker.java
+++ b/services/core/java/com/android/server/display/DisplayBlanker.java
@@ -20,5 +20,5 @@
  * Interface used to update the actual display state.
  */
 public interface DisplayBlanker {
-    void requestDisplayState(int state, float brightness);
+    void requestDisplayState(int displayId, int state, float brightness);
 }
diff --git a/services/core/java/com/android/server/display/DisplayDeviceRepository.java b/services/core/java/com/android/server/display/DisplayDeviceRepository.java
index f4f77db..5c0fceb 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceRepository.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceRepository.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.util.Slog;
+import android.view.Display;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.server.display.DisplayManagerService.SyncRoot;
@@ -48,15 +49,27 @@
     @GuardedBy("mSyncRoot")
     private final List<DisplayDevice> mDisplayDevices = new ArrayList<>();
 
-    /** Listener for {link DisplayDevice} events. */
-    private final Listener mListener;
+    /** Listeners for {link DisplayDevice} events. */
+    @GuardedBy("mSyncRoot")
+    private final List<Listener> mListeners = new ArrayList<>();
 
     /** Global lock object from {@link DisplayManagerService}. */
     private final SyncRoot mSyncRoot;
 
-    DisplayDeviceRepository(@NonNull SyncRoot syncRoot, @NonNull Listener listener) {
+    private final PersistentDataStore mPersistentDataStore;
+
+    /**
+     * @param syncRoot The global lock for DisplayManager related objects.
+     * @param persistentDataStore Settings data store from {@link DisplayManagerService}.
+     */
+    DisplayDeviceRepository(@NonNull SyncRoot syncRoot,
+            @NonNull PersistentDataStore persistentDataStore) {
         mSyncRoot = syncRoot;
-        mListener = listener;
+        mPersistentDataStore = persistentDataStore;
+    }
+
+    public void addListener(@NonNull Listener listener) {
+        mListeners.add(listener);
     }
 
     @Override
@@ -78,7 +91,10 @@
 
     @Override
     public void onTraversalRequested() {
-        mListener.onTraversalRequested();
+        final int size = mListeners.size();
+        for (int i = 0; i < size; i++) {
+            mListeners.get(i).onTraversalRequested();
+        }
     }
 
     public boolean containsLocked(DisplayDevice d) {
@@ -96,6 +112,17 @@
         }
     }
 
+    public DisplayDevice getByIdLocked(@NonNull String uniqueId) {
+        final int count = mDisplayDevices.size();
+        for (int i = 0; i < count; i++) {
+            final DisplayDevice d = mDisplayDevices.get(i);
+            if (uniqueId.equals(d.getUniqueId())) {
+                return d;
+            }
+        }
+        return null;
+    }
+
     private void handleDisplayDeviceAdded(DisplayDevice device) {
         synchronized (mSyncRoot) {
             DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
@@ -107,18 +134,37 @@
             device.mDebugLastLoggedDeviceInfo = info;
 
             mDisplayDevices.add(device);
-            mListener.onDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
+            sendEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
         }
     }
 
     private void handleDisplayDeviceChanged(DisplayDevice device) {
         synchronized (mSyncRoot) {
-            DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
+            final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
             if (!mDisplayDevices.contains(device)) {
                 Slog.w(TAG, "Attempted to change non-existent display device: " + info);
                 return;
             }
-            mListener.onDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
+
+            int diff = device.mDebugLastLoggedDeviceInfo.diff(info);
+            if (diff == DisplayDeviceInfo.DIFF_STATE) {
+                Slog.i(TAG, "Display device changed state: \"" + info.name
+                        + "\", " + Display.stateToString(info.state));
+            } else if (diff != 0) {
+                Slog.i(TAG, "Display device changed: " + info);
+            }
+
+            if ((diff & DisplayDeviceInfo.DIFF_COLOR_MODE) != 0) {
+                try {
+                    mPersistentDataStore.setColorMode(device, info.colorMode);
+                } finally {
+                    mPersistentDataStore.saveIfNeeded();
+                }
+            }
+            device.mDebugLastLoggedDeviceInfo = info;
+
+            device.applyPendingDisplayDeviceInfoChangesLocked();
+            sendEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
         }
     }
 
@@ -132,14 +178,21 @@
 
             Slog.i(TAG, "Display device removed: " + info);
             device.mDebugLastLoggedDeviceInfo = info;
-            mListener.onDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_REMOVED);
+            sendEventLocked(device, DISPLAY_DEVICE_EVENT_REMOVED);
+        }
+    }
+
+    private void sendEventLocked(DisplayDevice device, int event) {
+        final int size = mListeners.size();
+        for (int i = 0; i < size; i++) {
+            mListeners.get(i).onDisplayDeviceEventLocked(device, event);
         }
     }
 
     /**
      * Listens to {@link DisplayDevice} events from {@link DisplayDeviceRepository}.
      */
-    interface Listener {
+    public interface Listener {
         void onDisplayDeviceEventLocked(DisplayDevice device, int event);
 
         // TODO: multi-display - Try to remove the need for requestTraversal...it feels like
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 597f49c6..60c83905e 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -91,6 +91,7 @@
 import android.util.Spline;
 import android.view.Display;
 import android.view.DisplayInfo;
+import android.view.IDisplayFoldListener;
 import android.view.Surface;
 import android.view.SurfaceControl;
 
@@ -103,16 +104,17 @@
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.UiThread;
+import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.wm.SurfaceAnimationThread;
 import com.android.server.wm.WindowManagerInternal;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 import java.util.Optional;
 import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.function.Consumer;
 
 /**
  * Manages attached displays.
@@ -181,7 +183,6 @@
     private final Context mContext;
     private final DisplayManagerHandler mHandler;
     private final Handler mUiHandler;
-    private final DisplayDeviceListener mDisplayDeviceListener;
     private final DisplayModeDirector mDisplayModeDirector;
     private WindowManagerInternal mWindowManagerInternal;
     private InputManagerInternal mInputManagerInternal;
@@ -202,13 +203,6 @@
     // services should be started.  This option may disable certain display adapters.
     public boolean mOnlyCore;
 
-    // True if the display manager service should pretend there is only one display
-    // and only tell applications about the existence of the default logical display.
-    // The display manager can still mirror content to secondary displays but applications
-    // cannot present unique content on those displays.
-    // Used for demonstration purposes only.
-    private final boolean mSingleDisplayDemoMode;
-
     // All callback records indexed by calling process id.
     public final SparseArray<CallbackRecord> mCallbacks =
             new SparseArray<CallbackRecord>();
@@ -216,13 +210,17 @@
     // List of all currently registered display adapters.
     private final ArrayList<DisplayAdapter> mDisplayAdapters = new ArrayList<DisplayAdapter>();
 
+    /**
+     * Repository of all active {@link DisplayDevice}s.
+     */
     private final DisplayDeviceRepository mDisplayDeviceRepo;
 
-    // List of all logical displays indexed by logical display id.
-    // Any modification to mLogicalDisplays must invalidate the DisplayManagerGlobal cache.
-    private final SparseArray<LogicalDisplay> mLogicalDisplays =
-            new SparseArray<LogicalDisplay>();
-    private int mNextNonDefaultDisplayId = Display.DEFAULT_DISPLAY + 1;
+    /**
+     * Contains all the {@link LogicalDisplay} instances and is responsible for mapping
+     * {@link DisplayDevice}s to {@link LogicalDisplay}s. DisplayManagerService listens to display
+     * event on this object.
+     */
+    private final LogicalDisplayMapper mLogicalDisplayMapper;
 
     // List of all display transaction listeners.
     private final CopyOnWriteArrayList<DisplayTransactionListener> mDisplayTransactionListeners =
@@ -282,10 +280,6 @@
     // May be used outside of the lock but only on the handler thread.
     private final ArrayList<CallbackRecord> mTempCallbacks = new ArrayList<CallbackRecord>();
 
-    // Temporary display info, used for comparing display configurations.
-    private final DisplayInfo mTempDisplayInfo = new DisplayInfo();
-    private final DisplayInfo mTempNonOverrideDisplayInfo = new DisplayInfo();
-
     // Temporary viewports, used when sending new viewport information to the
     // input system.  May be used outside of the lock but only on the handler thread.
     private final ArrayList<DisplayViewport> mTempViewports = new ArrayList<>();
@@ -320,6 +314,9 @@
     // Receives notifications about changes to Settings.
     private SettingsObserver mSettingsObserver;
 
+    // Received notifications of the display-fold action
+    private DisplayFoldListener mDisplayFoldListener;
+
     public DisplayManagerService(Context context) {
         this(context, new Injector());
     }
@@ -331,10 +328,10 @@
         mContext = context;
         mHandler = new DisplayManagerHandler(DisplayThread.get().getLooper());
         mUiHandler = UiThread.getHandler();
-        mDisplayDeviceListener = new DisplayDeviceListener();
-        mDisplayDeviceRepo = new DisplayDeviceRepository(mSyncRoot, mDisplayDeviceListener);
+        mDisplayDeviceRepo = new DisplayDeviceRepository(mSyncRoot, mPersistentDataStore);
+        mLogicalDisplayMapper = new LogicalDisplayMapper(context, mDisplayDeviceRepo,
+                new LogicalDisplayListener(), mPersistentDataStore);
         mDisplayModeDirector = new DisplayModeDirector(context, mHandler);
-        mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false);
         Resources resources = mContext.getResources();
         mDefaultDisplayDefaultColorMode = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_defaultDisplayDefaultColorMode);
@@ -395,13 +392,13 @@
             synchronized (mSyncRoot) {
                 long timeout = SystemClock.uptimeMillis()
                         + mInjector.getDefaultDisplayDelayTimeout();
-                while (mLogicalDisplays.get(Display.DEFAULT_DISPLAY) == null ||
-                        mVirtualDisplayAdapter == null) {
+                while (mLogicalDisplayMapper.getLocked(Display.DEFAULT_DISPLAY) == null
+                        || mVirtualDisplayAdapter == null) {
                     long delay = timeout - SystemClock.uptimeMillis();
                     if (delay <= 0) {
                         throw new RuntimeException("Timeout waiting for default display "
                                 + "to be initialized. DefaultDisplay="
-                                + mLogicalDisplays.get(Display.DEFAULT_DISPLAY)
+                                + mLogicalDisplayMapper.getLocked(Display.DEFAULT_DISPLAY)
                                 + ", mVirtualDisplayAdapter=" + mVirtualDisplayAdapter);
                     }
                     if (DEBUG) {
@@ -437,6 +434,11 @@
         synchronized (mSyncRoot) {
             mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
             mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
+            WindowManagerPolicy policy = LocalServices.getService(WindowManagerPolicy.class);
+
+            mDisplayFoldListener = new DisplayFoldListener();
+            policy.registerDisplayFoldListener(mDisplayFoldListener);
+
             scheduleTraversalLocked(false);
         }
     }
@@ -451,7 +453,7 @@
             mSystemReady = true;
             // Just in case the top inset changed before the system was ready. At this point, any
             // relevant configuration should be in place.
-            recordTopInsetLocked(mLogicalDisplays.get(Display.DEFAULT_DISPLAY));
+            recordTopInsetLocked(mLogicalDisplayMapper.getLocked(Display.DEFAULT_DISPLAY));
 
             updateSettingsLocked();
         }
@@ -516,13 +518,12 @@
     }
 
     @VisibleForTesting
-    void setDisplayInfoOverrideFromWindowManagerInternal(
-            int displayId, DisplayInfo info) {
+    void setDisplayInfoOverrideFromWindowManagerInternal(int displayId, DisplayInfo info) {
         synchronized (mSyncRoot) {
-            LogicalDisplay display = mLogicalDisplays.get(displayId);
+            LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId);
             if (display != null) {
                 if (display.setDisplayInfoOverrideFromWindowManagerLocked(info)) {
-                    handleLogicalDisplayChanged(displayId, display);
+                    handleLogicalDisplayChangedLocked(display);
                     scheduleTraversalLocked(false);
                 }
             }
@@ -534,7 +535,7 @@
      */
     private void getNonOverrideDisplayInfoInternal(int displayId, DisplayInfo outInfo) {
         synchronized (mSyncRoot) {
-            final LogicalDisplay display = mLogicalDisplays.get(displayId);
+            final LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId);
             if (display != null) {
                 display.getNonOverrideDisplayInfoLocked(outInfo);
             }
@@ -585,6 +586,7 @@
                     Trace.traceBegin(Trace.TRACE_TAG_POWER, "requestGlobalDisplayState("
                             + Display.stateToString(state)
                             + ", brightness=" + brightnessState + ")");
+
                     mGlobalDisplayState = state;
                     mGlobalDisplayBrightness = brightnessState;
                     applyGlobalDisplayStateLocked(mTempDisplayStateWorkQueue);
@@ -633,7 +635,7 @@
 
     private DisplayInfo getDisplayInfoInternal(int displayId, int callingUid) {
         synchronized (mSyncRoot) {
-            LogicalDisplay display = mLogicalDisplays.get(displayId);
+            LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId);
             if (display != null) {
                 DisplayInfo info = display.getDisplayInfoLocked();
                 if (info.hasAccess(callingUid)
@@ -645,25 +647,6 @@
         }
     }
 
-    private int[] getDisplayIdsInternal(int callingUid) {
-        synchronized (mSyncRoot) {
-            final int count = mLogicalDisplays.size();
-            int[] displayIds = new int[count];
-            int n = 0;
-            for (int i = 0; i < count; i++) {
-                LogicalDisplay display = mLogicalDisplays.valueAt(i);
-                DisplayInfo info = display.getDisplayInfoLocked();
-                if (info.hasAccess(callingUid)) {
-                    displayIds[n++] = mLogicalDisplays.keyAt(i);
-                }
-            }
-            if (n != count) {
-                displayIds = Arrays.copyOfRange(displayIds, 0, n);
-            }
-            return displayIds;
-        }
-    }
-
     private void registerCallbackInternal(IDisplayManagerCallback callback, int callingPid) {
         synchronized (mSyncRoot) {
             if (mCallbacks.get(callingPid) != null) {
@@ -798,7 +781,7 @@
 
     private void requestColorModeInternal(int displayId, int colorMode) {
         synchronized (mSyncRoot) {
-            LogicalDisplay display = mLogicalDisplays.get(displayId);
+            LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId);
             if (display != null &&
                     display.getRequestedColorModeLocked() != colorMode) {
                 display.setRequestedColorModeLocked(colorMode);
@@ -835,7 +818,7 @@
             mDisplayDeviceRepo.onDisplayDeviceEvent(device,
                     DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED);
 
-            LogicalDisplay display = findLogicalDisplayForDeviceLocked(device);
+            LogicalDisplay display = mLogicalDisplayMapper.getLocked(device);
             if (display != null) {
                 return display.getDisplayIdLocked();
             }
@@ -880,7 +863,7 @@
             DisplayDevice device =
                     mVirtualDisplayAdapter.releaseVirtualDisplayLocked(appToken);
             if (device != null) {
-                // TODO - handle virtual displays the same as other display adapters.
+                // TODO: multi-display - handle virtual displays the same as other display adapters.
                 mDisplayDeviceRepo.onDisplayDeviceEvent(device,
                         DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED);
             }
@@ -958,14 +941,31 @@
     }
 
     @VisibleForTesting
-    void handleDisplayDeviceAdded(DisplayDevice device) {
+    void handleLogicalDisplayAdded(LogicalDisplay display) {
         synchronized (mSyncRoot) {
-            handleDisplayDeviceAddedLocked(device);
+            handleLogicalDisplayAddedLocked(display);
         }
     }
 
-    private void handleDisplayDeviceAddedLocked(DisplayDevice device) {
-        LogicalDisplay display = addLogicalDisplayLocked(device);
+    private void handleLogicalDisplayAddedLocked(LogicalDisplay display) {
+        final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
+        final int displayId = display.getDisplayIdLocked();
+        final boolean isDefault = displayId == Display.DEFAULT_DISPLAY;
+        configureColorModeLocked(display, device);
+        if (isDefault) {
+            recordStableDisplayStatsIfNeededLocked(display);
+            recordTopInsetLocked(display);
+        }
+
+        DisplayManagerGlobal.invalidateLocalDisplayInfoCaches();
+
+        // Wake up waitForDefaultDisplay.
+        if (isDefault) {
+            mSyncRoot.notifyAll();
+        }
+
+        sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_ADDED);
+
         Runnable work = updateDisplayStateLocked(device);
         if (work != null) {
             work.run();
@@ -973,62 +973,10 @@
         scheduleTraversalLocked(false);
     }
 
-    @VisibleForTesting
-    void handleDisplayDeviceChanged(DisplayDevice device) {
-        synchronized (mSyncRoot) {
-            handleDisplayDeviceChangedLocked(device);
-        }
-    }
+    private void handleLogicalDisplayChangedLocked(@NonNull LogicalDisplay display) {
+        updateViewportPowerStateLocked(display);
 
-    private void handleDisplayDeviceChangedLocked(DisplayDevice device) {
-        DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
-
-        int diff = device.mDebugLastLoggedDeviceInfo.diff(info);
-        if (diff == DisplayDeviceInfo.DIFF_STATE) {
-            Slog.i(TAG, "Display device changed state: \"" + info.name
-                    + "\", " + Display.stateToString(info.state));
-            final Optional<Integer> viewportType = getViewportType(info);
-            if (viewportType.isPresent()) {
-                for (DisplayViewport d : mViewports) {
-                    if (d.type == viewportType.get() && info.uniqueId.equals(d.uniqueId)) {
-                        // Update display view port power state
-                        d.isActive = Display.isActiveState(info.state);
-                    }
-                }
-                if (mInputManagerInternal != null) {
-                    mHandler.sendEmptyMessage(MSG_UPDATE_VIEWPORT);
-                }
-            }
-        } else if (diff != 0) {
-            Slog.i(TAG, "Display device changed: " + info);
-        }
-        if ((diff & DisplayDeviceInfo.DIFF_COLOR_MODE) != 0) {
-            try {
-                mPersistentDataStore.setColorMode(device, info.colorMode);
-            } finally {
-                mPersistentDataStore.saveIfNeeded();
-            }
-        }
-        device.mDebugLastLoggedDeviceInfo = info;
-
-        device.applyPendingDisplayDeviceInfoChangesLocked();
-        if (updateLogicalDisplaysLocked()) {
-            scheduleTraversalLocked(false);
-        }
-    }
-
-    private void handleDisplayDeviceRemoved(DisplayDevice device) {
-        synchronized (mSyncRoot) {
-            handleDisplayDeviceRemovedLocked(device);
-        }
-    }
-
-    private void handleDisplayDeviceRemovedLocked(DisplayDevice device) {
-        updateLogicalDisplaysLocked();
-        scheduleTraversalLocked(false);
-    }
-
-    private void handleLogicalDisplayChanged(int displayId, @NonNull LogicalDisplay display) {
+        final int displayId = display.getDisplayIdLocked();
         if (displayId == Display.DEFAULT_DISPLAY) {
             recordTopInsetLocked(display);
         }
@@ -1036,11 +984,23 @@
         // display info will trigger a cache invalidation inside of LogicalDisplay before we hit
         // this point.
         sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
+        scheduleTraversalLocked(false);
     }
 
-    private void handleLogicalDisplayRemoved(int displayId) {
+    private void handleLogicalDisplayRemovedLocked(@NonNull LogicalDisplay display) {
+        final int displayId = display.getDisplayIdLocked();
         DisplayManagerGlobal.invalidateLocalDisplayInfoCaches();
         sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED);
+        scheduleTraversalLocked(false);
+    }
+
+    private void handleLogicalDisplaySwappedLocked(@NonNull LogicalDisplay display) {
+        final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
+        final Runnable work = updateDisplayStateLocked(device);
+        if (work != null) {
+            mHandler.post(work);
+        }
+        handleLogicalDisplayChangedLocked(display);
     }
 
     private void applyGlobalDisplayStateLocked(List<Runnable> workQueue) {
@@ -1057,71 +1017,19 @@
         // by the display power controller (if known).
         DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
         if ((info.flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0) {
-            // TODO - multi-display - The rules regarding what display state to apply to each
+            // TODO - b/170498827 The rules regarding what display state to apply to each
             // display will depend on the configuration/mapping of logical displays.
-            return device.requestDisplayStateLocked(
-                    mGlobalDisplayState, mGlobalDisplayBrightness);
+            // Clean up LogicalDisplay.isEnabled() mechanism once this is fixed.
+            int state = mGlobalDisplayState;
+            final LogicalDisplay display = mLogicalDisplayMapper.getLocked(device);
+            if (display != null && !display.isEnabled()) {
+                state = Display.STATE_OFF;
+            }
+            return device.requestDisplayStateLocked(state, mGlobalDisplayBrightness);
         }
         return null;
     }
 
-    // Adds a new logical display based on the given display device.
-    // Sends notifications if needed.
-    private LogicalDisplay addLogicalDisplayLocked(DisplayDevice device) {
-        DisplayDeviceInfo deviceInfo = device.getDisplayDeviceInfoLocked();
-        boolean isDefault = (deviceInfo.flags
-                & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0;
-        if (isDefault && mLogicalDisplays.get(Display.DEFAULT_DISPLAY) != null) {
-            Slog.w(TAG, "Ignoring attempt to add a second default display: " + deviceInfo);
-            isDefault = false;
-        }
-
-        if (!isDefault && mSingleDisplayDemoMode) {
-            Slog.i(TAG, "Not creating a logical display for a secondary display "
-                    + " because single display demo mode is enabled: " + deviceInfo);
-            return null;
-        }
-
-        final int displayId = assignDisplayIdLocked(isDefault);
-        final int layerStack = assignLayerStackLocked(displayId);
-
-        LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device);
-        display.updateLocked(mDisplayDeviceRepo);
-        if (!display.isValidLocked()) {
-            // This should never happen currently.
-            Slog.w(TAG, "Ignoring display device because the logical display "
-                    + "created from it was not considered valid: " + deviceInfo);
-            return null;
-        }
-
-        configureColorModeLocked(display, device);
-        if (isDefault) {
-            recordStableDisplayStatsIfNeededLocked(display);
-            recordTopInsetLocked(display);
-        }
-
-        mLogicalDisplays.put(displayId, display);
-        DisplayManagerGlobal.invalidateLocalDisplayInfoCaches();
-
-        // Wake up waitForDefaultDisplay.
-        if (isDefault) {
-            mSyncRoot.notifyAll();
-        }
-
-        sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_ADDED);
-        return display;
-    }
-
-    private int assignDisplayIdLocked(boolean isDefault) {
-        return isDefault ? Display.DEFAULT_DISPLAY : mNextNonDefaultDisplayId++;
-    }
-
-    private int assignLayerStackLocked(int displayId) {
-        // Currently layer stacks and display ids are the same.
-        // This need not be the case.
-        return displayId;
-    }
-
     private void configureColorModeLocked(LogicalDisplay display, DisplayDevice device) {
         if (display.getPrimaryDisplayDeviceLocked() == device) {
             int colorMode = mPersistentDataStore.getColorMode(device);
@@ -1238,39 +1146,6 @@
         }
     }
 
-    // Updates all existing logical displays given the current set of display devices.
-    // Removes invalid logical displays.
-    // Sends notifications if needed.
-    private boolean updateLogicalDisplaysLocked() {
-        boolean changed = false;
-        for (int i = mLogicalDisplays.size(); i-- > 0; ) {
-            final int displayId = mLogicalDisplays.keyAt(i);
-            LogicalDisplay display = mLogicalDisplays.valueAt(i);
-
-            mTempDisplayInfo.copyFrom(display.getDisplayInfoLocked());
-            display.getNonOverrideDisplayInfoLocked(mTempNonOverrideDisplayInfo);
-            display.updateLocked(mDisplayDeviceRepo);
-            if (!display.isValidLocked()) {
-                mLogicalDisplays.removeAt(i);
-                handleLogicalDisplayRemoved(displayId);
-                changed = true;
-            } else if (!mTempDisplayInfo.equals(display.getDisplayInfoLocked())) {
-                handleLogicalDisplayChanged(displayId, display);
-                changed = true;
-            } else {
-                // While applications shouldn't know nor care about the non-overridden info, we
-                // still need to let WindowManager know so it can update its own internal state for
-                // things like display cutouts.
-                display.getNonOverrideDisplayInfoLocked(mTempDisplayInfo);
-                if (!mTempNonOverrideDisplayInfo.equals(mTempDisplayInfo)) {
-                    handleLogicalDisplayChanged(displayId, display);
-                    changed = true;
-                }
-            }
-        }
-        return changed;
-    }
-
     private void performTraversalLocked(SurfaceControl.Transaction t) {
         // Clear all viewports before configuring displays so that we can keep
         // track of which ones we have configured.
@@ -1292,7 +1167,7 @@
             float requestedRefreshRate, int requestedModeId, boolean preferMinimalPostProcessing,
             boolean inTraversal) {
         synchronized (mSyncRoot) {
-            LogicalDisplay display = mLogicalDisplays.get(displayId);
+            LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId);
             if (display == null) {
                 return;
             }
@@ -1334,7 +1209,7 @@
 
     private void setDisplayOffsetsInternal(int displayId, int x, int y) {
         synchronized (mSyncRoot) {
-            LogicalDisplay display = mLogicalDisplays.get(displayId);
+            LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId);
             if (display == null) {
                 return;
             }
@@ -1352,7 +1227,7 @@
 
     private void setDisplayScalingDisabledInternal(int displayId, boolean disable) {
         synchronized (mSyncRoot) {
-            final LogicalDisplay display = mLogicalDisplays.get(displayId);
+            final LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId);
             if (display == null) {
                 return;
             }
@@ -1388,7 +1263,7 @@
     @Nullable
     private IBinder getDisplayToken(int displayId) {
         synchronized (mSyncRoot) {
-            final LogicalDisplay display = mLogicalDisplays.get(displayId);
+            final LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId);
             if (display != null) {
                 final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
                 if (device != null) {
@@ -1406,7 +1281,7 @@
             if (token == null) {
                 return null;
             }
-            final LogicalDisplay logicalDisplay = mLogicalDisplays.get(displayId);
+            final LogicalDisplay logicalDisplay = mLogicalDisplayMapper.getLocked(displayId);
             if (logicalDisplay == null) {
                 return null;
             }
@@ -1496,30 +1371,9 @@
         }
     }
 
-    private void onDesiredDisplayModeSpecsChangedInternal() {
-        boolean changed = false;
+    void setFoldOverride(Boolean isFolded) {
         synchronized (mSyncRoot) {
-            final int count = mLogicalDisplays.size();
-            for (int i = 0; i < count; i++) {
-                LogicalDisplay display = mLogicalDisplays.valueAt(i);
-                int displayId = mLogicalDisplays.keyAt(i);
-                DisplayModeDirector.DesiredDisplayModeSpecs desiredDisplayModeSpecs =
-                        mDisplayModeDirector.getDesiredDisplayModeSpecs(displayId);
-                DisplayModeDirector.DesiredDisplayModeSpecs existingDesiredDisplayModeSpecs =
-                        display.getDesiredDisplayModeSpecsLocked();
-                if (DEBUG) {
-                    Slog.i(TAG,
-                            "Comparing display specs: " + desiredDisplayModeSpecs
-                                    + ", existing: " + existingDesiredDisplayModeSpecs);
-                }
-                if (!desiredDisplayModeSpecs.equals(existingDesiredDisplayModeSpecs)) {
-                    display.setDesiredDisplayModeSpecsLocked(desiredDisplayModeSpecs);
-                    changed = true;
-                }
-            }
-            if (changed) {
-                scheduleTraversalLocked(false);
-            }
+            mLogicalDisplayMapper.setFoldOverrideLocked(isFolded);
         }
     }
 
@@ -1550,15 +1404,15 @@
 
         // Find the logical display that the display device is showing.
         // Certain displays only ever show their own content.
-        LogicalDisplay display = findLogicalDisplayForDeviceLocked(device);
+        LogicalDisplay display = mLogicalDisplayMapper.getLocked(device);
         if (!ownContent) {
             if (display != null && !display.hasContentLocked()) {
                 // If the display does not have any content of its own, then
                 // automatically mirror the requested logical display contents if possible.
-                display = mLogicalDisplays.get(device.getDisplayIdToMirrorLocked());
+                display = mLogicalDisplayMapper.getLocked(device.getDisplayIdToMirrorLocked());
             }
             if (display == null) {
-                display = mLogicalDisplays.get(Display.DEFAULT_DISPLAY);
+                display = mLogicalDisplayMapper.getLocked(Display.DEFAULT_DISPLAY);
             }
         }
 
@@ -1622,15 +1476,21 @@
         viewport.isActive = Display.isActiveState(info.state);
     }
 
-    private LogicalDisplay findLogicalDisplayForDeviceLocked(DisplayDevice device) {
-        final int count = mLogicalDisplays.size();
-        for (int i = 0; i < count; i++) {
-            LogicalDisplay display = mLogicalDisplays.valueAt(i);
-            if (display.getPrimaryDisplayDeviceLocked() == device) {
-                return display;
+    private void updateViewportPowerStateLocked(LogicalDisplay display) {
+        final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
+        final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
+        final Optional<Integer> viewportType = getViewportType(info);
+        if (viewportType.isPresent()) {
+            for (DisplayViewport d : mViewports) {
+                if (d.type == viewportType.get() && info.uniqueId.equals(d.uniqueId)) {
+                    // Update display view port power state
+                    d.isActive = Display.isActiveState(info.state);
+                }
+            }
+            if (mInputManagerInternal != null) {
+                mHandler.sendEmptyMessage(MSG_UPDATE_VIEWPORT);
             }
         }
-        return null;
     }
 
     private void sendDisplayEventLocked(int displayId, int event) {
@@ -1694,10 +1554,8 @@
             pw.println("  mSafeMode=" + mSafeMode);
             pw.println("  mPendingTraversal=" + mPendingTraversal);
             pw.println("  mGlobalDisplayState=" + Display.stateToString(mGlobalDisplayState));
-            pw.println("  mNextNonDefaultDisplayId=" + mNextNonDefaultDisplayId);
             pw.println("  mViewports=" + mViewports);
             pw.println("  mDefaultDisplayDefaultColorMode=" + mDefaultDisplayDefaultColorMode);
-            pw.println("  mSingleDisplayDemoMode=" + mSingleDisplayDemoMode);
             pw.println("  mWifiDisplayScanRequestCount=" + mWifiDisplayScanRequestCount);
             pw.println("  mStableDisplaySize=" + mStableDisplaySize);
             pw.println("  mMinimumBrightnessCurve=" + mMinimumBrightnessCurve);
@@ -1719,15 +1577,8 @@
                 device.dumpLocked(ipw);
             });
 
-            final int logicalDisplayCount = mLogicalDisplays.size();
             pw.println();
-            pw.println("Logical Displays: size=" + logicalDisplayCount);
-            for (int i = 0; i < logicalDisplayCount; i++) {
-                int displayId = mLogicalDisplays.keyAt(i);
-                LogicalDisplay display = mLogicalDisplays.valueAt(i);
-                pw.println("  Display " + displayId + ":");
-                display.dumpLocked(ipw);
-            }
+            mLogicalDisplayMapper.dumpLocked(pw);
 
             pw.println();
             mDisplayModeDirector.dump(pw);
@@ -1784,7 +1635,7 @@
     @VisibleForTesting
     DisplayDeviceInfo getDisplayDeviceInfoInternal(int displayId) {
         synchronized (mSyncRoot) {
-            LogicalDisplay display = mLogicalDisplays.get(displayId);
+            LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId);
             if (display != null) {
                 DisplayDevice displayDevice = display.getPrimaryDisplayDeviceLocked();
                 return displayDevice.getDisplayDeviceInfoLocked();
@@ -1796,7 +1647,7 @@
     @VisibleForTesting
     int getDisplayIdToMirrorInternal(int displayId) {
         synchronized (mSyncRoot) {
-            LogicalDisplay display = mLogicalDisplays.get(displayId);
+            LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId);
             if (display != null) {
                 DisplayDevice displayDevice = display.getPrimaryDisplayDeviceLocked();
                 return displayDevice.getDisplayIdToMirrorLocked();
@@ -1863,20 +1714,24 @@
         }
     }
 
-    private final class DisplayDeviceListener implements DisplayDeviceRepository.Listener {
+    private final class LogicalDisplayListener implements LogicalDisplayMapper.Listener {
         @Override
-        public void onDisplayDeviceEventLocked(DisplayDevice device, int event) {
+        public void onLogicalDisplayEventLocked(LogicalDisplay display, int event) {
             switch (event) {
-                case DisplayDeviceRepository.DISPLAY_DEVICE_EVENT_ADDED:
-                    handleDisplayDeviceAddedLocked(device);
+                case LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_ADDED:
+                    handleLogicalDisplayAddedLocked(display);
                     break;
 
-                case DisplayDeviceRepository.DISPLAY_DEVICE_EVENT_CHANGED:
-                    handleDisplayDeviceChangedLocked(device);
+                case LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_CHANGED:
+                    handleLogicalDisplayChangedLocked(display);
                     break;
 
-                case DisplayDeviceRepository.DISPLAY_DEVICE_EVENT_REMOVED:
-                    handleDisplayDeviceRemovedLocked(device);
+                case LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_REMOVED:
+                    handleLogicalDisplayRemovedLocked(display);
+                    break;
+
+                case LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_SWAPPED:
+                    handleLogicalDisplaySwappedLocked(display);
                     break;
             }
         }
@@ -1948,7 +1803,9 @@
             final int callingUid = Binder.getCallingUid();
             final long token = Binder.clearCallingIdentity();
             try {
-                return getDisplayIdsInternal(callingUid);
+                synchronized (mSyncRoot) {
+                    return mLogicalDisplayMapper.getDisplayIdsLocked(callingUid);
+                }
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -2409,7 +2266,8 @@
         @Override // Binder call
         public boolean isMinimalPostProcessingRequested(int displayId) {
             synchronized (mSyncRoot) {
-                return mLogicalDisplays.get(displayId).getRequestedMinimalPostProcessingLocked();
+                return mLogicalDisplayMapper.getLocked(displayId)
+                        .getRequestedMinimalPostProcessingLocked();
             }
         }
 
@@ -2559,7 +2417,7 @@
             synchronized (mSyncRoot) {
                 DisplayBlanker blanker = new DisplayBlanker() {
                     @Override
-                    public void requestDisplayState(int state, float brightness) {
+                    public void requestDisplayState(int displayId, int state, float brightness) {
                         // The order of operations is important for legacy reasons.
                         if (state == Display.STATE_OFF) {
                             requestGlobalDisplayStateInternal(state, brightness);
@@ -2572,10 +2430,9 @@
                         }
                     }
                 };
-                LogicalDisplay defaultDisplay = mLogicalDisplays.get(Display.DEFAULT_DISPLAY);
-                DisplayDevice defaultDevice = defaultDisplay.getPrimaryDisplayDeviceLocked();
                 mDisplayPowerController = new DisplayPowerController(
-                        mContext, callbacks, handler, sensorManager, blanker, defaultDevice);
+                        mContext, callbacks, handler, sensorManager, blanker,
+                        Display.DEFAULT_DISPLAY);
                 mSensorManager = sensorManager;
             }
 
@@ -2615,7 +2472,7 @@
         @Override
         public Point getDisplayPosition(int displayId) {
             synchronized (mSyncRoot) {
-                LogicalDisplay display = mLogicalDisplays.get(displayId);
+                LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId);
                 if (display != null) {
                     return display.getDisplayPosition();
                 }
@@ -2720,8 +2577,49 @@
 
     class DesiredDisplayModeSpecsObserver
             implements DisplayModeDirector.DesiredDisplayModeSpecsListener {
+
+        private final Consumer<LogicalDisplay> mSpecsChangedConsumer = display -> {
+            int displayId = display.getDisplayIdLocked();
+            DisplayModeDirector.DesiredDisplayModeSpecs desiredDisplayModeSpecs =
+                    mDisplayModeDirector.getDesiredDisplayModeSpecs(displayId);
+            DisplayModeDirector.DesiredDisplayModeSpecs existingDesiredDisplayModeSpecs =
+                    display.getDesiredDisplayModeSpecsLocked();
+            if (DEBUG) {
+                Slog.i(TAG,
+                        "Comparing display specs: " + desiredDisplayModeSpecs
+                                + ", existing: " + existingDesiredDisplayModeSpecs);
+            }
+            if (!desiredDisplayModeSpecs.equals(existingDesiredDisplayModeSpecs)) {
+                display.setDesiredDisplayModeSpecsLocked(desiredDisplayModeSpecs);
+                mChanged = true;
+            }
+        };
+
+        @GuardedBy("mSyncRoot")
+        private boolean mChanged = false;
+
         public void onDesiredDisplayModeSpecsChanged() {
-            onDesiredDisplayModeSpecsChangedInternal();
+            synchronized (mSyncRoot) {
+                mChanged = false;
+                mLogicalDisplayMapper.forEachLocked(mSpecsChangedConsumer);
+                if (mChanged) {
+                    scheduleTraversalLocked(false);
+                    mChanged = false;
+                }
+            }
         }
     }
+
+    class DisplayFoldListener extends IDisplayFoldListener.Stub {
+        @Override
+        public void onDisplayFoldChanged(int displayId, boolean folded) {
+            // TODO: multi-display - IDisplayFoldListener callback only really works for the
+            // Display.DEFAULT_DISPLAY.
+            if (displayId == Display.DEFAULT_DISPLAY) {
+                synchronized (mSyncRoot) {
+                    mLogicalDisplayMapper.setDeviceFoldedLocked(folded);
+                }
+            }
+        }
+    };
 }
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index 0c6c797..111664a 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -56,6 +56,8 @@
                 return setDisplayWhiteBalanceLoggingEnabled(false);
             case "dwb-set-cct":
                 return setAmbientColorTemperatureOverride();
+            case "set-fold":
+                return setFoldOverride();
             default:
                 return handleDefaultCommands(cmd);
         }
@@ -82,6 +84,8 @@
         pw.println("    Disable display white-balance logging.");
         pw.println("  dwb-set-cct CCT");
         pw.println("    Sets the ambient color temperature override to CCT (use -1 to disable).");
+        pw.println("  set-fold [fold|unfold|reset]");
+        pw.println("    Simulates the 'fold' state of a device. 'reset' for default behavior.");
         pw.println();
         Intent.printIntentArgsHelp(pw , "");
     }
@@ -148,4 +152,26 @@
         mService.setAmbientColorTemperatureOverride(cct);
         return 0;
     }
+
+    private int setFoldOverride() {
+        String state = getNextArg();
+        if (state == null) {
+            getErrPrintWriter().println("Error: no parameter specified for set-fold");
+            return 1;
+        }
+        final Boolean isFolded;
+        if ("fold".equals(state)) {
+            isFolded = true;
+        } else if ("unfold".equals(state)) {
+            isFolded = false;
+        } else if ("reset".equals(state)) {
+            isFolded = null;
+        } else {
+            getErrPrintWriter().println("Error: Invalid fold state request: " + state);
+            return 1;
+        }
+
+        mService.setFoldOverride(isFolded);
+        return 0;
+    }
 }
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 4f5a0fa..71b377c 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -123,7 +123,6 @@
     public void start(SensorManager sensorManager) {
         mSettingsObserver.observe();
         mDisplayObserver.observe();
-        mSettingsObserver.observe();
         mBrightnessObserver.observe(sensorManager);
         synchronized (mLock) {
             // We may have a listener already registered before the call to start, so go ahead and
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 58ef9d1..0211876 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -163,8 +163,8 @@
     // The display blanker.
     private final DisplayBlanker mBlanker;
 
-    // The display device.
-    private final DisplayDevice mDisplayDevice;
+    // The ID of the LogicalDisplay tied to this DisplayPowerController.
+    private final int mDisplayId;
 
     // Tracker for brightness changes.
     private final BrightnessTracker mBrightnessTracker;
@@ -406,7 +406,7 @@
      */
     public DisplayPowerController(Context context,
             DisplayPowerCallbacks callbacks, Handler handler,
-            SensorManager sensorManager, DisplayBlanker blanker, DisplayDevice displayDevice) {
+            SensorManager sensorManager, DisplayBlanker blanker, int displayId) {
         mHandler = new DisplayControllerHandler(handler.getLooper());
         mBrightnessTracker = new BrightnessTracker(context, null);
         mSettingsObserver = new SettingsObserver(mHandler);
@@ -417,10 +417,9 @@
         mBlanker = blanker;
         mContext = context;
         mBrightnessSynchronizer = new BrightnessSynchronizer(context);
-        mDisplayDevice = displayDevice;
+        mDisplayId = displayId;
 
         PowerManager pm =  context.getSystemService(PowerManager.class);
-        DisplayDeviceConfig displayDeviceConfig = mDisplayDevice.getDisplayDeviceConfig();
 
         final Resources resources = context.getResources();
 
@@ -515,7 +514,7 @@
                         mScreenBrightnessRangeMaximum, dozeScaleFactor, lightSensorRate,
                         initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce,
                         autoBrightnessResetAmbientLuxAfterWarmUp, ambientBrightnessThresholds,
-                        screenBrightnessThresholds, context, displayDeviceConfig);
+                        screenBrightnessThresholds, context);
             } else {
                 mUseSoftwareAutoBrightnessConfig = false;
             }
@@ -684,7 +683,7 @@
         // Initialize the power state object for the default display.
         // In the future, we might manage multiple displays independently.
         mPowerState = new DisplayPowerState(mBlanker,
-                mColorFadeEnabled ? new ColorFade(Display.DEFAULT_DISPLAY) : null);
+                mColorFadeEnabled ? new ColorFade(mDisplayId) : null, mDisplayId);
 
         if (mColorFadeEnabled) {
             mColorFadeOnAnimator = ObjectAnimator.ofFloat(
@@ -1153,7 +1152,7 @@
         if (ready && state != Display.STATE_OFF
                 && mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_TURNING_ON) {
             setReportedScreenState(REPORTED_TO_POLICY_SCREEN_ON);
-            mWindowManagerPolicy.screenTurnedOn();
+            mWindowManagerPolicy.screenTurnedOn(mDisplayId);
         }
 
         // Grab a wake lock if we have unfinished business.
@@ -1277,7 +1276,7 @@
                 if (mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_ON) {
                     setReportedScreenState(REPORTED_TO_POLICY_SCREEN_TURNING_OFF);
                     blockScreenOff();
-                    mWindowManagerPolicy.screenTurningOff(mPendingScreenOffUnblocker);
+                    mWindowManagerPolicy.screenTurningOff(mDisplayId, mPendingScreenOffUnblocker);
                     unblockScreenOff();
                 } else if (mPendingScreenOffUnblocker != null) {
                     // Abort doing the state change until screen off is unblocked.
@@ -1309,14 +1308,14 @@
                 && !mScreenOffBecauseOfProximity) {
             setReportedScreenState(REPORTED_TO_POLICY_SCREEN_OFF);
             unblockScreenOn();
-            mWindowManagerPolicy.screenTurnedOff();
+            mWindowManagerPolicy.screenTurnedOff(mDisplayId);
         } else if (!isOff
                 && mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_TURNING_OFF) {
 
             // We told policy already that screen was turning off, but now we changed our minds.
             // Complete the full state transition on -> turningOff -> off.
             unblockScreenOff();
-            mWindowManagerPolicy.screenTurnedOff();
+            mWindowManagerPolicy.screenTurnedOff(mDisplayId);
             setReportedScreenState(REPORTED_TO_POLICY_SCREEN_OFF);
         }
         if (!isOff && mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_OFF) {
@@ -1326,7 +1325,7 @@
             } else {
                 unblockScreenOn();
             }
-            mWindowManagerPolicy.screenTurningOn(mPendingScreenOnUnblocker);
+            mWindowManagerPolicy.screenTurningOn(mDisplayId, mPendingScreenOnUnblocker);
         }
 
         // Return true if the screen isn't blocked.
diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java
index 4b6430d..54f30a9 100644
--- a/services/core/java/com/android/server/display/DisplayPowerState.java
+++ b/services/core/java/com/android/server/display/DisplayPowerState.java
@@ -58,6 +58,7 @@
     private final DisplayBlanker mBlanker;
     private final ColorFade mColorFade;
     private final PhotonicModulator mPhotonicModulator;
+    private final int mDisplayId;
 
     private int mScreenState;
     private float mScreenBrightness;
@@ -71,13 +72,14 @@
 
     private Runnable mCleanListener;
 
-    public DisplayPowerState(DisplayBlanker blanker, ColorFade colorFade) {
+    public DisplayPowerState(DisplayBlanker blanker, ColorFade colorFade, int displayId) {
         mHandler = new Handler(true /*async*/);
         mChoreographer = Choreographer.getInstance();
         mBlanker = blanker;
         mColorFade = colorFade;
         mPhotonicModulator = new PhotonicModulator();
         mPhotonicModulator.start();
+        mDisplayId = displayId;
 
         // At boot time, we know that the screen is on and the electron beam
         // animation is not playing.  We don't know the screen's brightness though,
@@ -434,10 +436,10 @@
 
                 // Apply pending change.
                 if (DEBUG) {
-                    Slog.d(TAG, "Updating screen state: state="
+                    Slog.d(TAG, "Updating screen state: id=" + mDisplayId +  ", state="
                             + Display.stateToString(state) + ", backlight=" + brightnessState);
                 }
-                mBlanker.requestDisplayState(state, brightnessState);
+                mBlanker.requestDisplayState(mDisplayId, state, brightnessState);
             }
         }
     }
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 507a265..6597aa5 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -69,21 +69,27 @@
 
     private final LongSparseArray<LocalDisplayDevice> mDevices = new LongSparseArray<>();
 
-    @SuppressWarnings("unused")  // Becomes active at instantiation time.
-    private PhysicalDisplayEventReceiver mPhysicalDisplayEventReceiver;
+    private final Injector mInjector;
 
 
     // Called with SyncRoot lock held.
     public LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
             Context context, Handler handler, Listener listener) {
+        this(syncRoot, context, handler, listener, new Injector());
+    }
+
+    LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
+            Context context, Handler handler, Listener listener, Injector injector) {
         super(syncRoot, context, handler, listener, TAG);
+        mInjector = injector;
     }
 
     @Override
     public void registerLocked() {
         super.registerLocked();
 
-        mPhysicalDisplayEventReceiver = new PhysicalDisplayEventReceiver(getHandler().getLooper());
+        mInjector.setDisplayEventListenerLocked(getHandler().getLooper(),
+                new LocalDisplayEventListener());
 
         for (long physicalDisplayId : SurfaceControl.getPhysicalDisplayIds()) {
             tryConnectDisplayLocked(physicalDisplayId);
@@ -183,13 +189,11 @@
         private int mDefaultModeId;
         private int mDefaultConfigGroup;
         private int mActiveModeId;
-        private boolean mActiveModeInvalid;
         private DisplayModeDirector.DesiredDisplayModeSpecs mDisplayModeSpecs =
                 new DisplayModeDirector.DesiredDisplayModeSpecs();
         private boolean mDisplayModeSpecsInvalid;
         private int mActiveConfigId;
         private int mActiveColorMode;
-        private boolean mActiveColorModeInvalid;
         private Display.HdrCapabilities mHdrCapabilities;
         private boolean mAllmSupported;
         private boolean mGameContentTypeSupported;
@@ -300,7 +304,6 @@
             // Schedule traversals to ensure that the correct state is reapplied if necessary.
             if (mActiveModeId != NO_DISPLAY_MODE_ID
                     && mActiveModeId != activeRecord.mMode.getModeId()) {
-                mActiveModeInvalid = true;
                 sendTraversalRequestLocked();
             }
 
@@ -373,7 +376,6 @@
                             + " mode.");
                 }
                 mActiveModeId = mDefaultModeId;
-                mActiveModeInvalid = true;
             }
 
             // Schedule traversals so that we apply pending changes.
@@ -464,14 +466,12 @@
                     Slog.w(TAG, "Active color mode no longer available, reverting"
                             + " to default mode.");
                     mActiveColorMode = Display.COLOR_MODE_DEFAULT;
-                    mActiveColorModeInvalid = true;
                 } else {
                     if (!mSupportedColorModes.isEmpty()) {
                         // This should never happen.
                         Slog.e(TAG, "Default and active color mode is no longer available!"
                                 + " Reverting to first available mode.");
                         mActiveColorMode = mSupportedColorModes.get(0);
-                        mActiveColorModeInvalid = true;
                     } else {
                         // This should really never happen.
                         Slog.e(TAG, "No color modes available!");
@@ -848,8 +848,7 @@
             }
             mActiveConfigId = activeConfigId;
             mActiveModeId = findMatchingModeIdLocked(activeConfigId);
-            mActiveModeInvalid = mActiveModeId == NO_DISPLAY_MODE_ID;
-            if (mActiveModeInvalid) {
+            if (mActiveModeId == NO_DISPLAY_MODE_ID) {
                 Slog.w(TAG, "In unknown mode after setting allowed configs"
                         + ", activeConfigId=" + mActiveConfigId);
             }
@@ -867,7 +866,6 @@
             }
 
             mActiveColorMode = colorMode;
-            mActiveColorModeInvalid = false;
             getHandler().sendMessage(PooledLambda.obtainMessage(
                     LocalDisplayDevice::requestColorModeAsync, this,
                     getDisplayTokenLocked(), colorMode));
@@ -969,6 +967,10 @@
         }
 
         private int findMatchingModeIdLocked(int configId) {
+            if (configId < 0 || configId >= mDisplayConfigs.length) {
+                Slog.e(TAG, "Invalid display config index " + configId);
+                return NO_DISPLAY_MODE_ID;
+            }
             SurfaceControl.DisplayConfig config = mDisplayConfigs[configId];
             for (int i = 0; i < mSupportedModes.size(); i++) {
                 DisplayModeRecord record = mSupportedModes.valueAt(i);
@@ -1048,12 +1050,33 @@
         }
     }
 
-    private final class PhysicalDisplayEventReceiver extends DisplayEventReceiver {
-        PhysicalDisplayEventReceiver(Looper looper) {
-            super(looper, VSYNC_SOURCE_APP, CONFIG_CHANGED_EVENT_DISPATCH);
+    public static class Injector {
+        private ProxyDisplayEventReceiver mReceiver;
+        public void setDisplayEventListenerLocked(Looper looper, DisplayEventListener listener) {
+            mReceiver = new ProxyDisplayEventReceiver(looper, listener);
         }
+    }
 
-        @Override
+    public interface DisplayEventListener {
+        void onHotplug(long timestampNanos, long physicalDisplayId, boolean connected);
+        void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId);
+    }
+
+    public static final class ProxyDisplayEventReceiver extends DisplayEventReceiver {
+        private final DisplayEventListener mListener;
+        ProxyDisplayEventReceiver(Looper looper, DisplayEventListener listener) {
+            super(looper, VSYNC_SOURCE_APP, CONFIG_CHANGED_EVENT_DISPATCH);
+            mListener = listener;
+        }
+        public void onHotplug(long timestampNanos, long physicalDisplayId, boolean connected) {
+            mListener.onHotplug(timestampNanos, physicalDisplayId, connected);
+        }
+        public void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId) {
+            mListener.onConfigChanged(timestampNanos, physicalDisplayId, configId);
+        }
+    }
+
+    private final class LocalDisplayEventListener implements DisplayEventListener {
         public void onHotplug(long timestampNanos, long physicalDisplayId, boolean connected) {
             synchronized (getSyncRoot()) {
                 if (connected) {
@@ -1063,8 +1086,6 @@
                 }
             }
         }
-
-        @Override
         public void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId) {
             if (DEBUG) {
                 Slog.d(TAG, "onConfigChanged("
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index bf8b891..a17a294 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -16,9 +16,11 @@
 
 package com.android.server.display;
 
+import android.annotation.NonNull;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.hardware.display.DisplayManagerInternal;
+import android.util.Slog;
 import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.Surface;
@@ -57,6 +59,7 @@
  * </p>
  */
 final class LogicalDisplay {
+    private static final String TAG = "LogicalDisplay";
     private final DisplayInfo mBaseDisplayInfo = new DisplayInfo();
 
     // The layer stack we use when the display has been blanked to prevent any
@@ -114,6 +117,12 @@
     private final Rect mTempLayerStackRect = new Rect();
     private final Rect mTempDisplayRect = new Rect();
 
+    /**
+     * Indicates that the Logical display is enabled (default). See {@link #setEnabled} for
+     * more information.
+     */
+    private boolean mIsEnabled = true;
+
     public LogicalDisplay(int displayId, int layerStack, DisplayDevice primaryDisplayDevice) {
         mDisplayId = displayId;
         mLayerStack = layerStack;
@@ -575,6 +584,44 @@
         mDisplayScalingDisabled = disableScaling;
     }
 
+    /**
+     * Swap the underlying {@link DisplayDevice} with the specificed LogicalDisplay.
+     *
+     * @param targetDisplay The display with which to swap display-devices.
+     * @return {@code true} if the displays were swapped, {@code false} otherwise.
+     */
+    public boolean swapDisplaysLocked(@NonNull LogicalDisplay targetDisplay) {
+        final DisplayDevice targetDevice = targetDisplay.getPrimaryDisplayDeviceLocked();
+        if (mPrimaryDisplayDevice == null || targetDevice == null) {
+            Slog.e(TAG, "Missing display device during swap: " + mPrimaryDisplayDevice + " , "
+                    + targetDevice);
+            return false;
+        }
+
+        final DisplayDevice tmpDevice = mPrimaryDisplayDevice;
+        mPrimaryDisplayDevice = targetDisplay.mPrimaryDisplayDevice;
+        targetDisplay.mPrimaryDisplayDevice = tmpDevice;
+        return true;
+    }
+
+    /**
+     * Sets the LogicalDisplay to be enabled or disabled. If the display is not enabled,
+     * the system will always set the display to power off, regardless of the global state of the
+     * device.
+     * TODO: b/170498827 - Remove when updateDisplayStateLocked is updated.
+     */
+    public void setEnabled(boolean isEnabled) {
+        mIsEnabled = isEnabled;
+    }
+
+    /**
+     * @return {@code true} iff the LogicalDisplay is enabled or {@code false}
+     * if disabled indicating that the display has been forced to be OFF.
+     */
+    public boolean isEnabled() {
+        return mIsEnabled;
+    }
+
     public void dumpLocked(PrintWriter pw) {
         pw.println("mDisplayId=" + mDisplayId);
         pw.println("mLayerStack=" + mLayerStack);
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
new file mode 100644
index 0000000..fc3ba35
--- /dev/null
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -0,0 +1,368 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import android.content.Context;
+import android.os.SystemProperties;
+import android.text.TextUtils;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.Display;
+import android.view.DisplayInfo;
+
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.function.Consumer;
+
+/**
+ * Responsible for creating {@link LogicalDisplay}s and associating them to the
+ * {@link DisplayDevice} objects supplied through {@link DisplayAdapter.Listener}.
+ *
+ * For devices with a single internal display, the mapping is done once and left
+ * alone. For devices with multiple built-in displays, such as foldable devices,
+ * {@link LogicalDisplay}s can be remapped to different {@link DisplayDevice}s.
+ */
+class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
+    private static final String TAG = "LogicalDisplayMapper";
+
+    private static final boolean DEBUG = false;
+
+    public static final int LOGICAL_DISPLAY_EVENT_ADDED = 1;
+    public static final int LOGICAL_DISPLAY_EVENT_CHANGED = 2;
+    public static final int LOGICAL_DISPLAY_EVENT_REMOVED = 3;
+    public static final int LOGICAL_DISPLAY_EVENT_SWAPPED = 4;
+
+    /**
+     * Temporary display info, used for comparing display configurations.
+     */
+    private final DisplayInfo mTempDisplayInfo = new DisplayInfo();
+    private final DisplayInfo mTempNonOverrideDisplayInfo = new DisplayInfo();
+
+    /**
+     * True if the display mapper service should pretend there is only one display
+     * and only tell applications about the existence of the default logical display.
+     * The display manager can still mirror content to secondary displays but applications
+     * cannot present unique content on those displays.
+     * Used for demonstration purposes only.
+     */
+    private final boolean mSingleDisplayDemoMode;
+
+    /**
+     * Physical Display ID of the DisplayDevice to associate with the default LogicalDisplay
+     * when {@link mIsFolded} is set to {@code true}.
+     */
+    private String mDisplayIdToUseWhenFolded;
+
+    /**
+     * Physical Display ID of the DisplayDevice to associate with the default LogicalDisplay
+     * when {@link mIsFolded} is set to {@code false}.
+     */
+    private String mDisplayIdToUseWhenUnfolded;
+
+    /** Overrides the folded state of the device. For use with ADB commands. */
+    private Boolean mIsFoldedOverride;
+
+    /** Saves the last device fold state. */
+    private boolean mIsFolded;
+
+    /**
+     * List of all logical displays indexed by logical display id.
+     * Any modification to mLogicalDisplays must invalidate the DisplayManagerGlobal cache.
+     * TODO: multi-display - Move the aforementioned comment?
+     */
+    private final SparseArray<LogicalDisplay> mLogicalDisplays =
+            new SparseArray<LogicalDisplay>();
+    private int mNextNonDefaultDisplayId = Display.DEFAULT_DISPLAY + 1;
+
+    private final DisplayDeviceRepository mDisplayDeviceRepo;
+    private final PersistentDataStore mPersistentDataStore;
+    private final Listener mListener;
+
+    LogicalDisplayMapper(Context context, DisplayDeviceRepository repo, Listener listener,
+            PersistentDataStore persistentDataStore) {
+        mDisplayDeviceRepo = repo;
+        mPersistentDataStore = persistentDataStore;
+        mListener = listener;
+        mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false);
+        mDisplayDeviceRepo.addListener(this);
+
+        loadFoldedDisplayConfig(context);
+    }
+
+    @Override
+    public void onDisplayDeviceEventLocked(DisplayDevice device, int event) {
+        switch (event) {
+            case DisplayDeviceRepository.DISPLAY_DEVICE_EVENT_ADDED:
+                handleDisplayDeviceAddedLocked(device);
+                break;
+
+            case DisplayDeviceRepository.DISPLAY_DEVICE_EVENT_CHANGED:
+                updateLogicalDisplaysLocked();
+                break;
+
+            case DisplayDeviceRepository.DISPLAY_DEVICE_EVENT_REMOVED:
+                updateLogicalDisplaysLocked();
+                break;
+        }
+    }
+
+    @Override
+    public void onTraversalRequested() {
+        mListener.onTraversalRequested();
+    }
+
+    public LogicalDisplay getLocked(int displayId) {
+        return mLogicalDisplays.get(displayId);
+    }
+
+    public LogicalDisplay getLocked(DisplayDevice device) {
+        final int count = mLogicalDisplays.size();
+        for (int i = 0; i < count; i++) {
+            LogicalDisplay display = mLogicalDisplays.valueAt(i);
+            if (display.getPrimaryDisplayDeviceLocked() == device) {
+                return display;
+            }
+        }
+        return null;
+    }
+
+    public int[] getDisplayIdsLocked(int callingUid) {
+        final int count = mLogicalDisplays.size();
+        int[] displayIds = new int[count];
+        int n = 0;
+        for (int i = 0; i < count; i++) {
+            LogicalDisplay display = mLogicalDisplays.valueAt(i);
+            DisplayInfo info = display.getDisplayInfoLocked();
+            if (info.hasAccess(callingUid)) {
+                displayIds[n++] = mLogicalDisplays.keyAt(i);
+            }
+        }
+        if (n != count) {
+            displayIds = Arrays.copyOfRange(displayIds, 0, n);
+        }
+        return displayIds;
+    }
+
+    public void forEachLocked(Consumer<LogicalDisplay> consumer) {
+        final int count = mLogicalDisplays.size();
+        for (int i = 0; i < count; i++) {
+            consumer.accept(mLogicalDisplays.valueAt(i));
+        }
+    }
+
+    public void dumpLocked(PrintWriter pw) {
+        pw.println("LogicalDisplayMapper:");
+        IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
+        ipw.increaseIndent();
+
+        ipw.println("mSingleDisplayDemoMode=" + mSingleDisplayDemoMode);
+        ipw.println("mNextNonDefaultDisplayId=" + mNextNonDefaultDisplayId);
+
+        final int logicalDisplayCount = mLogicalDisplays.size();
+        ipw.println();
+        ipw.println("Logical Displays: size=" + logicalDisplayCount);
+
+
+        for (int i = 0; i < logicalDisplayCount; i++) {
+            int displayId = mLogicalDisplays.keyAt(i);
+            LogicalDisplay display = mLogicalDisplays.valueAt(i);
+            ipw.println("Display " + displayId + ":");
+            ipw.increaseIndent();
+            display.dumpLocked(ipw);
+            ipw.decreaseIndent();
+            ipw.println();
+        }
+    }
+
+    void setDeviceFoldedLocked(boolean isFolded) {
+        mIsFolded = isFolded;
+        if (mIsFoldedOverride != null) {
+            isFolded = mIsFoldedOverride.booleanValue();
+        }
+
+        if (mDisplayIdToUseWhenFolded == null || mDisplayIdToUseWhenUnfolded == null
+                || mLogicalDisplays.size() < 2) {
+            // Do nothing if this behavior is disabled or there are less than two displays.
+            return;
+        }
+
+        final DisplayDevice deviceFolded =
+                mDisplayDeviceRepo.getByIdLocked(mDisplayIdToUseWhenFolded);
+        final DisplayDevice deviceUnfolded =
+                mDisplayDeviceRepo.getByIdLocked(mDisplayIdToUseWhenUnfolded);
+        if (deviceFolded == null || deviceUnfolded == null) {
+            // If the expected devices for folding functionality are not present, return early.
+            return;
+        }
+
+        // Find the associated LogicalDisplays for the configured "folding" DeviceDisplays.
+        final LogicalDisplay displayFolded = getLocked(deviceFolded);
+        final LogicalDisplay displayUnfolded = getLocked(deviceUnfolded);
+        if (displayFolded == null || displayFolded == null) {
+            // If the expected displays are not present, return early.
+            return;
+        }
+
+        // Find out which display is currently default and which is disabled.
+        final LogicalDisplay defaultDisplay = mLogicalDisplays.get(Display.DEFAULT_DISPLAY);
+        final LogicalDisplay disabledDisplay;
+        if (defaultDisplay == displayFolded) {
+            disabledDisplay = displayUnfolded;
+        } else if (defaultDisplay == displayUnfolded) {
+            disabledDisplay = displayFolded;
+        } else {
+            // If neither folded or unfolded displays are currently set to the default display, we
+            // are in an unknown state and it's best to log the error and bail.
+            Slog.e(TAG, "Unexpected: when attempting to swap displays, neither of the two"
+                    + " configured displays were set up as the default display. Default: "
+                    + defaultDisplay.getDisplayInfoLocked() + ",  ConfiguredDisplays: [ folded="
+                    + displayFolded.getDisplayInfoLocked() + ", unfolded="
+                    + displayUnfolded.getDisplayInfoLocked() + " ]");
+            return;
+        }
+
+        if (isFolded == (defaultDisplay == displayFolded)) {
+            // Nothing to do, already in the right state.
+            return;
+        }
+
+        // Everything was checked and we need to swap, lets swap.
+        displayFolded.swapDisplaysLocked(displayUnfolded);
+
+        // We ensure that the non-default Display is always forced to be off. This was likely
+        // already done in a previous iteration, but we do it with each swap in case something in
+        // the underlying LogicalDisplays changed: like LogicalDisplay recreation, for example.
+        defaultDisplay.setEnabled(true);
+        disabledDisplay.setEnabled(false);
+
+        // Update the world
+        updateLogicalDisplaysLocked();
+
+        if (DEBUG) {
+            Slog.d(TAG, "Folded displays: isFolded: " + isFolded + ", defaultDisplay? "
+                    + defaultDisplay.getDisplayInfoLocked());
+        }
+    }
+
+    void setFoldOverrideLocked(Boolean isFolded) {
+        if (!Objects.equals(isFolded, mIsFoldedOverride)) {
+            mIsFoldedOverride = isFolded;
+            setDeviceFoldedLocked(mIsFolded);
+        }
+    }
+
+    private void handleDisplayDeviceAddedLocked(DisplayDevice device) {
+        DisplayDeviceInfo deviceInfo = device.getDisplayDeviceInfoLocked();
+        boolean isDefault = (deviceInfo.flags & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0;
+        if (isDefault && mLogicalDisplays.get(Display.DEFAULT_DISPLAY) != null) {
+            Slog.w(TAG, "Ignoring attempt to add a second default display: " + deviceInfo);
+            isDefault = false;
+        }
+
+        if (!isDefault && mSingleDisplayDemoMode) {
+            Slog.i(TAG, "Not creating a logical display for a secondary display "
+                    + " because single display demo mode is enabled: " + deviceInfo);
+            return;
+        }
+
+        final int displayId = assignDisplayIdLocked(isDefault);
+        final int layerStack = assignLayerStackLocked(displayId);
+
+        LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device);
+        display.updateLocked(mDisplayDeviceRepo);
+        if (!display.isValidLocked()) {
+            // This should never happen currently.
+            Slog.w(TAG, "Ignoring display device because the logical display "
+                    + "created from it was not considered valid: " + deviceInfo);
+            return;
+        }
+
+        mLogicalDisplays.put(displayId, display);
+
+        mListener.onLogicalDisplayEventLocked(display,
+                LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_ADDED);
+    }
+
+    /**
+     * Updates all existing logical displays given the current set of display devices.
+     * Removes invalid logical displays. Sends notifications if needed.
+     */
+    private void updateLogicalDisplaysLocked() {
+        for (int i = mLogicalDisplays.size() - 1; i >= 0; i--) {
+            final int displayId = mLogicalDisplays.keyAt(i);
+            LogicalDisplay display = mLogicalDisplays.valueAt(i);
+
+            mTempDisplayInfo.copyFrom(display.getDisplayInfoLocked());
+            display.getNonOverrideDisplayInfoLocked(mTempNonOverrideDisplayInfo);
+            display.updateLocked(mDisplayDeviceRepo);
+            if (!display.isValidLocked()) {
+                mLogicalDisplays.removeAt(i);
+
+                mListener.onLogicalDisplayEventLocked(display,
+                        LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_REMOVED);
+            } else if (!mTempDisplayInfo.equals(display.getDisplayInfoLocked())) {
+                final String oldUniqueId = mTempDisplayInfo.uniqueId;
+                final String newUniqueId = display.getDisplayInfoLocked().uniqueId;
+                final int eventMsg = TextUtils.equals(oldUniqueId, newUniqueId)
+                        ? LOGICAL_DISPLAY_EVENT_CHANGED : LOGICAL_DISPLAY_EVENT_SWAPPED;
+                mListener.onLogicalDisplayEventLocked(display, eventMsg);
+            } else {
+                // While applications shouldn't know nor care about the non-overridden info, we
+                // still need to let WindowManager know so it can update its own internal state for
+                // things like display cutouts.
+                display.getNonOverrideDisplayInfoLocked(mTempDisplayInfo);
+                if (!mTempNonOverrideDisplayInfo.equals(mTempDisplayInfo)) {
+                    mListener.onLogicalDisplayEventLocked(display,
+                            LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_CHANGED);
+                }
+            }
+        }
+    }
+
+    private int assignDisplayIdLocked(boolean isDefault) {
+        return isDefault ? Display.DEFAULT_DISPLAY : mNextNonDefaultDisplayId++;
+    }
+
+    private int assignLayerStackLocked(int displayId) {
+        // Currently layer stacks and display ids are the same.
+        // This need not be the case.
+        return displayId;
+    }
+
+    private void loadFoldedDisplayConfig(Context context) {
+        final String[] displayIds = context.getResources().getStringArray(
+                com.android.internal.R.array.config_internalFoldedPhysicalDisplayIds);
+
+        if (displayIds.length != 2 || TextUtils.isEmpty(displayIds[0])
+                || TextUtils.isEmpty(displayIds[1])) {
+            Slog.w(TAG, "Folded display configuration invalid: [" + Arrays.toString(displayIds)
+                    + "]");
+            return;
+        }
+
+        mDisplayIdToUseWhenFolded = displayIds[0];
+        mDisplayIdToUseWhenUnfolded = displayIds[1];
+    }
+
+    public interface Listener {
+        void onLogicalDisplayEventLocked(LogicalDisplay display, int event);
+        void onTraversalRequested();
+    }
+}
diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java
index 9aec43b..09b0b3a 100644
--- a/services/core/java/com/android/server/display/PersistentDataStore.java
+++ b/services/core/java/com/android/server/display/PersistentDataStore.java
@@ -216,7 +216,7 @@
     }
 
     public boolean forgetWifiDisplay(String deviceAddress) {
-		loadIfNeeded();
+        loadIfNeeded();
         int index = findRememberedWifiDisplay(deviceAddress);
         if (index >= 0) {
             mRememberedWifiDisplays.remove(index);
@@ -259,17 +259,17 @@
         return false;
     }
 
-	public Point getStableDisplaySize() {
-		loadIfNeeded();
-		return mStableDeviceValues.getDisplaySize();
-	}
+    public Point getStableDisplaySize() {
+        loadIfNeeded();
+        return mStableDeviceValues.getDisplaySize();
+    }
 
-	public void setStableDisplaySize(Point size) {
-		loadIfNeeded();
-		if (mStableDeviceValues.setDisplaySize(size)) {
-			setDirty();
-		}
-	}
+    public void setStableDisplaySize(Point size) {
+        loadIfNeeded();
+        if (mStableDeviceValues.setDisplaySize(size)) {
+            setDirty();
+        }
+    }
 
     public void setBrightnessConfigurationForUser(BrightnessConfiguration c, int userSerial,
             @Nullable String packageName) {
diff --git a/services/core/java/com/android/server/display/utils/History.java b/services/core/java/com/android/server/display/utils/History.java
index ed171b8..988d573 100644
--- a/services/core/java/com/android/server/display/utils/History.java
+++ b/services/core/java/com/android/server/display/utils/History.java
@@ -83,7 +83,7 @@
      * @return The buffer as string.
      */
     public String toString() {
-        StringBuffer sb = new StringBuffer();
+        StringBuilder sb = new StringBuilder();
         sb.append("[");
         for (int i = 0; i < mCount; i++) {
             final int index = (mStart + i) % mSize;
diff --git a/services/core/java/com/android/server/display/utils/RollingBuffer.java b/services/core/java/com/android/server/display/utils/RollingBuffer.java
index dd5b7ab..883f6eb 100644
--- a/services/core/java/com/android/server/display/utils/RollingBuffer.java
+++ b/services/core/java/com/android/server/display/utils/RollingBuffer.java
@@ -136,7 +136,7 @@
      * @return The buffer as string.
      */
     public String toString() {
-        StringBuffer sb = new StringBuffer();
+        StringBuilder sb = new StringBuilder();
         sb.append("[");
         for (int i = 0; i < mCount; i++) {
             final int index = offsetOf(i);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
new file mode 100644
index 0000000..2b4d515
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
@@ -0,0 +1,334 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.hdmi;
+
+import static android.hardware.hdmi.HdmiControlManager.CecSettingName;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.hdmi.HdmiControlManager;
+import android.os.Environment;
+import android.os.SystemProperties;
+import android.provider.Settings.Global;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.hdmi.cec.config.CecSettings;
+import com.android.server.hdmi.cec.config.Setting;
+import com.android.server.hdmi.cec.config.Value;
+import com.android.server.hdmi.cec.config.XmlParser;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.xml.datatype.DatatypeConfigurationException;
+
+/**
+ * The {@link HdmiCecConfig} class is used for getting information about
+ * available HDMI CEC settings.
+ */
+public class HdmiCecConfig {
+    private static final String TAG = "HdmiCecConfig";
+
+    private static final String ETC_DIR = "etc";
+    private static final String CONFIG_FILE = "cec_config.xml";
+
+    @Nullable private CecSettings mProductConfig = null;
+    @Nullable private CecSettings mVendorOverride = null;
+    @Nullable private StorageAdapter mStorageAdapter = null;
+
+    @IntDef({
+        STORAGE_SYSPROPS,
+        STORAGE_GLOBAL_SETTINGS,
+    })
+    private @interface Storage {}
+
+    private static final int STORAGE_SYSPROPS = 0;
+    private static final int STORAGE_GLOBAL_SETTINGS = 1;
+
+    /**
+     * System property key for Power State Change on Active Source Lost.
+     */
+    public static final String SYSPROP_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST =
+            "ro.hdmi.cec.source.power_state_change_on_active_source_lost";
+
+    /**
+     * System property key for Audio Mode Muting.
+     */
+    public static final String SYSPROP_SYSTEM_AUDIO_MODE_MUTING =
+            "ro.hdmi.cec.audio.system_audio_mode_muting.enabled";
+
+    /**
+     * Setting storage input/output helper class.
+     */
+    public static class StorageAdapter {
+        /**
+         * Read the value from a system property.
+         * Returns the given default value if the system property is not set.
+         */
+        public String retrieveSystemProperty(@NonNull String storageKey,
+                                             @NonNull String defaultValue) {
+            return SystemProperties.get(storageKey, defaultValue);
+        }
+
+        /**
+         * Write the value to a system property.
+         */
+        public void storeSystemProperty(@NonNull String storageKey,
+                                        @NonNull String value) {
+            SystemProperties.set(storageKey, value);
+        }
+
+        /**
+         * Read the value from a global setting.
+         * Returns the given default value if the system property is not set.
+         */
+        public String retrieveGlobalSetting(@NonNull Context context,
+                                            @NonNull String storageKey,
+                                            @NonNull String defaultValue) {
+            String value = Global.getString(context.getContentResolver(), storageKey);
+            return value != null ? value : defaultValue;
+        }
+
+        /**
+         * Write the value to a global setting.
+         */
+        public void storeGlobalSetting(@NonNull Context context,
+                                       @NonNull String storageKey,
+                                       @NonNull String value) {
+            Global.putString(context.getContentResolver(), storageKey, value);
+        }
+    }
+
+    @VisibleForTesting
+    HdmiCecConfig(@Nullable CecSettings productConfig,
+                  @Nullable CecSettings vendorOverride,
+                  @Nullable StorageAdapter storageAdapter) {
+        mProductConfig = productConfig;
+        mVendorOverride = vendorOverride;
+        mStorageAdapter = storageAdapter;
+    }
+
+    HdmiCecConfig() {
+        this(readSettingsFromFile(Environment.buildPath(Environment.getProductDirectory(),
+                                                        ETC_DIR, CONFIG_FILE)),
+             readSettingsFromFile(Environment.buildPath(Environment.getVendorDirectory(),
+                                                        ETC_DIR, CONFIG_FILE)),
+             new StorageAdapter());
+    }
+
+    @Nullable
+    private static CecSettings readSettingsFromFile(@NonNull File file) {
+        if (!file.exists()) {
+            return null;
+        }
+        if (!file.isFile()) {
+            Slog.e(TAG, "CEC configuration is not a file: " + file + ", skipping.");
+            return null;
+        }
+        try (InputStream in = new BufferedInputStream(new FileInputStream(file))) {
+            return XmlParser.read(in);
+        } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
+            Slog.e(TAG, "Encountered an error while reading/parsing CEC config file: " + file, e);
+        }
+        return null;
+    }
+
+    @Nullable
+    private Setting getSetting(@NonNull String name) {
+        if (mVendorOverride != null) {
+            // First read from the vendor override.
+            for (Setting setting : mVendorOverride.getSetting()) {
+                if (setting.getName().equals(name)) {
+                    return setting;
+                }
+            }
+        }
+        // If not found, try the product config.
+        for (Setting setting : mProductConfig.getSetting()) {
+            if (setting.getName().equals(name)) {
+                return setting;
+            }
+        }
+        return null;
+    }
+
+    @Storage
+    private int getStorage(@NonNull Setting setting) {
+        switch (setting.getName()) {
+            case HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED:
+                return STORAGE_GLOBAL_SETTINGS;
+            case HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP:
+                return STORAGE_GLOBAL_SETTINGS;
+            case HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST:
+                return STORAGE_SYSPROPS;
+            case HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING:
+                return STORAGE_SYSPROPS;
+            default:
+                throw new RuntimeException("Invalid CEC setting '" + setting.getName()
+                    + "' storage.");
+        }
+    }
+
+    private String getStorageKey(@NonNull Setting setting) {
+        switch (setting.getName()) {
+            case HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED:
+                return Global.HDMI_CONTROL_ENABLED;
+            case HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP:
+                return Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP;
+            case HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST:
+                return SYSPROP_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST;
+            case HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING:
+                return SYSPROP_SYSTEM_AUDIO_MODE_MUTING;
+            default:
+                throw new RuntimeException("Invalid CEC setting '" + setting.getName()
+                    + "' storage key.");
+        }
+    }
+
+    private String retrieveValue(@NonNull Context context, @NonNull Setting setting) {
+        @Storage int storage = getStorage(setting);
+        String storageKey = getStorageKey(setting);
+        if (storage == STORAGE_SYSPROPS) {
+            Slog.d(TAG, "Reading '" + storageKey + "' sysprop.");
+            return mStorageAdapter.retrieveSystemProperty(storageKey,
+                    setting.getDefaultValue().getStringValue());
+        } else if (storage == STORAGE_GLOBAL_SETTINGS) {
+            Slog.d(TAG, "Reading '" + storageKey + "' global setting.");
+            return mStorageAdapter.retrieveGlobalSetting(context, storageKey,
+                    setting.getDefaultValue().getStringValue());
+        }
+        return null;
+    }
+
+    private void storeValue(@NonNull Context context, @NonNull Setting setting,
+                            @NonNull String value) {
+        @Storage int storage = getStorage(setting);
+        String storageKey = getStorageKey(setting);
+        if (storage == STORAGE_SYSPROPS) {
+            Slog.d(TAG, "Setting '" + storageKey + "' sysprop.");
+            mStorageAdapter.storeSystemProperty(storageKey, value);
+        } else if (storage == STORAGE_GLOBAL_SETTINGS) {
+            Slog.d(TAG, "Setting '" + storageKey + "' global setting.");
+            mStorageAdapter.storeGlobalSetting(context, storageKey, value);
+        }
+    }
+
+    /**
+     * Returns a list of all settings based on the XML metadata.
+     */
+    public @CecSettingName List<String> getAllSettings() {
+        List<String> allSettings = new ArrayList<String>();
+        for (Setting setting : mProductConfig.getSetting()) {
+            allSettings.add(setting.getName());
+        }
+        return allSettings;
+    }
+
+    /**
+     * Returns a list of user-modifiable settings based on the XML metadata.
+     */
+    public @CecSettingName List<String> getUserSettings() {
+        Set<String> userSettings = new HashSet<String>();
+        // First read from the product config.
+        for (Setting setting : mProductConfig.getSetting()) {
+            if (setting.getUserConfigurable()) {
+                userSettings.add(setting.getName());
+            }
+        }
+        if (mVendorOverride != null) {
+            // Next either add or remove based on the vendor override.
+            for (Setting setting : mVendorOverride.getSetting()) {
+                if (setting.getUserConfigurable()) {
+                    userSettings.add(setting.getName());
+                } else {
+                    userSettings.remove(setting.getName());
+                }
+            }
+        }
+        return new ArrayList(userSettings);
+    }
+
+    /**
+     * For a given setting name returns values that are allowed for that setting.
+     */
+    public List<String> getAllowedValues(@NonNull @CecSettingName String name) {
+        Setting setting = getSetting(name);
+        if (setting == null) {
+            throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
+        }
+        List<String> allowedValues = new ArrayList<String>();
+        for (Value allowedValue : setting.getAllowedValues().getValue()) {
+            allowedValues.add(allowedValue.getStringValue());
+        }
+        return allowedValues;
+    }
+
+    /**
+     * For a given setting name returns the default value for that setting.
+     */
+    public String getDefaultValue(@NonNull @CecSettingName String name) {
+        Setting setting = getSetting(name);
+        if (setting == null) {
+            throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
+        }
+        return getSetting(name).getDefaultValue().getStringValue();
+    }
+
+    /**
+     * For a given setting name returns the current value of that setting.
+     */
+    public String getValue(@NonNull Context context, @NonNull @CecSettingName String name) {
+        Setting setting = getSetting(name);
+        if (setting == null) {
+            throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
+        }
+        Slog.d(TAG, "Getting CEC setting value '" + name + "'.");
+        return retrieveValue(context, setting);
+    }
+
+    /**
+     * For a given setting name and value sets the current value of that setting.
+     */
+    public void setValue(@NonNull Context context, @NonNull @CecSettingName String name,
+                         @NonNull String value) {
+        Setting setting = getSetting(name);
+        if (setting == null) {
+            throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
+        }
+        if (!setting.getUserConfigurable()) {
+            throw new IllegalArgumentException("Updating CEC setting '" + name + "' prohibited.");
+        }
+        if (!getAllowedValues(name).contains(value)) {
+            throw new IllegalArgumentException("Invalid CEC setting '" + name
+                                               + "' value: '" + value + "'.");
+        }
+        Slog.d(TAG, "Updating CEC setting '" + name + "' to '" + value + "'.");
+        storeValue(context, setting, value);
+    }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index b88a37e..946fb0d 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -127,7 +127,7 @@
 
         @Override
         public String toString() {
-            StringBuffer s = new StringBuffer();
+            StringBuilder s = new StringBuilder();
             String logicalAddressString =
                     (logicalAddress == Constants.ADDR_INVALID)
                             ? "invalid"
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessage.java b/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
index ff7da11..7a6ce8d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
@@ -111,7 +111,7 @@
 
     @Override
     public String toString() {
-        StringBuffer s = new StringBuffer();
+        StringBuilder s = new StringBuilder();
         s.append(String.format("<%s> %X%X:%02X",
                 opcodeToString(mOpcode), mSource, mDestination, mOpcode));
         if (mParams.length > 0) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index a60a676..0034df3 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -95,10 +95,12 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 import java.util.stream.Collectors;
 
 /**
@@ -187,6 +189,9 @@
     @GuardedBy("mLock")
     private boolean mHdmiCecVolumeControlEnabled;
 
+    // Make sure HdmiCecConfig is instantiated and the XMLs are read.
+    private final HdmiCecConfig mHdmiCecConfig = new HdmiCecConfig();
+
     /**
      * Interface to report send result.
      */
@@ -2240,7 +2245,7 @@
         @Override
         public void setHdmiCecVolumeControlEnabled(final boolean isHdmiCecVolumeControlEnabled) {
             enforceAccessPermission();
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 HdmiControlService.this.setHdmiCecVolumeControlEnabled(
                         isHdmiCecVolumeControlEnabled);
@@ -2316,6 +2321,19 @@
             pw.println("mHdmiCecVolumeControlEnabled: " + mHdmiCecVolumeControlEnabled);
             pw.decreaseIndent();
 
+            // CEC settings
+            pw.println("CEC settings:");
+            pw.increaseIndent();
+            HdmiCecConfig hdmiCecConfig = HdmiControlService.this.getHdmiCecConfig();
+            List<String> allSettings = hdmiCecConfig.getAllSettings();
+            Set<String> userSettings = new HashSet<>(hdmiCecConfig.getUserSettings());
+            for (String setting : allSettings) {
+                pw.println(setting + ": " + hdmiCecConfig.getValue(getContext(), setting)
+                        + " (default: " + hdmiCecConfig.getDefaultValue(setting) + ")"
+                        + (userSettings.contains(setting) ? " [modifiable]" : ""));
+            }
+            pw.decreaseIndent();
+
             pw.println("mMhlController: ");
             pw.increaseIndent();
             mMhlController.dump(pw);
@@ -2329,6 +2347,50 @@
                 pw.decreaseIndent();
             }
         }
+
+        @Override
+        public List<String> getUserCecSettings() {
+            enforceAccessPermission();
+            long token = Binder.clearCallingIdentity();
+            try {
+                return HdmiControlService.this.getHdmiCecConfig().getUserSettings();
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public List<String> getAllowedCecSettingValues(String name) {
+            enforceAccessPermission();
+            long token = Binder.clearCallingIdentity();
+            try {
+                return HdmiControlService.this.getHdmiCecConfig().getAllowedValues(name);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public String getCecSettingValue(String name) {
+            enforceAccessPermission();
+            long token = Binder.clearCallingIdentity();
+            try {
+                return HdmiControlService.this.getHdmiCecConfig().getValue(getContext(), name);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void setCecSettingValue(String name, String value) {
+            enforceAccessPermission();
+            long token = Binder.clearCallingIdentity();
+            try {
+                HdmiControlService.this.getHdmiCecConfig().setValue(getContext(), name, value);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
     }
 
     // Get the source address to send out commands to devices connected to the current device
@@ -3389,4 +3451,8 @@
         getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
                 HdmiControlService.PERMISSION);
     }
+
+    HdmiCecConfig getHdmiCecConfig() {
+        return mHdmiCecConfig;
+    }
 }
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index cc8a330..3f4ddea 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -55,6 +55,8 @@
 import android.os.Environment;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.InputEventInjectionResult;
+import android.os.InputEventInjectionSync;
 import android.os.LocaleList;
 import android.os.Looper;
 import android.os.Message;
@@ -231,6 +233,8 @@
     private static native void nativePilferPointers(long ptr, IBinder token);
     private static native void nativeSetInputFilterEnabled(long ptr, boolean enable);
     private static native void nativeSetInTouchMode(long ptr, boolean inTouchMode);
+    private static native void nativeSetMaximumObscuringOpacityForTouch(long ptr, float opacity);
+    private static native void nativeSetBlockUntrustedTouchesMode(long ptr, int mode);
     private static native int nativeInjectInputEvent(long ptr, InputEvent event,
             int injectorPid, int injectorUid, int syncMode, int timeoutMillis,
             int policyFlags);
@@ -266,12 +270,6 @@
     private static native void nativeNotifyPortAssociationsChanged(long ptr);
     private static native void nativeSetMotionClassifierEnabled(long ptr, boolean enabled);
 
-    // Input event injection constants defined in InputDispatcher.h.
-    private static final int INPUT_EVENT_INJECTION_SUCCEEDED = 0;
-    private static final int INPUT_EVENT_INJECTION_PERMISSION_DENIED = 1;
-    private static final int INPUT_EVENT_INJECTION_FAILED = 2;
-    private static final int INPUT_EVENT_INJECTION_TIMED_OUT = 3;
-
     // Maximum number of milliseconds to wait for input event injection.
     private static final int INJECTION_TIMEOUT_MILLIS = 30 * 1000;
 
@@ -399,6 +397,8 @@
         registerShowTouchesSettingObserver();
         registerAccessibilityLargePointerSettingObserver();
         registerLongPressTimeoutObserver();
+        registerMaximumObscuringOpacityForTouchSettingObserver();
+        registerBlockUntrustedTouchesModeSettingObserver();
 
         mContext.registerReceiver(new BroadcastReceiver() {
             @Override
@@ -414,6 +414,8 @@
         updateShowTouchesFromSettings();
         updateAccessibilityLargePointerFromSettings();
         updateDeepPressStatusFromSettings("just booted");
+        updateMaximumObscuringOpacityForTouchFromSettings();
+        updateBlockUntrustedTouchesModeFromSettings();
     }
 
     // TODO(BT) Pass in parameter for bluetooth system
@@ -685,9 +687,9 @@
         if (event == null) {
             throw new IllegalArgumentException("event must not be null");
         }
-        if (mode != InputManager.INJECT_INPUT_EVENT_MODE_ASYNC
-                && mode != InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH
-                && mode != InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT) {
+        if (mode != InputEventInjectionSync.NONE
+                && mode != InputEventInjectionSync.WAIT_FOR_FINISHED
+                && mode != InputEventInjectionSync.WAIT_FOR_RESULT) {
             throw new IllegalArgumentException("mode is invalid");
         }
 
@@ -702,16 +704,16 @@
             Binder.restoreCallingIdentity(ident);
         }
         switch (result) {
-            case INPUT_EVENT_INJECTION_PERMISSION_DENIED:
+            case InputEventInjectionResult.PERMISSION_DENIED:
                 Slog.w(TAG, "Input event injection from pid " + pid + " permission denied.");
                 throw new SecurityException(
                         "Injecting to another application requires INJECT_EVENTS permission");
-            case INPUT_EVENT_INJECTION_SUCCEEDED:
+            case InputEventInjectionResult.SUCCEEDED:
                 return true;
-            case INPUT_EVENT_INJECTION_TIMED_OUT:
+            case InputEventInjectionResult.TIMED_OUT:
                 Slog.w(TAG, "Input event injection from pid " + pid + " timed out.");
                 return false;
-            case INPUT_EVENT_INJECTION_FAILED:
+            case InputEventInjectionResult.FAILED:
             default:
                 Slog.w(TAG, "Input event injection from pid " + pid + " failed.");
                 return false;
@@ -1739,6 +1741,46 @@
                 }, UserHandle.USER_ALL);
     }
 
+    private void registerBlockUntrustedTouchesModeSettingObserver() {
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.BLOCK_UNTRUSTED_TOUCHES_MODE),
+                /* notifyForDescendants */ true,
+                new ContentObserver(mHandler) {
+                    @Override
+                    public void onChange(boolean selfChange) {
+                        updateBlockUntrustedTouchesModeFromSettings();
+                    }
+                }, UserHandle.USER_ALL);
+    }
+
+    private void updateBlockUntrustedTouchesModeFromSettings() {
+        final int mode = InputManager.getInstance().getBlockUntrustedTouchesMode(mContext);
+        nativeSetBlockUntrustedTouchesMode(mPtr, mode);
+    }
+
+    private void registerMaximumObscuringOpacityForTouchSettingObserver() {
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH),
+                /* notifyForDescendants */ true,
+                new ContentObserver(mHandler) {
+                    @Override
+                    public void onChange(boolean selfChange) {
+                        updateMaximumObscuringOpacityForTouchFromSettings();
+                    }
+                }, UserHandle.USER_ALL);
+    }
+
+    private void updateMaximumObscuringOpacityForTouchFromSettings() {
+        final float opacity = InputManager.getInstance().getMaximumObscuringOpacityForTouch(
+                mContext);
+        if (opacity < 0 || opacity > 1) {
+            Log.e(TAG, "Invalid maximum obscuring opacity " + opacity
+                    + ", it should be >= 0 and <= 1, rejecting update.");
+            return;
+        }
+        nativeSetMaximumObscuringOpacityForTouch(mPtr, opacity);
+    }
+
     private int getShowTouchesSetting(int defaultValue) {
         int result = defaultValue;
         try {
@@ -2047,6 +2089,16 @@
         }
     }
 
+    // Native callback
+    private void notifyUntrustedTouch(String packageName) {
+        // TODO(b/169067926): Remove toast after gathering feedback on dogfood.
+        DisplayThread.getHandler().post(() ->
+                Toast.makeText(mContext,
+                        "Touch obscured by " + packageName
+                                + " will be blocked. Check go/s-untrusted-touches",
+                        Toast.LENGTH_SHORT).show());
+    }
+
     // Native callback.
     private long notifyANR(InputApplicationHandle inputApplicationHandle, IBinder token,
             String reason) {
diff --git a/services/core/java/com/android/server/input/InputShellCommand.java b/services/core/java/com/android/server/input/InputShellCommand.java
index fd5f48c..51e6cf4 100644
--- a/services/core/java/com/android/server/input/InputShellCommand.java
+++ b/services/core/java/com/android/server/input/InputShellCommand.java
@@ -215,7 +215,7 @@
      * @param text is a string of characters you want to input to the device.
      */
     private void sendText(int source, final String text, int displayId) {
-        final StringBuffer buff = new StringBuffer(text);
+        final StringBuilder buff = new StringBuilder(text);
         boolean escapeFlag = false;
         for (int i = 0; i < buff.length(); i++) {
             if (escapeFlag) {
diff --git a/services/core/java/com/android/server/inputmethod/InputContentUriTokenHandler.java b/services/core/java/com/android/server/inputmethod/InputContentUriTokenHandler.java
index 845fca1..78c4144 100644
--- a/services/core/java/com/android/server/inputmethod/InputContentUriTokenHandler.java
+++ b/services/core/java/com/android/server/inputmethod/InputContentUriTokenHandler.java
@@ -73,7 +73,7 @@
     }
 
     private void doTakeLocked(@NonNull IBinder permissionOwner) {
-        long origId = Binder.clearCallingIdentity();
+        final long origId = Binder.clearCallingIdentity();
         try {
             try {
                 UriGrantsManager.getService().grantUriPermissionFromOwner(
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 2eccaf1..9947ecd 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -15,10 +15,36 @@
 
 package com.android.server.inputmethod;
 
+import static android.server.inputmethod.InputMethodManagerServiceProto.ACCESSIBILITY_REQUESTING_NO_SOFT_KEYBOARD;
+import static android.server.inputmethod.InputMethodManagerServiceProto.BACK_DISPOSITION;
+import static android.server.inputmethod.InputMethodManagerServiceProto.BOUND_TO_METHOD;
+import static android.server.inputmethod.InputMethodManagerServiceProto.CUR_ATTRIBUTE;
+import static android.server.inputmethod.InputMethodManagerServiceProto.CUR_CLIENT;
+import static android.server.inputmethod.InputMethodManagerServiceProto.CUR_FOCUSED_WINDOW_NAME;
+import static android.server.inputmethod.InputMethodManagerServiceProto.CUR_FOCUSED_WINDOW_SOFT_INPUT_MODE;
+import static android.server.inputmethod.InputMethodManagerServiceProto.CUR_ID;
+import static android.server.inputmethod.InputMethodManagerServiceProto.CUR_METHOD_ID;
+import static android.server.inputmethod.InputMethodManagerServiceProto.CUR_SEQ;
+import static android.server.inputmethod.InputMethodManagerServiceProto.CUR_TOKEN;
+import static android.server.inputmethod.InputMethodManagerServiceProto.CUR_TOKEN_DISPLAY_ID;
+import static android.server.inputmethod.InputMethodManagerServiceProto.HAVE_CONNECTION;
+import static android.server.inputmethod.InputMethodManagerServiceProto.IME_WINDOW_VISIBILITY;
+import static android.server.inputmethod.InputMethodManagerServiceProto.INPUT_SHOWN;
+import static android.server.inputmethod.InputMethodManagerServiceProto.IN_FULLSCREEN_MODE;
+import static android.server.inputmethod.InputMethodManagerServiceProto.IS_INTERACTIVE;
+import static android.server.inputmethod.InputMethodManagerServiceProto.LAST_IME_TARGET_WINDOW_NAME;
+import static android.server.inputmethod.InputMethodManagerServiceProto.LAST_SWITCH_USER_ID;
+import static android.server.inputmethod.InputMethodManagerServiceProto.SHOW_EXPLICITLY_REQUESTED;
+import static android.server.inputmethod.InputMethodManagerServiceProto.SHOW_FORCED;
+import static android.server.inputmethod.InputMethodManagerServiceProto.SHOW_IME_WITH_HARD_KEYBOARD;
+import static android.server.inputmethod.InputMethodManagerServiceProto.SHOW_REQUESTED;
+import static android.server.inputmethod.InputMethodManagerServiceProto.SYSTEM_READY;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorProto.CLIENTS;
 import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorProto.ELAPSED_REALTIME_NANOS;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorProto.INPUT_METHOD_MANAGER_SERVICE;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorProto.INPUT_METHOD_SERVICE;
 import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorTraceFileProto.ENTRY;
 
 import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -37,8 +63,6 @@
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
-import android.app.ActivityThread;
-import android.app.AlertDialog;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
 import android.app.KeyguardManager;
@@ -50,9 +74,6 @@
 import android.content.ContentProvider;
 import android.content.ContentResolver;
 import android.content.Context;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnCancelListener;
-import android.content.DialogInterface.OnClickListener;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
@@ -64,10 +85,8 @@
 import android.content.pm.ServiceInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
-import android.content.res.TypedArray;
 import android.database.ContentObserver;
 import android.graphics.Matrix;
-import android.graphics.drawable.Drawable;
 import android.hardware.display.DisplayManagerInternal;
 import android.hardware.input.InputManagerInternal;
 import android.inputmethodservice.InputMethodService;
@@ -77,6 +96,7 @@
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.IInterface;
 import android.os.LocaleList;
@@ -108,14 +128,10 @@
 import android.util.SparseArray;
 import android.util.imetracing.ImeTracing;
 import android.util.proto.ProtoOutputStream;
-import android.view.ContextThemeWrapper;
 import android.view.DisplayInfo;
 import android.view.IWindowManager;
 import android.view.InputChannel;
-import android.view.LayoutInflater;
 import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
 import android.view.WindowManager.LayoutParams;
 import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
 import android.view.autofill.AutofillId;
@@ -129,12 +145,6 @@
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodManager;
 import android.view.inputmethod.InputMethodSubtype;
-import android.widget.ArrayAdapter;
-import android.widget.CompoundButton;
-import android.widget.CompoundButton.OnCheckedChangeListener;
-import android.widget.RadioButton;
-import android.widget.Switch;
-import android.widget.TextView;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.content.PackageMonitor;
@@ -165,7 +175,6 @@
 import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
 import com.android.server.inputmethod.InputMethodManagerInternal.InputMethodListListener;
 import com.android.server.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
 import com.android.server.inputmethod.InputMethodUtils.InputMethodSettings;
@@ -330,10 +339,10 @@
     private final ArrayMap<String, List<InputMethodSubtype>> mAdditionalSubtypeMap =
             new ArrayMap<>();
     private final boolean mIsLowRam;
-    private final HardKeyboardListener mHardKeyboardListener;
     private final AppOpsManager mAppOpsManager;
     private final UserManager mUserManager;
     private final UserManagerInternal mUserManagerInternal;
+    private final InputMethodMenuController mMenuController;
 
     /**
      * Cache the result of {@code LocalServices.getService(AudioManagerInternal.class)}.
@@ -351,7 +360,7 @@
     final ArrayMap<String, InputMethodInfo> mMethodMap = new ArrayMap<>();
     private final LruCache<SuggestionSpan, InputMethodInfo> mSecureSuggestionSpans =
             new LruCache<>(SECURE_SUGGESTION_SPANS_MAX_SIZE);
-    private final InputMethodSubtypeSwitchingController mSwitchingController;
+    final InputMethodSubtypeSwitchingController mSwitchingController;
 
     /**
      * Tracks how many times {@link #mMethodMap} was updated.
@@ -380,7 +389,7 @@
 
     // Ongoing notification
     private NotificationManager mNotificationManager;
-    private KeyguardManager mKeyguardManager;
+    KeyguardManager mKeyguardManager;
     private @Nullable StatusBarManagerService mStatusBar;
     private Notification.Builder mImeSwitcherNotification;
     private PendingIntent mImeSwitchPendingIntent;
@@ -724,21 +733,16 @@
      */
     int mImeWindowVis;
 
-    private AlertDialog.Builder mDialogBuilder;
-    private AlertDialog mSwitchingDialog;
-    private IBinder mSwitchingDialogToken = new Binder();
-    private View mSwitchingDialogTitleView;
-    private InputMethodInfo[] mIms;
-    private int[] mSubtypeIds;
     private LocaleList mLastSystemLocales;
-    private boolean mShowImeWithHardKeyboard;
     private boolean mAccessibilityRequestingNoSoftKeyboard;
     private final MyPackageMonitor mMyPackageMonitor = new MyPackageMonitor();
     private final IPackageManager mIPackageManager;
     private final String mSlotIme;
 
+    private HandlerThread mTracingThread;
+
     /**
-     * Registered {@link InputMethodListListeners}.
+     * Registered {@link InputMethodListListener}.
      * This variable can be accessed from both of MainThread and BinderThread.
      */
     private final CopyOnWriteArrayList<InputMethodListListener> mInputMethodListListeners =
@@ -1138,7 +1142,7 @@
                     Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE);
             synchronized (mMethodMap) {
                 if (showImeUri.equals(uri)) {
-                    updateKeyboardFromSettingsLocked();
+                    mMenuController.updateKeyboardFromSettingsLocked();
                 } else if (accessibilityRequestingNoImeUri.equals(uri)) {
                     final int accessibilitySoftKeyboardSetting = Settings.Secure.getIntForUser(
                             mContext.getContentResolver(),
@@ -1226,7 +1230,7 @@
                         return;
                     }
                 }
-                hideInputMethodMenu();
+                mMenuController.hideInputMethodMenu();
             } else {
                 Slog.w(TAG, "Unexpected intent " + intent);
             }
@@ -1517,7 +1521,7 @@
 
         @Override
         public void sessionCreated(IInputMethodSession session) {
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
                 mParentIMMS.onSessionCreated(mMethod, session, mChannel);
             } finally {
@@ -1526,29 +1530,6 @@
         }
     }
 
-    private class HardKeyboardListener
-            implements WindowManagerInternal.OnHardKeyboardStatusChangeListener {
-        @Override
-        public void onHardKeyboardStatusChange(boolean available) {
-            mHandler.sendMessage(mHandler.obtainMessage(MSG_HARD_KEYBOARD_SWITCH_CHANGED,
-                        available ? 1 : 0));
-        }
-
-        public void handleHardKeyboardStatusChange(boolean available) {
-            if (DEBUG) {
-                Slog.w(TAG, "HardKeyboardStatusChanged: available=" + available);
-            }
-            synchronized(mMethodMap) {
-                if (mSwitchingDialog != null && mSwitchingDialogTitleView != null
-                        && mSwitchingDialog.isShowing()) {
-                    mSwitchingDialogTitleView.findViewById(
-                            com.android.internal.R.id.hard_keyboard_section).setVisibility(
-                                    available ? View.VISIBLE : View.GONE);
-                }
-            }
-        }
-    }
-
     private static final class UserSwitchHandlerTask implements Runnable {
         final InputMethodManagerService mService;
 
@@ -1687,7 +1668,6 @@
         mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
         mUserManager = mContext.getSystemService(UserManager.class);
         mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
-        mHardKeyboardListener = new HardKeyboardListener();
         mHasFeature = context.getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_INPUT_METHODS);
         mSlotIme = mContext.getString(com.android.internal.R.string.status_bar_ime);
@@ -1731,6 +1711,10 @@
         AdditionalSubtypeUtils.load(mAdditionalSubtypeMap, userId);
         mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked(
                 mSettings, context);
+        mMenuController = new InputMethodMenuController(this);
+
+        mTracingThread = new HandlerThread("android.tracing", Process.THREAD_PRIORITY_BACKGROUND);
+        mTracingThread.start();
     }
 
     private void resetDefaultImeLocked(Context context) {
@@ -1861,8 +1845,10 @@
                 mShowOngoingImeSwitcherForPhones = mRes.getBoolean(
                         com.android.internal.R.bool.show_ongoing_ime_switcher);
                 if (mShowOngoingImeSwitcherForPhones) {
+                    final InputMethodMenuController.HardKeyboardListener hardKeyboardListener =
+                            mMenuController.getHardKeyboardListener();
                     mWindowManagerInternal.setOnHardKeyboardStatusChangeListener(
-                            mHardKeyboardListener);
+                            hardKeyboardListener);
                 }
 
                 mMyPackageMonitor.register(mContext, null, UserHandle.ALL, true);
@@ -2331,7 +2317,7 @@
             mCurClient = null;
             mCurActivityViewToScreenMatrix = null;
 
-            hideInputMethodMenuLocked();
+            mMenuController.hideInputMethodMenuLocked();
         }
     }
 
@@ -2813,7 +2799,7 @@
 
     private boolean shouldShowImeSwitcherLocked(int visibility) {
         if (!mShowOngoingImeSwitcherForPhones) return false;
-        if (mSwitchingDialog != null) return false;
+        if (mMenuController.getSwitchingDialogLocked() != null) return false;
         if (mWindowManagerInternal.isKeyguardShowingAndNotOccluded()
                 && mKeyguardManager != null && mKeyguardManager.isKeyguardSecure()) return false;
         if ((visibility & InputMethodService.IME_ACTIVE) == 0
@@ -2930,6 +2916,10 @@
         }
     }
 
+    void updateSystemUiLocked() {
+        updateSystemUiLocked(mImeWindowVis, mBackDisposition);
+    }
+
     // Caution! This method is called in this class. Handle multi-user carefully
     private void updateSystemUiLocked(int vis, int backDisposition) {
         if (mCurToken == null) {
@@ -3000,7 +2990,7 @@
 
     void updateFromSettingsLocked(boolean enabledMayChange) {
         updateInputMethodsFromSettingsLocked(enabledMayChange);
-        updateKeyboardFromSettingsLocked();
+        mMenuController.updateKeyboardFromSettingsLocked();
     }
 
     void updateInputMethodsFromSettingsLocked(boolean enabledMayChange) {
@@ -3057,17 +3047,6 @@
 
     }
 
-    public void updateKeyboardFromSettingsLocked() {
-        mShowImeWithHardKeyboard = mSettings.isShowImeWithHardKeyboardEnabled();
-        if (mSwitchingDialog != null
-                && mSwitchingDialogTitleView != null
-                && mSwitchingDialog.isShowing()) {
-            final Switch hardKeySwitch = (Switch)mSwitchingDialogTitleView.findViewById(
-                    com.android.internal.R.id.hard_keyboard_switch);
-            hardKeySwitch.setChecked(mShowImeWithHardKeyboard);
-        }
-    }
-
     /* package */ void setInputMethodLocked(String id, int subtypeId) {
         InputMethodInfo info = mMethodMap.get(id);
         if (info == null) {
@@ -3697,10 +3676,7 @@
 
     public boolean isInputMethodPickerShownForTest() {
         synchronized(mMethodMap) {
-            if (mSwitchingDialog == null) {
-                return false;
-            }
-            return mSwitchingDialog.isShowing();
+            return mMenuController.isisInputMethodPickerShownForTestLocked();
         }
     }
 
@@ -3935,7 +3911,7 @@
      * {@link InputMethodService#onCreate()}.
      *
      * <p>TODO(Bug 113914148): Check if we can remove this.</p>
-     * @return {@link WindowManagerInternal#getInputMethodWindowVisibleHeight()}
+     * @return {@link WindowManagerInternal#getInputMethodWindowVisibleHeight(int)}
      */
     @Override
     public int getInputMethodWindowVisibleHeight() {
@@ -4045,40 +4021,59 @@
      */
     @BinderThread
     @Override
+    @GuardedBy("mMethodMap")
     public void startProtoDump(byte[] clientProtoDump) {
-        if (!ImeTracing.getInstance().isAvailable() || !ImeTracing.getInstance().isEnabled()) {
-            return;
-        }
-        if (clientProtoDump == null && mCurClient == null) {
-            return;
-        }
+        mTracingThread.getThreadHandler().post(() -> {
+            if (!ImeTracing.getInstance().isAvailable() || !ImeTracing.getInstance().isEnabled()) {
+                return;
+            }
+            if (clientProtoDump == null && mCurClient == null) {
+                return;
+            }
 
-        ProtoOutputStream proto = new ProtoOutputStream();
-        final long token = proto.start(ENTRY);
-        proto.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos());
-        // TODO: get server side dump
-        if (clientProtoDump != null) {
-            proto.write(CLIENTS, clientProtoDump);
-        } else {
-            IBinder client = null;
+            ProtoOutputStream proto = new ProtoOutputStream();
+            final long token = proto.start(ENTRY);
+            proto.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos());
+            dumpDebug(proto, INPUT_METHOD_MANAGER_SERVICE);
 
+            IBinder service = null;
             synchronized (mMethodMap) {
-                if (mCurClient != null && mCurClient.client != null) {
-                    client = mCurClient.client.asBinder();
+                if (mCurMethod != null) {
+                    service = mCurMethod.asBinder();
                 }
             }
 
-            if (client != null) {
+            if (service != null) {
                 try {
-                    proto.write(CLIENTS,
-                            TransferPipe.dumpAsync(client, ImeTracing.PROTO_ARG));
+                    proto.write(INPUT_METHOD_SERVICE,
+                            TransferPipe.dumpAsync(service, ImeTracing.PROTO_ARG));
                 } catch (IOException | RemoteException e) {
-                    Log.e(TAG, "Exception while collecting client side ime dump", e);
+                    Log.e(TAG, "Exception while collecting ime process dump", e);
                 }
             }
-        }
-        proto.end(token);
-        ImeTracing.getInstance().addToBuffer(proto);
+
+            if (clientProtoDump != null) {
+                proto.write(CLIENTS, clientProtoDump);
+            } else {
+                IBinder client = null;
+                synchronized (mMethodMap) {
+                    if (mCurClient != null && mCurClient.client != null) {
+                        client = mCurClient.client.asBinder();
+                    }
+                }
+
+                if (client != null) {
+                    try {
+                        proto.write(CLIENTS,
+                                TransferPipe.dumpAsync(client, ImeTracing.PROTO_ARG));
+                    } catch (IOException | RemoteException e) {
+                        Log.e(TAG, "Exception while collecting client side ime dump", e);
+                    }
+                }
+            }
+            proto.end(token);
+            ImeTracing.getInstance().addToBuffer(proto);
+        });
     }
 
     @BinderThread
@@ -4087,6 +4082,44 @@
         return ImeTracing.getInstance().isEnabled();
     }
 
+    @GuardedBy("mMethodMap")
+    private void dumpDebug(ProtoOutputStream proto, long fieldId) {
+        synchronized (mMethodMap) {
+            final long token = proto.start(fieldId);
+            proto.write(CUR_METHOD_ID, mCurMethodId);
+            proto.write(CUR_SEQ, mCurSeq);
+            proto.write(CUR_CLIENT, Objects.toString(mCurClient));
+            proto.write(CUR_FOCUSED_WINDOW_NAME,
+                    mWindowManagerInternal.getWindowName(mCurFocusedWindow));
+            proto.write(LAST_IME_TARGET_WINDOW_NAME,
+                    mWindowManagerInternal.getWindowName(mLastImeTargetWindow));
+            proto.write(CUR_FOCUSED_WINDOW_SOFT_INPUT_MODE,
+                    InputMethodDebug.softInputModeToString(mCurFocusedWindowSoftInputMode));
+            if (mCurAttribute != null) {
+                mCurAttribute.dumpDebug(proto, CUR_ATTRIBUTE);
+            }
+            proto.write(CUR_ID, mCurId);
+            proto.write(SHOW_REQUESTED, mShowRequested);
+            proto.write(SHOW_EXPLICITLY_REQUESTED, mShowExplicitlyRequested);
+            proto.write(SHOW_FORCED, mShowForced);
+            proto.write(INPUT_SHOWN, mInputShown);
+            proto.write(IN_FULLSCREEN_MODE, mInFullscreenMode);
+            proto.write(CUR_TOKEN, Objects.toString(mCurToken));
+            proto.write(CUR_TOKEN_DISPLAY_ID, mCurTokenDisplayId);
+            proto.write(SYSTEM_READY, mSystemReady);
+            proto.write(LAST_SWITCH_USER_ID, mLastSwitchUserId);
+            proto.write(HAVE_CONNECTION, mHaveConnection);
+            proto.write(BOUND_TO_METHOD, mBoundToMethod);
+            proto.write(IS_INTERACTIVE, mIsInteractive);
+            proto.write(BACK_DISPOSITION, mBackDisposition);
+            proto.write(IME_WINDOW_VISIBILITY, mImeWindowVis);
+            proto.write(SHOW_IME_WITH_HARD_KEYBOARD, mMenuController.getShowImeWithHardKeyboard());
+            proto.write(ACCESSIBILITY_REQUESTING_NO_SOFT_KEYBOARD,
+                    mAccessibilityRequestingNoSoftKeyboard);
+            proto.end(token);
+        }
+    }
+
     @BinderThread
     private void notifyUserAction(@NonNull IBinder token) {
         if (DEBUG) {
@@ -4174,7 +4207,7 @@
             if (!calledWithValidTokenLocked(token)) {
                 return;
             }
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
                 hideCurrentInputLocked(
                         mLastImeTargetWindow, flags, null,
@@ -4192,7 +4225,7 @@
             if (!calledWithValidTokenLocked(token)) {
                 return;
             }
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
                 showCurrentInputLocked(mLastImeTargetWindow, flags, null,
                         SoftInputShowHideReason.SHOW_MY_SOFT_INPUT);
@@ -4247,7 +4280,7 @@
                         Slog.e(TAG, "Unknown subtype picker mode = " + msg.arg1);
                         return false;
                 }
-                showInputMethodMenu(showAuxSubtypes, displayId);
+                mMenuController.showInputMethodMenu(showAuxSubtypes, displayId);
                 return true;
 
             case MSG_SHOW_IM_SUBTYPE_ENABLER:
@@ -4484,7 +4517,9 @@
 
             // --------------------------------------------------------------
             case MSG_HARD_KEYBOARD_SWITCH_CHANGED:
-                mHardKeyboardListener.handleHardKeyboardStatusChange(msg.arg1 == 1);
+                final InputMethodMenuController.HardKeyboardListener hardKeyboardListener =
+                        mMenuController.getHardKeyboardListener();
+                hardKeyboardListener.handleHardKeyboardStatusChange(msg.arg1 == 1);
                 return true;
             case MSG_SYSTEM_UNLOCK_USER: {
                 final int userId = msg.arg1;
@@ -4735,208 +4770,6 @@
         mContext.startActivityAsUser(intent, null, UserHandle.CURRENT);
     }
 
-    private boolean isScreenLocked() {
-        return mKeyguardManager != null
-                && mKeyguardManager.isKeyguardLocked() && mKeyguardManager.isKeyguardSecure();
-    }
-
-    private void showInputMethodMenu(boolean showAuxSubtypes, int displayId) {
-        if (DEBUG) Slog.v(TAG, "Show switching menu. showAuxSubtypes=" + showAuxSubtypes);
-
-        final boolean isScreenLocked = isScreenLocked();
-
-        final String lastInputMethodId = mSettings.getSelectedInputMethod();
-        int lastInputMethodSubtypeId = mSettings.getSelectedInputMethodSubtypeId(lastInputMethodId);
-        if (DEBUG) Slog.v(TAG, "Current IME: " + lastInputMethodId);
-
-        synchronized (mMethodMap) {
-            final List<ImeSubtypeListItem> imList =
-                    mSwitchingController.getSortedInputMethodAndSubtypeListLocked(
-                            showAuxSubtypes, isScreenLocked);
-            if (imList.isEmpty()) {
-                return;
-            }
-
-            hideInputMethodMenuLocked();
-
-            if (lastInputMethodSubtypeId == NOT_A_SUBTYPE_ID) {
-                final InputMethodSubtype currentSubtype = getCurrentInputMethodSubtypeLocked();
-                if (currentSubtype != null) {
-                    final InputMethodInfo currentImi = mMethodMap.get(mCurMethodId);
-                    lastInputMethodSubtypeId = InputMethodUtils.getSubtypeIdFromHashCode(
-                            currentImi, currentSubtype.hashCode());
-                }
-            }
-
-            final int N = imList.size();
-            mIms = new InputMethodInfo[N];
-            mSubtypeIds = new int[N];
-            int checkedItem = 0;
-            for (int i = 0; i < N; ++i) {
-                final ImeSubtypeListItem item = imList.get(i);
-                mIms[i] = item.mImi;
-                mSubtypeIds[i] = item.mSubtypeId;
-                if (mIms[i].getId().equals(lastInputMethodId)) {
-                    int subtypeId = mSubtypeIds[i];
-                    if ((subtypeId == NOT_A_SUBTYPE_ID)
-                            || (lastInputMethodSubtypeId == NOT_A_SUBTYPE_ID && subtypeId == 0)
-                            || (subtypeId == lastInputMethodSubtypeId)) {
-                        checkedItem = i;
-                    }
-                }
-            }
-
-            final ActivityThread currentThread = ActivityThread.currentActivityThread();
-            final Context settingsContext = new ContextThemeWrapper(
-                    displayId == DEFAULT_DISPLAY ? currentThread.getSystemUiContext()
-                            : currentThread.createSystemUiContext(displayId),
-                    com.android.internal.R.style.Theme_DeviceDefault_Settings);
-
-            mDialogBuilder = new AlertDialog.Builder(settingsContext);
-            mDialogBuilder.setOnCancelListener(new OnCancelListener() {
-                @Override
-                public void onCancel(DialogInterface dialog) {
-                    hideInputMethodMenu();
-                }
-            });
-
-            final Context dialogContext = mDialogBuilder.getContext();
-            final TypedArray a = dialogContext.obtainStyledAttributes(null,
-                    com.android.internal.R.styleable.DialogPreference,
-                    com.android.internal.R.attr.alertDialogStyle, 0);
-            final Drawable dialogIcon = a.getDrawable(
-                    com.android.internal.R.styleable.DialogPreference_dialogIcon);
-            a.recycle();
-
-            mDialogBuilder.setIcon(dialogIcon);
-
-            final LayoutInflater inflater = dialogContext.getSystemService(LayoutInflater.class);
-            final View tv = inflater.inflate(
-                    com.android.internal.R.layout.input_method_switch_dialog_title, null);
-            mDialogBuilder.setCustomTitle(tv);
-
-            // Setup layout for a toggle switch of the hardware keyboard
-            mSwitchingDialogTitleView = tv;
-            mSwitchingDialogTitleView
-                    .findViewById(com.android.internal.R.id.hard_keyboard_section)
-                    .setVisibility(mWindowManagerInternal.isHardKeyboardAvailable()
-                            ? View.VISIBLE : View.GONE);
-            final Switch hardKeySwitch = (Switch) mSwitchingDialogTitleView.findViewById(
-                    com.android.internal.R.id.hard_keyboard_switch);
-            hardKeySwitch.setChecked(mShowImeWithHardKeyboard);
-            hardKeySwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() {
-                @Override
-                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
-                    mSettings.setShowImeWithHardKeyboard(isChecked);
-                    // Ensure that the input method dialog is dismissed when changing
-                    // the hardware keyboard state.
-                    hideInputMethodMenu();
-                }
-            });
-
-            final ImeSubtypeListAdapter adapter = new ImeSubtypeListAdapter(dialogContext,
-                    com.android.internal.R.layout.input_method_switch_item, imList, checkedItem);
-            final OnClickListener choiceListener = new OnClickListener() {
-                @Override
-                public void onClick(final DialogInterface dialog, final int which) {
-                    synchronized (mMethodMap) {
-                        if (mIms == null || mIms.length <= which || mSubtypeIds == null
-                                || mSubtypeIds.length <= which) {
-                            return;
-                        }
-                        final InputMethodInfo im = mIms[which];
-                        int subtypeId = mSubtypeIds[which];
-                        adapter.mCheckedItem = which;
-                        adapter.notifyDataSetChanged();
-                        hideInputMethodMenu();
-                        if (im != null) {
-                            if (subtypeId < 0 || subtypeId >= im.getSubtypeCount()) {
-                                subtypeId = NOT_A_SUBTYPE_ID;
-                            }
-                            setInputMethodLocked(im.getId(), subtypeId);
-                        }
-                    }
-                }
-            };
-            mDialogBuilder.setSingleChoiceItems(adapter, checkedItem, choiceListener);
-
-            mSwitchingDialog = mDialogBuilder.create();
-            mSwitchingDialog.setCanceledOnTouchOutside(true);
-            final Window w = mSwitchingDialog.getWindow();
-            final LayoutParams attrs = w.getAttributes();
-            w.setType(LayoutParams.TYPE_INPUT_METHOD_DIALOG);
-            // Use an alternate token for the dialog for that window manager can group the token
-            // with other IME windows based on type vs. grouping based on whichever token happens
-            // to get selected by the system later on.
-            attrs.token = mSwitchingDialogToken;
-            attrs.privateFlags |= LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
-            attrs.setTitle("Select input method");
-            w.setAttributes(attrs);
-            updateSystemUiLocked(mImeWindowVis, mBackDisposition);
-            mSwitchingDialog.show();
-        }
-    }
-
-    private static class ImeSubtypeListAdapter extends ArrayAdapter<ImeSubtypeListItem> {
-        private final LayoutInflater mInflater;
-        private final int mTextViewResourceId;
-        private final List<ImeSubtypeListItem> mItemsList;
-        public int mCheckedItem;
-        public ImeSubtypeListAdapter(Context context, int textViewResourceId,
-                List<ImeSubtypeListItem> itemsList, int checkedItem) {
-            super(context, textViewResourceId, itemsList);
-
-            mTextViewResourceId = textViewResourceId;
-            mItemsList = itemsList;
-            mCheckedItem = checkedItem;
-            mInflater = context.getSystemService(LayoutInflater.class);
-        }
-
-        @Override
-        public View getView(int position, View convertView, ViewGroup parent) {
-            final View view = convertView != null ? convertView
-                    : mInflater.inflate(mTextViewResourceId, null);
-            if (position < 0 || position >= mItemsList.size()) return view;
-            final ImeSubtypeListItem item = mItemsList.get(position);
-            final CharSequence imeName = item.mImeName;
-            final CharSequence subtypeName = item.mSubtypeName;
-            final TextView firstTextView = (TextView)view.findViewById(android.R.id.text1);
-            final TextView secondTextView = (TextView)view.findViewById(android.R.id.text2);
-            if (TextUtils.isEmpty(subtypeName)) {
-                firstTextView.setText(imeName);
-                secondTextView.setVisibility(View.GONE);
-            } else {
-                firstTextView.setText(subtypeName);
-                secondTextView.setText(imeName);
-                secondTextView.setVisibility(View.VISIBLE);
-            }
-            final RadioButton radioButton =
-                    (RadioButton)view.findViewById(com.android.internal.R.id.radio);
-            radioButton.setChecked(position == mCheckedItem);
-            return view;
-        }
-    }
-
-    void hideInputMethodMenu() {
-        synchronized (mMethodMap) {
-            hideInputMethodMenuLocked();
-        }
-    }
-
-    void hideInputMethodMenuLocked() {
-        if (DEBUG) Slog.v(TAG, "Hide switching menu");
-
-        if (mSwitchingDialog != null) {
-            mSwitchingDialog.dismiss();
-            mSwitchingDialog = null;
-            mSwitchingDialogTitleView = null;
-        }
-
-        updateSystemUiLocked(mImeWindowVis, mBackDisposition);
-        mDialogBuilder = null;
-        mIms = null;
-    }
-
     // ----------------------------------------------------------------------
 
     /**
@@ -5040,7 +4873,7 @@
         }
     }
 
-    private InputMethodSubtype getCurrentInputMethodSubtypeLocked() {
+    InputMethodSubtype getCurrentInputMethodSubtypeLocked() {
         if (mCurMethodId == null) {
             return null;
         }
@@ -5079,6 +4912,11 @@
         return mCurrentSubtype;
     }
 
+    @Nullable
+    String getCurrentMethodId() {
+        return mCurMethodId;
+    }
+
     private List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId) {
         synchronized (mMethodMap) {
             return getInputMethodListLocked(userId);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
new file mode 100644
index 0000000..7b29e5b
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.inputmethod;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+import static com.android.server.inputmethod.InputMethodManagerService.DEBUG;
+import static com.android.server.inputmethod.InputMethodManagerService.MSG_HARD_KEYBOARD_SWITCH_CHANGED;
+import static com.android.server.inputmethod.InputMethodUtils.NOT_A_SUBTYPE_ID;
+
+import android.app.ActivityThread;
+import android.app.AlertDialog;
+import android.app.KeyguardManager;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.IBinder;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Slog;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
+import android.widget.ArrayAdapter;
+import android.widget.RadioButton;
+import android.widget.Switch;
+import android.widget.TextView;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
+import com.android.server.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
+import com.android.server.wm.WindowManagerInternal;
+
+import java.util.List;
+
+/** A controller to show/hide the input method menu */
+@VisibleForTesting(visibility = PACKAGE)
+public class InputMethodMenuController {
+    private static final String TAG = InputMethodMenuController.class.getSimpleName();
+
+    private final InputMethodManagerService mService;
+    private final InputMethodUtils.InputMethodSettings mSettings;
+    private final InputMethodSubtypeSwitchingController mSwitchingController;
+    private final Handler mHandler;
+    private final ArrayMap<String, InputMethodInfo> mMethodMap;
+    private final KeyguardManager mKeyguardManager;
+    private final HardKeyboardListener mHardKeyboardListener;
+    private final WindowManagerInternal mWindowManagerInternal;
+
+    private Context mSettingsContext;
+    private AlertDialog.Builder mDialogBuilder;
+    private AlertDialog mSwitchingDialog;
+    private IBinder mSwitchingDialogToken;
+    private View mSwitchingDialogTitleView;
+    private InputMethodInfo[] mIms;
+    private int[] mSubtypeIds;
+
+    private boolean mShowImeWithHardKeyboard;
+
+    @VisibleForTesting(visibility = PACKAGE)
+    public InputMethodMenuController(InputMethodManagerService service) {
+        mService = service;
+        mSettings = mService.mSettings;
+        mSwitchingController = mService.mSwitchingController;
+        mHandler = mService.mHandler;
+        mMethodMap = mService.mMethodMap;
+        mKeyguardManager = mService.mKeyguardManager;
+        mHardKeyboardListener = new HardKeyboardListener();
+        mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
+    }
+
+    void showInputMethodMenu(boolean showAuxSubtypes, int displayId) {
+        if (DEBUG) Slog.v(TAG, "Show switching menu. showAuxSubtypes=" + showAuxSubtypes);
+
+        final boolean isScreenLocked = isScreenLocked();
+
+        final String lastInputMethodId = mSettings.getSelectedInputMethod();
+        int lastInputMethodSubtypeId = mSettings.getSelectedInputMethodSubtypeId(lastInputMethodId);
+        if (DEBUG) Slog.v(TAG, "Current IME: " + lastInputMethodId);
+
+        synchronized (mMethodMap) {
+            final List<ImeSubtypeListItem> imList = mSwitchingController
+                    .getSortedInputMethodAndSubtypeListLocked(showAuxSubtypes, isScreenLocked);
+            if (imList.isEmpty()) {
+                return;
+            }
+
+            hideInputMethodMenuLocked();
+
+            if (lastInputMethodSubtypeId == NOT_A_SUBTYPE_ID) {
+                final InputMethodSubtype currentSubtype =
+                        mService.getCurrentInputMethodSubtypeLocked();
+                if (currentSubtype != null) {
+                    final String curMethodId = mService.getCurrentMethodId();
+                    final InputMethodInfo currentImi = mMethodMap.get(curMethodId);
+                    lastInputMethodSubtypeId = InputMethodUtils.getSubtypeIdFromHashCode(
+                            currentImi, currentSubtype.hashCode());
+                }
+            }
+
+            final int size = imList.size();
+            mIms = new InputMethodInfo[size];
+            mSubtypeIds = new int[size];
+            int checkedItem = 0;
+            for (int i = 0; i < size; ++i) {
+                final ImeSubtypeListItem item = imList.get(i);
+                mIms[i] = item.mImi;
+                mSubtypeIds[i] = item.mSubtypeId;
+                if (mIms[i].getId().equals(lastInputMethodId)) {
+                    int subtypeId = mSubtypeIds[i];
+                    if ((subtypeId == NOT_A_SUBTYPE_ID)
+                            || (lastInputMethodSubtypeId == NOT_A_SUBTYPE_ID && subtypeId == 0)
+                            || (subtypeId == lastInputMethodSubtypeId)) {
+                        checkedItem = i;
+                    }
+                }
+            }
+
+            final Context settingsContext = getSettingsContext(displayId);
+            mDialogBuilder = new AlertDialog.Builder(settingsContext);
+            mDialogBuilder.setOnCancelListener(dialog -> hideInputMethodMenu());
+
+            final Context dialogContext = mDialogBuilder.getContext();
+            final TypedArray a = dialogContext.obtainStyledAttributes(null,
+                    com.android.internal.R.styleable.DialogPreference,
+                    com.android.internal.R.attr.alertDialogStyle, 0);
+            final Drawable dialogIcon = a.getDrawable(
+                    com.android.internal.R.styleable.DialogPreference_dialogIcon);
+            a.recycle();
+
+            mDialogBuilder.setIcon(dialogIcon);
+
+            final LayoutInflater inflater = dialogContext.getSystemService(LayoutInflater.class);
+            final View tv = inflater.inflate(
+                    com.android.internal.R.layout.input_method_switch_dialog_title, null);
+            mDialogBuilder.setCustomTitle(tv);
+
+            // Setup layout for a toggle switch of the hardware keyboard
+            mSwitchingDialogTitleView = tv;
+            mSwitchingDialogTitleView
+                    .findViewById(com.android.internal.R.id.hard_keyboard_section)
+                    .setVisibility(mWindowManagerInternal.isHardKeyboardAvailable()
+                            ? View.VISIBLE : View.GONE);
+            final Switch hardKeySwitch = mSwitchingDialogTitleView.findViewById(
+                    com.android.internal.R.id.hard_keyboard_switch);
+            hardKeySwitch.setChecked(mShowImeWithHardKeyboard);
+            hardKeySwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
+                mSettings.setShowImeWithHardKeyboard(isChecked);
+                // Ensure that the input method dialog is dismissed when changing
+                // the hardware keyboard state.
+                hideInputMethodMenu();
+            });
+
+            final ImeSubtypeListAdapter adapter = new ImeSubtypeListAdapter(dialogContext,
+                    com.android.internal.R.layout.input_method_switch_item, imList, checkedItem);
+            final DialogInterface.OnClickListener choiceListener = (dialog, which) -> {
+                synchronized (mMethodMap) {
+                    if (mIms == null || mIms.length <= which || mSubtypeIds == null
+                            || mSubtypeIds.length <= which) {
+                        return;
+                    }
+                    final InputMethodInfo im = mIms[which];
+                    int subtypeId = mSubtypeIds[which];
+                    adapter.mCheckedItem = which;
+                    adapter.notifyDataSetChanged();
+                    hideInputMethodMenu();
+                    if (im != null) {
+                        if (subtypeId < 0 || subtypeId >= im.getSubtypeCount()) {
+                            subtypeId = NOT_A_SUBTYPE_ID;
+                        }
+                        mService.setInputMethodLocked(im.getId(), subtypeId);
+                    }
+                }
+            };
+            mDialogBuilder.setSingleChoiceItems(adapter, checkedItem, choiceListener);
+
+            mSwitchingDialog = mDialogBuilder.create();
+            mSwitchingDialog.setCanceledOnTouchOutside(true);
+            final Window w = mSwitchingDialog.getWindow();
+            final WindowManager.LayoutParams attrs = w.getAttributes();
+            w.setType(WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG);
+            // Use an alternate token for the dialog for that window manager can group the token
+            // with other IME windows based on type vs. grouping based on whichever token happens
+            // to get selected by the system later on.
+            attrs.token = mSwitchingDialogToken;
+            attrs.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+            attrs.setTitle("Select input method");
+            w.setAttributes(attrs);
+            mService.updateSystemUiLocked();
+            mSwitchingDialog.show();
+        }
+    }
+
+    /**
+     * Returns the window context for IME switch dialogs to receive configuration changes.
+     *
+     * This method initializes the window context if it was not initialized. This method also moves
+     * the context to the targeted display if the current display of context is different than
+     * the display specified by {@code displayId}.
+     */
+    @VisibleForTesting
+    public Context getSettingsContext(int displayId) {
+        if (mSettingsContext == null) {
+            final Context systemUiContext = ActivityThread.currentActivityThread()
+                    .createSystemUiContext(displayId);
+            final Context windowContext = systemUiContext.createWindowContext(
+                    WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG, null /* options */);
+            mSettingsContext = new ContextThemeWrapper(
+                    windowContext, com.android.internal.R.style.Theme_DeviceDefault_Settings);
+            mSwitchingDialogToken = mSettingsContext.getActivityToken();
+        }
+        // TODO(b/159767464): register the listener to another display again if window token is not
+        // yet created.
+        if (mSettingsContext.getDisplayId() != displayId) {
+            mWindowManagerInternal.moveWindowTokenToDisplay(mSwitchingDialogToken, displayId);
+        }
+        return mSettingsContext;
+    }
+
+    private boolean isScreenLocked() {
+        return mKeyguardManager != null && mKeyguardManager.isKeyguardLocked()
+                && mKeyguardManager.isKeyguardSecure();
+    }
+
+    void updateKeyboardFromSettingsLocked() {
+        mShowImeWithHardKeyboard = mSettings.isShowImeWithHardKeyboardEnabled();
+        if (mSwitchingDialog != null && mSwitchingDialogTitleView != null
+                && mSwitchingDialog.isShowing()) {
+            final Switch hardKeySwitch = mSwitchingDialogTitleView.findViewById(
+                    com.android.internal.R.id.hard_keyboard_switch);
+            hardKeySwitch.setChecked(mShowImeWithHardKeyboard);
+        }
+    }
+
+    void hideInputMethodMenu() {
+        synchronized (mMethodMap) {
+            hideInputMethodMenuLocked();
+        }
+    }
+
+    void hideInputMethodMenuLocked() {
+        if (DEBUG) Slog.v(TAG, "Hide switching menu");
+
+        if (mSwitchingDialog != null) {
+            mSwitchingDialog.dismiss();
+            mSwitchingDialog = null;
+            mSwitchingDialogTitleView = null;
+        }
+
+        mService.updateSystemUiLocked();
+        mDialogBuilder = null;
+        mIms = null;
+    }
+
+    HardKeyboardListener getHardKeyboardListener() {
+        return mHardKeyboardListener;
+    }
+
+    AlertDialog getSwitchingDialogLocked() {
+        return mSwitchingDialog;
+    }
+
+    boolean getShowImeWithHardKeyboard() {
+        return mShowImeWithHardKeyboard;
+    }
+
+    boolean isisInputMethodPickerShownForTestLocked() {
+        if (mSwitchingDialog == null) {
+            return false;
+        }
+        return mSwitchingDialog.isShowing();
+    }
+
+    class HardKeyboardListener implements WindowManagerInternal.OnHardKeyboardStatusChangeListener {
+        @Override
+        public void onHardKeyboardStatusChange(boolean available) {
+            mHandler.sendMessage(mHandler.obtainMessage(MSG_HARD_KEYBOARD_SWITCH_CHANGED,
+                    available ? 1 : 0));
+        }
+
+        public void handleHardKeyboardStatusChange(boolean available) {
+            if (DEBUG) {
+                Slog.w(TAG, "HardKeyboardStatusChanged: available=" + available);
+            }
+            synchronized (mMethodMap) {
+                if (mSwitchingDialog != null && mSwitchingDialogTitleView != null
+                        && mSwitchingDialog.isShowing()) {
+                    mSwitchingDialogTitleView.findViewById(
+                            com.android.internal.R.id.hard_keyboard_section).setVisibility(
+                            available ? View.VISIBLE : View.GONE);
+                }
+            }
+        }
+    }
+
+    private static class ImeSubtypeListAdapter extends ArrayAdapter<ImeSubtypeListItem> {
+        private final LayoutInflater mInflater;
+        private final int mTextViewResourceId;
+        private final List<ImeSubtypeListItem> mItemsList;
+        public int mCheckedItem;
+        private ImeSubtypeListAdapter(Context context, int textViewResourceId,
+                List<ImeSubtypeListItem> itemsList, int checkedItem) {
+            super(context, textViewResourceId, itemsList);
+
+            mTextViewResourceId = textViewResourceId;
+            mItemsList = itemsList;
+            mCheckedItem = checkedItem;
+            mInflater = LayoutInflater.from(context);
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            final View view = convertView != null ? convertView
+                    : mInflater.inflate(mTextViewResourceId, null);
+            if (position < 0 || position >= mItemsList.size()) return view;
+            final ImeSubtypeListItem item = mItemsList.get(position);
+            final CharSequence imeName = item.mImeName;
+            final CharSequence subtypeName = item.mSubtypeName;
+            final TextView firstTextView = view.findViewById(android.R.id.text1);
+            final TextView secondTextView = view.findViewById(android.R.id.text2);
+            if (TextUtils.isEmpty(subtypeName)) {
+                firstTextView.setText(imeName);
+                secondTextView.setVisibility(View.GONE);
+            } else {
+                firstTextView.setText(subtypeName);
+                secondTextView.setText(imeName);
+                secondTextView.setVisibility(View.VISIBLE);
+            }
+            final RadioButton radioButton = view.findViewById(com.android.internal.R.id.radio);
+            radioButton.setChecked(position == mCheckedItem);
+            return view;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/location/AbstractLocationProvider.java b/services/core/java/com/android/server/location/AbstractLocationProvider.java
index 56982a8..bd2676e 100644
--- a/services/core/java/com/android/server/location/AbstractLocationProvider.java
+++ b/services/core/java/com/android/server/location/AbstractLocationProvider.java
@@ -222,7 +222,7 @@
         // we know that we only updated the state, so the listener for the old state is the same as
         // the listener for the new state.
         if (oldInternalState.listener != null) {
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 oldInternalState.listener.onStateChanged(oldInternalState.state, newState);
             } finally {
@@ -246,7 +246,7 @@
         // we know that we only updated the state, so the listener for the old state is the same as
         // the listener for the new state.
         if (oldInternalState.listener != null) {
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 oldInternalState.listener.onStateChanged(oldInternalState.state, newState);
             } finally {
@@ -305,7 +305,7 @@
     protected void reportLocation(Location location) {
         Listener listener = mInternalState.get().listener;
         if (listener != null) {
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 // copy location so if provider makes further changes they do not propagate
                 listener.onReportLocation(new Location(location));
@@ -321,7 +321,7 @@
     protected void reportLocation(List<Location> locations) {
         Listener listener = mInternalState.get().listener;
         if (listener != null) {
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 // copy location so if provider makes further changes they do not propagate
                 ArrayList<Location> copy = new ArrayList<>(locations.size());
diff --git a/services/core/java/com/android/server/location/ContextHubService.java b/services/core/java/com/android/server/location/ContextHubService.java
index 264c611..7a2e4ed 100644
--- a/services/core/java/com/android/server/location/ContextHubService.java
+++ b/services/core/java/com/android/server/location/ContextHubService.java
@@ -17,7 +17,10 @@
 package com.android.server.location;
 
 import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.database.ContentObserver;
 import android.hardware.contexthub.V1_0.AsyncEventType;
 import android.hardware.contexthub.V1_0.ContextHub;
@@ -26,8 +29,6 @@
 import android.hardware.contexthub.V1_0.IContexthubCallback;
 import android.hardware.contexthub.V1_0.Result;
 import android.hardware.contexthub.V1_0.TransactionResult;
-import android.hardware.contexthub.V1_1.Setting;
-import android.hardware.contexthub.V1_1.SettingValue;
 import android.hardware.location.ContextHubInfo;
 import android.hardware.location.ContextHubMessage;
 import android.hardware.location.ContextHubTransaction;
@@ -43,6 +44,7 @@
 import android.hardware.location.NanoAppMessage;
 import android.hardware.location.NanoAppState;
 import android.location.LocationManager;
+import android.net.wifi.WifiManager;
 import android.os.Binder;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
@@ -111,6 +113,12 @@
     // The manager for the internal nanoapp state cache
     private final NanoAppStateManager mNanoAppStateManager = new NanoAppStateManager();
 
+    // True if WiFi is available for the Context Hub
+    private boolean mIsWifiAvailable = false;
+
+    // Lock object for sendWifiSettingUpdate()
+    private final Object mSendWifiSettingUpdateLock = new Object();
+
     /**
      * Class extending the callback to register with a Context Hub.
      */
@@ -196,7 +204,7 @@
         }
         mDefaultClientMap = Collections.unmodifiableMap(defaultClientMap);
 
-        if (mContextHubWrapper.supportsSettingNotifications()) {
+        if (mContextHubWrapper.supportsLocationSettingNotifications()) {
             sendLocationSettingUpdate();
             mContext.getContentResolver().registerContentObserver(
                     Settings.Secure.getUriFor(Settings.Secure.LOCATION_MODE),
@@ -208,6 +216,45 @@
                         }
                     }, UserHandle.USER_ALL);
         }
+
+        if (mContextHubWrapper.supportsWifiSettingNotifications()) {
+            sendWifiSettingUpdate(true /* forceUpdate */);
+
+            BroadcastReceiver wifiReceiver = new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(intent.getAction())) {
+                        sendWifiSettingUpdate(false /* forceUpdate */);
+                    }
+                }
+            };
+            IntentFilter filter = new IntentFilter();
+            filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+            mContext.registerReceiver(wifiReceiver, filter);
+
+            mContext.getContentResolver().registerContentObserver(
+                    Settings.Global.getUriFor(Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE),
+                    true /* notifyForDescendants */,
+                    new ContentObserver(null /* handler */) {
+                        @Override
+                        public void onChange(boolean selfChange) {
+                            sendWifiSettingUpdate(false /* forceUpdate */);
+                        }
+                    }, UserHandle.USER_ALL);
+        }
+
+        if (mContextHubWrapper.supportsAirplaneModeSettingNotifications()) {
+            sendAirplaneModeSettingUpdate();
+            mContext.getContentResolver().registerContentObserver(
+                    Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON),
+                    true /* notifyForDescendants */,
+                    new ContentObserver(null /* handler */) {
+                        @Override
+                        public void onChange(boolean selfChange) {
+                            sendAirplaneModeSettingUpdate();
+                        }
+                    }, UserHandle.USER_ALL);
+        }
     }
 
     /**
@@ -260,7 +307,10 @@
      * @return the IContextHubWrapper interface
      */
     private IContextHubWrapper getContextHubWrapper() {
-        IContextHubWrapper wrapper = IContextHubWrapper.maybeConnectTo1_1();
+        IContextHubWrapper wrapper = IContextHubWrapper.maybeConnectTo1_2();
+        if (wrapper == null) {
+            wrapper = IContextHubWrapper.maybeConnectTo1_1();
+        }
         if (wrapper == null) {
             wrapper = IContextHubWrapper.maybeConnectTo1_0();
         }
@@ -454,7 +504,6 @@
      *
      * @param contextHubId the ID of the hub to do the query
      * @return the result of the query
-     *
      * @throws IllegalStateException if the transaction queue is full
      */
     private int queryNanoAppsInternal(int contextHubId) {
@@ -528,7 +577,6 @@
 
     /**
      * A helper function to handle a load response from the Context Hub for the old API.
-     *
      * TODO(b/69270990): Remove this once the old APIs are obsolete.
      */
     private void handleLoadResponseOldApi(
@@ -578,6 +626,8 @@
     private void handleHubEventCallback(int contextHubId, int eventType) {
         if (eventType == AsyncEventType.RESTARTED) {
             sendLocationSettingUpdate();
+            sendWifiSettingUpdate(true /* forceUpdate */);
+            sendAirplaneModeSettingUpdate();
 
             mTransactionManager.onHubReset();
             queryNanoAppsInternal(contextHubId);
@@ -628,10 +678,9 @@
      * @param contextHubId   the ID of the hub this client is attached to
      * @param clientCallback the client interface to register with the service
      * @return the generated client interface, null if registration was unsuccessful
-     *
      * @throws IllegalArgumentException if contextHubId is not a valid ID
-     * @throws IllegalStateException if max number of clients have already registered
-     * @throws NullPointerException if clientCallback is null
+     * @throws IllegalStateException    if max number of clients have already registered
+     * @throws NullPointerException     if clientCallback is null
      */
     @Override
     public IContextHubClient createClient(
@@ -655,7 +704,6 @@
      * @param pendingIntent the PendingIntent associated with this client
      * @param nanoAppId     the ID of the nanoapp PendingIntent events will be sent for
      * @return the generated client interface
-     *
      * @throws IllegalArgumentException if hubInfo does not represent a valid hub
      * @throws IllegalStateException    if there were too many registered clients at the service
      */
@@ -677,7 +725,6 @@
      * @param contextHubId        the ID of the hub to load the binary
      * @param transactionCallback the client-facing transaction callback interface
      * @param nanoAppBinary       the binary to load
-     *
      * @throws IllegalStateException if the transaction queue is full
      */
     @Override
@@ -707,7 +754,6 @@
      * @param contextHubId        the ID of the hub to unload the nanoapp
      * @param transactionCallback the client-facing transaction callback interface
      * @param nanoAppId           the ID of the nanoapp to unload
-     *
      * @throws IllegalStateException if the transaction queue is full
      */
     @Override
@@ -731,7 +777,6 @@
      * @param contextHubId        the ID of the hub to enable the nanoapp
      * @param transactionCallback the client-facing transaction callback interface
      * @param nanoAppId           the ID of the nanoapp to enable
-     *
      * @throws IllegalStateException if the transaction queue is full
      */
     @Override
@@ -755,7 +800,6 @@
      * @param contextHubId        the ID of the hub to disable the nanoapp
      * @param transactionCallback the client-facing transaction callback interface
      * @param nanoAppId           the ID of the nanoapp to disable
-     *
      * @throws IllegalStateException if the transaction queue is full
      */
     @Override
@@ -778,7 +822,6 @@
      *
      * @param contextHubId        the ID of the hub to query
      * @param transactionCallback the client-facing transaction callback interface
-     *
      * @throws IllegalStateException if the transaction queue is full
      */
     @Override
@@ -889,7 +932,6 @@
      * @param contextHubId    the ID of the hub to start the transaction
      * @param callback        the client transaction callback interface
      * @param transactionType the type of the transaction
-     *
      * @return {@code true} if mContextHubWrapper and contextHubId is valid, {@code false} otherwise
      */
     private boolean checkHalProxyAndContextHubId(
@@ -920,14 +962,40 @@
     }
 
     /**
-     * Obtains the latest location setting value and notifies the Contexthub.
+     * Obtains the latest location setting value and notifies the Context Hub.
      */
     private void sendLocationSettingUpdate() {
         boolean enabled = mContext.getSystemService(LocationManager.class)
                 .isLocationEnabledForUser(UserHandle.CURRENT);
+        mContextHubWrapper.onLocationSettingChanged(enabled);
+    }
 
-        mContextHubWrapper.onSettingChanged(Setting.LOCATION,
-                enabled ? SettingValue.ENABLED : SettingValue.DISABLED);
+    /**
+     * Obtains the latest WiFi availability setting value and notifies the Context Hub.
+     *
+     * @param forceUpdate True to force send update to the Context Hub, otherwise only send the
+     *                    update when the WiFi availability changes.
+     */
+    private void sendWifiSettingUpdate(boolean forceUpdate) {
+        synchronized (mSendWifiSettingUpdateLock) {
+            WifiManager wifiManager = mContext.getSystemService(WifiManager.class);
+            boolean enabled = wifiManager.isWifiEnabled() || wifiManager.isScanAlwaysAvailable();
+            if (forceUpdate || mIsWifiAvailable != enabled) {
+                mIsWifiAvailable = enabled;
+                mContextHubWrapper.onWifiSettingChanged(enabled);
+            }
+        }
+    }
+
+    /**
+     * Obtains the latest airplane mode setting value and notifies the Context Hub.
+     */
+    private void sendAirplaneModeSettingUpdate() {
+        boolean enabled =
+                (Settings.Global.getInt(mContext.getContentResolver(),
+                        Settings.Global.AIRPLANE_MODE_ON, 0)
+                        == 1);
+        mContextHubWrapper.onAirplaneModeSettingChanged(enabled);
     }
 
     private String getCallingPackageName() {
diff --git a/services/core/java/com/android/server/location/IContextHubWrapper.java b/services/core/java/com/android/server/location/IContextHubWrapper.java
index 79fa5c7..9ac7c6b 100644
--- a/services/core/java/com/android/server/location/IContextHubWrapper.java
+++ b/services/core/java/com/android/server/location/IContextHubWrapper.java
@@ -45,12 +45,7 @@
             Log.i(TAG, "Context Hub HAL service not found");
         }
 
-        ContextHubWrapperV1_0 wrapper = null;
-        if (proxy != null) {
-            wrapper = new ContextHubWrapperV1_0(proxy);
-        }
-
-        return wrapper;
+        return (proxy == null) ? null : new ContextHubWrapperV1_0(proxy);
     }
 
     /**
@@ -69,12 +64,26 @@
             Log.i(TAG, "Context Hub HAL service not found");
         }
 
-        ContextHubWrapperV1_1 wrapper = null;
-        if (proxy != null) {
-            wrapper = new ContextHubWrapperV1_1(proxy);
+        return (proxy == null) ? null : new ContextHubWrapperV1_1(proxy);
+    }
+
+    /**
+     * Attempts to connect to the Contexthub HAL 1.2 service, if it exists.
+     *
+     * @return A valid IContextHubWrapper if the connection was successful, null otherwise.
+     */
+    @Nullable
+    public static IContextHubWrapper maybeConnectTo1_2() {
+        android.hardware.contexthub.V1_2.IContexthub proxy = null;
+        try {
+            proxy = android.hardware.contexthub.V1_2.IContexthub.getService(true /* retry */);
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException while attaching to Context Hub HAL proxy", e);
+        } catch (NoSuchElementException e) {
+            Log.i(TAG, "Context Hub HAL service not found");
         }
 
-        return wrapper;
+        return (proxy == null) ? null : new ContextHubWrapperV1_2(proxy);
     }
 
     /**
@@ -83,19 +92,42 @@
     public abstract android.hardware.contexthub.V1_0.IContexthub getHub();
 
     /**
-     * @return True if this version of the Contexthub HAL supports setting notifications.
+     * @return True if this version of the Contexthub HAL supports Location setting notifications.
      */
-    public abstract boolean supportsSettingNotifications();
+    public abstract boolean supportsLocationSettingNotifications();
 
     /**
-     * Notifies the Contexthub implementation of a user setting change.
+     * Notifies the Contexthub implementation of a user Location setting change.
      *
-     * @param setting The user setting that has changed. MUST be one of the values from the
-     *     {@link Setting} enum
-     * @param newValue The value of the user setting that changed. MUST be one of the values
-     *     from the {@link SettingValue} enum.
+     * @param enabled True if the Location setting has been enabled.
      */
-    public abstract void onSettingChanged(byte setting, byte newValue);
+    public abstract void onLocationSettingChanged(boolean enabled);
+
+    /**
+     * @return True if this version of the Contexthub HAL supports WiFi availability setting
+     * notifications.
+     */
+    public abstract boolean supportsWifiSettingNotifications();
+
+    /**
+     * Notifies the Contexthub implementation of a user WiFi availability setting change.
+     *
+     * @param enabled true if the WiFi availability setting has been enabled.
+     */
+    public abstract void onWifiSettingChanged(boolean enabled);
+
+    /**
+     * @return True if this version of the Contexthub HAL supports airplane mode setting
+     * notifications.
+     */
+    public abstract boolean supportsAirplaneModeSettingNotifications();
+
+    /**
+     * Notifies the Contexthub implementation of an airplane mode setting change.
+     *
+     * @param enabled true if the airplane mode setting has been enabled.
+     */
+    public abstract void onAirplaneModeSettingChanged(boolean enabled);
 
     private static class ContextHubWrapperV1_0 extends IContextHubWrapper {
         private android.hardware.contexthub.V1_0.IContexthub mHub;
@@ -108,11 +140,26 @@
             return mHub;
         }
 
-        public boolean supportsSettingNotifications() {
+        public boolean supportsLocationSettingNotifications() {
             return false;
         }
 
-        public void onSettingChanged(byte setting, byte newValue) {}
+        public boolean supportsWifiSettingNotifications() {
+            return false;
+        }
+
+        public boolean supportsAirplaneModeSettingNotifications() {
+            return false;
+        }
+
+        public void onLocationSettingChanged(boolean enabled) {
+        }
+
+        public void onWifiSettingChanged(boolean enabled) {
+        }
+
+        public void onAirplaneModeSettingChanged(boolean enabled) {
+        }
     }
 
     private static class ContextHubWrapperV1_1 extends IContextHubWrapper {
@@ -126,14 +173,76 @@
             return mHub;
         }
 
-        public boolean supportsSettingNotifications() {
+        public boolean supportsLocationSettingNotifications() {
             return true;
         }
 
-        public void onSettingChanged(byte setting, byte newValue) {
+        public boolean supportsWifiSettingNotifications() {
+            return false;
+        }
+
+        public boolean supportsAirplaneModeSettingNotifications() {
+            return false;
+        }
+
+        public void onLocationSettingChanged(boolean enabled) {
             try {
-                mHub.onSettingChanged(setting, newValue);
-            } catch  (RemoteException e) {
+                mHub.onSettingChanged(Setting.LOCATION,
+                        enabled ? SettingValue.ENABLED : SettingValue.DISABLED);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to send setting change to Contexthub", e);
+            }
+        }
+
+        public void onWifiSettingChanged(boolean enabled) {
+        }
+
+        public void onAirplaneModeSettingChanged(boolean enabled) {
+        }
+    }
+
+    private static class ContextHubWrapperV1_2 extends IContextHubWrapper {
+        private android.hardware.contexthub.V1_2.IContexthub mHub;
+
+        ContextHubWrapperV1_2(android.hardware.contexthub.V1_2.IContexthub hub) {
+            mHub = hub;
+        }
+
+        public android.hardware.contexthub.V1_0.IContexthub getHub() {
+            return mHub;
+        }
+
+        public boolean supportsLocationSettingNotifications() {
+            return true;
+        }
+
+        public boolean supportsWifiSettingNotifications() {
+            return true;
+        }
+
+        public boolean supportsAirplaneModeSettingNotifications() {
+            return true;
+        }
+
+        public void onLocationSettingChanged(boolean enabled) {
+            sendSettingChanged(Setting.LOCATION,
+                    enabled ? SettingValue.ENABLED : SettingValue.DISABLED);
+        }
+
+        public void onWifiSettingChanged(boolean enabled) {
+            sendSettingChanged(android.hardware.contexthub.V1_2.Setting.WIFI_AVAILABLE,
+                    enabled ? SettingValue.ENABLED : SettingValue.DISABLED);
+        }
+
+        public void onAirplaneModeSettingChanged(boolean enabled) {
+            sendSettingChanged(android.hardware.contexthub.V1_2.Setting.AIRPLANE_MODE,
+                    enabled ? SettingValue.ENABLED : SettingValue.DISABLED);
+        }
+
+        private void sendSettingChanged(byte setting, byte newValue) {
+            try {
+                mHub.onSettingChanged_1_2(setting, newValue);
+            } catch (RemoteException e) {
                 Log.e(TAG, "Failed to send setting change to Contexthub", e);
             }
         }
diff --git a/services/core/java/com/android/server/location/LocationFudger.java b/services/core/java/com/android/server/location/LocationFudger.java
index 6f35c8b..ecdd429 100644
--- a/services/core/java/com/android/server/location/LocationFudger.java
+++ b/services/core/java/com/android/server/location/LocationFudger.java
@@ -111,7 +111,7 @@
      */
     public Location createCoarse(Location fine) {
         synchronized (this) {
-            if (fine == mCachedFineLocation) {
+            if (fine == mCachedFineLocation || fine == mCachedCoarseLocation) {
                 return mCachedCoarseLocation;
             }
         }
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 227cdee..cdb73d8 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -17,12 +17,14 @@
 package com.android.server.location;
 
 import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.app.compat.CompatChanges.isChangeEnabled;
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
 import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.location.LocationManager.FUSED_PROVIDER;
 import static android.location.LocationManager.GPS_PROVIDER;
 import static android.location.LocationManager.NETWORK_PROVIDER;
+import static android.location.LocationManager.PREVENT_PENDING_INTENT_SYSTEM_API_USAGE;
 import static android.location.LocationRequest.LOW_POWER_EXCEPTIONS;
 
 import static com.android.server.location.LocationPermissions.PERMISSION_COARSE;
@@ -75,7 +77,6 @@
 import android.stats.location.LocationStatsEnums;
 import android.util.IndentingPrintWriter;
 import android.util.Log;
-import android.util.TimeUtils;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.location.ProviderProperties;
@@ -215,7 +216,7 @@
     private final LocalService mLocalService;
 
     private final GeofenceManager mGeofenceManager;
-    @Nullable private volatile GnssManagerService mGnssManagerService = null;
+    private volatile @Nullable GnssManagerService mGnssManagerService = null;
     private GeocoderProxy mGeocodeProvider;
 
     @GuardedBy("mLock")
@@ -570,7 +571,7 @@
                     new IllegalArgumentException());
         }
 
-        request = validateLocationRequest(request);
+        request = validateLocationRequest(request, identity);
 
         LocationProviderManager manager = getLocationProviderManager(provider);
         Preconditions.checkArgument(manager != null,
@@ -592,7 +593,22 @@
         // clients in the system process must have an attribution tag set
         Preconditions.checkArgument(identity.getPid() != Process.myPid() || attributionTag != null);
 
-        request = validateLocationRequest(request);
+        // pending intents requests may not use system apis because we do not keep track if clients
+        // lose the relevant permissions, and thus should not get the benefit of those apis. its
+        // simplest to ensure these apis are simply never set for pending intent requests. the same
+        // does not apply for listener requests since those will have the process (including the
+        // listener) killed on permission removal
+        boolean usesSystemApi = request.isLowPower()
+                || request.isHiddenFromAppOps()
+                || request.isLocationSettingsIgnored()
+                || !request.getWorkSource().isEmpty();
+        if (usesSystemApi
+                && isChangeEnabled(PREVENT_PENDING_INTENT_SYSTEM_API_USAGE, identity.getUid())) {
+            throw new SecurityException(
+                    "PendingIntent location requests may not use system APIs: " + request);
+        }
+
+        request = validateLocationRequest(request, identity);
 
         LocationProviderManager manager = getLocationProviderManager(provider);
         Preconditions.checkArgument(manager != null,
@@ -601,9 +617,9 @@
         manager.registerLocationRequest(request, identity, permissionLevel, pendingIntent);
     }
 
-    private LocationRequest validateLocationRequest(LocationRequest request) {
-        WorkSource workSource = request.getWorkSource();
-        if (workSource != null && !workSource.isEmpty()) {
+    private LocationRequest validateLocationRequest(LocationRequest request,
+            CallerIdentity identity) {
+        if (!request.getWorkSource().isEmpty()) {
             mContext.enforceCallingOrSelfPermission(
                     permission.UPDATE_DEVICE_STATS,
                     "setting a work source requires " + permission.UPDATE_DEVICE_STATS);
@@ -634,23 +650,25 @@
             }
         }
 
-        if (request.getWorkSource() != null) {
-            if (request.getWorkSource().isEmpty()) {
-                sanitized.setWorkSource(null);
-            } else if (request.getWorkSource().getPackageName(0) == null) {
-                Log.w(TAG, "received (and ignoring) illegal worksource with no package name");
-                sanitized.setWorkSource(null);
-            } else {
-                List<WorkChain> workChains = request.getWorkSource().getWorkChains();
-                if (workChains != null && !workChains.isEmpty() && workChains.get(
-                        0).getAttributionTag() == null) {
-                    Log.w(TAG,
-                            "received (and ignoring) illegal worksource with no attribution tag");
-                    sanitized.setWorkSource(null);
-                }
+        WorkSource workSource = new WorkSource(request.getWorkSource());
+        if (workSource.size() > 0 && workSource.getPackageName(0) == null) {
+            Log.w(TAG, "received (and ignoring) illegal worksource with no package name");
+            workSource.clear();
+        } else {
+            List<WorkChain> workChains = workSource.getWorkChains();
+            if (workChains != null && !workChains.isEmpty()
+                    && workChains.get(0).getAttributionTag() == null) {
+                Log.w(TAG,
+                        "received (and ignoring) illegal worksource with no attribution tag");
+                workSource.clear();
             }
         }
 
+        if (workSource.isEmpty()) {
+            identity.addToWorkSource(workSource);
+        }
+        sanitized.setWorkSource(workSource);
+
         return sanitized.build();
     }
 
@@ -684,15 +702,7 @@
             return null;
         }
 
-        Location location = manager.getLastLocation(identity, permissionLevel, false);
-
-        // lastly - note app ops
-        if (!mInjector.getAppOpsHelper().noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel),
-                identity)) {
-            return null;
-        }
-
-        return location;
+        return manager.getLastLocation(identity, permissionLevel, false);
     }
 
     @Nullable
@@ -710,7 +720,7 @@
         // clients in the system process must have an attribution tag set
         Preconditions.checkState(identity.getPid() != Process.myPid() || attributionTag != null);
 
-        request = validateLocationRequest(request);
+        request = validateLocationRequest(request, identity);
 
         LocationProviderManager manager = getLocationProviderManager(provider);
         Preconditions.checkArgument(manager != null,
@@ -727,7 +737,6 @@
                 return null;
             }
 
-            // use fine permission level to avoid creating unnecessary coarse locations
             Location location = gpsManager.getLastLocationUnsafe(UserHandle.USER_ALL,
                     PERMISSION_FINE, false, Long.MAX_VALUE);
             if (location == null) {
@@ -1083,19 +1092,6 @@
     }
 
     @Override
-    @NonNull
-    public List<LocationRequest> getTestProviderCurrentRequests(String provider) {
-        mContext.enforceCallingOrSelfPermission(permission.READ_DEVICE_CONFIG, null);
-
-        LocationProviderManager manager = getLocationProviderManager(provider);
-        if (manager == null) {
-            throw new IllegalArgumentException("provider doesn't exist: " + provider);
-        }
-
-        return manager.getMockProviderRequests();
-    }
-
-    @Override
     public int handleShellCommand(ParcelFileDescriptor in, ParcelFileDescriptor out,
             ParcelFileDescriptor err, String[] args) {
         return new LocationShellCommand(this).exec(
@@ -1116,9 +1112,8 @@
             return;
         }
 
-        ipw.print("Location Manager State:");
+        ipw.println("Location Manager State:");
         ipw.increaseIndent();
-        ipw.println("Elapsed Realtime: " + TimeUtils.formatDuration(SystemClock.elapsedRealtime()));
 
         ipw.println("User Info:");
         ipw.increaseIndent();
diff --git a/services/core/java/com/android/server/location/LocationProviderManager.java b/services/core/java/com/android/server/location/LocationProviderManager.java
index 48f8da4..179fb7d 100644
--- a/services/core/java/com/android/server/location/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/LocationProviderManager.java
@@ -23,14 +23,12 @@
 import static android.location.LocationManager.KEY_LOCATION_CHANGED;
 import static android.location.LocationManager.KEY_PROVIDER_ENABLED;
 import static android.location.LocationManager.PASSIVE_PROVIDER;
-import static android.location.LocationRequest.PASSIVE_INTERVAL;
 import static android.os.IPowerManager.LOCATION_MODE_NO_CHANGE;
 import static android.os.PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF;
 import static android.os.PowerManager.LOCATION_MODE_FOREGROUND_ONLY;
 import static android.os.PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF;
 import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF;
 
-import static com.android.internal.location.ProviderRequest.EMPTY_REQUEST;
 import static com.android.server.location.LocationManagerService.D;
 import static com.android.server.location.LocationManagerService.TAG;
 import static com.android.server.location.LocationPermissions.PERMISSION_COARSE;
@@ -238,8 +236,7 @@
     protected abstract class Registration extends RemoteListenerRegistration<LocationRequest,
             LocationTransport, LocationListenerOperation> {
 
-        @PermissionLevel protected final int mPermissionLevel;
-        private final WorkSource mWorkSource;
+        private final @PermissionLevel int mPermissionLevel;
 
         // we cache these values because checking/calculating on the fly is more expensive
         private boolean mPermitted;
@@ -247,22 +244,17 @@
         private LocationRequest mProviderLocationRequest;
         private boolean mIsUsingHighPower;
 
-        @Nullable private Location mLastLocation = null;
+        private @Nullable Location mLastLocation = null;
 
         protected Registration(LocationRequest request, CallerIdentity identity,
                 LocationTransport transport, @PermissionLevel int permissionLevel) {
             super(Objects.requireNonNull(request), identity, transport);
 
             Preconditions.checkArgument(permissionLevel > PERMISSION_NONE);
+            Preconditions.checkArgument(!request.getWorkSource().isEmpty());
+
             mPermissionLevel = permissionLevel;
-
-            if (request.getWorkSource() != null && !request.getWorkSource().isEmpty()) {
-                mWorkSource = request.getWorkSource();
-            } else {
-                mWorkSource = identity.addToWorkSource(null);
-            }
-
-            mProviderLocationRequest = super.getRequest();
+            mProviderLocationRequest = request;
         }
 
         @GuardedBy("mLock")
@@ -376,6 +368,10 @@
             return mLastLocation;
         }
 
+        public @PermissionLevel int getPermissionLevel() {
+            return mPermissionLevel;
+        }
+
         public final boolean isForeground() {
             return mForeground;
         }
@@ -389,10 +385,6 @@
             return LocationProviderManager.this;
         }
 
-        protected final WorkSource getWorkSource() {
-            return mWorkSource;
-        }
-
         @GuardedBy("mLock")
         private void onHighPowerUsageChanged() {
             boolean isUsingHighPower = isUsingHighPower();
@@ -508,14 +500,7 @@
             LocationRequest.Builder builder = new LocationRequest.Builder(baseRequest);
 
             if (mPermissionLevel < PERMISSION_FINE) {
-                switch (baseRequest.getQuality()) {
-                    case LocationRequest.ACCURACY_FINE:
-                        builder.setQuality(LocationRequest.ACCURACY_BLOCK);
-                        break;
-                    case LocationRequest.POWER_HIGH:
-                        builder.setQuality(LocationRequest.POWER_LOW);
-                        break;
-                }
+                builder.setQuality(LocationRequest.QUALITY_LOW_POWER);
                 if (baseRequest.getIntervalMillis() < MIN_COARSE_INTERVAL_MS) {
                     builder.setIntervalMillis(MIN_COARSE_INTERVAL_MS);
                 }
@@ -524,16 +509,18 @@
                 }
             }
 
-            if (baseRequest.isLocationSettingsIgnored()) {
+            boolean locationSettingsIgnored = baseRequest.isLocationSettingsIgnored();
+            if (locationSettingsIgnored) {
                 // if we are not currently allowed use location settings ignored, disable it
                 if (!mSettingsHelper.getIgnoreSettingsPackageWhitelist().contains(
                         getIdentity().getPackageName()) && !mLocationManagerInternal.isProvider(
                         null, getIdentity())) {
                     builder.setLocationSettingsIgnored(false);
+                    locationSettingsIgnored = false;
                 }
             }
 
-            if (!baseRequest.isLocationSettingsIgnored() && !isThrottlingExempt()) {
+            if (!locationSettingsIgnored && !isThrottlingExempt()) {
                 // throttle in the background
                 if (!mForeground) {
                     builder.setIntervalMillis(max(baseRequest.getIntervalMillis(),
@@ -607,7 +594,7 @@
             mWakeLock = Objects.requireNonNull(mContext.getSystemService(PowerManager.class))
                     .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
             mWakeLock.setReferenceCounted(true);
-            mWakeLock.setWorkSource(getWorkSource());
+            mWakeLock.setWorkSource(request.getWorkSource());
         }
 
         @Override
@@ -626,7 +613,7 @@
                 onAlarm();
             } else if (mExpirationRealtimeMs < Long.MAX_VALUE) {
                 mAlarmHelper.setDelayedAlarm(mExpirationRealtimeMs - registerTimeMs, this,
-                        getWorkSource());
+                        getRequest().getWorkSource());
             }
 
             // start listening for provider enabled/disabled events
@@ -688,7 +675,7 @@
                 if (maxLocationAgeMs > MIN_REQUEST_DELAY_MS) {
                     Location lastLocation = getLastLocationUnsafe(
                             getIdentity().getUserId(),
-                            PERMISSION_FINE, // acceptLocationChange() handles coarsening this
+                            getPermissionLevel(),
                             getRequest().isLocationSettingsIgnored(),
                             maxLocationAgeMs);
                     if (lastLocation != null) {
@@ -730,18 +717,8 @@
                 return null;
             }
 
-            Location location;
-            switch (mPermissionLevel) {
-                case PERMISSION_FINE:
-                    location = fineLocation;
-                    break;
-                case PERMISSION_COARSE:
-                    location = mLocationFudger.createCoarse(fineLocation);
-                    break;
-                default:
-                    // shouldn't be possible to have a client added without location permissions
-                    throw new AssertionError();
-            }
+            Location location = Objects.requireNonNull(
+                    getPermittedLocation(fineLocation, getPermissionLevel()));
 
             Location lastDeliveredLocation = getLastDeliveredLocation();
             if (lastDeliveredLocation != null) {
@@ -763,7 +740,7 @@
             }
 
             // note app ops
-            if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(mPermissionLevel),
+            if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(getPermissionLevel()),
                     getIdentity())) {
                 if (D) {
                     Log.w(TAG, "noteOp denied for " + getIdentity());
@@ -960,7 +937,7 @@
     }
 
     protected final class GetCurrentLocationListenerRegistration extends Registration implements
-            IBinder.DeathRecipient, ProviderEnabledListener, OnAlarmListener {
+            IBinder.DeathRecipient, OnAlarmListener {
 
         private volatile LocationTransport mTransport;
 
@@ -972,11 +949,6 @@
             mTransport = transport;
         }
 
-        @GuardedBy("mLock")
-        void deliverLocation(@Nullable Location location) {
-            executeSafely(getExecutor(), () -> mTransport, acceptLocationChange(location));
-        }
-
         @Override
         protected void onListenerUnregister() {
             mTransport = null;
@@ -999,29 +971,13 @@
                 onAlarm();
             } else if (mExpirationRealtimeMs < Long.MAX_VALUE) {
                 mAlarmHelper.setDelayedAlarm(mExpirationRealtimeMs - registerTimeMs, this,
-                        getWorkSource());
-            }
-
-            // if this request is ignoring location settings, then we don't want to immediately fail
-            // it if the provider is disabled or becomes disabled.
-            if (!getRequest().isLocationSettingsIgnored()) {
-                // start listening for provider enabled/disabled events
-                addEnabledListener(this);
-
-                // if the provider is currently disabled fail immediately
-                int userId = getIdentity().getUserId();
-                if (!getRequest().isLocationSettingsIgnored() && !isEnabled(userId)) {
-                    deliverLocation(null);
-                }
+                        getRequest().getWorkSource());
             }
         }
 
         @GuardedBy("mLock")
         @Override
         protected void onProviderListenerUnregister() {
-            // stop listening for provider enabled/disabled events
-            removeEnabledListener(this);
-
             // remove alarm for expiration
             if (mExpirationRealtimeMs < Long.MAX_VALUE) {
                 mAlarmHelper.cancel(this);
@@ -1030,6 +986,38 @@
             ((IBinder) getKey()).unlinkToDeath(this, 0);
         }
 
+        @GuardedBy("mLock")
+        @Override
+        protected LocationListenerOperation onProviderListenerActive() {
+            Location lastLocation = getLastLocationUnsafe(
+                    getIdentity().getUserId(),
+                    getPermissionLevel(),
+                    getRequest().isLocationSettingsIgnored(),
+                    MAX_CURRENT_LOCATION_AGE_MS);
+            if (lastLocation != null) {
+                return acceptLocationChange(lastLocation);
+            }
+
+            return null;
+        }
+
+        @GuardedBy("mLock")
+        @Override
+        protected LocationListenerOperation onProviderListenerInactive() {
+            if (!getRequest().isLocationSettingsIgnored()) {
+                // if we go inactive for any reason, fail immediately
+                return acceptLocationChange(null);
+            }
+
+            return null;
+        }
+
+        void deliverNull() {
+            synchronized (mLock) {
+                executeSafely(getExecutor(), () -> mTransport, acceptLocationChange(null));
+            }
+        }
+
         @Override
         public void onAlarm() {
             if (D) {
@@ -1039,9 +1027,10 @@
             }
 
             synchronized (mLock) {
-                deliverLocation(null);
                 // no need to remove alarm after it's fired
                 mExpirationRealtimeMs = Long.MAX_VALUE;
+
+                deliverNull();
             }
         }
 
@@ -1060,29 +1049,16 @@
             }
 
             // lastly - note app ops
-            Location location;
-            if (fineLocation == null) {
-                location = null;
-            } else if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(mPermissionLevel),
+            if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(getPermissionLevel()),
                     getIdentity())) {
                 if (D) {
                     Log.w(TAG, "noteOp denied for " + getIdentity());
                 }
-                location = null;
-            } else {
-                switch (mPermissionLevel) {
-                    case PERMISSION_FINE:
-                        location = fineLocation;
-                        break;
-                    case PERMISSION_COARSE:
-                        location = mLocationFudger.createCoarse(fineLocation);
-                        break;
-                    default:
-                        // shouldn't be possible to have a client added without location permissions
-                        throw new AssertionError();
-                }
+                fineLocation = null;
             }
 
+            Location location = getPermittedLocation(fineLocation, getPermissionLevel());
+
             return new LocationListenerOperation() {
                 @Override
                 public Location getLocation() {
@@ -1118,22 +1094,6 @@
         }
 
         @Override
-        public void onProviderEnabledChanged(String provider, int userId, boolean enabled) {
-            Preconditions.checkState(mName.equals(provider));
-
-            if (userId != getIdentity().getUserId()) {
-                return;
-            }
-
-            // if the provider is disabled we give up on current location immediately
-            if (!getRequest().isLocationSettingsIgnored() && !enabled) {
-                synchronized (mLock) {
-                    deliverLocation(null);
-                }
-            }
-        }
-
-        @Override
         public void binderDied() {
             try {
                 if (D) {
@@ -1172,7 +1132,7 @@
 
     protected final LocationManagerInternal mLocationManagerInternal;
     protected final SettingsHelper mSettingsHelper;
-    protected final UserInfoHelper mUserInfoHelper;
+    protected final UserInfoHelper mUserHelper;
     protected final AlarmHelper mAlarmHelper;
     protected final AppOpsHelper mAppOpsHelper;
     protected final LocationPermissionsHelper mLocationPermissionsHelper;
@@ -1234,7 +1194,7 @@
         mLocationManagerInternal = Objects.requireNonNull(
                 LocalServices.getService(LocationManagerInternal.class));
         mSettingsHelper = injector.getSettingsHelper();
-        mUserInfoHelper = injector.getUserInfoHelper();
+        mUserHelper = injector.getUserInfoHelper();
         mAlarmHelper = injector.getAlarmHelper();
         mAppOpsHelper = injector.getAppOpsHelper();
         mLocationPermissionsHelper = injector.getLocationPermissionsHelper();
@@ -1259,10 +1219,10 @@
         synchronized (mLock) {
             mStarted = true;
 
-            mUserInfoHelper.addListener(mUserChangedListener);
+            mUserHelper.addListener(mUserChangedListener);
             mSettingsHelper.addOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
 
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 // initialize enabled state
                 onUserStarted(UserHandle.USER_ALL);
@@ -1274,20 +1234,19 @@
 
     public void stopManager() {
         synchronized (mLock) {
-            mUserInfoHelper.removeListener(mUserChangedListener);
+            mUserHelper.removeListener(mUserChangedListener);
             mSettingsHelper.removeOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
 
-            // notify and remove all listeners
-            long identity = Binder.clearCallingIdentity();
+            mStarted = false;
+
+            final long identity = Binder.clearCallingIdentity();
             try {
-                onUserStopped(UserHandle.USER_ALL);
+                onEnabledChanged(UserHandle.USER_ALL);
                 removeRegistrationIf(key -> true);
+                mEnabledListeners.clear();
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
-
-            mEnabledListeners.clear();
-            mStarted = false;
         }
     }
 
@@ -1348,7 +1307,7 @@
         synchronized (mLock) {
             Preconditions.checkState(mStarted);
 
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 mProvider.setRealProvider(provider);
             } finally {
@@ -1363,7 +1322,7 @@
 
             mLocationEventLog.logProviderMocked(mName, provider != null);
 
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 mProvider.setMockProvider(provider);
             } finally {
@@ -1390,7 +1349,7 @@
                 throw new IllegalArgumentException(mName + " provider is not a test provider");
             }
 
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 mProvider.setMockProviderAllowed(enabled);
             } finally {
@@ -1405,7 +1364,7 @@
                 throw new IllegalArgumentException(mName + " provider is not a test provider");
             }
 
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 String locationProvider = location.getProvider();
                 if (!TextUtils.isEmpty(locationProvider) && !mName.equals(locationProvider)) {
@@ -1422,16 +1381,6 @@
         }
     }
 
-    public List<LocationRequest> getMockProviderRequests() {
-        synchronized (mLock) {
-            if (!mProvider.isMock()) {
-                throw new IllegalArgumentException(mName + " provider is not a test provider");
-            }
-
-            return mProvider.getCurrentRequest().getLocationRequests();
-        }
-    }
-
     @Nullable
     public Location getLastLocation(CallerIdentity identity, @PermissionLevel int permissionLevel,
             boolean ignoreLocationSettings) {
@@ -1439,31 +1388,43 @@
                 identity.getPackageName())) {
             return null;
         }
-        if (!mUserInfoHelper.isCurrentUserId(identity.getUserId())) {
-            return null;
+        if (!ignoreLocationSettings) {
+            if (!isEnabled(identity.getUserId())) {
+                return null;
+            }
+            if (!identity.isSystem() && !mUserHelper.isCurrentUserId(identity.getUserId())) {
+                return null;
+            }
         }
-        if (!ignoreLocationSettings && !isEnabled(identity.getUserId())) {
+
+        // lastly - note app ops
+        if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel),
+                identity)) {
             return null;
         }
 
-        Location location = getLastLocationUnsafe(identity.getUserId(), permissionLevel,
-                ignoreLocationSettings, Long.MAX_VALUE);
+        Location location = getPermittedLocation(
+                getLastLocationUnsafe(
+                        identity.getUserId(),
+                        permissionLevel,
+                        ignoreLocationSettings,
+                        Long.MAX_VALUE),
+                permissionLevel);
 
-        // we don't note op here because we don't know what the client intends to do with the
-        // location, the client is responsible for noting if necessary
-
-        if (identity.getPid() == Process.myPid() && location != null) {
+        if (location != null && identity.getPid() == Process.myPid()) {
             // if delivering to the same process, make a copy of the location first (since
             // location is mutable)
-            return new Location(location);
-        } else {
-            return location;
+            location = new Location(location);
         }
+
+        return location;
     }
 
     /**
      * This function does not perform any permissions or safety checks, by calling it you are
-     * committing to performing all applicable checks yourself.
+     * committing to performing all applicable checks yourself. This always returns a "fine"
+     * location, even if the permissionLevel is coarse. You are responsible for coarsening the
+     * location if necessary.
      */
     @Nullable
     public Location getLastLocationUnsafe(int userId, @PermissionLevel int permissionLevel,
@@ -1471,7 +1432,7 @@
         if (userId == UserHandle.USER_ALL) {
             // find the most recent location across all users
             Location lastLocation = null;
-            final int[] runningUserIds = mUserInfoHelper.getRunningUserIds();
+            final int[] runningUserIds = mUserHelper.getRunningUserIds();
             for (int i = 0; i < runningUserIds.length; i++) {
                 Location next = getLastLocationUnsafe(runningUserIds[i], permissionLevel,
                         ignoreLocationSettings, maximumAgeMs);
@@ -1516,7 +1477,7 @@
 
     private void setLastLocation(Location location, int userId) {
         if (userId == UserHandle.USER_ALL) {
-            final int[] runningUserIds = mUserInfoHelper.getRunningUserIds();
+            final int[] runningUserIds = mUserHelper.getRunningUserIds();
             for (int i = 0; i < runningUserIds.length; i++) {
                 setLastLocation(location, runningUserIds[i]);
             }
@@ -1532,17 +1493,16 @@
                 mLastLocations.put(userId, lastLocation);
             }
 
-            Location coarseLocation = mLocationFudger.createCoarse(location);
             if (isEnabled(userId)) {
-                lastLocation.set(location, coarseLocation);
+                lastLocation.set(location);
             }
-            lastLocation.setBypass(location, coarseLocation);
+            lastLocation.setBypass(location);
         }
     }
 
     @Nullable
     public ICancellationSignal getCurrentLocation(LocationRequest request,
-            CallerIdentity callerIdentity, int permissionLevel, ILocationCallback callback) {
+            CallerIdentity identity, int permissionLevel, ILocationCallback callback) {
         if (request.getDurationMillis() > GET_CURRENT_LOCATION_MAX_TIMEOUT_MS) {
             request = new LocationRequest.Builder(request)
                     .setDurationMillis(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS)
@@ -1552,57 +1512,36 @@
         GetCurrentLocationListenerRegistration registration =
                 new GetCurrentLocationListenerRegistration(
                         request,
-                        callerIdentity,
+                        identity,
                         new GetCurrentLocationTransport(callback),
                         permissionLevel);
 
         synchronized (mLock) {
-            if (mSettingsHelper.isLocationPackageBlacklisted(callerIdentity.getUserId(),
-                    callerIdentity.getPackageName())) {
-                registration.deliverLocation(null);
-                return null;
-            }
-            if (!mUserInfoHelper.isCurrentUserId(callerIdentity.getUserId())) {
-                registration.deliverLocation(null);
-                return null;
-            }
-            if (!request.isLocationSettingsIgnored() && !isEnabled(callerIdentity.getUserId())) {
-                registration.deliverLocation(null);
-                return null;
-            }
-
-            Location lastLocation = getLastLocationUnsafe(
-                    callerIdentity.getUserId(),
-                    permissionLevel,
-                    request.isLocationSettingsIgnored(),
-                    MAX_CURRENT_LOCATION_AGE_MS);
-            if (lastLocation != null) {
-                registration.deliverLocation(lastLocation);
-                return null;
-            }
-
-            // if last location isn't good enough then we add a location request
-            long identity = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
                 addRegistration(callback.asBinder(), registration);
+                if (!registration.isActive()) {
+                    // if the registration never activated, fail it immediately
+                    registration.deliverNull();
+                }
             } finally {
-                Binder.restoreCallingIdentity(identity);
+                Binder.restoreCallingIdentity(ident);
             }
         }
 
         ICancellationSignal cancelTransport = CancellationSignal.createTransport();
-        CancellationSignal cancellationSignal = CancellationSignal.fromTransport(cancelTransport);
-        cancellationSignal.setOnCancelListener(SingleUseCallback.wrap(
-                () -> {
-                    synchronized (mLock) {
-                        removeRegistration(callback.asBinder(), registration);
-                    }
-                }));
+        CancellationSignal.fromTransport(cancelTransport)
+                .setOnCancelListener(SingleUseCallback.wrap(
+                        () -> {
+                            synchronized (mLock) {
+                                removeRegistration(callback.asBinder(), registration);
+                            }
+                        }));
         return cancelTransport;
     }
 
     public void sendExtraCommand(int uid, int pid, String command, Bundle extras) {
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             mProvider.sendExtraCommand(uid, pid, command, extras);
         } finally {
@@ -1610,36 +1549,36 @@
         }
     }
 
-    public void registerLocationRequest(LocationRequest request, CallerIdentity callerIdentity,
+    public void registerLocationRequest(LocationRequest request, CallerIdentity identity,
             @PermissionLevel int permissionLevel, ILocationListener listener) {
+        LocationListenerRegistration registration = new LocationListenerRegistration(
+                request,
+                identity,
+                new LocationListenerTransport(listener),
+                permissionLevel);
+
         synchronized (mLock) {
-            long identity = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
-                addRegistration(
-                        listener.asBinder(),
-                        new LocationListenerRegistration(
-                                request,
-                                callerIdentity,
-                                new LocationListenerTransport(listener),
-                                permissionLevel));
+                addRegistration(listener.asBinder(), registration);
             } finally {
-                Binder.restoreCallingIdentity(identity);
+                Binder.restoreCallingIdentity(ident);
             }
         }
     }
 
     public void registerLocationRequest(LocationRequest request, CallerIdentity callerIdentity,
             @PermissionLevel int permissionLevel, PendingIntent pendingIntent) {
+        LocationPendingIntentRegistration registration = new LocationPendingIntentRegistration(
+                request,
+                callerIdentity,
+                new LocationPendingIntentTransport(mContext, pendingIntent),
+                permissionLevel);
+
         synchronized (mLock) {
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
-                addRegistration(
-                        pendingIntent,
-                        new LocationPendingIntentRegistration(
-                                request,
-                                callerIdentity,
-                                new LocationPendingIntentTransport(mContext, pendingIntent),
-                                permissionLevel));
+                addRegistration(pendingIntent, registration);
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -1648,7 +1587,7 @@
 
     public void unregisterLocationRequest(ILocationListener listener) {
         synchronized (mLock) {
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 removeRegistration(listener.asBinder());
             } finally {
@@ -1659,7 +1598,7 @@
 
     public void unregisterLocationRequest(PendingIntent pendingIntent) {
         synchronized (mLock) {
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 removeRegistration(pendingIntent);
             } finally {
@@ -1760,7 +1699,7 @@
     @Override
     protected boolean registerWithService(ProviderRequest request,
             Collection<Registration> registrations) {
-        return reregisterWithService(EMPTY_REQUEST, request, registrations);
+        return reregisterWithService(ProviderRequest.EMPTY_REQUEST, request, registrations);
     }
 
     @GuardedBy("mLock")
@@ -1829,8 +1768,8 @@
             Preconditions.checkState(Thread.holdsLock(mLock));
         }
 
-        mLocationEventLog.logProviderUpdateRequest(mName, EMPTY_REQUEST);
-        mProvider.setRequest(EMPTY_REQUEST);
+        mLocationEventLog.logProviderUpdateRequest(mName, ProviderRequest.EMPTY_REQUEST);
+        mProvider.setRequest(ProviderRequest.EMPTY_REQUEST);
     }
 
     @GuardedBy("mLock")
@@ -1850,6 +1789,9 @@
             if (!isEnabled(identity.getUserId())) {
                 return false;
             }
+            if (!identity.isSystem() && !mUserHelper.isCurrentUserId(identity.getUserId())) {
+                return false;
+            }
 
             switch (mLocationPowerSaveModeHelper.getLocationPowerSaveMode()) {
                 case LOCATION_MODE_FOREGROUND_ONLY:
@@ -1887,48 +1829,54 @@
             Preconditions.checkState(Thread.holdsLock(mLock));
         }
 
-        long intervalMs = Long.MAX_VALUE;
+        long intervalMs = ProviderRequest.INTERVAL_DISABLED;
+        int quality = LocationRequest.QUALITY_LOW_POWER;
         boolean locationSettingsIgnored = false;
         boolean lowPower = true;
-        ArrayList<LocationRequest> locationRequests = new ArrayList<>(registrations.size());
 
         for (Registration registration : registrations) {
             LocationRequest request = registration.getRequest();
 
             // passive requests do not contribute to the provider request
-            if (request.getIntervalMillis() == PASSIVE_INTERVAL) {
+            if (request.getIntervalMillis() == LocationRequest.PASSIVE_INTERVAL) {
                 continue;
             }
 
             intervalMs = min(request.getIntervalMillis(), intervalMs);
+            quality = min(request.getQuality(), quality);
             locationSettingsIgnored |= request.isLocationSettingsIgnored();
             lowPower &= request.isLowPower();
-            locationRequests.add(request);
+        }
+
+        if (intervalMs == ProviderRequest.INTERVAL_DISABLED) {
+            return ProviderRequest.EMPTY_REQUEST;
         }
 
         // calculate who to blame for power in a somewhat arbitrary fashion. we pick a threshold
         // interval slightly higher that the minimum interval, and spread the blame across all
         // contributing registrations under that threshold (since worksource does not allow us to
         // represent differing power blame ratios).
-        long thresholdIntervalMs = (intervalMs + 1000) * 3 / 2;
-        if (thresholdIntervalMs < 0 || thresholdIntervalMs >= PASSIVE_INTERVAL) {
+        long thresholdIntervalMs;
+        try {
+            thresholdIntervalMs = Math.multiplyExact(Math.addExact(intervalMs, 1000) / 2, 3);
+        } catch (ArithmeticException e) {
             // check for and handle overflow by setting to one below the passive interval so passive
             // requests are automatically skipped
-            thresholdIntervalMs = PASSIVE_INTERVAL - 1;
+            thresholdIntervalMs = LocationRequest.PASSIVE_INTERVAL - 1;
         }
 
         WorkSource workSource = new WorkSource();
         for (Registration registration : registrations) {
             if (registration.getRequest().getIntervalMillis() <= thresholdIntervalMs) {
-                workSource.add(registration.getWorkSource());
+                workSource.add(registration.getRequest().getWorkSource());
             }
         }
 
         return new ProviderRequest.Builder()
                 .setIntervalMillis(intervalMs)
+                .setQuality(quality)
                 .setLocationSettingsIgnored(locationSettingsIgnored)
                 .setLowPower(lowPower)
-                .setLocationRequests(locationRequests)
                 .setWorkSource(workSource)
                 .build();
     }
@@ -1953,7 +1901,7 @@
                 // location for our calculations instead. this prevents spammy add/remove behavior
                 last = getLastLocationUnsafe(
                         registration.getIdentity().getUserId(),
-                        PERMISSION_FINE,
+                        registration.getPermissionLevel(),
                         false,
                         locationRequest.getIntervalMillis());
             }
@@ -1978,7 +1926,8 @@
         synchronized (mLock) {
             switch (change) {
                 case UserListener.CURRENT_USER_CHANGED:
-                    onEnabledChanged(userId);
+                    updateRegistrations(
+                            registration -> registration.getIdentity().getUserId() == userId);
                     break;
                 case UserListener.USER_STARTED:
                     onUserStarted(userId);
@@ -2159,13 +2108,10 @@
         }
 
         if (userId == UserHandle.USER_ALL) {
-            onEnabledChanged(UserHandle.USER_ALL);
             mEnabled.clear();
             mLastLocations.clear();
         } else {
             Preconditions.checkArgument(userId >= 0);
-
-            onEnabledChanged(userId);
             mEnabled.delete(userId);
             mLastLocations.remove(userId);
         }
@@ -2182,7 +2128,7 @@
             // settings for instance) do not support the null user
             return;
         } else if (userId == UserHandle.USER_ALL) {
-            final int[] runningUserIds = mUserInfoHelper.getRunningUserIds();
+            final int[] runningUserIds = mUserHelper.getRunningUserIds();
             for (int i = 0; i < runningUserIds.length; i++) {
                 onEnabledChanged(runningUserIds[i]);
             }
@@ -2193,7 +2139,6 @@
 
         boolean enabled = mStarted
                 && mProvider.getState().allowed
-                && mUserInfoHelper.isCurrentUserId(userId)
                 && mSettingsHelper.isLocationEnabled(userId);
 
         int index = mEnabled.indexOfKey(userId);
@@ -2249,6 +2194,20 @@
         updateRegistrations(registration -> registration.getIdentity().getUserId() == userId);
     }
 
+    @Nullable
+    private Location getPermittedLocation(@Nullable Location fineLocation,
+            @PermissionLevel int permissionLevel) {
+        switch (permissionLevel) {
+            case PERMISSION_FINE:
+                return fineLocation;
+            case PERMISSION_COARSE:
+                return fineLocation != null ? mLocationFudger.createCoarse(fineLocation) : null;
+            default:
+                // shouldn't be possible to have a client added without location permissions
+                throw new AssertionError();
+        }
+    }
+
     public void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
         synchronized (mLock) {
             ipw.print(mName);
@@ -2261,7 +2220,7 @@
 
             super.dump(fd, ipw, args);
 
-            int[] userIds = mUserInfoHelper.getRunningUserIds();
+            int[] userIds = mUserHelper.getRunningUserIds();
             for (int userId : userIds) {
                 if (userIds.length != 1) {
                     ipw.print("user ");
@@ -2299,10 +2258,14 @@
         public void clearMock() {
             if (mFineLocation != null && mFineLocation.isFromMockProvider()) {
                 mFineLocation = null;
+            }
+            if (mCoarseLocation != null && mCoarseLocation.isFromMockProvider()) {
                 mCoarseLocation = null;
             }
             if (mFineBypassLocation != null && mFineBypassLocation.isFromMockProvider()) {
                 mFineBypassLocation = null;
+            }
+            if (mCoarseBypassLocation != null && mCoarseBypassLocation.isFromMockProvider()) {
                 mCoarseBypassLocation = null;
             }
         }
@@ -2333,14 +2296,14 @@
             }
         }
 
-        public void set(Location fineLocation, Location coarseLocation) {
-            mFineLocation = calculateNextFine(mFineLocation, fineLocation);
-            mCoarseLocation = calculateNextCoarse(mCoarseLocation, coarseLocation);
+        public void set(Location location) {
+            mFineLocation = calculateNextFine(mFineLocation, location);
+            mCoarseLocation = calculateNextCoarse(mCoarseLocation, location);
         }
 
-        public void setBypass(Location fineLocation, Location coarseLocation) {
-            mFineBypassLocation = calculateNextFine(mFineBypassLocation, fineLocation);
-            mCoarseBypassLocation = calculateNextCoarse(mCoarseBypassLocation, coarseLocation);
+        public void setBypass(Location location) {
+            mFineBypassLocation = calculateNextFine(mFineBypassLocation, location);
+            mCoarseBypassLocation = calculateNextCoarse(mCoarseBypassLocation, location);
         }
 
         private Location calculateNextFine(@Nullable Location oldFine, Location newFine) {
@@ -2362,8 +2325,8 @@
             }
 
             // update last coarse interval only if enough time has passed
-            if (newCoarse.getElapsedRealtimeNanos() - MIN_COARSE_INTERVAL_MS
-                    > oldCoarse.getElapsedRealtimeNanos()) {
+            if (newCoarse.getElapsedRealtimeMillis() - MIN_COARSE_INTERVAL_MS
+                    > oldCoarse.getElapsedRealtimeMillis()) {
                 return newCoarse;
             } else {
                 return oldCoarse;
@@ -2411,7 +2374,7 @@
                 return;
             }
 
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 callback.run();
             } catch (RuntimeException e) {
diff --git a/services/core/java/com/android/server/location/PassiveLocationProviderManager.java b/services/core/java/com/android/server/location/PassiveLocationProviderManager.java
index 2870d41..b771861 100644
--- a/services/core/java/com/android/server/location/PassiveLocationProviderManager.java
+++ b/services/core/java/com/android/server/location/PassiveLocationProviderManager.java
@@ -52,7 +52,7 @@
             PassiveProvider passiveProvider = (PassiveProvider) mProvider.getProvider();
             Preconditions.checkState(passiveProvider != null);
 
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 passiveProvider.updateLocation(location);
             } finally {
@@ -63,15 +63,7 @@
 
     @Override
     protected ProviderRequest mergeRegistrations(Collection<Registration> registrations) {
-        boolean locationSettingsIgnored = false;
-        for (Registration registration : registrations) {
-            locationSettingsIgnored |= registration.getRequest().isLocationSettingsIgnored();
-        }
-
-        return new ProviderRequest.Builder()
-                .setIntervalMillis(0)
-                .setLocationSettingsIgnored(locationSettingsIgnored)
-                .build();
+        return new ProviderRequest.Builder().setIntervalMillis(0).build();
     }
 
     @Override
@@ -79,4 +71,9 @@
             Collection<Registration> registrations) {
         return 0;
     }
+
+    @Override
+    protected String getServiceState() {
+        return mProvider.getCurrentRequest().isActive() ? "registered" : "unregistered";
+    }
 }
diff --git a/services/core/java/com/android/server/location/geofence/GeofenceManager.java b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
index 58e6d59..c91ee82 100644
--- a/services/core/java/com/android/server/location/geofence/GeofenceManager.java
+++ b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
@@ -302,7 +302,7 @@
         CallerIdentity callerIdentity = CallerIdentity.fromBinder(mContext, packageName,
                 attributionTag, AppOpsManager.toReceiverId(pendingIntent));
 
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             addRegistration(new GeofenceKey(pendingIntent, geofence),
                     new GeofenceRegistration(geofence, callerIdentity, pendingIntent));
@@ -315,7 +315,7 @@
      * Removes the geofence associated with the PendingIntent.
      */
     public void removeGeofence(PendingIntent pendingIntent) {
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             removeRegistrationIf(key -> key.getPendingIntent().equals(pendingIntent));
         } finally {
@@ -327,7 +327,7 @@
     protected boolean isActive(GeofenceRegistration registration) {
         CallerIdentity identity = registration.getIdentity();
         return registration.isPermitted()
-                && mUserInfoHelper.isCurrentUserId(identity.getUserId())
+                && (identity.isSystem() || mUserInfoHelper.isCurrentUserId(identity.getUserId()))
                 && mSettingsHelper.isLocationEnabled(identity.getUserId())
                 && !mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(),
                 identity.getPackageName());
diff --git a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
index e782d5e..ec48d4c 100644
--- a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
@@ -67,7 +67,7 @@
     /**
      * Registration object for GNSS listeners.
      */
-    protected final class GnssListenerRegistration extends
+    protected class GnssListenerRegistration extends
             BinderListenerRegistration<TRequest, TListener> {
 
         // we store these values because we don't trust the listeners not to give us dupes, not to
@@ -229,20 +229,28 @@
      */
     protected void addListener(TRequest request, CallerIdentity callerIdentity,
             TListener listener) {
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             addRegistration(listener.asBinder(),
-                    new GnssListenerRegistration(request, callerIdentity, listener));
+                    createRegistration(request, callerIdentity, listener));
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
     }
 
     /**
+     * May be overridden by subclasses to change the registration type.
+     */
+    protected GnssListenerRegistration createRegistration(TRequest request,
+            CallerIdentity callerIdentity, TListener listener) {
+        return new GnssListenerRegistration(request, callerIdentity, listener);
+    }
+
+    /**
      * Removes the given listener.
      */
     public void removeListener(TListener listener) {
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             removeRegistration(listener.asBinder());
         } finally {
@@ -259,7 +267,7 @@
         CallerIdentity identity = registration.getIdentity();
         return registration.isPermitted()
                 && (registration.isForeground() || isBackgroundRestrictionExempt(identity))
-                && mUserInfoHelper.isCurrentUserId(identity.getUserId())
+                && (identity.isSystem() || mUserInfoHelper.isCurrentUserId(identity.getUserId()))
                 && mLocationManagerInternal.isProviderEnabledForUser(GPS_PROVIDER,
                 identity.getUserId())
                 && !mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(),
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index f879b19..e25e605 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -708,12 +708,12 @@
             // For fast GNSS TTFF
             provider = LocationManager.NETWORK_PROVIDER;
             locationListener = mNetworkLocationListener;
-            locationRequest.setQuality(LocationRequest.POWER_LOW);
+            locationRequest.setQuality(LocationRequest.QUALITY_LOW_POWER);
         } else {
             // For Device-Based Hybrid (E911)
             provider = LocationManager.FUSED_PROVIDER;
             locationListener = mFusedLocationListener;
-            locationRequest.setQuality(LocationRequest.ACCURACY_FINE);
+            locationRequest.setQuality(LocationRequest.QUALITY_HIGH_ACCURACY);
         }
 
         // Ignore location settings if in emergency mode. This is only allowed for
@@ -1120,7 +1120,7 @@
     @Override
     public void onExtraCommand(int uid, int pid, String command, Bundle extras) {
 
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             if ("delete_aiding_data".equals(command)) {
                 deleteAidingData(extras);
diff --git a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
index 2faa15f..74284f3 100644
--- a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
@@ -19,6 +19,7 @@
 import static com.android.server.location.gnss.GnssManagerService.D;
 import static com.android.server.location.gnss.GnssManagerService.TAG;
 
+import android.annotation.Nullable;
 import android.app.AppOpsManager;
 import android.location.GnssMeasurementsEvent;
 import android.location.GnssRequest;
@@ -32,6 +33,7 @@
 import com.android.internal.util.Preconditions;
 import com.android.server.location.util.AppOpsHelper;
 import com.android.server.location.util.Injector;
+import com.android.server.location.util.LocationAttributionHelper;
 import com.android.server.location.util.LocationUsageLogger;
 import com.android.server.location.util.SettingsHelper;
 
@@ -44,11 +46,40 @@
  *
  * @hide
  */
-public class GnssMeasurementsProvider extends
+public final class GnssMeasurementsProvider extends
         GnssListenerMultiplexer<GnssRequest, IGnssMeasurementsListener, Boolean> implements
         SettingsHelper.GlobalSettingChangedListener {
 
+    private class GnssMeasurementListenerRegistration extends GnssListenerRegistration {
+
+        private static final String GNSS_MEASUREMENTS_BUCKET = "gnss_measurement";
+
+        protected GnssMeasurementListenerRegistration(
+                @Nullable GnssRequest gnssRequest,
+                CallerIdentity callerIdentity,
+                IGnssMeasurementsListener iGnssMeasurementsListener) {
+            super(gnssRequest, callerIdentity, iGnssMeasurementsListener);
+        }
+
+        @Nullable
+        @Override
+        protected ListenerOperation<IGnssMeasurementsListener> onActive() {
+            mLocationAttributionHelper.reportHighPowerLocationStart(
+                    getIdentity(), GNSS_MEASUREMENTS_BUCKET, getKey());
+            return null;
+        }
+
+        @Nullable
+        @Override
+        protected ListenerOperation<IGnssMeasurementsListener> onInactive() {
+            mLocationAttributionHelper.reportHighPowerLocationStop(
+                    getIdentity(), GNSS_MEASUREMENTS_BUCKET, getKey());
+            return null;
+        }
+    }
+
     private final AppOpsHelper mAppOpsHelper;
+    private final LocationAttributionHelper mLocationAttributionHelper;
     private final LocationUsageLogger mLogger;
     private final GnssMeasurementProviderNative mNative;
 
@@ -60,6 +91,7 @@
     public GnssMeasurementsProvider(Injector injector, GnssMeasurementProviderNative aNative) {
         super(injector);
         mAppOpsHelper = injector.getAppOpsHelper();
+        mLocationAttributionHelper = injector.getLocationAttributionHelper();
         mLogger = injector.getLocationUsageLogger();
         mNative = aNative;
     }
@@ -76,6 +108,12 @@
     }
 
     @Override
+    protected GnssListenerRegistration createRegistration(GnssRequest request,
+            CallerIdentity callerIdentity, IGnssMeasurementsListener listener) {
+        return new GnssMeasurementListenerRegistration(request, callerIdentity, listener);
+    }
+
+    @Override
     protected boolean registerWithService(Boolean fullTrackingRequest,
             Collection<GnssListenerRegistration> registrations) {
         Preconditions.checkState(mNative.isMeasurementSupported());
diff --git a/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java b/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java
index 5dc4318..43eb3ca 100644
--- a/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java
+++ b/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java
@@ -200,8 +200,8 @@
         synchronized (mSharedLock) {
             return "BinderLocationTimeZoneProvider{"
                     + "mProviderName=" + mProviderName
-                    + "mCurrentState=" + mCurrentState
-                    + "mProxy=" + mProxy
+                    + ", mCurrentState=" + mCurrentState
+                    + ", mProxy=" + mProxy
                     + '}';
         }
     }
diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java b/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java
index a8589d4..a817759 100644
--- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java
+++ b/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java
@@ -198,9 +198,6 @@
         if (isInSimulationMode(SECONDARY_PROVIDER_NAME)) {
             proxy = new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
         } else {
-            // TODO Uncomment this code in a later commit.
-            throw new UnsupportedOperationException("Not implemented");
-            /*
             proxy = RealLocationTimeZoneProviderProxy.createAndRegister(
                     mContext,
                     mThreadingDomain,
@@ -209,7 +206,6 @@
                     com.android.internal.R.string
                             .config_secondaryLocationTimeZoneProviderPackageName
             );
-            */
         }
         return createLocationTimeZoneProvider(SECONDARY_PROVIDER_NAME, proxy);
     }
diff --git a/services/core/java/com/android/server/location/timezone/NullLocationTimeZoneProvider.java b/services/core/java/com/android/server/location/timezone/NullLocationTimeZoneProvider.java
index 53afaf0..2bbae56 100644
--- a/services/core/java/com/android/server/location/timezone/NullLocationTimeZoneProvider.java
+++ b/services/core/java/com/android/server/location/timezone/NullLocationTimeZoneProvider.java
@@ -92,7 +92,7 @@
         synchronized (mSharedLock) {
             return "NullLocationTimeZoneProvider{"
                     + "mProviderName='" + mProviderName + '\''
-                    + "mCurrentState='" + mCurrentState + '\''
+                    + ", mCurrentState='" + mCurrentState + '\''
                     + '}';
         }
     }
diff --git a/services/core/java/com/android/server/location/util/LocationAttributionHelper.java b/services/core/java/com/android/server/location/util/LocationAttributionHelper.java
index bc3ac0f..36b3ef3 100644
--- a/services/core/java/com/android/server/location/util/LocationAttributionHelper.java
+++ b/services/core/java/com/android/server/location/util/LocationAttributionHelper.java
@@ -38,12 +38,12 @@
  */
 public class LocationAttributionHelper {
 
-    private static class ProviderListener {
-        private final String mProvider;
+    private static class BucketKey {
+        private final String mBucket;
         private final Object mKey;
 
-        private ProviderListener(String provider, Object key) {
-            mProvider = Objects.requireNonNull(provider);
+        private BucketKey(String bucket, Object key) {
+            mBucket = Objects.requireNonNull(bucket);
             mKey = Objects.requireNonNull(key);
         }
 
@@ -56,23 +56,23 @@
                 return false;
             }
 
-            ProviderListener that = (ProviderListener) o;
-            return mProvider.equals(that.mProvider)
+            BucketKey that = (BucketKey) o;
+            return mBucket.equals(that.mBucket)
                     && mKey.equals(that.mKey);
         }
 
         @Override
         public int hashCode() {
-            return Objects.hash(mProvider, mKey);
+            return Objects.hash(mBucket, mKey);
         }
     }
 
     private final AppOpsHelper mAppOpsHelper;
 
     @GuardedBy("this")
-    private final Map<CallerIdentity, Set<ProviderListener>> mAttributions;
+    private final Map<CallerIdentity, Set<BucketKey>> mAttributions;
     @GuardedBy("this")
-    private final Map<CallerIdentity, Set<ProviderListener>> mHighPowerAttributions;
+    private final Map<CallerIdentity, Set<BucketKey>> mHighPowerAttributions;
 
     public LocationAttributionHelper(AppOpsHelper appOpsHelper) {
         mAppOpsHelper = appOpsHelper;
@@ -82,14 +82,14 @@
     }
 
     /**
-     * Report normal location usage for the given caller on the given provider, with a unique key.
+     * Report normal location usage for the given caller in the given bucket, with a unique key.
      */
-    public synchronized void reportLocationStart(CallerIdentity identity, String provider,
+    public synchronized void reportLocationStart(CallerIdentity identity, String bucket,
             Object key) {
-        Set<ProviderListener> keySet = mAttributions.computeIfAbsent(identity,
+        Set<BucketKey> keySet = mAttributions.computeIfAbsent(identity,
                 i -> new ArraySet<>());
         boolean empty = keySet.isEmpty();
-        if (keySet.add(new ProviderListener(provider, key)) && empty) {
+        if (keySet.add(new BucketKey(bucket, key)) && empty) {
             if (!mAppOpsHelper.startOpNoThrow(OP_MONITOR_LOCATION, identity)) {
                 mAttributions.remove(identity);
             }
@@ -97,13 +97,13 @@
     }
 
     /**
-     * Report normal location usage has stopped for the given caller on the given provider, with a
+     * Report normal location usage has stopped for the given caller in the given bucket, with a
      * unique key.
      */
-    public synchronized void reportLocationStop(CallerIdentity identity, String provider,
+    public synchronized void reportLocationStop(CallerIdentity identity, String bucket,
             Object key) {
-        Set<ProviderListener> keySet = mAttributions.get(identity);
-        if (keySet != null && keySet.remove(new ProviderListener(provider, key))
+        Set<BucketKey> keySet = mAttributions.get(identity);
+        if (keySet != null && keySet.remove(new BucketKey(bucket, key))
                 && keySet.isEmpty()) {
             mAttributions.remove(identity);
             mAppOpsHelper.finishOp(OP_MONITOR_LOCATION, identity);
@@ -111,15 +111,15 @@
     }
 
     /**
-     * Report high power location usage for the given caller on the given provider, with a unique
+     * Report high power location usage for the given caller in the given bucket, with a unique
      * key.
      */
-    public synchronized void reportHighPowerLocationStart(CallerIdentity identity, String provider,
+    public synchronized void reportHighPowerLocationStart(CallerIdentity identity, String bucket,
             Object key) {
-        Set<ProviderListener> keySet = mHighPowerAttributions.computeIfAbsent(identity,
+        Set<BucketKey> keySet = mHighPowerAttributions.computeIfAbsent(identity,
                 i -> new ArraySet<>());
         boolean empty = keySet.isEmpty();
-        if (keySet.add(new ProviderListener(provider, key)) && empty) {
+        if (keySet.add(new BucketKey(bucket, key)) && empty) {
             if (mAppOpsHelper.startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, identity)) {
                 if (D) {
                     Log.v(TAG, "starting high power location attribution for " + identity);
@@ -131,13 +131,13 @@
     }
 
     /**
-     * Report high power location usage has stopped for the given caller on the given provider,
+     * Report high power location usage has stopped for the given caller in the given bucket,
      * with a unique key.
      */
-    public synchronized void reportHighPowerLocationStop(CallerIdentity identity, String provider,
+    public synchronized void reportHighPowerLocationStop(CallerIdentity identity, String bucket,
             Object key) {
-        Set<ProviderListener> keySet = mHighPowerAttributions.get(identity);
-        if (keySet != null && keySet.remove(new ProviderListener(provider, key))
+        Set<BucketKey> keySet = mHighPowerAttributions.get(identity);
+        if (keySet != null && keySet.remove(new BucketKey(bucket, key))
                 && keySet.isEmpty()) {
             if (D) {
                 Log.v(TAG, "stopping high power location attribution for " + identity);
diff --git a/services/core/java/com/android/server/location/util/LocationPowerSaveModeHelper.java b/services/core/java/com/android/server/location/util/LocationPowerSaveModeHelper.java
index d85ca5e..6e4cf06 100644
--- a/services/core/java/com/android/server/location/util/LocationPowerSaveModeHelper.java
+++ b/services/core/java/com/android/server/location/util/LocationPowerSaveModeHelper.java
@@ -16,7 +16,13 @@
 
 package com.android.server.location.util;
 
+import static android.os.PowerManager.locationPowerSaveModeToString;
+
+import static com.android.server.location.LocationManagerService.D;
+import static com.android.server.location.LocationManagerService.TAG;
+
 import android.os.PowerManager.LocationPowerSaveMode;
+import android.util.Log;
 
 import java.util.concurrent.CopyOnWriteArrayList;
 
@@ -60,7 +66,12 @@
 
     protected final void notifyLocationPowerSaveModeChanged(
             @LocationPowerSaveMode int locationPowerSaveMode) {
+        if (D) {
+            Log.d(TAG, "location power save mode is now " + locationPowerSaveModeToString(
+                    locationPowerSaveMode));
+        }
         mLocationEventLog.logLocationPowerSaveMode(locationPowerSaveMode);
+
         for (LocationPowerSaveModeChangedListener listener : mListeners) {
             listener.onLocationPowerSaveModeChanged(locationPowerSaveMode);
         }
diff --git a/services/core/java/com/android/server/location/util/ScreenInteractiveHelper.java b/services/core/java/com/android/server/location/util/ScreenInteractiveHelper.java
index d47bce3..ecd6966 100644
--- a/services/core/java/com/android/server/location/util/ScreenInteractiveHelper.java
+++ b/services/core/java/com/android/server/location/util/ScreenInteractiveHelper.java
@@ -16,6 +16,11 @@
 
 package com.android.server.location.util;
 
+import static com.android.server.location.LocationManagerService.D;
+import static com.android.server.location.LocationManagerService.TAG;
+
+import android.util.Log;
+
 import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
@@ -55,6 +60,10 @@
     }
 
     protected final void notifyScreenInteractiveChanged(boolean interactive) {
+        if (D) {
+            Log.d(TAG, "screen interactive is now " + interactive);
+        }
+
         for (ScreenInteractiveChangedListener listener : mListeners) {
             listener.onScreenInteractiveChanged(interactive);
         }
diff --git a/services/core/java/com/android/server/location/util/SystemAppForegroundHelper.java b/services/core/java/com/android/server/location/util/SystemAppForegroundHelper.java
index d8b1d5b..3ff572b 100644
--- a/services/core/java/com/android/server/location/util/SystemAppForegroundHelper.java
+++ b/services/core/java/com/android/server/location/util/SystemAppForegroundHelper.java
@@ -63,7 +63,7 @@
     public boolean isAppForeground(int uid) {
         Preconditions.checkState(mActivityManager != null);
 
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             return isForeground(mActivityManager.getUidImportance(uid));
         } finally {
diff --git a/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java b/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java
index cfb7697..8742d65 100644
--- a/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java
+++ b/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java
@@ -60,7 +60,7 @@
     public boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity) {
         Preconditions.checkState(mAppOps != null);
 
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             return mAppOps.startOpNoThrow(
                     appOp,
@@ -78,7 +78,7 @@
     public void finishOp(int appOp, CallerIdentity callerIdentity) {
         Preconditions.checkState(mAppOps != null);
 
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             mAppOps.finishOp(
                     appOp,
@@ -94,7 +94,7 @@
     public boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity) {
         Preconditions.checkState(mAppOps != null);
 
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             return mAppOps.checkOpNoThrow(
                     appOp,
@@ -109,7 +109,7 @@
     public boolean noteOp(int appOp, CallerIdentity callerIdentity) {
         Preconditions.checkState(mAppOps != null);
 
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             return mAppOps.noteOp(
                     appOp,
@@ -126,7 +126,7 @@
     public boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity) {
         Preconditions.checkState(mAppOps != null);
 
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             return mAppOps.noteOpNoThrow(
                     appOp,
diff --git a/services/core/java/com/android/server/location/util/SystemLocationPermissionsHelper.java b/services/core/java/com/android/server/location/util/SystemLocationPermissionsHelper.java
index b9c0dde..4f1f7a0 100644
--- a/services/core/java/com/android/server/location/util/SystemLocationPermissionsHelper.java
+++ b/services/core/java/com/android/server/location/util/SystemLocationPermissionsHelper.java
@@ -54,7 +54,7 @@
 
     @Override
     protected boolean hasPermission(String permission, CallerIdentity callerIdentity) {
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             return mContext.checkPermission(permission, callerIdentity.getPid(),
                     callerIdentity.getUid()) == PERMISSION_GRANTED;
diff --git a/services/core/java/com/android/server/location/util/SystemSettingsHelper.java b/services/core/java/com/android/server/location/util/SystemSettingsHelper.java
index 39aeaba..560cd3c 100644
--- a/services/core/java/com/android/server/location/util/SystemSettingsHelper.java
+++ b/services/core/java/com/android/server/location/util/SystemSettingsHelper.java
@@ -123,7 +123,7 @@
      */
     @Override
     public void setLocationEnabled(boolean enabled, int userId) {
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             Settings.Secure.putIntForUser(
                     mContext.getContentResolver(),
@@ -314,7 +314,7 @@
      */
     @Override
     public long getBackgroundThrottleProximityAlertIntervalMs() {
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             return Settings.Global.getLong(mContext.getContentResolver(),
                     LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS,
@@ -330,7 +330,7 @@
      */
     @Override
     public float getCoarseLocationAccuracyM() {
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         final ContentResolver cr = mContext.getContentResolver();
         try {
             return Settings.Secure.getFloatForUser(
@@ -458,7 +458,7 @@
         }
 
         public int getValueForUser(int defaultValue, int userId) {
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 return Settings.Secure.getIntForUser(mContext.getContentResolver(), mSettingName,
                         defaultValue, userId);
@@ -496,7 +496,7 @@
 
             List<String> value = mCachedValue;
             if (userId != mCachedUserId) {
-                long identity = Binder.clearCallingIdentity();
+                final long identity = Binder.clearCallingIdentity();
                 try {
                     String setting = Settings.Secure.getStringForUser(mContext.getContentResolver(),
                             mSettingName, userId);
@@ -548,7 +548,7 @@
         }
 
         public boolean getValue(boolean defaultValue) {
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 return Settings.Global.getInt(mContext.getContentResolver(), mSettingName,
                         defaultValue ? 1 : 0) != 0;
@@ -574,7 +574,7 @@
         }
 
         public long getValue(long defaultValue) {
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 return Settings.Global.getLong(mContext.getContentResolver(), mSettingName,
                         defaultValue);
@@ -612,7 +612,7 @@
         public synchronized Set<String> getValue() {
             ArraySet<String> value = mCachedValue;
             if (!mValid) {
-                long identity = Binder.clearCallingIdentity();
+                final long identity = Binder.clearCallingIdentity();
                 try {
                     value = new ArraySet<>(mBaseValuesSupplier.get());
                     String setting = Settings.Global.getString(mContext.getContentResolver(),
diff --git a/services/core/java/com/android/server/location/util/SystemUserInfoHelper.java b/services/core/java/com/android/server/location/util/SystemUserInfoHelper.java
index e9836aad..141afa7 100644
--- a/services/core/java/com/android/server/location/util/SystemUserInfoHelper.java
+++ b/services/core/java/com/android/server/location/util/SystemUserInfoHelper.java
@@ -97,7 +97,7 @@
     public int[] getRunningUserIds() {
         IActivityManager activityManager = getActivityManager();
         if (activityManager != null) {
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 return activityManager.getRunningUserIds();
             } catch (RemoteException e) {
@@ -118,7 +118,7 @@
     public boolean isCurrentUserId(@UserIdInt int userId) {
         ActivityManagerInternal activityManagerInternal = getActivityManagerInternal();
         if (activityManagerInternal != null) {
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 return activityManagerInternal.isCurrentProfile(userId);
             } finally {
@@ -136,7 +136,7 @@
         // if you're hitting this precondition then you are invoking this before the system is ready
         Preconditions.checkState(userManager != null);
 
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             return userManager.getEnabledProfileIds(userId);
         } finally {
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 7e53e6f..c42c84f 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -1388,7 +1388,7 @@
             // Now we have unlocked the parent user and attempted to unlock the profile we should
             // show notifications if the profile is still locked.
             if (!alreadyUnlocked) {
-                long ident = clearCallingIdentity();
+                final long ident = clearCallingIdentity();
                 try {
                     maybeShowEncryptionNotificationForUser(profile.id);
                 } finally {
@@ -2227,7 +2227,7 @@
         final IStorageManager service = mInjector.getStorageManager();
         // TODO(b/120484642): Update vold to return a password as a byte array
         String password;
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             password = service.getPassword();
             service.clearPassword();
@@ -3447,7 +3447,7 @@
 
         @Override
         public PasswordMetrics getUserPasswordMetrics(int userHandle) {
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 if (isManagedProfileWithUnifiedLock(userHandle)) {
                     // A managed profile with unified challenge is supposed to be protected by the
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
index 715e41c..5d0544b 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -17,6 +17,7 @@
 package com.android.server.locksettings;
 
 import static android.content.Context.USER_SERVICE;
+import static android.text.TextUtils.formatSimple;
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
 import static com.android.internal.widget.LockPatternUtils.USER_FRP;
@@ -527,7 +528,7 @@
     protected String getSynthenticPasswordStateFilePathForUser(int userId, long handle,
             String name) {
         final File baseDir = getSyntheticPasswordDirectoryForUser(userId);
-        final String baseName = String.format("%016x.%s", handle, name);
+        final String baseName = formatSimple("%016x.%s", handle, name);
         return new File(baseDir, baseName).getAbsolutePath();
     }
 
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 793cfcd..f997352 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -382,7 +382,7 @@
         if (mPlaybackState == null) {
             return false;
         }
-        return MediaSession.isActiveState(mPlaybackState.getState()) == expected;
+        return mPlaybackState.isActiveState() == expected;
     }
 
     /**
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 8e93215..828a0ac 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -1397,7 +1397,7 @@
          */
         @Override
         public boolean dispatchMediaKeyEventToSessionAsSystemService(String packageName,
-                MediaSession.Token sessionToken, KeyEvent keyEvent) {
+                KeyEvent keyEvent, MediaSession.Token sessionToken) {
             final int pid = Binder.getCallingPid();
             final int uid = Binder.getCallingUid();
             final long token = Binder.clearCallingIdentity();
@@ -1772,7 +1772,7 @@
          */
         @Override
         public void dispatchVolumeKeyEventToSessionAsSystemService(String packageName,
-                String opPackageName, MediaSession.Token sessionToken, KeyEvent keyEvent) {
+                String opPackageName, KeyEvent keyEvent, MediaSession.Token sessionToken) {
             int pid = Binder.getCallingPid();
             int uid = Binder.getCallingUid();
             final long token = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
index f15e22f9..f8ff5b5 100644
--- a/services/core/java/com/android/server/media/MediaSessionStack.java
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -416,7 +416,7 @@
     // Code copied from android.os.Debug#getCallers(int)
     private static String getCallers(final int depth) {
         final StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
-        StringBuffer sb = new StringBuffer();
+        StringBuilder sb = new StringBuilder();
         for (int i = 0; i < depth; i++) {
             sb.append(getCaller(callStack, i)).append(" ");
         }
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index 94776f8..3ce8e46 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -261,7 +261,7 @@
 
         @Override // Binder call
         public boolean hasProjectionPermission(int uid, String packageName) {
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             boolean hasPermission = false;
             try {
                 hasPermission |= checkPermission(packageName,
@@ -288,7 +288,7 @@
             }
 
             final UserHandle callingUser = Binder.getCallingUserHandle();
-            long callingToken = Binder.clearCallingIdentity();
+            final long callingToken = Binder.clearCallingIdentity();
 
             MediaProjection projection;
             try {
diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java
index 05f2808..3a262d6 100644
--- a/services/core/java/com/android/server/net/LockdownVpnTracker.java
+++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java
@@ -95,11 +95,13 @@
         mProfile = Objects.requireNonNull(profile);
 
         final Intent configIntent = new Intent(ACTION_VPN_SETTINGS);
-        mConfigIntent = PendingIntent.getActivity(mContext, 0, configIntent, 0);
+        mConfigIntent = PendingIntent.getActivity(mContext, 0 /* requestCode */, configIntent,
+                PendingIntent.FLAG_IMMUTABLE);
 
         final Intent resetIntent = new Intent(ACTION_LOCKDOWN_RESET);
         resetIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-        mResetIntent = PendingIntent.getBroadcast(mContext, 0, resetIntent, 0);
+        mResetIntent = PendingIntent.getBroadcast(mContext, 0 /* requestCode */, resetIntent,
+                PendingIntent.FLAG_IMMUTABLE);
     }
 
     /**
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 2992877..35d3621 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -3472,10 +3472,10 @@
                 }
 
                 fout.println();
-                fout.print("mRestrictBackgroundLowPowerMode: " + mRestrictBackgroundLowPowerMode);
-                fout.print("mRestrictBackgroundBeforeBsm: " + mRestrictBackgroundBeforeBsm);
-                fout.print("mLoadedRestrictBackground: " + mLoadedRestrictBackground);
-                fout.print("mRestrictBackgroundChangedInBsm: " + mRestrictBackgroundChangedInBsm);
+                fout.println("mRestrictBackgroundLowPowerMode: " + mRestrictBackgroundLowPowerMode);
+                fout.println("mRestrictBackgroundBeforeBsm: " + mRestrictBackgroundBeforeBsm);
+                fout.println("mLoadedRestrictBackground: " + mLoadedRestrictBackground);
+                fout.println("mRestrictBackgroundChangedInBsm: " + mRestrictBackgroundChangedInBsm);
 
                 fout.println();
                 fout.println("Network policies:");
@@ -3900,7 +3900,7 @@
                         // quick check: if this uid doesn't have INTERNET permission, it
                         // doesn't have network access anyway, so it is a waste to mess
                         // with it here.
-                        if (hasInternetPermissionUL(uid)) {
+                        if (hasInternetPermissionUL(uid) && !isUidForegroundOnRestrictPowerUL(uid)) {
                             uidRules.put(uid, FIREWALL_RULE_DENY);
                         }
                     }
diff --git a/services/core/java/com/android/server/net/NetworkStatsAccess.java b/services/core/java/com/android/server/net/NetworkStatsAccess.java
index 7c1c1c7..7cdc4cc 100644
--- a/services/core/java/com/android/server/net/NetworkStatsAccess.java
+++ b/services/core/java/com/android/server/net/NetworkStatsAccess.java
@@ -24,10 +24,10 @@
 import android.Manifest;
 import android.annotation.IntDef;
 import android.app.AppOpsManager;
-import android.app.admin.DeviceAdminInfo;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.os.Process;
 import android.os.UserHandle;
 import android.telephony.TelephonyManager;
 
@@ -111,12 +111,12 @@
         boolean hasCarrierPrivileges = tm != null &&
                 tm.checkCarrierPrivilegesForPackageAnyPhone(callingPackage) ==
                         TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
-        boolean isDeviceOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(callingUid,
-                DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+        final boolean isDeviceOwner = dpmi != null && dpmi.isActiveDeviceOwner(callingUid);
+        final int appId = UserHandle.getAppId(callingUid);
         if (hasCarrierPrivileges || isDeviceOwner
-                || UserHandle.getAppId(callingUid) == android.os.Process.SYSTEM_UID) {
-            // Carrier-privileged apps and device owners, and the system can access data usage for
-            // all apps on the device.
+                || appId == Process.SYSTEM_UID || appId == Process.NETWORK_STACK_UID) {
+            // Carrier-privileged apps and device owners, and the system (including the
+            // network stack) can access data usage for all apps on the device.
             return NetworkStatsAccess.Level.DEVICE;
         }
 
@@ -126,8 +126,9 @@
             return NetworkStatsAccess.Level.DEVICESUMMARY;
         }
 
-        boolean isProfileOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(callingUid,
-                DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+        //TODO(b/169395065) Figure out if this flow makes sense in Device Owner mode.
+        boolean isProfileOwner = dpmi != null && (dpmi.isActiveProfileOwner(callingUid)
+                || dpmi.isActiveDeviceOwner(callingUid));
         if (isProfileOwner) {
             // Apps with the AppOps permission, profile owners, and apps with the privileged
             // permission can access data usage for all apps in this user/profile.
diff --git a/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java b/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
index d202a2a..5646c75 100644
--- a/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
+++ b/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
@@ -30,6 +30,7 @@
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.Pair;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.CollectionUtils;
@@ -94,39 +95,41 @@
         // also needed to track CBRS.
         final List<Integer> newSubs = getActiveSubIdList(mSubscriptionManager);
 
-        for (final int subId : newSubs) {
-            final RatTypeListener match = CollectionUtils.find(mRatListeners,
-                    it -> it.mSubId == subId);
-            if (match != null) continue;
+        // IMSI is needed for every newly added sub. Listener stores subscriberId into it to
+        // prevent binder call to telephony when querying RAT. Keep listener registration with empty
+        // IMSI is meaningless since the RAT type changed is ambiguous for multi-SIM if reported
+        // with empty IMSI. So filter the subs w/o a valid IMSI to prevent such registration.
+        final List<Pair<Integer, String>> filteredNewSubs =
+                CollectionUtils.mapNotNull(newSubs, subId -> {
+                    final String subscriberId = mTeleManager.getSubscriberId(subId);
+                    return TextUtils.isEmpty(subscriberId) ? null : new Pair(subId, subscriberId);
+                });
 
-            // Create listener for every newly added sub. Also store subscriberId into it to
-            // prevent binder call to telephony when querying RAT. If the subscriberId is empty
-            // for any reason, such as SIM PIN locked, skip registration.
-            // SubscriberId will be unavailable again if 1. modem crashed 2. reboot
-            // 3. re-insert SIM. If that happens, the listeners will be eventually synchronized
-            // with active sub list once all subscriberIds are ready.
-            final String subscriberId = mTeleManager.getSubscriberId(subId);
-            if (TextUtils.isEmpty(subscriberId)) {
-                Log.d(NetworkStatsService.TAG, "Empty subscriberId for newly added sub "
-                        + subId + ", skip listener registration");
+        for (final Pair<Integer, String> sub : filteredNewSubs) {
+            // Fully match listener with subId and IMSI, since in some rare cases, IMSI might be
+            // suddenly change regardless of subId, such as switch IMSI feature in modem side.
+            // If that happens, register new listener with new IMSI and remove old one later.
+            if (CollectionUtils.find(mRatListeners,
+                    it -> it.equalsKey(sub.first, sub.second)) != null) {
                 continue;
             }
+
             final RatTypeListener listener =
-                    new RatTypeListener(mExecutor, this, subId, subscriberId);
+                    new RatTypeListener(mExecutor, this, sub.first, sub.second);
             mRatListeners.add(listener);
 
             // Register listener to the telephony manager that associated with specific sub.
-            mTeleManager.createForSubscriptionId(subId)
+            mTeleManager.createForSubscriptionId(sub.first)
                     .listen(listener, PhoneStateListener.LISTEN_SERVICE_STATE);
-            Log.d(NetworkStatsService.TAG, "RAT type listener registered for sub " + subId);
+            Log.d(NetworkStatsService.TAG, "RAT type listener registered for sub " + sub.first);
         }
 
         for (final RatTypeListener listener : new ArrayList<>(mRatListeners)) {
-            // If the new list contains the subId of the listener, keeps it.
-            final Integer match = CollectionUtils.find(newSubs, it -> it == listener.mSubId);
-            if (match != null) continue;
-
-            handleRemoveRatTypeListener(listener);
+            // If there is no subId and IMSI matched the listener, removes it.
+            if (CollectionUtils.find(filteredNewSubs,
+                    it -> listener.equalsKey(it.first, it.second)) == null) {
+                handleRemoveRatTypeListener(listener);
+            }
         }
     }
 
@@ -232,5 +235,9 @@
         public int getSubId() {
             return mSubId;
         }
+
+        boolean equalsKey(int subId, @NonNull String subscriberId) {
+            return mSubId == subId && TextUtils.equals(mSubscriberId, subscriberId);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/net/TEST_MAPPING b/services/core/java/com/android/server/net/TEST_MAPPING
index 9f04260..f707597 100644
--- a/services/core/java/com/android/server/net/TEST_MAPPING
+++ b/services/core/java/com/android/server/net/TEST_MAPPING
@@ -9,6 +9,9 @@
         },
         {
           "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "android.platform.test.annotations.FlakyTest"
         }
       ]
     },
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 74b7bd7..4040f41 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -833,6 +833,7 @@
 
     public void onUserSwitched(int user) {
         if (DEBUG) Slog.d(TAG, "onUserSwitched u=" + user);
+        unbindOtherUserServices(user);
         rebindServices(true, user);
     }
 
@@ -1219,6 +1220,27 @@
         bindToServices(componentsToBind);
     }
 
+    /**
+     * Called when user switched to unbind all services from other users.
+     */
+    @VisibleForTesting
+    void unbindOtherUserServices(int currentUser) {
+        final SparseArray<Set<ComponentName>> componentsToUnbind = new SparseArray<>();
+
+        synchronized (mMutex) {
+            final Set<ManagedServiceInfo> removableBoundServices = getRemovableConnectedServices();
+            for (ManagedServiceInfo info : removableBoundServices) {
+                if (info.userid != currentUser) {
+                    Set<ComponentName> toUnbind =
+                            componentsToUnbind.get(info.userid, new ArraySet<>());
+                    toUnbind.add(info.component);
+                    componentsToUnbind.put(info.userid, toUnbind);
+                }
+            }
+        }
+        unbindFromServices(componentsToUnbind);
+    }
+
     protected void unbindFromServices(SparseArray<Set<ComponentName>> componentsToUnbind) {
         for (int i = 0; i < componentsToUnbind.size(); i++) {
             final int userId = componentsToUnbind.keyAt(i);
@@ -1264,7 +1286,8 @@
     /**
      * Version of registerService that takes the name of a service component to bind to.
      */
-    private void registerService(final ComponentName name, final int userid) {
+    @VisibleForTesting
+    void registerService(final ComponentName name, final int userid) {
         synchronized (mMutex) {
             registerServiceLocked(name, userid);
         }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 4da5fad..c2313db 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -60,6 +60,7 @@
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.media.AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY;
 import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
@@ -137,9 +138,9 @@
 import android.app.StatsManager;
 import android.app.StatusBarManager;
 import android.app.UriGrantsManager;
-import android.app.admin.DeviceAdminInfo;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.app.backup.BackupManager;
+import android.app.compat.CompatChanges;
 import android.app.role.OnRoleHoldersChangedListener;
 import android.app.role.RoleManager;
 import android.app.usage.UsageEvents;
@@ -408,9 +409,19 @@
     @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
     private static final long CHANGE_BACKGROUND_CUSTOM_TOAST_BLOCK = 128611929L;
 
+    /**
+     * Activity starts coming from broadcast receivers or services in response to notification and
+     * notification action clicks will be blocked for UX and performance reasons. Instead start the
+     * activity directly from the PendingIntent.
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
+    private static final long NOTIFICATION_TRAMPOLINE_BLOCK = 167676448L;
+
     private IActivityManager mAm;
     private ActivityTaskManagerInternal mAtm;
     private ActivityManager mActivityManager;
+    private ActivityManagerInternal mAmi;
     private IPackageManager mPackageManager;
     private PackageManager mPackageManagerClient;
     AudioManager mAudioManager;
@@ -869,7 +880,7 @@
                         (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
                 if (disableNotificationEffects(null) != null) {
                     // cancel whatever's going on
-                    long identity = Binder.clearCallingIdentity();
+                    final long identity = Binder.clearCallingIdentity();
                     try {
                         final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
                         if (player != null) {
@@ -880,11 +891,11 @@
                         Binder.restoreCallingIdentity(identity);
                     }
 
-                    identity = Binder.clearCallingIdentity();
+                    final long identity2 = Binder.clearCallingIdentity();
                     try {
                         mVibrator.cancel();
                     } finally {
-                        Binder.restoreCallingIdentity(identity);
+                        Binder.restoreCallingIdentity(identity2);
                     }
                 }
             }
@@ -1340,7 +1351,7 @@
     @GuardedBy("mNotificationLock")
     void clearSoundLocked() {
         mSoundNotificationKey = null;
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
             if (player != null) {
@@ -1355,7 +1366,7 @@
     @GuardedBy("mNotificationLock")
     void clearVibrateLocked() {
         mVibrateNotificationKey = null;
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             mVibrator.cancel();
         } finally {
@@ -1887,7 +1898,7 @@
             DevicePolicyManagerInternal dpm, IUriGrantsManager ugm,
             UriGrantsManagerInternal ugmInternal, AppOpsManager appOps, UserManager userManager,
             NotificationHistoryManager historyManager, StatsManager statsManager,
-            TelephonyManager telephonyManager) {
+            TelephonyManager telephonyManager, ActivityManagerInternal ami) {
         mHandler = handler;
         Resources resources = getContext().getResources();
         mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
@@ -1909,6 +1920,7 @@
         mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
         mCompanionManager = companionManager;
         mActivityManager = activityManager;
+        mAmi = ami;
         mDeviceIdleManager = getContext().getSystemService(DeviceIdleManager.class);
         mDpm = dpm;
         mUm = userManager;
@@ -2187,7 +2199,8 @@
                 new NotificationHistoryManager(getContext(), handler),
                 mStatsManager = (StatsManager) getContext().getSystemService(
                         Context.STATS_MANAGER),
-                getContext().getSystemService(TelephonyManager.class));
+                getContext().getSystemService(TelephonyManager.class),
+                LocalServices.getService(ActivityManagerInternal.class));
 
         publishBinderService(Context.NOTIFICATION_SERVICE, mService, /* allowIsolated= */ false,
                 DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL);
@@ -2866,7 +2879,7 @@
                     callingUid);
 
             final boolean appIsForeground;
-            long callingIdentity = Binder.clearCallingIdentity();
+            final long callingIdentity = Binder.clearCallingIdentity();
             try {
                 appIsForeground = mActivityManager.getUidImportance(callingUid)
                         == IMPORTANCE_FOREGROUND;
@@ -2886,7 +2899,7 @@
             if (isAppRenderedToast && !isSystemToast && !isPackageInForegroundForToast(pkg,
                     callingUid)) {
                 boolean block;
-                long id = Binder.clearCallingIdentity();
+                final long id = Binder.clearCallingIdentity();
                 try {
                     // CHANGE_BACKGROUND_CUSTOM_TOAST_BLOCK is gated on targetSdk, so block will be
                     // false for apps with targetSdk < R. For apps with targetSdk R+, text toasts
@@ -2912,7 +2925,7 @@
 
             synchronized (mToastQueue) {
                 int callingPid = Binder.getCallingPid();
-                long callingId = Binder.clearCallingIdentity();
+                final long callingId = Binder.clearCallingIdentity();
                 try {
                     ToastRecord record;
                     int index = indexOfToastLocked(pkg, token);
@@ -2991,7 +3004,7 @@
             }
 
             synchronized (mToastQueue) {
-                long callingId = Binder.clearCallingIdentity();
+                final long callingId = Binder.clearCallingIdentity();
                 try {
                     int index = indexOfToastLocked(pkg, token);
                     if (index >= 0) {
@@ -3009,7 +3022,7 @@
         @Override
         public void finishToken(String pkg, IBinder token) {
             synchronized (mToastQueue) {
-                long callingId = Binder.clearCallingIdentity();
+                final long callingId = Binder.clearCallingIdentity();
                 try {
                     int index = indexOfToastLocked(pkg, token);
                     if (index >= 0) {
@@ -3951,7 +3964,7 @@
         public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
             final int callingUid = Binder.getCallingUid();
             final int callingPid = Binder.getCallingPid();
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mNotificationLock) {
                     final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
@@ -3989,7 +4002,7 @@
         @Override
         public void requestBindListener(ComponentName component) {
             checkCallerIsSystemOrSameApp(component.getPackageName());
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 ManagedServices manager =
                         mAssistants.isComponentEnabledForCurrentProfiles(component)
@@ -4003,7 +4016,7 @@
 
         @Override
         public void requestUnbindListener(INotificationListener token) {
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 // allow bound services to disable themselves
                 synchronized (mNotificationLock) {
@@ -4017,7 +4030,7 @@
 
         @Override
         public void setNotificationsShownFromListener(INotificationListener token, String[] keys) {
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mNotificationLock) {
                     final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
@@ -4076,7 +4089,7 @@
         @Override
         public void snoozeNotificationUntilContextFromListener(INotificationListener token,
                 String key, String snoozeCriterionId) {
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mNotificationLock) {
                     final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
@@ -4095,7 +4108,7 @@
         @Override
         public void snoozeNotificationUntilFromListener(INotificationListener token, String key,
                 long duration) {
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mNotificationLock) {
                     final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
@@ -4113,7 +4126,7 @@
          */
         @Override
         public void unsnoozeNotificationFromAssistant(INotificationListener token, String key) {
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mNotificationLock) {
                     final ManagedServiceInfo info =
@@ -4133,7 +4146,7 @@
         @Override
         public void unsnoozeNotificationFromSystemListener(INotificationListener token,
                 String key) {
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mNotificationLock) {
                     final ManagedServiceInfo info =
@@ -4160,7 +4173,7 @@
                 String tag, int id) {
             final int callingUid = Binder.getCallingUid();
             final int callingPid = Binder.getCallingPid();
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mNotificationLock) {
                     final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
@@ -4457,7 +4470,7 @@
 
         @Override
         public void requestUnbindProvider(IConditionProvider provider) {
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 // allow bound services to disable themselves
                 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
@@ -4470,7 +4483,7 @@
         @Override
         public void requestBindProvider(ComponentName component) {
             checkCallerIsSystemOrSameApp(component.getPackageName());
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 mConditionProviders.setComponentState(component, true);
             } finally {
@@ -4543,11 +4556,11 @@
             } catch (NameNotFoundException e) {
                 return false;
             }
+            //TODO(b/169395065) Figure out if this flow makes sense in Device Owner mode.
             return checkPackagePolicyAccess(pkg)
                     || mListeners.isComponentEnabledForPackage(pkg)
-                    || (mDpm != null &&
-                            mDpm.isActiveAdminWithPolicy(Binder.getCallingUid(),
-                                    DeviceAdminInfo.USES_POLICY_PROFILE_OWNER));
+                    || (mDpm != null && (mDpm.isActiveProfileOwner(Binder.getCallingUid())
+                                || mDpm.isActiveDeviceOwner(Binder.getCallingUid())));
         }
 
         @Override
@@ -4756,7 +4769,7 @@
 
         @Override
         public List<ComponentName> getEnabledNotificationListeners(int userId) {
-            checkCallerIsSystem();
+            checkNotificationListenerAccess();
             return mListeners.getAllowedComponents(userId);
         }
 
@@ -4825,7 +4838,7 @@
         public void setNotificationListenerAccessGrantedForUser(ComponentName listener, int userId,
                 boolean granted) {
             Objects.requireNonNull(listener);
-            checkCallerIsSystemOrShell();
+            checkNotificationListenerAccess();
             final long identity = Binder.clearCallingIdentity();
             try {
                 if (mAllowedManagedServicePackages.test(
@@ -5037,7 +5050,7 @@
 
         private int getUidForPackageAndUser(String pkg, UserHandle user) throws RemoteException {
             int uid = 0;
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 uid = mPackageManager.getPackageUid(pkg, 0, user.getIdentifier());
             } finally {
@@ -5098,6 +5111,14 @@
         }
     };
 
+    protected void checkNotificationListenerAccess() {
+        if (!isCallerSystemOrPhone()) {
+            getContext().enforceCallingPermission(
+                    permission.MANAGE_NOTIFICATION_LISTENERS,
+                    "Caller must hold " + permission.MANAGE_NOTIFICATION_LISTENERS);
+        }
+    }
+
     @VisibleForTesting
     protected void setNotificationAssistantAccessGrantedForUserInternal(
             ComponentName assistant, int baseUserId, boolean granted) {
@@ -5229,8 +5250,8 @@
             if (!summaries.containsKey(pkg)) {
                 // Add summary
                 final ApplicationInfo appInfo =
-                       adjustedSbn.getNotification().extras.getParcelable(
-                               Notification.EXTRA_BUILDER_APPLICATION_INFO);
+                        adjustedSbn.getNotification().extras.getParcelable(
+                                Notification.EXTRA_BUILDER_APPLICATION_INFO);
                 final Bundle extras = new Bundle();
                 extras.putParcelable(Notification.EXTRA_BUILDER_APPLICATION_INFO, appInfo);
                 final String channelId = notificationRecord.getChannel().getId();
@@ -5266,11 +5287,11 @@
                         notificationRecord.getIsAppImportanceLocked());
                 summaries.put(pkg, summarySbn.getKey());
             }
-        }
-        if (summaryRecord != null && checkDisqualifyingFeatures(userId, MY_UID,
-                summaryRecord.getSbn().getId(), summaryRecord.getSbn().getTag(), summaryRecord,
-                true)) {
-            mHandler.post(new EnqueueNotificationRunnable(userId, summaryRecord, isAppForeground));
+            if (summaryRecord != null && checkDisqualifyingFeatures(userId, MY_UID,
+                    summaryRecord.getSbn().getId(), summaryRecord.getSbn().getTag(), summaryRecord,
+                    true)) {
+                mHandler.post(new EnqueueNotificationRunnable(userId, summaryRecord, isAppForeground));
+            }
         }
     }
 
@@ -6009,13 +6030,17 @@
                 + " cannot post for pkg " + targetPkg + " in user " + userId);
     }
 
+    public boolean hasFlag(final int flags, final int flag) {
+        return (flags & flag) != 0;
+    }
     /**
      * Checks if a notification can be posted. checks rate limiter, snooze helper, and blocking.
      *
      * Has side effects.
      */
-    private boolean checkDisqualifyingFeatures(int userId, int uid, int id, String tag,
+    boolean checkDisqualifyingFeatures(int userId, int uid, int id, String tag,
             NotificationRecord r, boolean isAutogroup) {
+        Notification n = r.getNotification();
         final String pkg = r.getSbn().getPackageName();
         final boolean isSystemNotification =
                 isUidSystemOrPhone(uid) || ("android".equals(pkg));
@@ -6024,71 +6049,101 @@
         // Limit the number of notifications that any given package except the android
         // package or a registered listener can enqueue.  Prevents DOS attacks and deals with leaks.
         if (!isSystemNotification && !isNotificationFromListener) {
-            synchronized (mNotificationLock) {
-                final int callingUid = Binder.getCallingUid();
-                if (mNotificationsByKey.get(r.getSbn().getKey()) == null
-                        && isCallerInstantApp(callingUid, userId)) {
-                    // Ephemeral apps have some special constraints for notifications.
-                    // They are not allowed to create new notifications however they are allowed to
-                    // update notifications created by the system (e.g. a foreground service
-                    // notification).
-                    throw new SecurityException("Instant app " + pkg
-                            + " cannot create notifications");
-                }
+            final int callingUid = Binder.getCallingUid();
+            if (mNotificationsByKey.get(r.getSbn().getKey()) == null
+                    && isCallerInstantApp(callingUid, userId)) {
+                // Ephemeral apps have some special constraints for notifications.
+                // They are not allowed to create new notifications however they are allowed to
+                // update notifications created by the system (e.g. a foreground service
+                // notification).
+                throw new SecurityException("Instant app " + pkg
+                        + " cannot create notifications");
+            }
 
-                // rate limit updates that aren't completed progress notifications
-                if (mNotificationsByKey.get(r.getSbn().getKey()) != null
-                        && !r.getNotification().hasCompletedProgress()
-                        && !isAutogroup) {
+            // rate limit updates that aren't completed progress notifications
+            if (mNotificationsByKey.get(r.getSbn().getKey()) != null
+                    && !r.getNotification().hasCompletedProgress()
+                    && !isAutogroup) {
 
-                    final float appEnqueueRate = mUsageStats.getAppEnqueueRate(pkg);
-                    if (appEnqueueRate > mMaxPackageEnqueueRate) {
-                        mUsageStats.registerOverRateQuota(pkg);
-                        final long now = SystemClock.elapsedRealtime();
-                        if ((now - mLastOverRateLogTime) > MIN_PACKAGE_OVERRATE_LOG_INTERVAL) {
-                            Slog.e(TAG, "Package enqueue rate is " + appEnqueueRate
-                                    + ". Shedding " + r.getSbn().getKey() + ". package=" + pkg);
-                            mLastOverRateLogTime = now;
-                        }
-                        return false;
+                final float appEnqueueRate = mUsageStats.getAppEnqueueRate(pkg);
+                if (appEnqueueRate > mMaxPackageEnqueueRate) {
+                    mUsageStats.registerOverRateQuota(pkg);
+                    final long now = SystemClock.elapsedRealtime();
+                    if ((now - mLastOverRateLogTime) > MIN_PACKAGE_OVERRATE_LOG_INTERVAL) {
+                        Slog.e(TAG, "Package enqueue rate is " + appEnqueueRate
+                                + ". Shedding " + r.getSbn().getKey() + ". package=" + pkg);
+                        mLastOverRateLogTime = now;
                     }
+                    return false;
                 }
+            }
 
-                // limit the number of non-fgs outstanding notificationrecords an app can have
-                if (!r.getNotification().isForegroundService()) {
-                    int count = getNotificationCountLocked(pkg, userId, id, tag);
-                    if (count >= MAX_PACKAGE_NOTIFICATIONS) {
-                        mUsageStats.registerOverCountQuota(pkg);
-                        Slog.e(TAG, "Package has already posted or enqueued " + count
-                                + " notifications.  Not showing more.  package=" + pkg);
-                        return false;
-                    }
+            // limit the number of non-fgs outstanding notificationrecords an app can have
+            if (!n.isForegroundService()) {
+                int count = getNotificationCountLocked(pkg, userId, id, tag);
+                if (count >= MAX_PACKAGE_NOTIFICATIONS) {
+                    mUsageStats.registerOverCountQuota(pkg);
+                    Slog.e(TAG, "Package has already posted or enqueued " + count
+                            + " notifications.  Not showing more.  package=" + pkg);
+                    return false;
                 }
             }
         }
 
-        synchronized (mNotificationLock) {
-            // snoozed apps
-            if (mSnoozeHelper.isSnoozed(userId, pkg, r.getKey())) {
-                MetricsLogger.action(r.getLogMaker()
-                        .setType(MetricsProto.MetricsEvent.TYPE_UPDATE)
-                        .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED));
-                mNotificationRecordLogger.log(
-                        NotificationRecordLogger.NotificationEvent.NOTIFICATION_NOT_POSTED_SNOOZED,
-                        r);
-                if (DBG) {
-                    Slog.d(TAG, "Ignored enqueue for snoozed notification " + r.getKey());
+        // bubble or inline reply that's immutable?
+        if (n.getBubbleMetadata() != null
+                && n.getBubbleMetadata().getIntent() != null
+                && hasFlag(mAmi.getPendingIntentFlags(
+                        n.getBubbleMetadata().getIntent().getTarget()),
+                        PendingIntent.FLAG_IMMUTABLE)) {
+            throw new IllegalArgumentException(r.getKey() + " Not posted."
+                    + " PendingIntents attached to bubbles must be mutable");
+        }
+
+        if (n.actions != null) {
+            for (Notification.Action action : n.actions) {
+                if ((action.getRemoteInputs() != null || action.getDataOnlyRemoteInputs() != null)
+                        && hasFlag(mAmi.getPendingIntentFlags(action.actionIntent.getTarget()),
+                        PendingIntent.FLAG_IMMUTABLE)) {
+                    throw new IllegalArgumentException(r.getKey() + " Not posted."
+                            + " PendingIntents attached to actions with remote"
+                            + " inputs must be mutable");
                 }
-                mSnoozeHelper.update(userId, r);
-                handleSavePolicyFile();
-                return false;
             }
+        }
+
+        if (r.getSystemGeneratedSmartActions() != null) {
+            for (Notification.Action action : r.getSystemGeneratedSmartActions()) {
+                if ((action.getRemoteInputs() != null || action.getDataOnlyRemoteInputs() != null)
+                        && hasFlag(mAmi.getPendingIntentFlags(action.actionIntent.getTarget()),
+                        PendingIntent.FLAG_IMMUTABLE)) {
+                    throw new IllegalArgumentException(r.getKey() + " Not posted."
+                            + " PendingIntents attached to contextual actions with remote inputs"
+                            + " must be mutable");
+                }
+            }
+        }
+
+        // snoozed apps
+        if (mSnoozeHelper.isSnoozed(userId, pkg, r.getKey())) {
+            MetricsLogger.action(r.getLogMaker()
+                    .setType(MetricsProto.MetricsEvent.TYPE_UPDATE)
+                    .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED));
+            mNotificationRecordLogger.log(
+                    NotificationRecordLogger.NotificationEvent.NOTIFICATION_NOT_POSTED_SNOOZED,
+                    r);
+            if (DBG) {
+                Slog.d(TAG, "Ignored enqueue for snoozed notification " + r.getKey());
+            }
+            mSnoozeHelper.update(userId, r);
+            handleSavePolicyFile();
+            return false;
+        }
 
 
-            // blocked apps
-            if (isBlocked(r, mUsageStats)) {
-                return false;
-            }
+        // blocked apps
+        if (isBlocked(r, mUsageStats)) {
+            return false;
         }
 
         return true;
@@ -6597,7 +6652,7 @@
 
                     // Log event to statsd
                     mNotificationRecordLogger.maybeLogNotificationPosted(r, old, position,
-                            buzzBeepBlinkLoggingCode, getGroupInstanceId(n.getGroupKey()));
+                            buzzBeepBlinkLoggingCode, getGroupInstanceId(r.getSbn().getGroupKey()));
                 } finally {
                     int N = mEnqueuedNotifications.size();
                     for (int i = 0; i < N; i++) {
@@ -7112,7 +7167,7 @@
             boolean delayVibForSound) {
         // Escalate privileges so we can use the vibrator even if the
         // notifying app does not have the VIBRATE permission.
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             final VibrationEffect effect;
             try {
@@ -7139,8 +7194,15 @@
                     // so need to check the notification still valide for vibrate.
                     synchronized (mNotificationLock) {
                         if (mNotificationsByKey.get(record.getKey()) != null) {
+                            // Vibrator checks the appops for the op package, not the caller,
+                            // so we need to add the bypass dnd flag to be heard. it's ok to
+                            // always add this flag here because we've already checked that we can
+                            // bypass dnd
+                            AudioAttributes.Builder aab =
+                                    new AudioAttributes.Builder(record.getAudioAttributes())
+                                    .setFlags(FLAG_BYPASS_INTERRUPTION_POLICY);
                             mVibrator.vibrate(record.getSbn().getUid(), record.getSbn().getOpPkg(),
-                                    effect, "Notification (delayed)", record.getAudioAttributes());
+                                    effect, "Notification (delayed)", aab.build());
                         } else {
                             Slog.e(TAG, "No vibration for canceled notification : "
                                     + record.getKey());
@@ -7720,7 +7782,7 @@
             // vibrate
             if (canceledKey.equals(mVibrateNotificationKey)) {
                 mVibrateNotificationKey = null;
-                long identity = Binder.clearCallingIdentity();
+                final long identity = Binder.clearCallingIdentity();
                 try {
                     mVibrator.cancel();
                 }
@@ -8645,7 +8707,7 @@
         if (mCompanionManager == null) {
             return false;
         }
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             List<String> associations = mCompanionManager.getAssociations(
                     info.component.getPackageName(), info.userid);
@@ -10006,7 +10068,7 @@
      * TODO(b/161957908): Remove dogfooder toast.
      */
     private class NotificationTrampolineCallback implements BackgroundActivityStartCallback {
-        private Set<String> mPackagesShown = new ArraySet<>();
+        private final Set<String> mPackagesShown = new ArraySet<>();
 
         @Override
         public IBinder getToken() {
@@ -10014,19 +10076,27 @@
         }
 
         @Override
-        public void onExclusiveTokenActivityStart(String packageName) {
-            Slog.w(TAG, "Indirect notification activity start from " + packageName);
-            boolean isFirstOccurrence = mPackagesShown.add(packageName);
-            if (!isFirstOccurrence) {
-                return;
-            }
+        public boolean isActivityStartAllowed(int uid, String packageName) {
+            String toastMessage = "Indirect activity start from " + packageName;
+            String logcatMessage =
+                    "Indirect notification activity start (trampoline) from " + packageName;
 
+            if (CompatChanges.isChangeEnabled(NOTIFICATION_TRAMPOLINE_BLOCK, uid)) {
+                toast(toastMessage + " blocked.");
+                Slog.e(TAG, logcatMessage + " blocked");
+                return false;
+            } else {
+                if (mPackagesShown.add(packageName)) {
+                    toast(toastMessage + ". This will be blocked in S.");
+                }
+                Slog.w(TAG, logcatMessage + ", this should be avoided for performance reasons");
+                return true;
+            }
+        }
+
+        private void toast(String message) {
             mUiHandler.post(() ->
-                    Toast.makeText(getUiContext(),
-                            "Indirect activity start from "
-                                    + packageName + ". "
-                                    + "This will be blocked in S.\n"
-                                    + "See go/s-trampolines.",
+                    Toast.makeText(getUiContext(), message + "\nSee go/s-trampolines.",
                             Toast.LENGTH_LONG).show());
         }
     }
diff --git a/services/core/java/com/android/server/notification/NotificationShellCmd.java b/services/core/java/com/android/server/notification/NotificationShellCmd.java
index 927dc25..73272a0 100644
--- a/services/core/java/com/android/server/notification/NotificationShellCmd.java
+++ b/services/core/java/com/android/server/notification/NotificationShellCmd.java
@@ -133,7 +133,7 @@
         }
         String callingPackage = null;
         final int callingUid = Binder.getCallingUid();
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             if (callingUid == Process.ROOT_UID) {
                 callingPackage = NotificationManagerService.ROOT_PKG;
diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java
index b42fe92..9e91875 100644
--- a/services/core/java/com/android/server/notification/NotificationUsageStats.java
+++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java
@@ -77,7 +77,6 @@
     private final Map<String, AggregatedStats> mStats = new HashMap<>();
     private final ArrayDeque<AggregatedStats[]> mStatsArrays = new ArrayDeque<>();
     private ArraySet<String> mStatExpiredkeys = new ArraySet<>();
-    private final SQLiteLog mSQLiteLog;
     private final Context mContext;
     private final Handler mHandler;
     private long mLastEmitTime;
@@ -85,7 +84,6 @@
     public NotificationUsageStats(Context context) {
         mContext = context;
         mLastEmitTime = SystemClock.elapsedRealtime();
-        mSQLiteLog = ENABLE_SQLITE_LOG ? new SQLiteLog(context) : null;
         mHandler = new Handler(mContext.getMainLooper()) {
             @Override
             public void handleMessage(Message msg) {
@@ -152,9 +150,6 @@
             stats.numUndecoratedRemoteViews += (notification.hasUndecoratedRemoteView() ? 1 : 0);
         }
         releaseAggregatedStatsLocked(aggregatedStatsArray);
-        if (ENABLE_SQLITE_LOG) {
-            mSQLiteLog.logPosted(notification);
-        }
     }
 
     /**
@@ -170,9 +165,6 @@
             stats.countApiUse(notification);
         }
         releaseAggregatedStatsLocked(aggregatedStatsArray);
-        if (ENABLE_SQLITE_LOG) {
-            mSQLiteLog.logPosted(notification);
-        }
     }
 
     /**
@@ -185,9 +177,6 @@
             stats.numRemovedByApp++;
         }
         releaseAggregatedStatsLocked(aggregatedStatsArray);
-        if (ENABLE_SQLITE_LOG) {
-            mSQLiteLog.logRemoved(notification);
-        }
     }
 
     /**
@@ -197,9 +186,6 @@
         MetricsLogger.histogram(mContext, "note_dismiss_longevity",
                 (int) (System.currentTimeMillis() - notification.getRankingTimeMs()) / (60 * 1000));
         notification.stats.onDismiss();
-        if (ENABLE_SQLITE_LOG) {
-            mSQLiteLog.logDismissed(notification);
-        }
     }
 
     /**
@@ -209,9 +195,6 @@
         MetricsLogger.histogram(mContext, "note_click_longevity",
                 (int) (System.currentTimeMillis() - notification.getRankingTimeMs()) / (60 * 1000));
         notification.stats.onClick();
-        if (ENABLE_SQLITE_LOG) {
-            mSQLiteLog.logClicked(notification);
-        }
     }
 
     public synchronized void registerPeopleAffinity(NotificationRecord notification, boolean valid,
@@ -328,21 +311,18 @@
                 // pass
             }
         }
-        if (ENABLE_SQLITE_LOG) {
-            try {
-                dump.put("historical", mSQLiteLog.dumpJson(filter));
-            } catch (JSONException e) {
-                // pass
-            }
-        }
         return dump;
     }
 
     public PulledStats remoteViewStats(long startMs, boolean aggregate) {
-        if (ENABLE_SQLITE_LOG) {
-            if (aggregate) {
-                return mSQLiteLog.remoteViewAggStats(startMs);
+        if (ENABLE_AGGREGATED_IN_MEMORY_STATS) {
+            PulledStats stats = new PulledStats(startMs);
+            for (AggregatedStats as : mStats.values()) {
+                if (as.numUndecoratedRemoteViews > 0) {
+                    stats.addUndecoratedPackage(as.key, as.mCreated);
+                }
             }
+            return stats;
         }
         return null;
     }
@@ -357,9 +337,6 @@
             pw.println(indent + "mStatsArrays.size(): " + mStatsArrays.size());
             pw.println(indent + "mStats.size(): " + mStats.size());
         }
-        if (ENABLE_SQLITE_LOG) {
-            mSQLiteLog.dump(pw, indent, filter);
-        }
     }
 
     public synchronized void emit() {
@@ -1046,353 +1023,4 @@
                     '}';
         }
     }
-
-    private static class SQLiteLog {
-        private static final String TAG = "NotificationSQLiteLog";
-
-        // Message types passed to the background handler.
-        private static final int MSG_POST = 1;
-        private static final int MSG_CLICK = 2;
-        private static final int MSG_REMOVE = 3;
-        private static final int MSG_DISMISS = 4;
-
-        private static final String DB_NAME = "notification_log.db";
-        private static final int DB_VERSION = 7;
-
-        /** Age in ms after which events are pruned from the DB. */
-        private static final long HORIZON_MS = 7 * 24 * 60 * 60 * 1000L;  // 1 week
-        /** Delay between pruning the DB. Used to throttle pruning. */
-        private static final long PRUNE_MIN_DELAY_MS = 6 * 60 * 60 * 1000L;  // 6 hours
-        /** Mininum number of writes between pruning the DB. Used to throttle pruning. */
-        private static final long PRUNE_MIN_WRITES = 1024;
-
-        // Table 'log'
-        private static final String TAB_LOG = "log";
-        private static final String COL_EVENT_USER_ID = "event_user_id";
-        private static final String COL_EVENT_TYPE = "event_type";
-        private static final String COL_EVENT_TIME = "event_time_ms";
-        private static final String COL_KEY = "key";
-        private static final String COL_PKG = "pkg";
-        private static final String COL_NOTIFICATION_ID = "nid";
-        private static final String COL_TAG = "tag";
-        private static final String COL_WHEN_MS = "when_ms";
-        private static final String COL_DEFAULTS = "defaults";
-        private static final String COL_FLAGS = "flags";
-        private static final String COL_IMPORTANCE_REQ = "importance_request";
-        private static final String COL_IMPORTANCE_FINAL = "importance_final";
-        private static final String COL_NOISY = "noisy";
-        private static final String COL_MUTED = "muted";
-        private static final String COL_DEMOTED = "demoted";
-        private static final String COL_CATEGORY = "category";
-        private static final String COL_ACTION_COUNT = "action_count";
-        private static final String COL_POSTTIME_MS = "posttime_ms";
-        private static final String COL_AIRTIME_MS = "airtime_ms";
-        private static final String COL_FIRST_EXPANSIONTIME_MS = "first_expansion_time_ms";
-        private static final String COL_AIRTIME_EXPANDED_MS = "expansion_airtime_ms";
-        private static final String COL_EXPAND_COUNT = "expansion_count";
-        private static final String COL_UNDECORATED = "undecorated";
-
-
-        private static final int EVENT_TYPE_POST = 1;
-        private static final int EVENT_TYPE_CLICK = 2;
-        private static final int EVENT_TYPE_REMOVE = 3;
-        private static final int EVENT_TYPE_DISMISS = 4;
-
-        private static final int IDLE_CONNECTION_TIMEOUT_MS = 30000;
-
-        private static long sLastPruneMs;
-
-        private static long sNumWrites;
-        private final SQLiteOpenHelper mHelper;
-
-        private final Handler mWriteHandler;
-        private static final long DAY_MS = 24 * 60 * 60 * 1000;
-        private static final String STATS_QUERY = "SELECT " +
-                COL_EVENT_USER_ID + ", " +
-                COL_PKG + ", " +
-                // Bucket by day by looking at 'floor((midnight - eventTimeMs) / dayMs)'
-                "CAST(((%d - " + COL_EVENT_TIME + ") / " + DAY_MS + ") AS int) " +
-                "AS day, " +
-                "COUNT(*) AS cnt, " +
-                "SUM(" + COL_MUTED + ") as muted, " +
-                "SUM(" + COL_NOISY + ") as noisy, " +
-                "SUM(" + COL_DEMOTED + ") as demoted, " +
-                "SUM(" + COL_UNDECORATED + ") as undecorated " +
-                "FROM " + TAB_LOG + " " +
-                "WHERE " +
-                COL_EVENT_TYPE + "=" + EVENT_TYPE_POST +
-                " AND " + COL_EVENT_TIME + " > %d " +
-                " GROUP BY " + COL_EVENT_USER_ID + ", day, " + COL_PKG;
-        private static final String UNDECORATED_QUERY = "SELECT " +
-                COL_PKG + ", " +
-                "MAX(" + COL_EVENT_TIME + ") as max_time " +
-                "FROM " + TAB_LOG + " " +
-                "WHERE " + COL_UNDECORATED + "> 0 " +
-                " AND " + COL_EVENT_TIME + " > %d " +
-                "GROUP BY " + COL_PKG;
-
-        public SQLiteLog(Context context) {
-            HandlerThread backgroundThread = new HandlerThread("notification-sqlite-log",
-                    android.os.Process.THREAD_PRIORITY_BACKGROUND);
-            backgroundThread.start();
-            mWriteHandler = new Handler(backgroundThread.getLooper()) {
-                @Override
-                public void handleMessage(Message msg) {
-                    NotificationRecord r = (NotificationRecord) msg.obj;
-                    long nowMs = System.currentTimeMillis();
-                    switch (msg.what) {
-                        case MSG_POST:
-                            writeEvent(r.getSbn().getPostTime(), EVENT_TYPE_POST, r);
-                            break;
-                        case MSG_CLICK:
-                            writeEvent(nowMs, EVENT_TYPE_CLICK, r);
-                            break;
-                        case MSG_REMOVE:
-                            writeEvent(nowMs, EVENT_TYPE_REMOVE, r);
-                            break;
-                        case MSG_DISMISS:
-                            writeEvent(nowMs, EVENT_TYPE_DISMISS, r);
-                            break;
-                        default:
-                            Log.wtf(TAG, "Unknown message type: " + msg.what);
-                            break;
-                    }
-                }
-            };
-            mHelper = new SQLiteOpenHelper(context, DB_NAME, null, DB_VERSION) {
-                @Override
-                public void onCreate(SQLiteDatabase db) {
-                    db.execSQL("CREATE TABLE " + TAB_LOG + " (" +
-                            "_id INTEGER PRIMARY KEY AUTOINCREMENT," +
-                            COL_EVENT_USER_ID + " INT," +
-                            COL_EVENT_TYPE + " INT," +
-                            COL_EVENT_TIME + " INT," +
-                            COL_KEY + " TEXT," +
-                            COL_PKG + " TEXT," +
-                            COL_NOTIFICATION_ID + " INT," +
-                            COL_TAG + " TEXT," +
-                            COL_WHEN_MS + " INT," +
-                            COL_DEFAULTS + " INT," +
-                            COL_FLAGS + " INT," +
-                            COL_IMPORTANCE_REQ + " INT," +
-                            COL_IMPORTANCE_FINAL + " INT," +
-                            COL_NOISY + " INT," +
-                            COL_MUTED + " INT," +
-                            COL_DEMOTED + " INT," +
-                            COL_CATEGORY + " TEXT," +
-                            COL_ACTION_COUNT + " INT," +
-                            COL_POSTTIME_MS + " INT," +
-                            COL_AIRTIME_MS + " INT," +
-                            COL_FIRST_EXPANSIONTIME_MS + " INT," +
-                            COL_AIRTIME_EXPANDED_MS + " INT," +
-                            COL_EXPAND_COUNT + " INT," +
-                            COL_UNDECORATED + " INT" +
-                            ")");
-                }
-
-                @Override
-                public void onConfigure(SQLiteDatabase db) {
-                    // Memory optimization - close idle connections after 30s of inactivity
-                    setIdleConnectionTimeout(IDLE_CONNECTION_TIMEOUT_MS);
-                }
-
-                @Override
-                public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
-                    if (oldVersion != newVersion) {
-                        db.execSQL("DROP TABLE IF EXISTS " + TAB_LOG);
-                        onCreate(db);
-                    }
-                }
-            };
-        }
-
-        public void logPosted(NotificationRecord notification) {
-            mWriteHandler.sendMessage(mWriteHandler.obtainMessage(MSG_POST, notification));
-        }
-
-        public void logClicked(NotificationRecord notification) {
-            mWriteHandler.sendMessage(mWriteHandler.obtainMessage(MSG_CLICK, notification));
-        }
-
-        public void logRemoved(NotificationRecord notification) {
-            mWriteHandler.sendMessage(mWriteHandler.obtainMessage(MSG_REMOVE, notification));
-        }
-
-        public void logDismissed(NotificationRecord notification) {
-            mWriteHandler.sendMessage(mWriteHandler.obtainMessage(MSG_DISMISS, notification));
-        }
-
-        private JSONArray jsonPostFrequencies(DumpFilter filter) throws JSONException {
-            JSONArray frequencies = new JSONArray();
-            SQLiteDatabase db = mHelper.getReadableDatabase();
-            long midnight = getMidnightMs();
-            String q = String.format(STATS_QUERY, midnight, filter.since);
-            Cursor cursor = db.rawQuery(q, null);
-            try {
-                for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
-                    int userId = cursor.getInt(0);
-                    String pkg = cursor.getString(1);
-                    if (filter != null && !filter.matches(pkg)) continue;
-                    int day = cursor.getInt(2);
-                    int count = cursor.getInt(3);
-                    int muted = cursor.getInt(4);
-                    int noisy = cursor.getInt(5);
-                    int demoted = cursor.getInt(6);
-                    JSONObject row = new JSONObject();
-                    row.put("user_id", userId);
-                    row.put("package", pkg);
-                    row.put("day", day);
-                    row.put("count", count);
-                    row.put("noisy", noisy);
-                    row.put("muted", muted);
-                    row.put("demoted", demoted);
-                    frequencies.put(row);
-                }
-            } finally {
-                cursor.close();
-            }
-            return frequencies;
-        }
-
-        public void printPostFrequencies(PrintWriter pw, String indent, DumpFilter filter) {
-            SQLiteDatabase db = mHelper.getReadableDatabase();
-            long midnight = getMidnightMs();
-            String q = String.format(STATS_QUERY, midnight, filter.since);
-            Cursor cursor = db.rawQuery(q, null);
-            try {
-                for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
-                    int userId = cursor.getInt(0);
-                    String pkg = cursor.getString(1);
-                    if (filter != null && !filter.matches(pkg)) continue;
-                    int day = cursor.getInt(2);
-                    int count = cursor.getInt(3);
-                    int muted = cursor.getInt(4);
-                    int noisy = cursor.getInt(5);
-                    int demoted = cursor.getInt(6);
-                    pw.println(indent + "post_frequency{user_id=" + userId + ",pkg=" + pkg +
-                            ",day=" + day + ",count=" + count + ",muted=" + muted + "/" + noisy +
-                            ",demoted=" + demoted + "}");
-                }
-            } finally {
-                cursor.close();
-            }
-        }
-
-        private long getMidnightMs() {
-            GregorianCalendar midnight = new GregorianCalendar();
-            midnight.set(midnight.get(Calendar.YEAR), midnight.get(Calendar.MONTH),
-                    midnight.get(Calendar.DATE), 23, 59, 59);
-            return midnight.getTimeInMillis();
-        }
-
-        private void writeEvent(long eventTimeMs, int eventType, NotificationRecord r) {
-            ContentValues cv = new ContentValues();
-            cv.put(COL_EVENT_USER_ID, r.getSbn().getUser().getIdentifier());
-            cv.put(COL_EVENT_TIME, eventTimeMs);
-            cv.put(COL_EVENT_TYPE, eventType);
-            putNotificationIdentifiers(r, cv);
-            if (eventType == EVENT_TYPE_POST) {
-                putNotificationDetails(r, cv);
-            } else {
-                putPosttimeVisibility(r, cv);
-            }
-            cv.put(COL_UNDECORATED, (r.hasUndecoratedRemoteView() ? 1 : 0));
-            SQLiteDatabase db = mHelper.getWritableDatabase();
-            if (db.insert(TAB_LOG, null, cv) < 0) {
-                Log.wtf(TAG, "Error while trying to insert values: " + cv);
-            }
-            sNumWrites++;
-            pruneIfNecessary(db);
-        }
-
-        private void pruneIfNecessary(SQLiteDatabase db) {
-            // Prune if we haven't in a while.
-            long nowMs = System.currentTimeMillis();
-            if (sNumWrites > PRUNE_MIN_WRITES ||
-                    nowMs - sLastPruneMs > PRUNE_MIN_DELAY_MS) {
-                sNumWrites = 0;
-                sLastPruneMs = nowMs;
-                long horizonStartMs = nowMs - HORIZON_MS;
-                try {
-                    int deletedRows = db.delete(TAB_LOG, COL_EVENT_TIME + " < ?",
-                            new String[]{String.valueOf(horizonStartMs)});
-                    Log.d(TAG, "Pruned event entries: " + deletedRows);
-                } catch (SQLiteFullException e) {
-                    Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
-                }
-            }
-        }
-
-        private static void putNotificationIdentifiers(NotificationRecord r, ContentValues outCv) {
-            outCv.put(COL_KEY, r.getSbn().getKey());
-            outCv.put(COL_PKG, r.getSbn().getPackageName());
-        }
-
-        private static void putNotificationDetails(NotificationRecord r, ContentValues outCv) {
-            outCv.put(COL_NOTIFICATION_ID, r.getSbn().getId());
-            if (r.getSbn().getTag() != null) {
-                outCv.put(COL_TAG, r.getSbn().getTag());
-            }
-            outCv.put(COL_WHEN_MS, r.getSbn().getPostTime());
-            outCv.put(COL_FLAGS, r.getNotification().flags);
-            final int before = r.stats.requestedImportance;
-            final int after = r.getImportance();
-            final boolean noisy = r.stats.isNoisy;
-            outCv.put(COL_IMPORTANCE_REQ, before);
-            outCv.put(COL_IMPORTANCE_FINAL, after);
-            outCv.put(COL_DEMOTED, after < before ? 1 : 0);
-            outCv.put(COL_NOISY, noisy);
-            if (noisy && after < IMPORTANCE_HIGH) {
-                outCv.put(COL_MUTED, 1);
-            } else {
-                outCv.put(COL_MUTED, 0);
-            }
-            if (r.getNotification().category != null) {
-                outCv.put(COL_CATEGORY, r.getNotification().category);
-            }
-            outCv.put(COL_ACTION_COUNT, r.getNotification().actions != null ?
-                    r.getNotification().actions.length : 0);
-        }
-
-        private static void putPosttimeVisibility(NotificationRecord r, ContentValues outCv) {
-            outCv.put(COL_POSTTIME_MS, r.stats.getCurrentPosttimeMs());
-            outCv.put(COL_AIRTIME_MS, r.stats.getCurrentAirtimeMs());
-            outCv.put(COL_EXPAND_COUNT, r.stats.userExpansionCount);
-            outCv.put(COL_AIRTIME_EXPANDED_MS, r.stats.getCurrentAirtimeExpandedMs());
-            outCv.put(COL_FIRST_EXPANSIONTIME_MS, r.stats.posttimeToFirstVisibleExpansionMs);
-        }
-
-        public void dump(PrintWriter pw, String indent, DumpFilter filter) {
-            printPostFrequencies(pw, indent, filter);
-        }
-
-        public JSONObject dumpJson(DumpFilter filter) {
-            JSONObject dump = new JSONObject();
-            try {
-                dump.put("post_frequency", jsonPostFrequencies(filter));
-                dump.put("since", filter.since);
-                dump.put("now", System.currentTimeMillis());
-            } catch (JSONException e) {
-                // pass
-            }
-            return dump;
-        }
-
-        public PulledStats remoteViewAggStats(long startMs) {
-            PulledStats stats = new PulledStats(startMs);
-            SQLiteDatabase db = mHelper.getReadableDatabase();
-            String q = String.format(UNDECORATED_QUERY, startMs);
-            Cursor cursor = db.rawQuery(q, null);
-            try {
-                for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
-                    String pkg = cursor.getString(0);
-                    long maxTimeMs = cursor.getLong(1);
-                    stats.addUndecoratedPackage(pkg, maxTimeMs);
-                }
-            } finally {
-                cursor.close();
-            }
-            return stats;
-        }
-    }
 }
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index b145e1e..b4347e1 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -15,6 +15,8 @@
  */
 package com.android.server.notification;
 
+import static android.text.TextUtils.formatSimple;
+
 import android.annotation.NonNull;
 import android.app.NotificationManager;
 import android.content.Context;
@@ -138,7 +140,7 @@
 
                 boolean isGroupSummary = record.getNotification().isGroupSummary();
                 record.setGlobalSortKey(
-                        String.format("crtcl=0x%04x:intrsv=%c:grnk=0x%04x:gsmry=%c:%s:rnk=0x%04x",
+                        formatSimple("crtcl=0x%04x:intrsv=%c:grnk=0x%04x:gsmry=%c:%s:rnk=0x%04x",
                         record.getCriticality(),
                         record.isRecentlyIntrusive()
                                 && record.getImportance() > NotificationManager.IMPORTANCE_MIN
diff --git a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
index 3517033..14affe7 100644
--- a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
+++ b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
@@ -302,7 +302,7 @@
 
     private void readSnoozed() {
         synchronized (mSnoozedForAlarm) {
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 final String setting = Settings.Secure.getStringForUser(
                         mContext.getContentResolver(),
diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java
index 9a9e733..f7d69fd 100644
--- a/services/core/java/com/android/server/notification/SnoozeHelper.java
+++ b/services/core/java/com/android/server/notification/SnoozeHelper.java
@@ -496,7 +496,7 @@
 
     private void scheduleRepostAtTime(String pkg, String key, int userId, long time) {
         Runnable runnable = () -> {
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 final PendingIntent pi = createPendingIntent(pkg, key, userId);
                 mAm.cancel(pi);
diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java
index cb6e960..eeb2655 100644
--- a/services/core/java/com/android/server/om/IdmapManager.java
+++ b/services/core/java/com/android/server/om/IdmapManager.java
@@ -57,7 +57,7 @@
     private final PackageManagerHelper mPackageManager;
 
     /**
-     * Package name of the reference package defined in 'config-signature' tag of
+     * Package name of the reference package defined in 'overlay-config-signature' tag of
      * SystemConfig or empty String if tag not defined. This package is vetted on scan by
      * PackageManagerService that it's a system package and is used to check if overlay matches
      * its signature in order to fulfill the config_signature policy.
@@ -159,7 +159,7 @@
             fulfilledPolicies |= OverlayablePolicy.ACTOR_SIGNATURE;
         }
 
-        // If SystemConfig defines 'config-signature' package, given that
+        // If SystemConfig defines 'overlay-config-signature' package, given that
         // this package is vetted by OverlayManagerService that it's a
         // preinstalled package, check if overlay matches its signature.
         if (!TextUtils.isEmpty(mConfigSignaturePackage)
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 5b5ec42..7992fea 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -207,13 +207,13 @@
     /**
      * Returns the active apex package's name that contains the (apk) package.
      *
-     * @param containedPackage The (apk) package that might be in a apex
+     * @param containedPackageName The (apk) package that might be in a apex
      * @return the apex package's name of {@code null} if the {@code containedPackage} is not inside
      *         any apex.
      */
     @Nullable
     public abstract String getActiveApexPackageNameContainingPackage(
-            @NonNull AndroidPackage containedPackage);
+            @NonNull String containedPackageName);
 
     /**
      * Retrieves information about an apexd staged session i.e. the internal state used by apexd to
@@ -650,15 +650,14 @@
 
         @Override
         @Nullable
-        public String getActiveApexPackageNameContainingPackage(AndroidPackage containedPackage) {
-            Objects.requireNonNull(containedPackage);
+        public String getActiveApexPackageNameContainingPackage(String containedPackageName) {
+            Objects.requireNonNull(containedPackageName);
             synchronized (mLock) {
                 Preconditions.checkState(mPackageNameToApexModuleName != null,
                         "APEX packages have not been scanned");
                 int numApksInApex = mApksInApex.size();
                 for (int apkInApexNum = 0; apkInApexNum < numApksInApex; apkInApexNum++) {
-                    if (mApksInApex.valueAt(apkInApexNum).contains(
-                            containedPackage.getPackageName())) {
+                    if (mApksInApex.valueAt(apkInApexNum).contains(containedPackageName)) {
                         String apexModuleName = mApksInApex.keyAt(apkInApexNum);
 
                         int numApexPkgs = mPackageNameToApexModuleName.size();
@@ -778,12 +777,15 @@
         void registerApkInApex(AndroidPackage pkg) {
             synchronized (mLock) {
                 for (ActiveApexInfo aai : mActiveApexInfosCache) {
-                    if (pkg.getBaseApkPath().startsWith(aai.apexDirectory.getAbsolutePath())) {
+                    if (pkg.getBaseApkPath().startsWith(
+                            aai.apexDirectory.getAbsolutePath() + File.separator)) {
                         List<String> apks = mApksInApex.get(aai.apexModuleName);
                         if (apks == null) {
                             apks = Lists.newArrayList();
                             mApksInApex.put(aai.apexModuleName, apks);
                         }
+                        Slog.i(TAG, "Registering " + pkg.getPackageName() + " as apk-in-apex of "
+                                + aai.apexModuleName);
                         apks.add(pkg.getPackageName());
                     }
                 }
@@ -1080,8 +1082,8 @@
         @Override
         @Nullable
         public String getActiveApexPackageNameContainingPackage(
-                @NonNull AndroidPackage containedPackage) {
-            Objects.requireNonNull(containedPackage);
+                @NonNull String containedPackageName) {
+            Objects.requireNonNull(containedPackageName);
 
             return null;
         }
diff --git a/services/core/java/com/android/server/pm/ApkChecksums.java b/services/core/java/com/android/server/pm/ApkChecksums.java
index 8640dbc..7e8ff94 100644
--- a/services/core/java/com/android/server/pm/ApkChecksums.java
+++ b/services/core/java/com/android/server/pm/ApkChecksums.java
@@ -16,13 +16,13 @@
 
 package com.android.server.pm;
 
-import static android.content.pm.Checksum.PARTIAL_MERKLE_ROOT_1M_SHA256;
-import static android.content.pm.Checksum.PARTIAL_MERKLE_ROOT_1M_SHA512;
-import static android.content.pm.Checksum.WHOLE_MD5;
-import static android.content.pm.Checksum.WHOLE_MERKLE_ROOT_4K_SHA256;
-import static android.content.pm.Checksum.WHOLE_SHA1;
-import static android.content.pm.Checksum.WHOLE_SHA256;
-import static android.content.pm.Checksum.WHOLE_SHA512;
+import static android.content.pm.Checksum.TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256;
+import static android.content.pm.Checksum.TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512;
+import static android.content.pm.Checksum.TYPE_WHOLE_MD5;
+import static android.content.pm.Checksum.TYPE_WHOLE_MERKLE_ROOT_4K_SHA256;
+import static android.content.pm.Checksum.TYPE_WHOLE_SHA1;
+import static android.content.pm.Checksum.TYPE_WHOLE_SHA256;
+import static android.content.pm.Checksum.TYPE_WHOLE_SHA512;
 import static android.content.pm.PackageManager.EXTRA_CHECKSUMS;
 import static android.content.pm.PackageParser.APK_FILE_EXTENSION;
 import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256;
@@ -182,13 +182,13 @@
                     dos.writeUTF(splitName);
                 }
 
-                dos.writeInt(checksum.getKind());
+                dos.writeInt(checksum.getType());
 
                 final byte[] valueBytes = checksum.getValue();
                 dos.writeInt(valueBytes.length);
                 dos.write(valueBytes);
 
-                final String packageName = checksum.getSourcePackageName();
+                final String packageName = checksum.getInstallerPackageName();
                 if (packageName == null) {
                     dos.writeInt(-1);
                 } else {
@@ -196,7 +196,7 @@
                     dos.writeUTF(packageName);
                 }
 
-                final Certificate cert = checksum.getSourceCertificate();
+                final Certificate cert = checksum.getInstallerCertificate();
                 final byte[] certBytes = (cert == null) ? null : cert.getEncoded();
                 if (certBytes == null) {
                     dos.writeInt(-1);
@@ -225,7 +225,7 @@
                     splitName = dis.readUTF();
                 }
 
-                final int kind = dis.readInt();
+                final int type = dis.readInt();
 
                 final byte[] valueBytes = new byte[dis.readInt()];
                 dis.read(valueBytes);
@@ -245,7 +245,7 @@
                     certBytes = new byte[certBytesLength];
                     dis.read(certBytes);
                 }
-                checksums[i] = new ApkChecksum(splitName, new Checksum(kind, valueBytes),
+                checksums[i] = new ApkChecksum(splitName, new Checksum(type, valueBytes),
                         packageName, certBytes);
             }
             return checksums;
@@ -265,8 +265,8 @@
      * @param statusReceiver    to receive the resulting checksums
      */
     public static void getChecksums(List<Pair<String, File>> filesToChecksum,
-            @Checksum.Kind int optional,
-            @Checksum.Kind int required,
+            @Checksum.Type int optional,
+            @Checksum.Type int required,
             @Nullable Certificate[] trustedInstallers,
             @NonNull IntentSender statusReceiver,
             @NonNull Injector injector) {
@@ -292,7 +292,7 @@
 
     private static void processRequiredChecksums(List<Pair<String, File>> filesToChecksum,
             List<Map<Integer, ApkChecksum>> result,
-            @Checksum.Kind int required,
+            @Checksum.Type int required,
             @NonNull IntentSender statusReceiver,
             @NonNull Injector injector,
             long startTime) {
@@ -339,32 +339,32 @@
      *
      * @param split             split name, null for base
      * @param file              to fetch checksums for
-     * @param kinds             mask to fetch checksums
+     * @param types             mask to fetch checksums
      * @param trustedInstallers array of certificate to trust, two specific cases:
      *                          null - trust anybody,
      *                          [] - trust nobody.
      * @param checksums         resulting checksums
      */
     private static void getAvailableApkChecksums(String split, File file,
-            @Checksum.Kind int kinds,
+            @Checksum.Type int types,
             @Nullable Certificate[] trustedInstallers,
             Map<Integer, ApkChecksum> checksums) {
         final String filePath = file.getAbsolutePath();
 
         // Always available: FSI or IncFs.
-        if (isRequired(WHOLE_MERKLE_ROOT_4K_SHA256, kinds, checksums)) {
+        if (isRequired(TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, types, checksums)) {
             // Hashes in fs-verity and IncFS are always verified.
             ApkChecksum checksum = extractHashFromFS(split, filePath);
             if (checksum != null) {
-                checksums.put(checksum.getKind(), checksum);
+                checksums.put(checksum.getType(), checksum);
             }
         }
 
         // System enforced: v2/v3.
-        if (isRequired(PARTIAL_MERKLE_ROOT_1M_SHA256, kinds, checksums) || isRequired(
-                PARTIAL_MERKLE_ROOT_1M_SHA512, kinds, checksums)) {
+        if (isRequired(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, types, checksums) || isRequired(
+                TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512, types, checksums)) {
             Map<Integer, ApkChecksum> v2v3checksums = extractHashFromV2V3Signature(
-                    split, filePath, kinds);
+                    split, filePath, types);
             if (v2v3checksums != null) {
                 checksums.putAll(v2v3checksums);
             }
@@ -377,9 +377,9 @@
                     final ApkChecksum[] digests = readChecksums(digestsFile);
                     final Set<Signature> trusted = convertToSet(trustedInstallers);
                     for (ApkChecksum digest : digests) {
-                        if (isRequired(digest.getKind(), kinds, checksums) && isTrusted(digest,
+                        if (isRequired(digest.getType(), types, checksums) && isTrusted(digest,
                                 trusted)) {
-                            checksums.put(digest.getKind(), digest);
+                            checksums.put(digest.getType(), digest);
                         }
                     }
                 } catch (IOException e) {
@@ -395,16 +395,16 @@
      * Whether the file is available for checksumming or we need to wait.
      */
     private static boolean needToWait(File file,
-            @Checksum.Kind int kinds,
+            @Checksum.Type int types,
             Map<Integer, ApkChecksum> checksums,
             @NonNull Injector injector) throws IOException {
-        if (!isRequired(WHOLE_MERKLE_ROOT_4K_SHA256, kinds, checksums)
-                && !isRequired(WHOLE_MD5, kinds, checksums)
-                && !isRequired(WHOLE_SHA1, kinds, checksums)
-                && !isRequired(WHOLE_SHA256, kinds, checksums)
-                && !isRequired(WHOLE_SHA512, kinds, checksums)
-                && !isRequired(PARTIAL_MERKLE_ROOT_1M_SHA256, kinds, checksums)
-                && !isRequired(PARTIAL_MERKLE_ROOT_1M_SHA512, kinds, checksums)) {
+        if (!isRequired(TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, types, checksums)
+                && !isRequired(TYPE_WHOLE_MD5, types, checksums)
+                && !isRequired(TYPE_WHOLE_SHA1, types, checksums)
+                && !isRequired(TYPE_WHOLE_SHA256, types, checksums)
+                && !isRequired(TYPE_WHOLE_SHA512, types, checksums)
+                && !isRequired(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, types, checksums)
+                && !isRequired(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512, types, checksums)) {
             return false;
         }
 
@@ -432,16 +432,16 @@
      *
      * @param split     split name, null for base
      * @param file      to fetch checksums for
-     * @param kinds     mask to forcefully calculate if not available
+     * @param types     mask to forcefully calculate if not available
      * @param checksums resulting checksums
      */
     private static void getRequiredApkChecksums(String split, File file,
-            @Checksum.Kind int kinds,
+            @Checksum.Type int types,
             Map<Integer, ApkChecksum> checksums) {
         final String filePath = file.getAbsolutePath();
 
         // Manually calculating required checksums if not readily available.
-        if (isRequired(WHOLE_MERKLE_ROOT_4K_SHA256, kinds, checksums)) {
+        if (isRequired(TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, types, checksums)) {
             try {
                 byte[] generatedRootHash = VerityBuilder.generateFsVerityRootHash(
                         filePath, /*salt=*/null,
@@ -451,27 +451,28 @@
                                 return ByteBuffer.allocate(capacity);
                             }
                         });
-                checksums.put(WHOLE_MERKLE_ROOT_4K_SHA256,
-                        new ApkChecksum(split, WHOLE_MERKLE_ROOT_4K_SHA256, generatedRootHash));
+                checksums.put(TYPE_WHOLE_MERKLE_ROOT_4K_SHA256,
+                        new ApkChecksum(split, TYPE_WHOLE_MERKLE_ROOT_4K_SHA256,
+                                generatedRootHash));
             } catch (IOException | NoSuchAlgorithmException | DigestException e) {
                 Slog.e(TAG, "Error calculating WHOLE_MERKLE_ROOT_4K_SHA256", e);
             }
         }
 
-        calculateChecksumIfRequested(checksums, split, file, kinds, WHOLE_MD5);
-        calculateChecksumIfRequested(checksums, split, file, kinds, WHOLE_SHA1);
-        calculateChecksumIfRequested(checksums, split, file, kinds, WHOLE_SHA256);
-        calculateChecksumIfRequested(checksums, split, file, kinds, WHOLE_SHA512);
+        calculateChecksumIfRequested(checksums, split, file, types, TYPE_WHOLE_MD5);
+        calculateChecksumIfRequested(checksums, split, file, types, TYPE_WHOLE_SHA1);
+        calculateChecksumIfRequested(checksums, split, file, types, TYPE_WHOLE_SHA256);
+        calculateChecksumIfRequested(checksums, split, file, types, TYPE_WHOLE_SHA512);
 
-        calculatePartialChecksumsIfRequested(checksums, split, file, kinds);
+        calculatePartialChecksumsIfRequested(checksums, split, file, types);
     }
 
-    private static boolean isRequired(@Checksum.Kind int kind,
-            @Checksum.Kind int kinds, Map<Integer, ApkChecksum> checksums) {
-        if ((kinds & kind) == 0) {
+    private static boolean isRequired(@Checksum.Type int type,
+            @Checksum.Type int types, Map<Integer, ApkChecksum> checksums) {
+        if ((types & type) == 0) {
             return false;
         }
-        if (checksums.containsKey(kind)) {
+        if (checksums.containsKey(type)) {
             return false;
         }
         return true;
@@ -497,7 +498,7 @@
         if (trusted == null) {
             return true;
         }
-        final Signature signature = new Signature(checksum.getSourceCertificateBytes());
+        final Signature signature = new Signature(checksum.getInstallerCertificateBytes());
         return trusted.contains(signature);
     }
 
@@ -506,7 +507,7 @@
         {
             byte[] hash = VerityUtils.getFsverityRootHash(filePath);
             if (hash != null) {
-                return new ApkChecksum(split, WHOLE_MERKLE_ROOT_4K_SHA256, hash);
+                return new ApkChecksum(split, TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, hash);
             }
         }
         // v4 next
@@ -516,7 +517,7 @@
             byte[] hash = signer.contentDigests.getOrDefault(CONTENT_DIGEST_VERITY_CHUNKED_SHA256,
                     null);
             if (hash != null) {
-                return new ApkChecksum(split, WHOLE_MERKLE_ROOT_4K_SHA256, hash);
+                return new ApkChecksum(split, TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, hash);
             }
         } catch (SignatureNotFoundException e) {
             // Nothing
@@ -527,7 +528,7 @@
     }
 
     private static Map<Integer, ApkChecksum> extractHashFromV2V3Signature(
-            String split, String filePath, int kinds) {
+            String split, String filePath, int types) {
         Map<Integer, byte[]> contentDigests = null;
         try {
             contentDigests = ApkSignatureVerifier.verifySignaturesInternal(filePath,
@@ -544,56 +545,56 @@
         }
 
         Map<Integer, ApkChecksum> checksums = new ArrayMap<>();
-        if ((kinds & PARTIAL_MERKLE_ROOT_1M_SHA256) != 0) {
+        if ((types & TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256) != 0) {
             byte[] hash = contentDigests.getOrDefault(CONTENT_DIGEST_CHUNKED_SHA256, null);
             if (hash != null) {
-                checksums.put(PARTIAL_MERKLE_ROOT_1M_SHA256,
-                        new ApkChecksum(split, PARTIAL_MERKLE_ROOT_1M_SHA256, hash));
+                checksums.put(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256,
+                        new ApkChecksum(split, TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, hash));
             }
         }
-        if ((kinds & PARTIAL_MERKLE_ROOT_1M_SHA512) != 0) {
+        if ((types & TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512) != 0) {
             byte[] hash = contentDigests.getOrDefault(CONTENT_DIGEST_CHUNKED_SHA512, null);
             if (hash != null) {
-                checksums.put(PARTIAL_MERKLE_ROOT_1M_SHA512,
-                        new ApkChecksum(split, PARTIAL_MERKLE_ROOT_1M_SHA512, hash));
+                checksums.put(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512,
+                        new ApkChecksum(split, TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512, hash));
             }
         }
         return checksums;
     }
 
-    private static String getMessageDigestAlgoForChecksumKind(int kind)
+    private static String getMessageDigestAlgoForChecksumKind(int type)
             throws NoSuchAlgorithmException {
-        switch (kind) {
-            case WHOLE_MD5:
+        switch (type) {
+            case TYPE_WHOLE_MD5:
                 return ALGO_MD5;
-            case WHOLE_SHA1:
+            case TYPE_WHOLE_SHA1:
                 return ALGO_SHA1;
-            case WHOLE_SHA256:
+            case TYPE_WHOLE_SHA256:
                 return ALGO_SHA256;
-            case WHOLE_SHA512:
+            case TYPE_WHOLE_SHA512:
                 return ALGO_SHA512;
             default:
-                throw new NoSuchAlgorithmException("Invalid checksum kind: " + kind);
+                throw new NoSuchAlgorithmException("Invalid checksum type: " + type);
         }
     }
 
     private static void calculateChecksumIfRequested(Map<Integer, ApkChecksum> checksums,
-            String split, File file, int required, int kind) {
-        if ((required & kind) != 0 && !checksums.containsKey(kind)) {
-            final byte[] checksum = getApkChecksum(file, kind);
+            String split, File file, int required, int type) {
+        if ((required & type) != 0 && !checksums.containsKey(type)) {
+            final byte[] checksum = getApkChecksum(file, type);
             if (checksum != null) {
-                checksums.put(kind, new ApkChecksum(split, kind, checksum));
+                checksums.put(type, new ApkChecksum(split, type, checksum));
             }
         }
     }
 
-    private static byte[] getApkChecksum(File file, int kind) {
+    private static byte[] getApkChecksum(File file, int type) {
         try (FileInputStream fis = new FileInputStream(file);
              BufferedInputStream bis = new BufferedInputStream(fis)) {
             byte[] dataBytes = new byte[512 * 1024];
             int nread = 0;
 
-            final String algo = getMessageDigestAlgoForChecksumKind(kind);
+            final String algo = getMessageDigestAlgoForChecksumKind(type);
             MessageDigest md = MessageDigest.getInstance(algo);
             while ((nread = bis.read(dataBytes)) != -1) {
                 md.update(dataBytes, 0, nread);
@@ -624,9 +625,9 @@
     private static int getChecksumKindForContentDigestAlgo(int contentDigestAlgo) {
         switch (contentDigestAlgo) {
             case CONTENT_DIGEST_CHUNKED_SHA256:
-                return PARTIAL_MERKLE_ROOT_1M_SHA256;
+                return TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256;
             case CONTENT_DIGEST_CHUNKED_SHA512:
-                return PARTIAL_MERKLE_ROOT_1M_SHA512;
+                return TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512;
             default:
                 return -1;
         }
@@ -635,11 +636,11 @@
     private static void calculatePartialChecksumsIfRequested(Map<Integer, ApkChecksum> checksums,
             String split, File file, int required) {
         boolean needSignatureSha256 =
-                (required & PARTIAL_MERKLE_ROOT_1M_SHA256) != 0 && !checksums.containsKey(
-                        PARTIAL_MERKLE_ROOT_1M_SHA256);
+                (required & TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256) != 0 && !checksums.containsKey(
+                        TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
         boolean needSignatureSha512 =
-                (required & PARTIAL_MERKLE_ROOT_1M_SHA512) != 0 && !checksums.containsKey(
-                        PARTIAL_MERKLE_ROOT_1M_SHA512);
+                (required & TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512) != 0 && !checksums.containsKey(
+                        TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512);
         if (!needSignatureSha256 && !needSignatureSha512) {
             return;
         }
diff --git a/services/core/java/com/android/server/pm/DumpState.java b/services/core/java/com/android/server/pm/DumpState.java
index 99a1093..520871f 100644
--- a/services/core/java/com/android/server/pm/DumpState.java
+++ b/services/core/java/com/android/server/pm/DumpState.java
@@ -43,6 +43,7 @@
     public static final int DUMP_SERVICE_PERMISSIONS = 1 << 24;
     public static final int DUMP_APEX = 1 << 25;
     public static final int DUMP_QUERIES = 1 << 26;
+    public static final int DUMP_KNOWN_PACKAGES = 1 << 27;
 
     public static final int OPTION_SHOW_FILTERS = 1 << 0;
     public static final int OPTION_DUMP_ALL_COMPONENTS = 1 << 1;
diff --git a/services/core/java/com/android/server/pm/IncrementalStates.java b/services/core/java/com/android/server/pm/IncrementalStates.java
index dda5faf..43f4a34 100644
--- a/services/core/java/com/android/server/pm/IncrementalStates.java
+++ b/services/core/java/com/android/server/pm/IncrementalStates.java
@@ -16,18 +16,21 @@
 
 package com.android.server.pm;
 
-import android.content.pm.IDataLoaderStatusListener;
+import static android.os.incremental.IStorageHealthListener.HEALTH_STATUS_OK;
+import static android.os.incremental.IStorageHealthListener.HEALTH_STATUS_UNHEALTHY;
+import static android.os.incremental.IStorageHealthListener.HEALTH_STATUS_UNHEALTHY_STORAGE;
+import static android.os.incremental.IStorageHealthListener.HEALTH_STATUS_UNHEALTHY_TRANSPORT;
+
 import android.content.pm.IncrementalStatesInfo;
 import android.content.pm.PackageManager;
 import android.os.Handler;
-import android.os.incremental.IStorageHealthListener;
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.function.pooled.PooledLambda;
 
-import java.util.function.BiConsumer;
+import java.util.function.Consumer;
 
 /**
  * Manages state transitions of a package installed on Incremental File System. Currently manages:
@@ -36,8 +39,7 @@
  *
  * The following events might change the states of a package:
  * 1. Installation commit
- * 2. Incremental storage health
- * 3. Data loader stream health
+ * 2. Incremental storage health changes
  * 4. Loading progress changes
  *
  * @hide
@@ -48,16 +50,14 @@
     private final Handler mHandler = BackgroundThread.getHandler();
     private final Object mLock = new Object();
     @GuardedBy("mLock")
-    private int mStreamStatus = IDataLoaderStatusListener.STREAM_HEALTHY;
-    @GuardedBy("mLock")
-    private int mStorageHealthStatus = IStorageHealthListener.HEALTH_STATUS_OK;
+    private int mStorageHealthStatus = HEALTH_STATUS_OK;
     @GuardedBy("mLock")
     private final LoadingState mLoadingState;
     @GuardedBy("mLock")
     private StartableState mStartableState;
     @GuardedBy("mLock")
     private Callback mCallback = null;
-    private final BiConsumer<Integer, Integer> mStatusConsumer;
+    private final Consumer<Integer> mStatusConsumer;
 
     public IncrementalStates() {
         // By default the package is not startable and not fully loaded (i.e., is loading)
@@ -148,12 +148,9 @@
         }
     }
 
-    private class StatusConsumer implements BiConsumer<Integer, Integer> {
+    private class StatusConsumer implements Consumer<Integer> {
         @Override
-        public void accept(Integer streamStatus, Integer storageStatus) {
-            if (streamStatus == null && storageStatus == null) {
-                return;
-            }
+        public void accept(Integer storageStatus) {
             final boolean oldState, newState;
             synchronized (mLock) {
                 if (!mLoadingState.isLoading()) {
@@ -161,12 +158,7 @@
                     return;
                 }
                 oldState = mStartableState.isStartable();
-                if (streamStatus != null) {
-                    mStreamStatus = (Integer) streamStatus;
-                }
-                if (storageStatus != null) {
-                    mStorageHealthStatus = (Integer) storageStatus;
-                }
+                mStorageHealthStatus = storageStatus;
                 updateStartableStateLocked();
                 newState = mStartableState.isStartable();
             }
@@ -188,21 +180,7 @@
             Slog.i(TAG, "received storage health status changed event : storageHealthStatus="
                     + storageHealthStatus);
         }
-        mStatusConsumer.accept(null, storageHealthStatus);
-    }
-
-    /**
-     * By calling this method, the caller indicates that the stream status of the package has
-     * been
-     * changed. This could indicate a streaming error. The state will change according to the
-     * status
-     * code defined in {@code IDataLoaderStatusListener}.
-     */
-    public void onStreamStatusChanged(int streamState) {
-        if (DEBUG) {
-            Slog.i(TAG, "received stream status changed event : streamState=" + streamState);
-        }
-        mStatusConsumer.accept(streamState, null);
+        mStatusConsumer.accept(storageHealthStatus);
     }
 
     /**
@@ -284,35 +262,16 @@
         final boolean currentState = mStartableState.isStartable();
         boolean nextState = currentState;
         if (!currentState) {
-            if (mStorageHealthStatus == IStorageHealthListener.HEALTH_STATUS_OK
-                    && mStreamStatus == IDataLoaderStatusListener.STREAM_HEALTHY) {
-                // change from unstartable -> startable when both stream and storage are healthy
+            if (mStorageHealthStatus == HEALTH_STATUS_OK) {
+                // change from unstartable -> startable
                 nextState = true;
             }
         } else {
-            if (mStorageHealthStatus == IStorageHealthListener.HEALTH_STATUS_UNHEALTHY) {
-                // unrecoverable if storage is unhealthy
+            if (mStorageHealthStatus == HEALTH_STATUS_UNHEALTHY
+                    || mStorageHealthStatus == HEALTH_STATUS_UNHEALTHY_STORAGE
+                    || mStorageHealthStatus == HEALTH_STATUS_UNHEALTHY_TRANSPORT) {
+                // change from startable -> unstartable
                 nextState = false;
-            } else {
-                switch (mStreamStatus) {
-                    case IDataLoaderStatusListener.STREAM_INTEGRITY_ERROR:
-                        // unrecoverable, fall through
-                    case IDataLoaderStatusListener.STREAM_SOURCE_ERROR: {
-                        // unrecoverable
-                        nextState = false;
-                        break;
-                    }
-                    case IDataLoaderStatusListener.STREAM_STORAGE_ERROR: {
-                        if (mStorageHealthStatus != IStorageHealthListener.HEALTH_STATUS_OK) {
-                            // unrecoverable if there is a pending read AND storage is limited
-                            nextState = false;
-                        }
-                        break;
-                    }
-                    default:
-                        // anything else, remain startable
-                        break;
-                }
             }
         }
         if (nextState == currentState) {
@@ -370,17 +329,11 @@
                 return PackageManager.UNSTARTABLE_REASON_UNKNOWN;
             }
             // Translate stream status to reason for unstartable state
-            switch (mStreamStatus) {
-                case IDataLoaderStatusListener.STREAM_TRANSPORT_ERROR:
-                    // fall through
-                case IDataLoaderStatusListener.STREAM_INTEGRITY_ERROR:
-                    // fall through
-                case IDataLoaderStatusListener.STREAM_SOURCE_ERROR: {
-                    return PackageManager.UNSTARTABLE_REASON_DATALOADER_TRANSPORT;
-                }
-                case IDataLoaderStatusListener.STREAM_STORAGE_ERROR: {
-                    return PackageManager.UNSTARTABLE_REASON_DATALOADER_STORAGE;
-                }
+            switch (mStorageHealthStatus) {
+                case HEALTH_STATUS_UNHEALTHY_STORAGE:
+                    return PackageManager.UNSTARTABLE_REASON_INSUFFICIENT_STORAGE;
+                case HEALTH_STATUS_UNHEALTHY_TRANSPORT:
+                    return PackageManager.UNSTARTABLE_REASON_CONNECTION_ERROR;
                 default:
                     return PackageManager.UNSTARTABLE_REASON_UNKNOWN;
             }
@@ -464,7 +417,6 @@
         }
         IncrementalStates l = (IncrementalStates) o;
         return l.mStorageHealthStatus == mStorageHealthStatus
-                && l.mStreamStatus == mStreamStatus
                 && l.mStartableState.equals(mStartableState)
                 && l.mLoadingState.equals(mLoadingState);
     }
@@ -474,7 +426,6 @@
         int hashCode = mStartableState.hashCode();
         hashCode = 31 * hashCode + mLoadingState.hashCode();
         hashCode = 31 * hashCode + mStorageHealthStatus;
-        hashCode = 31 * hashCode + mStreamStatus;
         return hashCode;
     }
 }
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index 9646b9c..c0329b2 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -771,8 +771,8 @@
             for (int i = 0; i < packageCount; i++) {
                 final String packageToDelete = packagesToDelete.get(i);
                 if (mService.deletePackageX(packageToDelete, PackageManager.VERSION_CODE_HIGHEST,
-                        UserHandle.USER_SYSTEM, PackageManager.DELETE_ALL_USERS)
-                                == PackageManager.DELETE_SUCCEEDED) {
+                        UserHandle.USER_SYSTEM, PackageManager.DELETE_ALL_USERS,
+                        true /*removedBySystem*/) == PackageManager.DELETE_SUCCEEDED) {
                     if (file.getUsableSpace() >= neededSpace) {
                         return true;
                     }
diff --git a/services/core/java/com/android/server/pm/InstantAppResolverConnection.java b/services/core/java/com/android/server/pm/InstantAppResolverConnection.java
index a9a4589..4f75f04 100644
--- a/services/core/java/com/android/server/pm/InstantAppResolverConnection.java
+++ b/services/core/java/com/android/server/pm/InstantAppResolverConnection.java
@@ -139,7 +139,7 @@
     @WorkerThread
     private IInstantAppResolver getRemoteInstanceLazy(String token)
             throws ConnectionException, TimeoutException, InterruptedException {
-        long binderToken = Binder.clearCallingIdentity();
+        final long binderToken = Binder.clearCallingIdentity();
         try {
             return bind(token);
         } finally {
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 4c1b700..9bb6f78 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -261,7 +261,7 @@
             verifyCallingPackage(callingPackage);
             List<SessionInfo> sessionInfos = new ArrayList<>();
             int[] userIds = mUm.getEnabledProfileIds(getCallingUserId());
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 for (int userId : userIds) {
                     sessionInfos.addAll(getPackageInstallerService().getAllSessions(userId)
@@ -541,7 +541,7 @@
             }
 
             final int callingUid = injectBinderCallingUid();
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
                 final PackageManagerInternal pmInt =
                         LocalServices.getService(PackageManagerInternal.class);
@@ -615,7 +615,7 @@
             }
 
             final int callingUid = injectBinderCallingUid();
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
                 final PackageManagerInternal pmInt =
                         LocalServices.getService(PackageManagerInternal.class);
@@ -649,7 +649,7 @@
             }
 
             final int callingUid = injectBinderCallingUid();
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
                 final PackageManagerInternal pmInt =
                         LocalServices.getService(PackageManagerInternal.class);
@@ -928,7 +928,7 @@
             }
 
             final int callingUid = injectBinderCallingUid();
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
                 final int state = mIPM.getComponentEnabledSetting(component, user.getIdentifier());
                 switch (state) {
@@ -999,7 +999,7 @@
             boolean canLaunch = false;
 
             final int callingUid = injectBinderCallingUid();
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
                 final PackageManagerInternal pmInt =
                         LocalServices.getService(PackageManagerInternal.class);
@@ -1050,7 +1050,7 @@
             }
 
             final Intent intent;
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
                 String packageName = component.getPackageName();
                 intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index da31c8c..c282b99 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -244,7 +244,7 @@
         mSessionsDir.mkdirs();
 
         mApexManager = ApexManager.getInstance();
-        mStagingManager = new StagingManager(this, context, apexParserSupplier);
+        mStagingManager = new StagingManager(context, apexParserSupplier);
 
         LocalServices.getService(SystemServiceManager.class).startService(
                 new Lifecycle(context, this));
@@ -745,9 +745,6 @@
         synchronized (mSessions) {
             mSessions.put(sessionId, session);
         }
-        if (params.isStaged) {
-            mStagingManager.createSession(session);
-        }
 
         mCallbacks.notifySessionCreated(session.sessionId, session.userId);
 
@@ -979,7 +976,7 @@
         } else if (canSilentlyInstallPackage) {
             // Allow the device owner and affiliated profile owner to silently delete packages
             // Need to clear the calling identity to get DELETE_PACKAGES permission
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
                 mPm.deletePackageVersioned(versionedPackage, adapter.getBinder(), userId, flags);
             } finally {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 2aafe9a..5d2928e 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -28,6 +28,8 @@
 import static android.content.pm.PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SPLIT;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING;
+import static android.content.pm.PackageManager.INSTALL_STAGED;
+import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
 import static android.content.pm.PackageParser.APEX_FILE_EXTENSION;
 import static android.content.pm.PackageParser.APK_FILE_EXTENSION;
 import static android.system.OsConstants.O_CREAT;
@@ -141,7 +143,6 @@
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
-import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.LocalServices;
 import com.android.server.pm.Installer.InstallerException;
 import com.android.server.pm.dex.DexManager;
@@ -1458,7 +1459,7 @@
                     mChildSessionsRemaining.removeAt(sessionIndex);
                     if (mChildSessionsRemaining.size() == 0) {
                         destroyInternal();
-                        dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED,
+                        dispatchSessionFinished(INSTALL_SUCCEEDED,
                                 "Session installed", null);
                     }
                 } else if (PackageInstaller.STATUS_PENDING_USER_ACTION == status) {
@@ -1533,7 +1534,7 @@
 
         synchronized (mLock) {
             assertCallerIsOwnerOrRootLocked();
-            assertPreparedAndNotDestroyedLocked("commit");
+            assertPreparedAndNotDestroyedLocked("commit of session " + sessionId);
             assertNoWriteFileTransfersOpenLocked();
 
             final boolean isSecureFrpEnabled =
@@ -1600,13 +1601,6 @@
                         validateApkInstallLocked();
                     }
                 }
-            }
-
-            if (params.isStaged) {
-                mStagingManager.checkNonOverlappingWithStagedSessions(this);
-            }
-
-            synchronized (mLock) {
                 if (mDestroyed) {
                     throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
                             "Session destroyed");
@@ -1663,7 +1657,7 @@
             throws PackageManagerException {
         try {
             assertNoWriteFileTransfersOpenLocked();
-            assertPreparedAndNotDestroyedLocked("sealing of session");
+            assertPreparedAndNotDestroyedLocked("sealing of session " + sessionId);
             mSealed = true;
         } catch (Throwable e) {
             // Convert all exceptions into package manager exceptions as only those are handled
@@ -1701,26 +1695,10 @@
         }
     }
 
-    private void onStorageHealthStatusChanged(int status) {
-        final String packageName = getPackageName();
-        if (TextUtils.isEmpty(packageName)) {
-            // The package has not been installed.
-            return;
-        }
-        mHandler.post(PooledLambda.obtainRunnable(
-                PackageManagerService::onStorageHealthStatusChanged,
-                mPm, packageName, status, userId).recycleOnUse());
-    }
-
-    private void onStreamHealthStatusChanged(int status) {
-        final String packageName = getPackageName();
-        if (TextUtils.isEmpty(packageName)) {
-            // The package has not been installed.
-            return;
-        }
-        mHandler.post(PooledLambda.obtainRunnable(
-                PackageManagerService::onStreamStatusChanged,
-                mPm, packageName, status, userId).recycleOnUse());
+    private void onSessionInstallationFailure(int error, String detailedMessage) {
+        Slog.e(TAG, "Install of session " + sessionId + " failed: " + detailedMessage);
+        destroyInternal();
+        dispatchSessionFinished(error, detailedMessage, null);
     }
 
     /**
@@ -1757,15 +1735,18 @@
             try {
                 sealLocked();
 
-                if (isApexSession()) {
-                    // APEX installations rely on certain fields to be populated after reboot.
-                    // E.g. mPackageName.
-                    validateApexInstallLocked();
-                } else {
-                    // Populate mPackageName for this APK session which is required by the staging
-                    // manager to check duplicate apk-in-apex.
-                    PackageInstallerSession parent = allSessions.get(mParentSessionId);
-                    if (parent != null && parent.isStagedSessionReady()) {
+                // Session that are staged, ready and not multi package will be installed during
+                // this boot. As such, we need populate all the fields for successful installation.
+                if (isMultiPackage()) {
+                    return;
+                }
+                final PackageInstallerSession root = hasParentSessionId()
+                        ? allSessions.get(getParentSessionId())
+                        : this;
+                if (root != null && root.isStagedSessionReady()) {
+                    if (isApexSession()) {
+                        validateApexInstallLocked();
+                    } else {
                         validateApkInstallLocked();
                     }
                 }
@@ -1829,7 +1810,7 @@
             mStagingManager.commitSession(this);
             // TODO(b/136257624): CTS test fails if we don't send session finished broadcast, even
             //  though ideally, we just need to send session committed broadcast.
-            dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "Session staged", null);
+            dispatchSessionFinished(INSTALL_SUCCEEDED, "Session staged", null);
             return;
         }
 
@@ -1911,12 +1892,53 @@
         }
     }
 
+    /**
+     * Installs apks of staged session while skipping the verification process for a committed and
+     * ready session.
+     */
+    void installStagedSession(IntentSender statusReceiver) {
+        assertCallerIsOwnerOrRootOrSystemLocked();
+        Preconditions.checkArgument(!hasParentSessionId()); // Don't allow installing child sessions
+        Preconditions.checkArgument(isCommitted() && isStagedSessionReady());
+
+        // Since staged sessions are installed during boot, the original reference to status
+        // receiver from the owner has already been lost. We can safely replace it with a
+        // status receiver from the system without effecting the flow.
+        updateRemoteStatusReceiver(statusReceiver);
+        install();
+    }
+
+    private void updateRemoteStatusReceiver(IntentSender remoteStatusReceiver) {
+        synchronized (mLock) {
+            mRemoteStatusReceiver = remoteStatusReceiver;
+            if (isMultiPackage()) {
+                final IntentSender childIntentSender =
+                        new ChildStatusIntentReceiver(mChildSessions.clone(), remoteStatusReceiver)
+                                .getIntentSender();
+                for (int i = mChildSessions.size() - 1; i >= 0; --i) {
+                    mChildSessions.valueAt(i).mRemoteStatusReceiver = childIntentSender;
+                }
+            }
+        }
+    }
+
+    private void install() {
+        try {
+            installNonStaged();
+        } catch (PackageManagerException e) {
+            final String completeMsg = ExceptionUtils.getCompleteMessage(e);
+            onSessionInstallationFailure(e.error, completeMsg);
+        }
+    }
+
     private void installNonStaged()
             throws PackageManagerException {
-        final PackageManagerService.InstallParams installingSession =
-                makeInstallParams();
+        Preconditions.checkArgument(containsApkSession());
+
+        final PackageManagerService.InstallParams installingSession = makeInstallParams();
         if (installingSession == null) {
-            return;
+            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+                    "Session should contain at least one apk session for installation");
         }
         if (isMultiPackage()) {
             final List<PackageInstallerSession> childSessions;
@@ -2084,7 +2106,7 @@
                 @Override
                 public void onPackageInstalled(String basePackageName, int returnCode, String msg,
                         Bundle extras) {
-                    if (returnCode == PackageManager.INSTALL_SUCCEEDED) {
+                    if (returnCode == INSTALL_SUCCEEDED) {
                         onVerificationComplete();
                     } else {
                         onSessionVerificationFailure(returnCode, msg);
@@ -2126,20 +2148,14 @@
             return;
         }
 
-        try {
-            installNonStaged();
-        } catch (PackageManagerException e) {
-            final String completeMsg = ExceptionUtils.getCompleteMessage(e);
-            Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
-            destroyInternal();
-            dispatchSessionFinished(e.error, completeMsg, null);
-        }
+        install();
     }
 
     /**
      * Stages this session for install and returns a
      * {@link PackageManagerService.InstallParams} representing this new staged state.
      */
+    @Nullable
     private PackageManagerService.InstallParams makeInstallParams()
             throws PackageManagerException {
         synchronized (mLock) {
@@ -2153,8 +2169,13 @@
             }
         }
 
-        // We've reached point of no return; call into PMS to install the stage.
-        // Regardless of success or failure we always destroy session.
+        // Do not try to install apex session. Parent session will have at least one apk session.
+        if (!isMultiPackage() && isApexSession()) {
+            sendUpdateToRemoteStatusReceiver(INSTALL_SUCCEEDED,
+                    "Apex package should have been installed by apexd", null);
+            return null;
+        }
+
         final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() {
             @Override
             public void onUserActionRequired(Intent intent) {
@@ -2164,8 +2185,14 @@
             @Override
             public void onPackageInstalled(String basePackageName, int returnCode, String msg,
                     Bundle extras) {
-                destroyInternal();
-                dispatchSessionFinished(returnCode, msg, extras);
+                if (isStaged()) {
+                    sendUpdateToRemoteStatusReceiver(returnCode, msg, extras);
+                } else {
+                    // We've reached point of no return; call into PMS to install the stage.
+                    // Regardless of success or failure we always destroy session.
+                    destroyInternal();
+                    dispatchSessionFinished(returnCode, msg, extras);
+                }
             }
         };
 
@@ -2176,6 +2203,10 @@
             user = new UserHandle(userId);
         }
 
+        if (params.isStaged) {
+            params.installFlags |= INSTALL_STAGED;
+        }
+
         synchronized (mLock) {
             return mPm.new InstallParams(stageDir, localObserver, params, mInstallSource, user,
                     mSigningDetails, mInstallerUid);
@@ -2250,7 +2281,7 @@
         return (params.installFlags & PackageManager.INSTALL_APEX) != 0;
     }
 
-    private boolean sessionContains(Predicate<PackageInstallerSession> filter) {
+    boolean sessionContains(Predicate<PackageInstallerSession> filter) {
         if (!isMultiPackage()) {
             return filter.test(this);
         }
@@ -2952,7 +2983,7 @@
             handle = NativeLibraryHelper.Handle.create(packageDir);
             final int res = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libDir,
                     abiOverride, isIncrementalInstallation());
-            if (res != PackageManager.INSTALL_SUCCEEDED) {
+            if (res != INSTALL_SUCCEEDED) {
                 throw new PackageManagerException(res,
                         "Failed to extract native libraries, res=" + res);
             }
@@ -3261,19 +3292,11 @@
                         return;
                 }
 
-                final boolean isDestroyedOrDataLoaderFinished;
                 synchronized (mLock) {
-                    isDestroyedOrDataLoaderFinished = mDestroyed || mDataLoaderFinished;
-                }
-                if (isDestroyedOrDataLoaderFinished) {
-                    switch (status) {
-                        case IDataLoaderStatusListener.DATA_LOADER_UNRECOVERABLE:
-                            // treat as unhealthy storage
-                            onStorageHealthStatusChanged(
-                                    IStorageHealthListener.HEALTH_STATUS_UNHEALTHY);
-                            return;
+                    if (mDestroyed || mDataLoaderFinished) {
+                        // No need to worry about post installation
+                        return;
                     }
-                    return;
                 }
 
                 try {
@@ -3369,13 +3392,10 @@
             }
             @Override
             public void reportStreamHealth(int dataLoaderId, int streamStatus) {
-                synchronized (mLock) {
-                    if (!mDestroyed && !mDataLoaderFinished) {
-                        // ignore streaming status if package isn't installed
-                        return;
-                    }
-                }
-                onStreamHealthStatusChanged(streamStatus);
+                // Currently the stream status is not used during package installation. It is
+                // technically possible for the data loader to report stream status via this
+                // callback, but if something is wrong with the streaming, it is more likely that
+                // prepareDataLoaderLocked will return false and the installation will be aborted.
             }
         };
 
@@ -3384,20 +3404,16 @@
             healthCheckParams.blockedTimeoutMs = INCREMENTAL_STORAGE_BLOCKED_TIMEOUT_MS;
             healthCheckParams.unhealthyTimeoutMs = INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS;
             healthCheckParams.unhealthyMonitoringMs = INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS;
-
             final boolean systemDataLoader =
                     params.getComponentName().getPackageName() == SYSTEM_DATA_LOADER_PACKAGE;
             final IStorageHealthListener healthListener = new IStorageHealthListener.Stub() {
                 @Override
                 public void onHealthStatus(int storageId, int status) {
-                    final boolean isDestroyedOrDataLoaderFinished;
                     synchronized (mLock) {
-                        isDestroyedOrDataLoaderFinished = mDestroyed || mDataLoaderFinished;
-                    }
-                    if (isDestroyedOrDataLoaderFinished) {
-                        // App's installed.
-                        onStorageHealthStatusChanged(status);
-                        return;
+                        if (mDestroyed || mDataLoaderFinished) {
+                            // No need to worry about post installation
+                            return;
+                        }
                     }
 
                     switch (status) {
@@ -3586,31 +3602,14 @@
     }
 
     private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) {
-        final IntentSender statusReceiver;
-        final String packageName;
+        sendUpdateToRemoteStatusReceiver(returnCode, msg, extras);
+
         synchronized (mLock) {
             mFinalStatus = returnCode;
             mFinalMessage = msg;
-
-            statusReceiver = mRemoteStatusReceiver;
-            packageName = mPackageName;
         }
 
-        if (statusReceiver != null) {
-            // Execute observer.onPackageInstalled on different thread as we don't want callers
-            // inside the system server have to worry about catching the callbacks while they are
-            // calling into the session
-            final SomeArgs args = SomeArgs.obtain();
-            args.arg1 = packageName;
-            args.arg2 = msg;
-            args.arg3 = extras;
-            args.arg4 = statusReceiver;
-            args.argi1 = returnCode;
-
-            mHandler.obtainMessage(MSG_ON_PACKAGE_INSTALLED, args).sendToTarget();
-        }
-
-        final boolean success = (returnCode == PackageManager.INSTALL_SUCCEEDED);
+        final boolean success = (returnCode == INSTALL_SUCCEEDED);
 
         // Send broadcast to default launcher only if it's a new install
         // TODO(b/144270665): Secure the usage of this broadcast.
@@ -3625,6 +3624,27 @@
         }
     }
 
+    private void sendUpdateToRemoteStatusReceiver(int returnCode, String msg, Bundle extras) {
+        final IntentSender statusReceiver;
+        final String packageName;
+        synchronized (mLock) {
+            statusReceiver = mRemoteStatusReceiver;
+            packageName = mPackageName;
+        }
+        if (statusReceiver != null) {
+            // Execute observer.onPackageInstalled on different thread as we don't want callers
+            // inside the system server have to worry about catching the callbacks while they are
+            // calling into the session
+            final SomeArgs args = SomeArgs.obtain();
+            args.arg1 = packageName;
+            args.arg2 = msg;
+            args.arg3 = extras;
+            args.arg4 = statusReceiver;
+            args.argi1 = returnCode;
+            mHandler.obtainMessage(MSG_ON_PACKAGE_INSTALLED, args).sendToTarget();
+        }
+    }
+
     /** {@hide} */
     void setStagedSessionReady() {
         synchronized (mLock) {
@@ -3834,7 +3854,7 @@
     private static void sendOnPackageInstalled(Context context, IntentSender target, int sessionId,
             boolean showNotification, int userId, String basePackageName, int returnCode,
             String msg, Bundle extras) {
-        if (PackageManager.INSTALL_SUCCEEDED == returnCode && showNotification) {
+        if (INSTALL_SUCCEEDED == returnCode && showNotification) {
             boolean update = (extras != null) && extras.getBoolean(Intent.EXTRA_REPLACING);
             Notification notification = PackageInstallerService.buildSuccessNotification(context,
                     context.getResources()
@@ -4055,7 +4075,7 @@
                     CertifiedChecksum checksum = checksums.get(j);
                     out.startTag(null, TAG_SESSION_CHECKSUM);
                     writeStringAttribute(out, ATTR_NAME, fileName);
-                    writeIntAttribute(out, ATTR_CHECKSUM_KIND, checksum.getChecksum().getKind());
+                    writeIntAttribute(out, ATTR_CHECKSUM_KIND, checksum.getChecksum().getType());
                     writeByteArrayAttribute(out, ATTR_CHECKSUM_VALUE,
                             checksum.getChecksum().getValue());
                     writeStringAttribute(out, ATTR_CHECKSUM_PACKAGE, checksum.getPackageName());
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 8638165..0031f75 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1,6 +1,5 @@
 /*
  * Copyright (C) 2006 The Android Open Source Project
- *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
@@ -47,8 +46,10 @@
 import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER;
 import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
+import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_PERMISSION_GROUP;
 import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION;
+import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION_GROUP;
 import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
 import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
@@ -66,6 +67,7 @@
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
 import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_RESTORE;
 import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_SETUP;
+import static android.content.pm.PackageManager.INSTALL_STAGED;
 import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
@@ -93,6 +95,7 @@
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.content.pm.PackageManager.RESTRICTION_NONE;
 import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN;
+import static android.content.pm.PackageManagerInternal.LAST_KNOWN_PACKAGE;
 import static android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4;
 import static android.content.pm.PackageParser.isApkFile;
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
@@ -231,6 +234,7 @@
 import android.content.pm.parsing.component.ParsedIntentInfo;
 import android.content.pm.parsing.component.ParsedMainComponent;
 import android.content.pm.parsing.component.ParsedPermission;
+import android.content.pm.parsing.component.ParsedPermissionGroup;
 import android.content.pm.parsing.component.ParsedProcess;
 import android.content.pm.parsing.component.ParsedProvider;
 import android.content.pm.parsing.component.ParsedService;
@@ -269,8 +273,10 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.UserManagerInternal;
+import android.os.incremental.IStorageHealthListener;
 import android.os.incremental.IncrementalManager;
 import android.os.incremental.IncrementalStorage;
+import android.os.incremental.StorageHealthCheckParams;
 import android.os.storage.DiskInfo;
 import android.os.storage.IStorageManager;
 import android.os.storage.StorageEventListener;
@@ -504,9 +510,6 @@
 
     private static final boolean HIDE_EPHEMERAL_APIS = false;
 
-    private static final boolean ENABLE_FREE_CACHE_V2 =
-            SystemProperties.getBoolean("fw.free_cache_v2", true);
-
     private static final String PRECOMPILE_LAYOUTS = "pm.precompile_layouts";
 
     private static final int RADIO_UID = Process.PHONE_UID;
@@ -685,6 +688,8 @@
 
     private static final String PACKAGE_SCHEME = "package";
 
+    private static final String COMPANION_PACKAGE_NAME = "com.android.companiondevicemanager";
+
     /** Canonical intent used to identify what counts as a "web browser" app */
     private static final Intent sBrowserIntent;
     static {
@@ -728,14 +733,23 @@
 
     private static final String RANDOM_DIR_PREFIX = "~~";
 
+    /**
+     * Timeout configurations for incremental storage health monitor.
+     * See {@link IStorageHealthListener}
+     */
+    private static final int INCREMENTAL_STORAGE_BLOCKED_TIMEOUT_MS = 2000;
+    private static final int INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS = 7000;
+    private static final int INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS = 60000;
+
     final ServiceThread mHandlerThread;
 
     final Handler mHandler;
 
     private final ProcessLoggingHandler mProcessLoggingHandler;
 
-    final int mSdkVersion = Build.VERSION.SDK_INT;
+    private final boolean mEnableFreeCacheV2;
 
+    final int mSdkVersion;
     final Context mContext;
     final boolean mFactoryTest;
     final boolean mOnlyCore;
@@ -890,24 +904,8 @@
             T produce(Injector injector, PackageManagerService packageManager);
         }
 
-        static class LocalServicesProducer<T> implements Producer<T> {
-            private final Class<T> mProducingClass;
-            LocalServicesProducer(Class<T> clazz) {
-                this.mProducingClass = clazz;
-            }
-            public T produce(Injector injector, PackageManagerService packageManager) {
-                return LocalServices.getService(mProducingClass);
-            }
-        }
-
-        static class SystemServiceProducer<T> implements Producer<T> {
-            private final Class<T> mProducingClass;
-            SystemServiceProducer(Class<T> clazz) {
-                this.mProducingClass = clazz;
-            }
-            public T produce(Injector injector, PackageManagerService packageManager) {
-                return packageManager.mContext.getSystemService(mProducingClass);
-            }
+        interface ServiceProducer {
+            <T> T produce(Class<T> c);
         }
 
         @VisibleForTesting(visibility = Visibility.PRIVATE)
@@ -937,41 +935,43 @@
 
         // ----- producers -----
         private final Singleton<ComponentResolver> mComponentResolverProducer;
-        private final Singleton<PermissionManagerServiceInternal> mPermissionManagerProducer;
+        private final Singleton<PermissionManagerServiceInternal> mPermissionManagerServiceProducer;
         private final Singleton<UserManagerService> mUserManagerProducer;
         private final Singleton<Settings> mSettingsProducer;
-        private final Singleton<ActivityTaskManagerInternal> mActivityTaskManagerProducer;
-        private final Singleton<ActivityManagerInternal> mActivityManagerInternalProducer;
-        private final Singleton<DeviceIdleInternal> mLocalDeviceIdleController;
-        private final Singleton<StorageManagerInternal> mStorageManagerInternalProducer;
-        private final Singleton<NetworkPolicyManagerInternal> mNetworkPolicyManagerProducer;
-        private final Singleton<PermissionPolicyInternal> mPermissionPolicyProducer;
-        private final Singleton<DeviceStorageMonitorInternal> mDeviceStorageMonitorProducer;
-        private final Singleton<DisplayManager> mDisplayManagerProducer;
-        private final Singleton<StorageManager> mStorageManagerProducer;
-        private final Singleton<AppOpsManager> mAppOpsManagerProducer;
         private final Singleton<AppsFilter> mAppsFilterProducer;
         private final Singleton<PlatformCompat> mPlatformCompatProducer;
+        private final Singleton<SystemConfig> mSystemConfigProducer;
+        private final Singleton<PackageDexOptimizer> mPackageDexOptimizerProducer;
+        private final Singleton<DexManager> mDexManagerProducer;
+        private final Singleton<ArtManagerService> mArtManagerServiceProducer;
+        private final Singleton<ApexManager> mApexManagerProducer;
+        private final Singleton<ViewCompiler> mViewCompilerProducer;
+        private final Singleton<IPermissionManager> mPermissionManagerProducer;
+        private final Singleton<IncrementalManager> mIncrementalManagerProducer;
+        private final SystemWrapper mSystemWrapper;
+        private final ServiceProducer mGetLocalServiceProducer;
+        private final ServiceProducer mGetSystemServiceProducer;
 
         Injector(Context context, Object lock, Installer installer,
                 Object installLock, PackageAbiHelper abiHelper,
                 Handler backgroundHandler,
                 Producer<ComponentResolver> componentResolverProducer,
-                Producer<PermissionManagerServiceInternal> permissionManagerProducer,
+                Producer<PermissionManagerServiceInternal> permissionManagerServiceProducer,
                 Producer<UserManagerService> userManagerProducer,
                 Producer<Settings> settingsProducer,
-                Producer<ActivityTaskManagerInternal> activityTaskManagerProducer,
-                Producer<ActivityManagerInternal> activityManagerInternalProducer,
-                Producer<DeviceIdleInternal> deviceIdleControllerProducer,
-                Producer<StorageManagerInternal> storageManagerInternalProducer,
-                Producer<NetworkPolicyManagerInternal> networkPolicyManagerProducer,
-                Producer<PermissionPolicyInternal> permissionPolicyProvider,
-                Producer<DeviceStorageMonitorInternal> deviceStorageMonitorProducer,
-                Producer<DisplayManager> displayManagerProducer,
-                Producer<StorageManager> storageManagerProducer,
-                Producer<AppOpsManager> appOpsManagerProducer,
                 Producer<AppsFilter> appsFilterProducer,
-                Producer<PlatformCompat> platformCompatProducer) {
+                Producer<PlatformCompat> platformCompatProducer,
+                Producer<SystemConfig> systemConfigProducer,
+                Producer<PackageDexOptimizer> packageDexOptimizerProducer,
+                Producer<DexManager> dexManagerProducer,
+                Producer<ArtManagerService> artManagerServiceProducer,
+                Producer<ApexManager> apexManagerProducer,
+                Producer<IPermissionManager> permissionManagerProducer,
+                Producer<ViewCompiler> viewCompilerProducer,
+                Producer<IncrementalManager> incrementalManagerProducer,
+                SystemWrapper systemWrapper,
+                ServiceProducer getLocalServiceProducer,
+                ServiceProducer getSystemServiceProducer) {
             mContext = context;
             mLock = lock;
             mInstaller = installer;
@@ -980,21 +980,22 @@
             mBackgroundHandler = backgroundHandler;
             mBackgroundExecutor = new HandlerExecutor(backgroundHandler);
             mComponentResolverProducer = new Singleton<>(componentResolverProducer);
-            mPermissionManagerProducer = new Singleton<>(permissionManagerProducer);
+            mPermissionManagerServiceProducer = new Singleton<>(permissionManagerServiceProducer);
             mUserManagerProducer = new Singleton<>(userManagerProducer);
             mSettingsProducer = new Singleton<>(settingsProducer);
-            mActivityTaskManagerProducer = new Singleton<>(activityTaskManagerProducer);
-            mActivityManagerInternalProducer = new Singleton<>(activityManagerInternalProducer);
-            mLocalDeviceIdleController = new Singleton<>(deviceIdleControllerProducer);
-            mStorageManagerInternalProducer = new Singleton<>(storageManagerInternalProducer);
-            mNetworkPolicyManagerProducer = new Singleton<>(networkPolicyManagerProducer);
-            mPermissionPolicyProducer = new Singleton<>(permissionPolicyProvider);
-            mDeviceStorageMonitorProducer = new Singleton<>(deviceStorageMonitorProducer);
-            mDisplayManagerProducer = new Singleton<>(displayManagerProducer);
-            mStorageManagerProducer = new Singleton<>(storageManagerProducer);
-            mAppOpsManagerProducer = new Singleton<>(appOpsManagerProducer);
             mAppsFilterProducer = new Singleton<>(appsFilterProducer);
             mPlatformCompatProducer = new Singleton<>(platformCompatProducer);
+            mSystemConfigProducer = new Singleton<>(systemConfigProducer);
+            mPackageDexOptimizerProducer = new Singleton<>(packageDexOptimizerProducer);
+            mDexManagerProducer = new Singleton<>(dexManagerProducer);
+            mArtManagerServiceProducer = new Singleton<>(artManagerServiceProducer);
+            mApexManagerProducer = new Singleton<>(apexManagerProducer);
+            mPermissionManagerProducer = new Singleton<>(permissionManagerProducer);
+            mViewCompilerProducer = new Singleton<>(viewCompilerProducer);
+            mIncrementalManagerProducer = new Singleton<>(incrementalManagerProducer);
+            mSystemWrapper = systemWrapper;
+            mGetLocalServiceProducer = getLocalServiceProducer;
+            mGetSystemServiceProducer = getSystemServiceProducer;
         }
 
         /**
@@ -1034,7 +1035,7 @@
         }
 
         public PermissionManagerServiceInternal getPermissionManagerServiceInternal() {
-            return mPermissionManagerProducer.get(this, mPackageManager);
+            return mPermissionManagerServiceProducer.get(this, mPackageManager);
         }
 
         public Context getContext() {
@@ -1045,46 +1046,6 @@
             return mSettingsProducer.get(this, mPackageManager);
         }
 
-        public ActivityTaskManagerInternal getActivityTaskManagerInternal() {
-            return mActivityTaskManagerProducer.get(this, mPackageManager);
-        }
-
-        public ActivityManagerInternal getActivityManagerInternal() {
-            return mActivityManagerInternalProducer.get(this, mPackageManager);
-        }
-
-        public DeviceIdleInternal getLocalDeviceIdleController() {
-            return mLocalDeviceIdleController.get(this, mPackageManager);
-        }
-
-        public StorageManagerInternal getStorageManagerInternal() {
-            return mStorageManagerInternalProducer.get(this, mPackageManager);
-        }
-
-        public NetworkPolicyManagerInternal getNetworkPolicyManagerInternal() {
-            return mNetworkPolicyManagerProducer.get(this, mPackageManager);
-        }
-
-        public PermissionPolicyInternal getPermissionPolicyInternal() {
-            return mPermissionPolicyProducer.get(this, mPackageManager);
-        }
-
-        public DeviceStorageMonitorInternal getDeviceStorageMonitorInternal() {
-            return mDeviceStorageMonitorProducer.get(this, mPackageManager);
-        }
-
-        public DisplayManager getDisplayManager() {
-            return mDisplayManagerProducer.get(this, mPackageManager);
-        }
-
-        public StorageManager getStorageManager() {
-            return mStorageManagerProducer.get(this, mPackageManager);
-        }
-
-        public AppOpsManager getAppOpsManager() {
-            return mAppOpsManagerProducer.get(this, mPackageManager);
-        }
-
         public AppsFilter getAppsFilter() {
             return mAppsFilterProducer.get(this, mPackageManager);
         }
@@ -1093,6 +1054,34 @@
             return mPlatformCompatProducer.get(this, mPackageManager);
         }
 
+        public SystemConfig getSystemConfig() {
+            return mSystemConfigProducer.get(this, mPackageManager);
+        }
+
+        public PackageDexOptimizer getPackageDexOptimizer() {
+            return mPackageDexOptimizerProducer.get(this, mPackageManager);
+        }
+
+        public DexManager getDexManager() {
+            return mDexManagerProducer.get(this, mPackageManager);
+        }
+
+        public ArtManagerService getArtManagerService() {
+            return mArtManagerServiceProducer.get(this, mPackageManager);
+        }
+
+        public ApexManager getApexManager() {
+            return mApexManagerProducer.get(this, mPackageManager);
+        }
+
+        public ViewCompiler getViewCompiler() {
+            return mViewCompilerProducer.get(this, mPackageManager);
+        }
+
+        public IPermissionManager getPermissionManagerService() {
+            return mPermissionManagerProducer.get(this, mPackageManager);
+        }
+
         public Handler getBackgroundHandler() {
             return mBackgroundHandler;
         }
@@ -1100,6 +1089,70 @@
         public Executor getBackgroundExecutor() {
             return mBackgroundExecutor;
         }
+
+        public <T> T getLocalService(Class<T> c) {
+            return mGetLocalServiceProducer.produce(c);
+        }
+
+        public <T> T getSystemService(Class<T> c) {
+            return mGetSystemServiceProducer.produce(c);
+        }
+
+        public SystemWrapper getSystemWrapper() {
+            return mSystemWrapper;
+        }
+
+        public IncrementalManager getIncrementalManager() {
+            return mIncrementalManagerProducer.get(this, mPackageManager);
+        }
+    }
+
+    /** Provides an abstraction to static access to system state. */
+    public interface SystemWrapper {
+        /** @see SystemProperties#get(String) */
+        String getProperty(String key);
+        /** @see SystemProperties#getInt(String, int) */
+        int getPropertyInt(String key, int defValue);
+        /** @see SystemProperties#getBoolean(String, boolean) */
+        boolean getPropertyBoolean(String key, boolean defValue);
+        /** @see SystemProperties#digestOf(String...) */
+        String digestOfProperties(@NonNull String... keys);
+        /** @see SystemProperties#set(String, String) */
+        void setProperty(String key, String value);
+        /** @see Build.VERSION#SDK_INT */
+        int getSdkInt();
+    }
+
+    private static class DefaultSystemWrapper implements SystemWrapper {
+        @Override
+        public String getProperty(String key) {
+            return SystemProperties.get(key);
+        }
+
+        @Override
+        public int getPropertyInt(String key, int defValue) {
+            return SystemProperties.getInt(key, defValue);
+        }
+
+        @Override
+        public boolean getPropertyBoolean(String key, boolean defValue) {
+            return SystemProperties.getBoolean(key, defValue);
+        }
+
+        @Override
+        public String digestOfProperties(String... keys) {
+            return SystemProperties.digestOf(keys);
+        }
+
+        @Override
+        public void setProperty(String key, String value) {
+            SystemProperties.set(key, value);
+        }
+
+        @Override
+        public int getSdkInt() {
+            return Build.VERSION.SDK_INT;
+        }
     }
 
     @VisibleForTesting(visibility = Visibility.PRIVATE)
@@ -1157,6 +1210,8 @@
         public @Nullable String retailDemoPackage;
         public ComponentName resolveComponentName;
         public ArrayMap<String, AndroidPackage> packages;
+        public boolean enableFreeCacheV2;
+        public int sdkVersion;
     }
 
     private final AppsFilter mAppsFilter;
@@ -1350,7 +1405,7 @@
             options.setTemporaryAppWhitelistDuration(whitelistTimeout);
 
             DeviceIdleInternal idleController =
-                    mInjector.getLocalDeviceIdleController();
+                    mInjector.getLocalService(DeviceIdleInternal.class);
             idleController.addPowerSaveTempWhitelistApp(Process.myUid(),
                     mIntentFilterVerifierComponent.getPackageName(), whitelistTimeout,
                     UserHandle.USER_SYSTEM, true, "intent filter verifier");
@@ -1423,7 +1478,7 @@
                         case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS:
                             if (!verified) {
                                 // Don't demote if sysconfig says 'always'
-                                SystemConfig systemConfig = SystemConfig.getInstance();
+                                SystemConfig systemConfig = mInjector.getSystemConfig();
                                 ArraySet<String> packages = systemConfig.getLinkedApps();
                                 if (!packages.contains(packageName)) {
                                     // updatedStatus is already UNDEFINED
@@ -2147,7 +2202,7 @@
         if (succeeded) {
             // Send the removed broadcasts
             if (res.removedInfo != null) {
-                res.removedInfo.sendPackageRemovedBroadcasts(killApp);
+                res.removedInfo.sendPackageRemovedBroadcasts(killApp, false /*removedBySystem*/);
             }
 
             // Allowlist any restricted permissions first as some may be runtime
@@ -2228,8 +2283,7 @@
 
             // Send installed broadcasts if the package is not a static shared lib.
             if (res.pkg.getStaticSharedLibName() == null) {
-                mProcessLoggingHandler.invalidateProcessLoggingBaseApkHash(
-                        res.pkg.getBaseApkPath());
+                mProcessLoggingHandler.invalidateBaseApkHash(res.pkg.getBaseApkPath());
 
                 // Send added for users that see the package for the first time
                 // sendPackageAddedForNewUsers also deals with system apps
@@ -2321,7 +2375,8 @@
                 // Send broadcast package appeared if external for all users
                 if (res.pkg.isExternalStorage()) {
                     if (!update) {
-                        final StorageManager storage = mInjector.getStorageManager();
+                        final StorageManager storage = mInjector.getSystemService(
+                                StorageManager.class);
                         VolumeInfo volume =
                                 storage.findVolumeByUuid(
                                         res.pkg.getStorageUuid().toString());
@@ -2467,9 +2522,9 @@
     }
 
     @Override
-    public void getChecksums(@NonNull String packageName, boolean includeSplits,
-            @Checksum.Kind int optional,
-            @Checksum.Kind int required, @Nullable List trustedInstallers,
+    public void requestChecksums(@NonNull String packageName, boolean includeSplits,
+            @Checksum.Type int optional,
+            @Checksum.Type int required, @Nullable List trustedInstallers,
             @NonNull IntentSender statusReceiver, int userId) {
         Objects.requireNonNull(packageName);
         Objects.requireNonNull(statusReceiver);
@@ -2493,13 +2548,6 @@
             }
         }
 
-        for (int i = 0, size = filesToChecksum.size(); i < size; ++i) {
-            final File file = filesToChecksum.get(i).second;
-            if (!file.exists()) {
-                throw new IllegalStateException("File not found: " + file.getPath());
-            }
-        }
-
         final Certificate[] trustedCerts = (trustedInstallers != null) ? decodeCertificates(
                 trustedInstallers) : null;
 
@@ -2507,7 +2555,7 @@
             ApkChecksums.Injector injector = new ApkChecksums.Injector(
                     () -> mContext,
                     () -> mInjector.getBackgroundHandler(),
-                    () -> mContext.getSystemService(IncrementalManager.class));
+                    () -> mInjector.getIncrementalManager());
             ApkChecksums.getChecksums(filesToChecksum, optional, required, trustedCerts,
                     statusReceiver, injector);
         });
@@ -2683,31 +2731,32 @@
         Injector injector = new Injector(
                 context, lock, installer, installLock, new PackageAbiHelperImpl(),
                 backgroundHandler,
-                (i, pm) ->
-                        new ComponentResolver(i.getUserManagerService(), pm.mPmInternal, lock),
-                (i, pm) ->
-                PermissionManagerService.create(context, lock),
-                (i, pm) ->
-                new UserManagerService(context, pm,
+                (i, pm) -> new ComponentResolver(i.getUserManagerService(), pm.mPmInternal, lock),
+                (i, pm) -> PermissionManagerService.create(context, lock),
+                (i, pm) -> new UserManagerService(context, pm,
                         new UserDataPreparer(installer, installLock, context, onlyCore),
                         lock),
-                (i, pm) ->
-                        new Settings(Environment.getDataDirectory(),
-                                i.getPermissionManagerServiceInternal().getPermissionSettings(),
-                                RuntimePermissionsPersistence.createInstance(),
-                                i.getPermissionManagerServiceInternal(), lock),
-                new Injector.LocalServicesProducer<>(ActivityTaskManagerInternal.class),
-                new Injector.LocalServicesProducer<>(ActivityManagerInternal.class),
-                new Injector.LocalServicesProducer<>(DeviceIdleInternal.class),
-                new Injector.LocalServicesProducer<>(StorageManagerInternal.class),
-                new Injector.LocalServicesProducer<>(NetworkPolicyManagerInternal.class),
-                new Injector.LocalServicesProducer<>(PermissionPolicyInternal.class),
-                new Injector.LocalServicesProducer<>(DeviceStorageMonitorInternal.class),
-                new Injector.SystemServiceProducer<>(DisplayManager.class),
-                new Injector.SystemServiceProducer<>(StorageManager.class),
-                new Injector.SystemServiceProducer<>(AppOpsManager.class),
+                (i, pm) -> new Settings(Environment.getDataDirectory(),
+                        i.getPermissionManagerServiceInternal().getPermissionSettings(),
+                        RuntimePermissionsPersistence.createInstance(),
+                        i.getPermissionManagerServiceInternal(), lock),
                 (i, pm) -> AppsFilter.create(pm.mPmInternal, i),
-                (i, pm) -> (PlatformCompat) ServiceManager.getService("platform_compat"));
+                (i, pm) -> (PlatformCompat) ServiceManager.getService("platform_compat"),
+                (i, pm) -> SystemConfig.getInstance(),
+                (i, pm) -> new PackageDexOptimizer(i.getInstaller(), i.getInstallLock(),
+                        i.getContext(), "*dexopt*"),
+                (i, pm) -> new DexManager(i.getContext(), pm, i.getPackageDexOptimizer(),
+                        i.getInstaller(), i.getInstallLock()),
+                (i, pm) -> new ArtManagerService(i.getContext(), pm, i.getInstaller(),
+                        i.getInstallLock()),
+                (i, pm) -> ApexManager.getInstance(),
+                (i, pm) -> (IPermissionManager) ServiceManager.getService("permissionmgr"),
+                (i, pm) -> new ViewCompiler(i.getInstallLock(), i.getInstaller()),
+                (i, pm) -> (IncrementalManager)
+                        pm.mContext.getSystemService(Context.INCREMENTAL_SERVICE),
+                new DefaultSystemWrapper(),
+                LocalServices::getService,
+                context::getSystemService);
 
         PackageManagerService m = new PackageManagerService(injector, onlyCore, factoryTest);
         t.traceEnd(); // "create package manager"
@@ -2780,16 +2829,17 @@
      * reasons. This simply requests that the copy takes place and awaits confirmation of its
      * completion. See platform/system/extras/cppreopt/ for the implementation of the actual copy.
      */
-    private static void requestCopyPreoptedFiles() {
+    private static void requestCopyPreoptedFiles(Injector injector) {
         final int WAIT_TIME_MS = 100;
         final String CP_PREOPT_PROPERTY = "sys.cppreopt";
-        if (SystemProperties.getInt("ro.cp_system_other_odex", 0) == 1) {
-            SystemProperties.set(CP_PREOPT_PROPERTY, "requested");
+        if (injector.getSystemWrapper().getPropertyInt("ro.cp_system_other_odex", 0) == 1) {
+            injector.getSystemWrapper().setProperty(CP_PREOPT_PROPERTY, "requested");
             // We will wait for up to 100 seconds.
             final long timeStart = SystemClock.uptimeMillis();
             final long timeEnd = timeStart + 100 * 1000;
             long timeNow = timeStart;
-            while (!SystemProperties.get(CP_PREOPT_PROPERTY).equals("finished")) {
+            while (!injector.getSystemWrapper()
+                    .getProperty(CP_PREOPT_PROPERTY).equals("finished")) {
                 try {
                     Thread.sleep(WAIT_TIME_MS);
                 } catch (InterruptedException e) {
@@ -2797,7 +2847,7 @@
                 }
                 timeNow = SystemClock.uptimeMillis();
                 if (timeNow > timeEnd) {
-                    SystemProperties.set(CP_PREOPT_PROPERTY, "timed-out");
+                    injector.getSystemWrapper().setProperty(CP_PREOPT_PROPERTY, "timed-out");
                     Slog.wtf(TAG, "cppreopt did not finish!");
                     break;
                 }
@@ -2929,6 +2979,8 @@
 
         mResolveComponentName = testParams.resolveComponentName;
         mPackages.putAll(testParams.packages);
+        mEnableFreeCacheV2 = testParams.enableFreeCacheV2;
+        mSdkVersion = testParams.sdkVersion;
     }
 
     public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest) {
@@ -2954,6 +3006,7 @@
         LockGuard.installLock(mLock, LockGuard.INDEX_PACKAGES);
         EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
                 SystemClock.uptimeMillis());
+        mSdkVersion = injector.getSystemWrapper().getSdkInt();
 
         if (mSdkVersion <= 0) {
             Slog.w(TAG, "**** ro.build.version.sdk not set!");
@@ -2964,6 +3017,8 @@
         mOnlyCore = onlyCore;
         mMetrics = new DisplayMetrics();
         mInstaller = injector.getInstaller();
+        mEnableFreeCacheV2 =
+                injector.getSystemWrapper().getPropertyBoolean("fw.free_cache_v2", true);
 
         // Create sub-components that provide services / data. Order here is important.
         t.traceBegin("createSubComponents");
@@ -2975,9 +3030,8 @@
         mComponentResolver = injector.getComponentResolver();
         mPermissionManager = injector.getPermissionManagerServiceInternal();
         mSettings = injector.getSettings();
-        mPermissionManagerService = (IPermissionManager) ServiceManager.getService("permissionmgr");
-        mIncrementalManager =
-                (IncrementalManager) mContext.getSystemService(Context.INCREMENTAL_SERVICE);
+        mPermissionManagerService = injector.getPermissionManagerService();
+        mIncrementalManager = mInjector.getIncrementalManager();
         PlatformCompat platformCompat = mInjector.getCompatibility();
         mPackageParserCallback = new PackageParser2.Callback() {
             @Override
@@ -3013,7 +3067,8 @@
                 ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
         t.traceEnd();
 
-        String separateProcesses = SystemProperties.get("debug.separate_processes");
+        String separateProcesses =
+                injector.getSystemWrapper().getProperty("debug.separate_processes");
         if (separateProcesses != null && separateProcesses.length() > 0) {
             if ("*".equals(separateProcesses)) {
                 mDefParseFlags = PackageParser.PARSE_IGNORE_PROCESSES;
@@ -3030,25 +3085,22 @@
             mSeparateProcesses = null;
         }
 
-        mPackageDexOptimizer = new PackageDexOptimizer(mInstaller, mInstallLock, mContext,
-                "*dexopt*");
-        mDexManager =
-                new DexManager(mContext, this, mPackageDexOptimizer, mInstaller, mInstallLock);
-        mArtManagerService = new ArtManagerService(mContext, this, mInstaller, mInstallLock);
+        mPackageDexOptimizer = injector.getPackageDexOptimizer();
+        mDexManager = injector.getDexManager();
+        mArtManagerService = injector.getArtManagerService();
         mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
+        mViewCompiler = injector.getViewCompiler();
 
-        mViewCompiler = new ViewCompiler(mInstallLock, mInstaller);
-
-        getDefaultDisplayMetrics(mInjector.getDisplayManager(), mMetrics);
+        getDefaultDisplayMetrics(mInjector.getSystemService(DisplayManager.class), mMetrics);
 
         t.traceBegin("get system config");
-        SystemConfig systemConfig = SystemConfig.getInstance();
+        SystemConfig systemConfig = injector.getSystemConfig();
         mAvailableFeatures = systemConfig.getAvailableFeatures();
         t.traceEnd();
 
         mProtectedPackages = new ProtectedPackages(mContext);
 
-        mApexManager = ApexManager.getInstance();
+        mApexManager = injector.getApexManager();
         mAppsFilter = mInjector.getAppsFilter();
 
         final List<ScanPartition> scanPartitions = new ArrayList<>();
@@ -3126,7 +3178,7 @@
             }
 
             if (!mOnlyCore && mFirstBoot) {
-                requestCopyPreoptedFiles();
+                requestCopyPreoptedFiles(mInjector);
             }
 
             String customResolverActivityName = Resources.getSystem().getString(
@@ -3181,7 +3233,7 @@
                 }
             }
 
-            mCacheDir = preparePackageParserCache();
+            mCacheDir = preparePackageParserCache(injector);
 
             // Set flag to monitor and not change apk file paths when
             // scanning install directories.
@@ -4004,7 +4056,7 @@
         setUpInstantAppInstallerActivityLP(getInstantAppInstallerLPr());
     }
 
-    private static @Nullable File preparePackageParserCache() {
+    private static @Nullable File preparePackageParserCache(Injector injector) {
         if (!FORCE_PACKAGE_PARSED_CACHE_ENABLED) {
             if (!DEFAULT_PACKAGE_PARSER_CACHE_ENABLED) {
                 return null;
@@ -4015,7 +4067,8 @@
                 return null;
             }
 
-            if (SystemProperties.getBoolean("pm.boot.disable_package_cache", false)) {
+            if (injector.getSystemWrapper()
+                    .getPropertyBoolean("pm.boot.disable_package_cache", false)) {
                 Slog.i(TAG, "Disabling package parser cache due to system property.");
                 return null;
             }
@@ -4031,7 +4084,7 @@
         // identify cached items. In particular, changing the value of certain
         // feature flags should cause us to invalidate any caches.
         final String cacheName = FORCE_PACKAGE_PARSED_CACHE_ENABLED ? "debug"
-                : SystemProperties.digestOf(
+                : injector.getSystemWrapper().digestOfProperties(
                         "ro.build.fingerprint",
                         StorageManager.PROP_ISOLATED_STORAGE,
                         StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT
@@ -4097,7 +4150,7 @@
     public boolean isDeviceUpgrading() {
         // allow instant applications
         // The system property allows testing ota flow when upgraded to the same image.
-        return mIsUpgrade || SystemProperties.getBoolean(
+        return mIsUpgrade || mInjector.getSystemWrapper().getPropertyBoolean(
                 "persist.pm.mock-upgrade", false /* default */);
     }
 
@@ -4364,7 +4417,7 @@
             Slog.d(TAG, "Priming domain verifications in user " + userId);
         }
 
-        SystemConfig systemConfig = SystemConfig.getInstance();
+        SystemConfig systemConfig = mInjector.getSystemConfig();
         ArraySet<String> packages = systemConfig.getLinkedApps();
 
         for (String packageName : packages) {
@@ -5173,11 +5226,11 @@
      * until the requested bytes are available.
      */
     public void freeStorage(String volumeUuid, long bytes, int storageFlags) throws IOException {
-        final StorageManager storage = mInjector.getStorageManager();
+        final StorageManager storage = mInjector.getSystemService(StorageManager.class);
         final File file = storage.findPathForUuid(volumeUuid);
         if (file.getUsableSpace() >= bytes) return;
 
-        if (ENABLE_FREE_CACHE_V2) {
+        if (mEnableFreeCacheV2) {
             final boolean internalVolume = Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL,
                     volumeUuid);
             final boolean aggressive = (storageFlags
@@ -5256,7 +5309,7 @@
 
     private boolean pruneUnusedStaticSharedLibraries(long neededSpace, long maxCachePeriod)
             throws IOException {
-        final StorageManager storage = mInjector.getStorageManager();
+        final StorageManager storage = mInjector.getSystemService(StorageManager.class);
         final File volume = storage.findPathForUuid(StorageManager.UUID_PRIVATE_INTERNAL);
 
         List<VersionedPackage> packagesToDelete = null;
@@ -5312,8 +5365,8 @@
                 final VersionedPackage pkgToDelete = packagesToDelete.get(i);
                 // Delete the package synchronously (will fail of the lib used for any user).
                 if (deletePackageX(pkgToDelete.getPackageName(), pkgToDelete.getLongVersionCode(),
-                        UserHandle.USER_SYSTEM, PackageManager.DELETE_ALL_USERS)
-                                == PackageManager.DELETE_SUCCEEDED) {
+                        UserHandle.USER_SYSTEM, PackageManager.DELETE_ALL_USERS,
+                        true /*removedBySystem*/) == PackageManager.DELETE_SUCCEEDED) {
                     if (volume.getUsableSpace() >= neededSpace) {
                         return true;
                     }
@@ -5504,7 +5557,8 @@
     }
 
     private boolean isRecentsAccessingChildProfiles(int callingUid, int targetUserId) {
-        if (!mInjector.getActivityTaskManagerInternal().isCallerRecents(callingUid)) {
+        if (!mInjector.getLocalService(ActivityTaskManagerInternal.class)
+                .isCallerRecents(callingUid)) {
             return false;
         }
         final long token = Binder.clearCallingIdentity();
@@ -5698,7 +5752,7 @@
                         continue;
                     }
 
-                    long identity = Binder.clearCallingIdentity();
+                    final long identity = Binder.clearCallingIdentity();
                     try {
                         PackageInfo packageInfo = getPackageInfoVersioned(declaringPackage, flags
                                 | PackageManager.MATCH_STATIC_SHARED_LIBRARIES, userId);
@@ -5987,7 +6041,7 @@
             res.addAll(mAvailableFeatures.values());
         }
         final FeatureInfo fi = new FeatureInfo();
-        fi.reqGlEsVersion = SystemProperties.getInt("ro.opengles.version",
+        fi.reqGlEsVersion = mInjector.getSystemWrapper().getPropertyInt("ro.opengles.version",
                 FeatureInfo.GL_ES_VERSION_UNDEFINED);
         res.add(fi);
 
@@ -7631,7 +7685,7 @@
     }
 
     private boolean isUserEnabled(int userId) {
-        long callingId = Binder.clearCallingIdentity();
+        final long callingId = Binder.clearCallingIdentity();
         try {
             UserInfo userInfo = mUserManager.getUserInfo(userId);
             return userInfo != null && userInfo.isEnabled();
@@ -8042,7 +8096,7 @@
     private ResolveInfo createForwardingResolveInfoUnchecked(IntentFilter filter,
             int sourceUserId, int targetUserId) {
         ResolveInfo forwardingResolveInfo = new ResolveInfo();
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         boolean targetIsProfile;
         try {
             targetIsProfile = mUserManager.getUserInfo(targetUserId).isManagedProfile();
@@ -9069,10 +9123,10 @@
         if (providerInfo != null) {
             // Looking for cross-user grants before enforcing the typical cross-users permissions
             if (userId != UserHandle.getUserId(callingUid)) {
-                final UriGrantsManagerInternal mUgmInternal =
-                        LocalServices.getService(UriGrantsManagerInternal.class);
+                final UriGrantsManagerInternal ugmInternal =
+                        mInjector.getLocalService(UriGrantsManagerInternal.class);
                 checkedGrants =
-                        mUgmInternal.checkAuthorityGrants(callingUid, providerInfo, userId, true);
+                        ugmInternal.checkAuthorityGrants(callingUid, providerInfo, userId, true);
             }
         }
         if (!checkedGrants) {
@@ -9682,7 +9736,7 @@
                                             pkgName, getSettingsVersionForPackage(parsedPackage)),
                                     Collections.singletonMap(pkgName,
                                             getSharedLibLatestVersionSetting(scanResult))),
-                            mSettings.mKeySetManagerService);
+                            mSettings.mKeySetManagerService, mInjector);
                     appIdCreated = optimisticallyRegisterAppId(scanResult);
                     commitReconciledScanResultLocked(
                             reconcileResult.get(pkgName), mUserManager.getUserIds());
@@ -9700,6 +9754,18 @@
                 mSettings.disableSystemPackageLPw(parsedPackage.getPackageName(), true);
             }
         }
+        if (mIncrementalManager != null && isIncrementalPath(parsedPackage.getPath())) {
+            if (pkgSetting != null && pkgSetting.isPackageLoading()) {
+                final StorageHealthCheckParams healthCheckParams = new StorageHealthCheckParams();
+                healthCheckParams.blockedTimeoutMs = INCREMENTAL_STORAGE_BLOCKED_TIMEOUT_MS;
+                healthCheckParams.unhealthyTimeoutMs = INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS;
+                healthCheckParams.unhealthyMonitoringMs =
+                        INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS;
+                mIncrementalManager.registerHealthListener(parsedPackage.getPath(),
+                        healthCheckParams,
+                        new IncrementalHealthListener(parsedPackage.getPackageName()));
+            }
+        }
         return scanResult.pkgSetting.pkg;
     }
 
@@ -9961,7 +10027,7 @@
                 pkgCompilationReason = PackageManagerService.REASON_BACKGROUND_DEXOPT;
             }
 
-            if (SystemProperties.getBoolean(PRECOMPILE_LAYOUTS, false)) {
+            if (mInjector.getSystemWrapper().getPropertyBoolean(PRECOMPILE_LAYOUTS, false)) {
                 mArtManagerService.compileLayouts(pkg);
             }
 
@@ -10177,7 +10243,7 @@
             mPackageUsage.maybeWriteAsync(mSettings.mPackages);
             mCompilerStats.maybeWriteAsync();
         }
-        long callingId = Binder.clearCallingIdentity();
+        final long callingId = Binder.clearCallingIdentity();
         try {
             synchronized (mInstallLock) {
                 return performDexOptInternalWithDependenciesLI(p, pkgSetting, options);
@@ -10937,7 +11003,7 @@
     }
 
     private int getVendorPartitionVersion() {
-        final String version = SystemProperties.get("ro.vndk.version");
+        final String version = mInjector.getSystemWrapper().getProperty("ro.vndk.version");
         if (!version.isEmpty()) {
             try {
                 return Integer.parseInt(version);
@@ -12353,9 +12419,10 @@
                     // A non-preloaded overlay package, without <overlay android:targetName>, will
                     // only be used if it is signed with the same certificate as its target OR if
                     // it is signed with the same certificate as a reference package declared
-                    // in 'config-signature' tag of SystemConfig.
-                    // If the target is already installed or 'config-signature' tag in SystemConfig
-                    // is set, check this here to augment the last line of defence which is OMS.
+                    // in 'overlay-config-signature' tag of SystemConfig.
+                    // If the target is already installed or 'overlay-config-signature' tag in
+                    // SystemConfig is set, check this here to augment the last line of defense
+                    // which is OMS.
                     if (pkg.getOverlayTargetName() == null) {
                         final PackageSetting targetPkgSetting =
                                 mSettings.getPackageLPr(pkg.getOverlayTarget());
@@ -12961,7 +13028,7 @@
                         + intent.toShortString(false, true, false, false)
                         + " " + intent.getExtras(), here);
             }
-            mInjector.getActivityManagerInternal().broadcastIntent(
+            mInjector.getLocalService(ActivityManagerInternal.class).broadcastIntent(
                     intent, finishedReceiver, requiredPermissions,
                     finishedReceiver != null, id,
                     broadcastAllowList == null ? null : broadcastAllowList.get(id));
@@ -13172,7 +13239,7 @@
             return false;
         }
 
-        long callingId = Binder.clearCallingIdentity();
+        final long callingId = Binder.clearCallingIdentity();
         try {
             boolean sendAdded = false;
             boolean sendRemoved = false;
@@ -13302,7 +13369,7 @@
         info.removedUsers = new int[] {userId};
         info.broadcastUsers = new int[] {userId};
         info.uid = UserHandle.getUid(userId, pkgSetting.appId);
-        info.sendPackageRemovedBroadcasts(true /*killApp*/);
+        info.sendPackageRemovedBroadcasts(true /*killApp*/, false /*removedBySystem*/);
     }
 
     private void sendDistractingPackagesChanged(String[] pkgList, int[] uidList, int userId,
@@ -13339,7 +13406,7 @@
                 true /* requireFullPermission */, false /* checkShell */,
                 "getApplicationHidden for user " + userId);
         PackageSetting ps;
-        long callingId = Binder.clearCallingIdentity();
+        final long callingId = Binder.clearCallingIdentity();
         try {
             // writer
             synchronized (mLock) {
@@ -13394,7 +13461,7 @@
             return PackageManager.INSTALL_FAILED_USER_RESTRICTED;
         }
 
-        long callingId = Binder.clearCallingIdentity();
+        final long callingId = Binder.clearCallingIdentity();
         try {
             boolean installed = false;
             final boolean instantApp =
@@ -14262,7 +14329,7 @@
                     && mInstantAppInstallerActivity.packageName.equals(
                             mRequiredVerifierPackage)) {
                 try {
-                    mInjector.getAppOpsManager()
+                    mInjector.getSystemService(AppOpsManager.class)
                             .checkPackage(installerUid, mRequiredVerifierPackage);
                     if (DEBUG_VERIFY) {
                         Slog.i(TAG, "disable verification for instant app");
@@ -14481,7 +14548,7 @@
                 // was never set.
                 EventLog.writeEvent(0x534e4554, "150857253", callingUid, "");
 
-                long binderToken = Binder.clearCallingIdentity();
+                final long binderToken = Binder.clearCallingIdentity();
                 try {
                     if (mInjector.getCompatibility().isChangeEnabledByUid(
                             THROW_EXCEPTION_ON_REQUIRE_INSTALL_PACKAGES_TO_ADD_INSTALLER_PACKAGE,
@@ -14512,7 +14579,7 @@
         if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
             throw new SecurityException("Instant applications don't have access to this method");
         }
-        mInjector.getAppOpsManager().checkPackage(Binder.getCallingUid(),
+        mInjector.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(),
                 callerPackageName);
         synchronized (mLock) {
             PackageSetting ps = mSettings.mPackages.get(packageName);
@@ -14672,7 +14739,7 @@
      */
     private boolean performRollbackManagerRestore(int userId, int token, PackageInstalledInfo res,
             PostInstallData data) {
-        RollbackManagerInternal rm = LocalServices.getService(RollbackManagerInternal.class);
+        RollbackManagerInternal rm = mInjector.getLocalService(RollbackManagerInternal.class);
 
         final String packageName = res.pkg.getPackageName();
         final int[] allUsers = mUserManager.getUserIds();
@@ -14773,20 +14840,6 @@
             return mUser;
         }
 
-        /**
-         * Gets the user handle for the user that the rollback agent should
-         * use to look up information about this installation when enabling
-         * rollback.
-         */
-        UserHandle getRollbackUser() {
-            // The session for packages installed for "all" users is
-            // associated with the "system" user.
-            if (mUser == UserHandle.ALL) {
-                return UserHandle.SYSTEM;
-            }
-            return mUser;
-        }
-
         HandlerParams setTraceMethod(String traceMethod) {
             this.traceMethod = traceMethod;
             return this;
@@ -14986,6 +15039,7 @@
         @Nullable MultiPackageInstallParams mParentInstallParams;
         final boolean forceQueryableOverride;
         final int mDataLoaderType;
+        final long requiredInstalledVersionCode;
 
         InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer,
                 int installFlags, InstallSource installSource, String volumeUuid,
@@ -15006,6 +15060,7 @@
             this.installReason = PackageManager.INSTALL_REASON_UNKNOWN;
             this.forceQueryableOverride = false;
             this.mDataLoaderType = DataLoaderType.NONE;
+            this.requiredInstalledVersionCode = PackageManager.VERSION_CODE_HIGHEST;
         }
 
         InstallParams(File stagedDir, IPackageInstallObserver2 observer,
@@ -15028,6 +15083,7 @@
             forceQueryableOverride = sessionParams.forceQueryableOverride;
             mDataLoaderType = (sessionParams.dataLoaderParams != null)
                     ? sessionParams.dataLoaderParams.getType() : DataLoaderType.NONE;
+            requiredInstalledVersionCode = sessionParams.requiredInstalledVersionCode;
         }
 
         @Override
@@ -15174,6 +15230,18 @@
         public void handleStartCopy() {
             PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
                     origin.resolvedPath, installFlags, packageAbiOverride);
+
+            // For staged session, there is a delay between its verification and install. Device
+            // state can change within this delay and hence we need to re-verify certain conditions.
+            boolean isStaged = (installFlags & INSTALL_STAGED) != 0;
+            if (isStaged) {
+                mRet = verifyReplacingVersionCode(
+                        pkgLite, requiredInstalledVersionCode, installFlags);
+                if (mRet != INSTALL_SUCCEEDED) {
+                    return;
+                }
+            }
+
             mRet = overrideInstallLocation(pkgLite);
         }
 
@@ -15320,11 +15388,14 @@
             PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
                     origin.resolvedPath, installFlags, packageAbiOverride);
 
-            mRet = verifyReplacingVersionCode(pkgLite);
+            mRet = verifyReplacingVersionCode(pkgLite, requiredInstalledVersionCode, installFlags);
+            if (mRet != INSTALL_SUCCEEDED) {
+                return;
+            }
 
             // Perform package verification and enable rollback (unless we are simply moving the
             // package).
-            if (mRet == INSTALL_SUCCEEDED && !origin.existing) {
+            if (!origin.existing) {
                 sendApkVerificationRequest(pkgLite);
                 if ((installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
                     sendEnableRollbackRequest();
@@ -15332,54 +15403,6 @@
             }
         }
 
-        private int verifyReplacingVersionCode(PackageInfoLite pkgLite) {
-            String packageName = pkgLite.packageName;
-            synchronized (mLock) {
-                // Package which currently owns the data that the new package will own if installed.
-                // If an app is uninstalled while keeping data (e.g. adb uninstall -k), installedPkg
-                // will be null whereas dataOwnerPkg will contain information about the package
-                // which was uninstalled while keeping its data.
-                AndroidPackage dataOwnerPkg = mPackages.get(packageName);
-                if (dataOwnerPkg  == null) {
-                    PackageSetting ps = mSettings.mPackages.get(packageName);
-                    if (ps != null) {
-                        dataOwnerPkg = ps.pkg;
-                    }
-                }
-
-                if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST) {
-                    if (dataOwnerPkg == null) {
-                        Slog.w(TAG, "Required installed version code was "
-                                + requiredInstalledVersionCode
-                                + " but package is not installed");
-                        return PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION;
-                    }
-
-                    if (dataOwnerPkg.getLongVersionCode() != requiredInstalledVersionCode) {
-                        Slog.w(TAG, "Required installed version code was "
-                                + requiredInstalledVersionCode
-                                + " but actual installed version is "
-                                + dataOwnerPkg.getLongVersionCode());
-                        return PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION;
-                    }
-                }
-
-                if (dataOwnerPkg != null) {
-                    if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags,
-                            dataOwnerPkg.isDebuggable())) {
-                        try {
-                            checkDowngrade(dataOwnerPkg, pkgLite);
-                        } catch (PackageManagerException e) {
-                            Slog.w(TAG, "Downgrade detected: " + e.getMessage());
-                            return PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
-                        }
-                    }
-                }
-            }
-            return PackageManager.INSTALL_SUCCEEDED;
-        }
-
-
         void sendApkVerificationRequest(PackageInfoLite pkgLite) {
             final int verificationId = mPendingVerificationToken++;
 
@@ -15479,7 +15502,7 @@
             integrityVerification.setPackage("android");
 
             DeviceIdleInternal idleController =
-                    mInjector.getLocalDeviceIdleController();
+                    mInjector.getLocalService(DeviceIdleInternal.class);
             final long idleDuration = getVerificationTimeout();
 
             idleController.addPowerSaveTempWhitelistAppDirect(Process.myUid(),
@@ -15589,7 +15612,7 @@
                         receivers, verificationState);
 
                 DeviceIdleInternal idleController =
-                        mInjector.getLocalDeviceIdleController();
+                        mInjector.getLocalService(DeviceIdleInternal.class);
                 final long idleDuration = getVerificationTimeout();
                 final BroadcastOptions options = BroadcastOptions.makeBasic();
                 options.setTemporaryAppWhitelistDuration(idleDuration);
@@ -15973,7 +15996,7 @@
                 return false;
             }
 
-            final File targetDir = codeFile.getParentFile();
+            final File targetDir = resolveTargetDir();
             final File beforeCodeFile = codeFile;
             final File afterCodeFile = getNextCodePath(targetDir, parsedPackage.getPackageName());
 
@@ -16016,6 +16039,17 @@
             return true;
         }
 
+        // TODO(b/168126411): Once staged install flow starts using the same folder as non-staged
+        //  flow, we won't need this method anymore.
+        private File resolveTargetDir() {
+            boolean isStagedInstall = (installFlags & INSTALL_STAGED) != 0;
+            if (isStagedInstall) {
+                return Environment.getDataAppDirectory(null);
+            } else {
+                return codeFile.getParentFile();
+            }
+        }
+
         int doPostInstall(int status, int uid) {
             if (status != PackageManager.INSTALL_SUCCEEDED) {
                 cleanUp();
@@ -16388,12 +16422,25 @@
 
                 // TODO(b/169721400): generalize Incremental States and create a Callback object
                 // that can be used for all the packages.
-                final IncrementalStatesCallback incrementalStatesCallback =
-                        new IncrementalStatesCallback(ps, userId);
                 final String codePath = ps.getPathString();
                 if (IncrementalManager.isIncrementalPath(codePath) && mIncrementalManager != null) {
-                    mIncrementalManager.registerCallback(codePath, incrementalStatesCallback);
+                    final IncrementalStatesCallback incrementalStatesCallback =
+                            new IncrementalStatesCallback(ps.name,
+                                    UserHandle.getUid(userId, ps.appId),
+                                    getInstalledUsers(ps, userId));
                     ps.setIncrementalStatesCallback(incrementalStatesCallback);
+                    mIncrementalManager.registerLoadingProgressCallback(codePath,
+                            new IncrementalProgressListener(ps.name));
+                    final IncrementalHealthListener incrementalHealthListener =
+                            new IncrementalHealthListener(ps.name);
+                    final StorageHealthCheckParams healthCheckParams =
+                            new StorageHealthCheckParams();
+                    healthCheckParams.blockedTimeoutMs = INCREMENTAL_STORAGE_BLOCKED_TIMEOUT_MS;
+                    healthCheckParams.unhealthyTimeoutMs = INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS;
+                    healthCheckParams.unhealthyMonitoringMs =
+                            INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS;
+                    mIncrementalManager.registerHealthListener(codePath,
+                            new StorageHealthCheckParams(), incrementalHealthListener);
                 }
 
                 // Ensure that the uninstall reason is UNKNOWN for users with the package installed.
@@ -16577,7 +16624,7 @@
 
     @GuardedBy("mLock")
     private static Map<String, ReconciledPackage> reconcilePackagesLocked(
-            final ReconcileRequest request, KeySetManagerService ksms)
+            final ReconcileRequest request, KeySetManagerService ksms, Injector injector)
             throws ReconcileFailure {
         final Map<String, ScanResult> scannedPackages = request.scannedPackages;
 
@@ -16728,7 +16775,8 @@
                                 && compareSignatures(sharedUserSignatures,
                                 parsedPackage.getSigningDetails().signatures)
                                         != PackageManager.SIGNATURE_MATCH) {
-                            if (SystemProperties.getInt("ro.product.first_api_level", 0) <= 29) {
+                            if (injector.getSystemWrapper()
+                                    .getPropertyInt("ro.product.first_api_level", 0) <= 29) {
                                 // Mismatched signatures is an error and silently skipping system
                                 // packages will likely break the device in unforeseen ways.
                                 // However, we allow the device to boot anyway because, prior to Q,
@@ -17102,7 +17150,7 @@
                 try {
                     Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages");
                     reconciledPackages = reconcilePackagesLocked(
-                            reconcileRequest, mSettings.mKeySetManagerService);
+                            reconcileRequest, mSettings.mKeySetManagerService, mInjector);
                 } catch (ReconcileFailure e) {
                     for (InstallRequest request : requests) {
                         request.installResult.setError("Reconciliation failed...", e);
@@ -17237,7 +17285,7 @@
 
             if (performDexopt) {
                 // Compile the layout resources.
-                if (SystemProperties.getBoolean(PRECOMPILE_LAYOUTS, false)) {
+                if (mInjector.getSystemWrapper().getPropertyBoolean(PRECOMPILE_LAYOUTS, false)) {
                     Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "compileLayouts");
                     mViewCompiler.compileLayouts(pkg);
                     Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
@@ -17296,71 +17344,74 @@
         NativeLibraryHelper.waitForNativeBinariesExtraction(incrementalStorages);
     }
 
-    private class IncrementalStatesCallback extends IPackageLoadingProgressCallback.Stub
-            implements IncrementalStates.Callback {
-        @GuardedBy("mPackageSetting")
-        private final PackageSetting mPackageSetting;
-        private final String mPackageName;
-        private final String mPathString;
-        private final int mUserId;
-        private final int[] mInstalledUserIds;
-
-        IncrementalStatesCallback(PackageSetting packageSetting, int userId) {
-            mPackageSetting = packageSetting;
-            mPackageName = packageSetting.name;
-            mUserId = userId;
-            mPathString = packageSetting.getPathString();
-            final int[] allUserIds = resolveUserIds(userId);
-            final ArrayList<Integer> installedUserIds = new ArrayList<>();
-            for (int i = 0; i < allUserIds.length; i++) {
-                if (packageSetting.getInstalled(allUserIds[i])) {
-                    installedUserIds.add(allUserIds[i]);
-                }
-            }
-            final int numInstalledUserId = installedUserIds.size();
-            mInstalledUserIds = new int[numInstalledUserId];
-            for (int i = 0; i < numInstalledUserId; i++) {
-                mInstalledUserIds[i] = installedUserIds.get(i);
+    private int[] getInstalledUsers(PackageSetting ps, int userId) {
+        final int[] allUserIds = resolveUserIds(userId);
+        final ArrayList<Integer> installedUserIdsList = new ArrayList<>();
+        for (int i = 0; i < allUserIds.length; i++) {
+            if (ps.getInstalled(allUserIds[i])) {
+                installedUserIdsList.add(allUserIds[i]);
             }
         }
+        final int numInstalledUserId = installedUserIdsList.size();
+        final int[] installedUserIds = new int[numInstalledUserId];
+        for (int i = 0; i < numInstalledUserId; i++) {
+            installedUserIds[i] = installedUserIdsList.get(i);
+        }
+        return installedUserIds;
+    }
 
-        @Override
-        public void onPackageLoadingProgressChanged(float progress) {
-            synchronized (mPackageSetting) {
-                mPackageSetting.setLoadingProgress(progress);
-            }
+    /**
+     * Package states callback, used to listen for package state changes and send broadcasts
+     */
+    private final class IncrementalStatesCallback implements IncrementalStates.Callback {
+        private final String mPackageName;
+        private final int mUid;
+        private final int[] mInstalledUserIds;
+        IncrementalStatesCallback(String packageName, int uid, int[] installedUserIds) {
+            mPackageName = packageName;
+            mUid = uid;
+            mInstalledUserIds = installedUserIds;
         }
 
         @Override
         public void onPackageFullyLoaded() {
-            mIncrementalManager.unregisterCallback(mPathString, this);
             final SparseArray<int[]> newBroadcastAllowList;
+            final String codePath;
             synchronized (mLock) {
+                final PackageSetting ps = mSettings.mPackages.get(mPackageName);
+                if (ps == null) {
+                    return;
+                }
                 newBroadcastAllowList = mAppsFilter.getVisibilityAllowList(
-                        getPackageSettingInternal(mPackageName, Process.SYSTEM_UID),
-                        mInstalledUserIds, mSettings.mPackages);
+                        ps, mInstalledUserIds, mSettings.mPackages);
+                codePath = ps.getPathString();
             }
-            Bundle extras = new Bundle(1);
-            extras.putInt(Intent.EXTRA_UID, mUserId);
+            Bundle extras = new Bundle();
+            extras.putInt(Intent.EXTRA_UID, mUid);
             extras.putString(Intent.EXTRA_PACKAGE_NAME, mPackageName);
             sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_LOADED, mPackageName,
                     extras, 0 /*flags*/,
                     null /*targetPackage*/, null /*finishedReceiver*/,
                     mInstalledUserIds, null /* instantUserIds */, newBroadcastAllowList);
+            // Unregister health listener as it will always be healthy from now
+            mIncrementalManager.unregisterHealthListener(codePath);
         }
 
         @Override
         public void onPackageUnstartable(int reason) {
             final SparseArray<int[]> newBroadcastAllowList;
             synchronized (mLock) {
+                final PackageSetting ps = mSettings.mPackages.get(mPackageName);
+                if (ps == null) {
+                    return;
+                }
                 newBroadcastAllowList = mAppsFilter.getVisibilityAllowList(
-                        getPackageSettingInternal(mPackageName, Process.SYSTEM_UID),
-                        mInstalledUserIds, mSettings.mPackages);
+                        ps, mInstalledUserIds, mSettings.mPackages);
             }
-            Bundle extras = new Bundle(1);
-            extras.putInt(Intent.EXTRA_UID, mUserId);
+            Bundle extras = new Bundle();
+            extras.putInt(Intent.EXTRA_UID, mUid);
             extras.putString(Intent.EXTRA_PACKAGE_NAME, mPackageName);
-            extras.putInt(Intent.EXTRA_REASON, reason);
+            extras.putInt(Intent.EXTRA_UNSTARTABLE_REASON, reason);
             // send broadcast to users with this app installed
             sendPackageBroadcast(Intent.ACTION_PACKAGE_UNSTARTABLE, mPackageName,
                     extras, 0 /*flags*/,
@@ -17372,12 +17423,15 @@
         public void onPackageStartable() {
             final SparseArray<int[]> newBroadcastAllowList;
             synchronized (mLock) {
+                final PackageSetting ps = mSettings.mPackages.get(mPackageName);
+                if (ps == null) {
+                    return;
+                }
                 newBroadcastAllowList = mAppsFilter.getVisibilityAllowList(
-                        getPackageSettingInternal(mPackageName, Process.SYSTEM_UID),
-                        mInstalledUserIds, mSettings.mPackages);
+                        ps, mInstalledUserIds, mSettings.mPackages);
             }
-            Bundle extras = new Bundle(1);
-            extras.putInt(Intent.EXTRA_UID, mUserId);
+            Bundle extras = new Bundle();
+            extras.putInt(Intent.EXTRA_UID, mUid);
             extras.putString(Intent.EXTRA_PACKAGE_NAME, mPackageName);
             // send broadcast to users with this app installed
             sendPackageBroadcast(Intent.ACTION_PACKAGE_STARTABLE, mPackageName,
@@ -17388,37 +17442,48 @@
     }
 
     /**
-     * This is an internal method that is used to indicate changes on the health status of the
-     * Incremental Storage used by an installed package with an associated user id. This might
-     * result in a change in the loading state of the package.
+     * Loading progress callback, used to listen for progress changes and update package setting
      */
-    public void onStorageHealthStatusChanged(String packageName, int status, int userId) {
-        final int callingUid = Binder.getCallingUid();
-        mPermissionManager.enforceCrossUserPermission(
-                callingUid, userId, true, false,
-                "onStorageHealthStatusChanged");
-        final PackageSetting ps = getPackageSettingForUser(packageName, callingUid, userId);
-        if (ps == null) {
-            return;
+    private class IncrementalProgressListener extends IPackageLoadingProgressCallback.Stub {
+        private final String mPackageName;
+        IncrementalProgressListener(String packageName) {
+            mPackageName = packageName;
         }
-        ps.setStorageHealthStatus(status);
+
+        @Override
+        public void onPackageLoadingProgressChanged(float progress) {
+            final PackageSetting ps;
+            synchronized (mLock) {
+                ps = mSettings.mPackages.get(mPackageName);
+            }
+            if (ps == null) {
+                return;
+            }
+            ps.setLoadingProgress(progress);
+        }
     }
 
     /**
-     * This is an internal method that is used to indicate changes on the stream status of the
-     * data loader used by an installed package with an associated user id. This might
-     * result in a change in the loading state of the package.
+     * Incremental storage health status callback, used to listen for monitoring changes and update
+     * package setting.
      */
-    public void onStreamStatusChanged(String packageName, int status, int userId) {
-        final int callingUid = Binder.getCallingUid();
-        mPermissionManager.enforceCrossUserPermission(
-                callingUid, userId, true, false,
-                "onStreamStatusChanged");
-        final PackageSetting ps = getPackageSettingForUser(packageName, callingUid, userId);
-        if (ps == null) {
-            return;
+    private class IncrementalHealthListener extends IStorageHealthListener.Stub {
+        private final String mPackageName;
+        IncrementalHealthListener(String packageName) {
+            mPackageName = packageName;
         }
-        ps.setStreamStatus(status);
+
+        @Override
+        public void onHealthStatus(int storageId, int status) throws RemoteException {
+            final PackageSetting ps;
+            synchronized (mLock) {
+                ps = mSettings.mPackages.get(mPackageName);
+            }
+            if (ps == null) {
+                return;
+            }
+            ps.setStorageHealthStatus(status);
+        }
     }
 
     @Nullable PackageSetting getPackageSettingForUser(String packageName, int callingUid,
@@ -17550,6 +17615,49 @@
         }
     }
 
+    private boolean doesSignatureMatchForPermissions(@NonNull String sourcePackageName,
+            @NonNull ParsedPackage parsedPackage, int scanFlags) {
+        // If the defining package is signed with our cert, it's okay.  This
+        // also includes the "updating the same package" case, of course.
+        // "updating same package" could also involve key-rotation.
+
+        final PackageSetting sourcePackageSetting;
+        synchronized (mLock) {
+            sourcePackageSetting = mSettings.getPackageLPr(sourcePackageName);
+        }
+
+        final SigningDetails sourceSigningDetails = (sourcePackageSetting == null
+                ? SigningDetails.UNKNOWN : sourcePackageSetting.getSigningDetails());
+        final KeySetManagerService ksms = mSettings.mKeySetManagerService;
+        if (sourcePackageName.equals(parsedPackage.getPackageName())
+                && (ksms.shouldCheckUpgradeKeySetLocked(
+                sourcePackageSetting, scanFlags))) {
+            return ksms.checkUpgradeKeySetLocked(sourcePackageSetting, parsedPackage);
+        } else {
+
+            // in the event of signing certificate rotation, we need to see if the
+            // package's certificate has rotated from the current one, or if it is an
+            // older certificate with which the current is ok with sharing permissions
+            if (sourceSigningDetails.checkCapability(
+                    parsedPackage.getSigningDetails(),
+                    PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
+                return true;
+            } else if (parsedPackage.getSigningDetails().checkCapability(
+                    sourceSigningDetails,
+                    PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
+                // the scanned package checks out, has signing certificate rotation
+                // history, and is newer; bring it over
+                synchronized (mLock) {
+                    sourcePackageSetting.signatures.mSigningDetails =
+                            parsedPackage.getSigningDetails();
+                }
+                return true;
+            } else {
+                return false;
+            }
+        }
+    }
+
     @GuardedBy("mInstallLock")
     private PrepareResult preparePackageLI(InstallArgs args, PackageInstalledInfo res)
             throws PrepareFailure {
@@ -17708,6 +17816,15 @@
                 }
             }
 
+            /*
+             * Cannot properly check CANNOT_INSTALL_WITH_BAD_PERMISSION_GROUPS using CompatChanges
+             * as this only works for packages that are installed
+             *
+             * TODO: Move logic for permission group compatibility into PermissionManagerService
+             */
+            boolean cannotInstallWithBadPermissionGroups =
+                    parsedPackage.getTargetSdkVersion() >= Build.VERSION_CODES.S;
+
             PackageSetting ps = mSettings.mPackages.get(pkgName);
             if (ps != null) {
                 if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps);
@@ -17759,8 +17876,33 @@
                 res.origUsers = ps.queryInstalledUsers(mUserManager.getUserIds(), true);
             }
 
+            final int numGroups = ArrayUtils.size(parsedPackage.getPermissionGroups());
+            for (int groupNum = 0; groupNum < numGroups; groupNum++) {
+                final ParsedPermissionGroup group =
+                        parsedPackage.getPermissionGroups().get(groupNum);
+                final PermissionGroupInfo sourceGroup = getPermissionGroupInfo(group.getName(), 0);
 
-            int N = ArrayUtils.size(parsedPackage.getPermissions());
+                if (sourceGroup != null && cannotInstallWithBadPermissionGroups) {
+                    final String sourcePackageName = sourceGroup.packageName;
+
+                    if ((replace || !parsedPackage.getPackageName().equals(sourcePackageName))
+                            && !doesSignatureMatchForPermissions(sourcePackageName, parsedPackage,
+                            scanFlags)) {
+                        EventLog.writeEvent(0x534e4554, "146211400", -1,
+                                parsedPackage.getPackageName());
+
+                        throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PERMISSION_GROUP,
+                                "Package "
+                                        + parsedPackage.getPackageName()
+                                        + " attempting to redeclare permission group "
+                                        + group.getName() + " already owned by "
+                                        + sourcePackageName);
+                    }
+                }
+            }
+
+           // TODO: Move logic for checking permission compatibility into PermissionManagerService
+            final int N = ArrayUtils.size(parsedPackage.getPermissions());
             for (int i = N - 1; i >= 0; i--) {
                 final ParsedPermission perm = parsedPackage.getPermissions().get(i);
                 final BasePermission bp = mPermissionManager.getPermissionTEMP(perm.getName());
@@ -17776,46 +17918,10 @@
 
                 // Check whether the newly-scanned package wants to define an already-defined perm
                 if (bp != null) {
-                    // If the defining package is signed with our cert, it's okay.  This
-                    // also includes the "updating the same package" case, of course.
-                    // "updating same package" could also involve key-rotation.
-                    final boolean sigsOk;
-                    final String sourcePackageName = bp.getSourcePackageName();
-                    final PackageSetting sourcePackageSetting;
-                    synchronized (mLock) {
-                        sourcePackageSetting = mSettings.getPackageLPr(sourcePackageName);
-                    }
-                    final SigningDetails sourceSigningDetails = (sourcePackageSetting == null
-                            ? SigningDetails.UNKNOWN : sourcePackageSetting.getSigningDetails());
-                    final KeySetManagerService ksms = mSettings.mKeySetManagerService;
-                    if (sourcePackageName.equals(parsedPackage.getPackageName())
-                            && (ksms.shouldCheckUpgradeKeySetLocked(
-                            sourcePackageSetting, scanFlags))) {
-                        sigsOk = ksms.checkUpgradeKeySetLocked(sourcePackageSetting, parsedPackage);
-                    } else {
+                    final String sourcePackageName = bp.getPackageName();
 
-                        // in the event of signing certificate rotation, we need to see if the
-                        // package's certificate has rotated from the current one, or if it is an
-                        // older certificate with which the current is ok with sharing permissions
-                        if (sourceSigningDetails.checkCapability(
-                                parsedPackage.getSigningDetails(),
-                                PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
-                            sigsOk = true;
-                        } else if (parsedPackage.getSigningDetails().checkCapability(
-                                sourceSigningDetails,
-                                PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
-                            // the scanned package checks out, has signing certificate rotation
-                            // history, and is newer; bring it over
-                            synchronized (mLock) {
-                                sourcePackageSetting.signatures.mSigningDetails =
-                                        parsedPackage.getSigningDetails();
-                            }
-                            sigsOk = true;
-                        } else {
-                            sigsOk = false;
-                        }
-                    }
-                    if (!sigsOk) {
+                    if (!doesSignatureMatchForPermissions(sourcePackageName, parsedPackage,
+                            scanFlags)) {
                         // If the owning package is the system itself, we log but allow
                         // install to proceed; we fail the install on all other permission
                         // redefinitions.
@@ -17850,6 +17956,52 @@
                         }
                     }
                 }
+
+                if (perm.getGroup() != null && cannotInstallWithBadPermissionGroups) {
+                    boolean isPermGroupDefinedByPackage = false;
+                    for (int groupNum = 0; groupNum < numGroups; groupNum++) {
+                        if (parsedPackage.getPermissionGroups().get(groupNum).getName()
+                                .equals(perm.getGroup())) {
+                            isPermGroupDefinedByPackage = true;
+                            break;
+                        }
+                    }
+
+                    if (!isPermGroupDefinedByPackage) {
+                        final PermissionGroupInfo sourceGroup =
+                                getPermissionGroupInfo(perm.getGroup(), 0);
+
+                        if (sourceGroup == null) {
+                            EventLog.writeEvent(0x534e4554, "146211400", -1,
+                                    parsedPackage.getPackageName());
+
+                            throw new PrepareFailure(INSTALL_FAILED_BAD_PERMISSION_GROUP,
+                                    "Package "
+                                            + parsedPackage.getPackageName()
+                                            + " attempting to declare permission "
+                                            + perm.getName() + " in non-existing group "
+                                            + perm.getGroup());
+                        } else {
+                            String groupSourcePackageName = sourceGroup.packageName;
+
+                            if (!PLATFORM_PACKAGE_NAME.equals(groupSourcePackageName)
+                                    && !doesSignatureMatchForPermissions(groupSourcePackageName,
+                                    parsedPackage, scanFlags)) {
+                                EventLog.writeEvent(0x534e4554, "146211400", -1,
+                                        parsedPackage.getPackageName());
+
+                                throw new PrepareFailure(INSTALL_FAILED_BAD_PERMISSION_GROUP,
+                                        "Package "
+                                                + parsedPackage.getPackageName()
+                                                + " attempting to declare permission "
+                                                + perm.getName() + " in group "
+                                                + perm.getGroup() + " owned by package "
+                                                + groupSourcePackageName
+                                                + " with incompatible certificate");
+                            }
+                        }
+                    }
+                }
             }
         }
 
@@ -18592,21 +18744,21 @@
             if (doDeletePackage) {
                 if (!deleteAllUsers) {
                     returnCode = deletePackageX(internalPackageName, versionCode,
-                            userId, deleteFlags);
+                            userId, deleteFlags, false /*removedBySystem*/);
                 } else {
                     int[] blockUninstallUserIds = getBlockUninstallForUsers(
                             internalPackageName, users);
                     // If nobody is blocking uninstall, proceed with delete for all users
                     if (ArrayUtils.isEmpty(blockUninstallUserIds)) {
                         returnCode = deletePackageX(internalPackageName, versionCode,
-                                userId, deleteFlags);
+                                userId, deleteFlags, false /*removedBySystem*/);
                     } else {
                         // Otherwise uninstall individually for users with blockUninstalls=false
                         final int userFlags = deleteFlags & ~PackageManager.DELETE_ALL_USERS;
                         for (int userId1 : users) {
                             if (!ArrayUtils.contains(blockUninstallUserIds, userId1)) {
                                 returnCode = deletePackageX(internalPackageName, versionCode,
-                                        userId1, userFlags);
+                                        userId1, userFlags, false /*removedBySystem*/);
                                 if (returnCode != PackageManager.DELETE_SUCCEEDED) {
                                     Slog.w(TAG, "Package delete failed for user " + userId1
                                             + ", returnCode " + returnCode);
@@ -18833,8 +18985,12 @@
      *  updating mSettings to reflect current status
      *  persisting settings for later use
      *  sending a broadcast if necessary
+     *
+     *  @param removedBySystem A boolean to indicate the package was removed automatically without
+     *                         the user-initiated action.
      */
-    int deletePackageX(String packageName, long versionCode, int userId, int deleteFlags) {
+    int deletePackageX(String packageName, long versionCode, int userId, int deleteFlags,
+            boolean removedBySystem) {
         final PackageRemovedInfo info = new PackageRemovedInfo(this);
         final boolean res;
 
@@ -18931,7 +19087,7 @@
 
         if (res) {
             final boolean killApp = (deleteFlags & PackageManager.DELETE_DONT_KILL_APP) == 0;
-            info.sendPackageRemovedBroadcasts(killApp);
+            info.sendPackageRemovedBroadcasts(killApp, removedBySystem);
             info.sendSystemPackageUpdatedBroadcasts();
         }
         // Force a gc here.
@@ -19007,8 +19163,8 @@
             this.packageSender = packageSender;
         }
 
-        void sendPackageRemovedBroadcasts(boolean killApp) {
-            sendPackageRemovedBroadcastInternal(killApp);
+        void sendPackageRemovedBroadcasts(boolean killApp, boolean removedBySystem) {
+            sendPackageRemovedBroadcastInternal(killApp, removedBySystem);
         }
 
         void sendSystemPackageUpdatedBroadcasts() {
@@ -19037,7 +19193,7 @@
             }
         }
 
-        private void sendPackageRemovedBroadcastInternal(boolean killApp) {
+        private void sendPackageRemovedBroadcastInternal(boolean killApp, boolean removedBySystem) {
             // Don't send static shared library removal broadcasts as these
             // libs are visible only the the apps that depend on them an one
             // cannot remove the library if it has a dependency.
@@ -19049,6 +19205,7 @@
             extras.putInt(Intent.EXTRA_UID, removedUid);
             extras.putBoolean(Intent.EXTRA_DATA_REMOVED, dataRemoved);
             extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, !killApp);
+            extras.putBoolean(Intent.EXTRA_USER_INITIATED, !removedBySystem);
             if (isUpdate || isRemovedPackageSystemUpdate) {
                 extras.putBoolean(Intent.EXTRA_REPLACING, true);
             }
@@ -19858,7 +20015,7 @@
     }
 
     private void resetNetworkPolicies(int userId) {
-        mInjector.getNetworkPolicyManagerInternal().resetUserState(userId);
+        mInjector.getLocalService(NetworkPolicyManagerInternal.class).resetUserState(userId);
     }
 
     /**
@@ -21124,7 +21281,7 @@
     }
 
     public String getOverlayConfigSignaturePackageName() {
-        return ensureSystemPackageName(SystemConfig.getInstance()
+        return ensureSystemPackageName(mInjector.getSystemConfig()
                 .getOverlayConfigSignaturePackage());
     }
 
@@ -21168,7 +21325,7 @@
         if (packageName == null) {
             return null;
         }
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             if (getPackageInfo(packageName, MATCH_FACTORY_ONLY, UserHandle.USER_SYSTEM) == null) {
                 PackageInfo packageInfo = getPackageInfo(packageName, 0, UserHandle.USER_SYSTEM);
@@ -21529,7 +21686,7 @@
             }
         }
 
-        long callingId = Binder.clearCallingIdentity();
+        final long callingId = Binder.clearCallingIdentity();
         try {
             if (sendNow) {
                 int packageUid = UserHandle.getUid(userId, pkgSetting.appId);
@@ -21894,7 +22051,7 @@
             mPermissionManager.updateAllPermissions(StorageManager.UUID_PRIVATE_INTERNAL, false);
 
             final PermissionPolicyInternal permissionPolicyInternal =
-                    mInjector.getPermissionPolicyInternal();
+                    mInjector.getLocalService(PermissionPolicyInternal.class);
             permissionPolicyInternal.setOnInitializedCallback(userId -> {
                 // The SDK updated case is already handled when we run during the ctor.
                 synchronized (mLock) {
@@ -21905,13 +22062,13 @@
         }
 
         // Watch for external volumes that come and go over time
-        final StorageManager storage = mInjector.getStorageManager();
+        final StorageManager storage = mInjector.getSystemService(StorageManager.class);
         storage.registerListener(mStorageListener);
 
         mInstallerService.systemReady();
         mPackageDexOptimizer.systemReady();
 
-        mInjector.getStorageManagerInternal().addExternalStoragePolicy(
+        mInjector.getLocalService(StorageManagerInternal.class).addExternalStoragePolicy(
                 new StorageManagerInternal.ExternalStorageMountPolicy() {
                     @Override
                     public int getMountMode(int uid, String packageName) {
@@ -22076,6 +22233,7 @@
                 pw.println("    dexopt: dump dexopt state");
                 pw.println("    compiler-stats: dump compiler statistics");
                 pw.println("    service-permissions: dump permissions required by services");
+                pw.println("    known-packages: dump known packages");
                 pw.println("    <package.name>: info about given package");
                 return;
             } else if ("--checkin".equals(opt)) {
@@ -22220,6 +22378,8 @@
                 dumpState.setDump(DumpState.DUMP_CHANGES);
             } else if ("service-permissions".equals(cmd)) {
                 dumpState.setDump(DumpState.DUMP_SERVICE_PERMISSIONS);
+            } else if ("known-packages".equals(cmd)) {
+                dumpState.setDump(DumpState.DUMP_KNOWN_PACKAGES);
             } else if ("write".equals(cmd)) {
                 synchronized (mLock) {
                     writeSettingsLPrTEMP();
@@ -22244,6 +22404,34 @@
                 }
             }
 
+            if (!checkin
+                    && dumpState.isDumping(DumpState.DUMP_KNOWN_PACKAGES)
+                    && packageName == null) {
+                if (dumpState.onTitlePrinted()) {
+                    pw.println();
+                }
+                final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ", 120);
+                ipw.println("Known Packages:");
+                ipw.increaseIndent();
+                for (int i = 0; i <= LAST_KNOWN_PACKAGE; i++) {
+                    final String knownPackage = mPmInternal.knownPackageToString(i);
+                    ipw.print(knownPackage);
+                    ipw.println(":");
+                    final String[] pkgNames = mPmInternal.getKnownPackageNames(i,
+                            UserHandle.USER_SYSTEM);
+                    ipw.increaseIndent();
+                    if (ArrayUtils.isEmpty(pkgNames)) {
+                        ipw.println("none");
+                    } else {
+                        for (String name : pkgNames) {
+                            ipw.println(name);
+                        }
+                    }
+                    ipw.decreaseIndent();
+                }
+                ipw.decreaseIndent();
+            }
+
             if (dumpState.isDumping(DumpState.DUMP_VERIFIERS) && packageName == null) {
                 if (!checkin) {
                     if (dumpState.onTitlePrinted())
@@ -22610,7 +22798,7 @@
         if (ArrayUtils.isEmpty(apkList)) {
            return;
         }
-        String sku = SystemProperties.get("ro.boot.hardware.sku");
+        String sku = mInjector.getSystemWrapper().getProperty("ro.boot.hardware.sku");
         if (!TextUtils.isEmpty(sku) && ArrayUtils.contains(skuArray, sku)) {
             return;
         }
@@ -22906,7 +23094,7 @@
         }
 
         // Reconcile app data for all started/unlocked users
-        final StorageManager sm = mInjector.getStorageManager();
+        final StorageManager sm = mInjector.getSystemService(StorageManager.class);
         UserManagerInternal umInternal = mInjector.getUserManagerInternal();
         for (UserInfo user : mUserManager.getUsers(false /* includeDying */)) {
             final int flags;
@@ -23107,7 +23295,7 @@
      * correct for all installed apps on all mounted volumes.
      */
     void reconcileAppsData(int userId, int flags, boolean migrateAppsData) {
-        final StorageManager storage = mInjector.getStorageManager();
+        final StorageManager storage = mInjector.getSystemService(StorageManager.class);
         for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
             final String volumeUuid = vol.getFsUuid();
             synchronized (mInstallLock) {
@@ -23242,7 +23430,7 @@
 
         Installer.Batch batch = new Installer.Batch();
         UserManagerInternal umInternal = mInjector.getUserManagerInternal();
-        StorageManagerInternal smInternal = mInjector.getStorageManagerInternal();
+        StorageManagerInternal smInternal = mInjector.getLocalService(StorageManagerInternal.class);
         for (UserInfo user : mUserManager.getUsers(false /*excludeDying*/)) {
             final int flags;
             if (umInternal.isUserUnlockingOrUnlocked(user.id)) {
@@ -23571,7 +23759,7 @@
     private void movePackageInternal(final String packageName, final String volumeUuid,
             final int moveId, final int callingUid, UserHandle user)
                     throws PackageManagerException {
-        final StorageManager storage = mInjector.getStorageManager();
+        final StorageManager storage = mInjector.getSystemService(StorageManager.class);
         final PackageManager pm = mContext.getPackageManager();
 
         final String currentVolumeUuid;
@@ -23809,7 +23997,7 @@
             return;
         }
 
-        final StorageManager storage = mInjector.getStorageManager();;
+        final StorageManager storage = mInjector.getSystemService(StorageManager.class);;
         VolumeInfo volume = storage.findVolumeByUuid(pkg.getStorageUuid().toString());
         int packageExternalStorageType = getPackageExternalStorageType(volume, pkg.isExternalStorage());
 
@@ -23849,7 +24037,7 @@
             }
         };
 
-        final StorageManager storage = mInjector.getStorageManager();
+        final StorageManager storage = mInjector.getSystemService(StorageManager.class);
         storage.setPrimaryStorageUuid(volumeUuid, callback);
         return realMoveId;
     }
@@ -23959,7 +24147,7 @@
                 }
                 //end run
                 mHandler.post(() -> deletePackageX(packageName, PackageManager.VERSION_CODE_HIGHEST,
-                        userId, 0));
+                        userId, 0, true /*removedBySystem*/));
             }
         }
     }
@@ -24029,7 +24217,7 @@
         final long token = Binder.clearCallingIdentity();
         try {
             final DeviceStorageMonitorInternal
-                    dsm = mInjector.getDeviceStorageMonitorInternal();
+                    dsm = mInjector.getLocalService(DeviceStorageMonitorInternal.class);
             if (dsm != null) {
                 return dsm.isMemoryLow();
             } else {
@@ -24189,10 +24377,58 @@
             // It is currently possible that the package will be deleted even if it is installed
             // after this method returns.
             mHandler.post(() -> deletePackageX(packageName, PackageManager.VERSION_CODE_HIGHEST,
-                    0, PackageManager.DELETE_ALL_USERS));
+                    0, PackageManager.DELETE_ALL_USERS, true /*removedBySystem*/));
         }
     }
 
+    private int verifyReplacingVersionCode(PackageInfoLite pkgLite,
+            long requiredInstalledVersionCode, int installFlags) {
+        String packageName = pkgLite.packageName;
+        synchronized (mLock) {
+            // Package which currently owns the data that the new package will own if installed.
+            // If an app is uninstalled while keeping data (e.g. adb uninstall -k), installedPkg
+            // will be null whereas dataOwnerPkg will contain information about the package
+            // which was uninstalled while keeping its data.
+            AndroidPackage dataOwnerPkg = mPackages.get(packageName);
+            if (dataOwnerPkg  == null) {
+                PackageSetting ps = mSettings.mPackages.get(packageName);
+                if (ps != null) {
+                    dataOwnerPkg = ps.pkg;
+                }
+            }
+
+            if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST) {
+                if (dataOwnerPkg == null) {
+                    Slog.w(TAG, "Required installed version code was "
+                            + requiredInstalledVersionCode
+                            + " but package is not installed");
+                    return PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION;
+                }
+
+                if (dataOwnerPkg.getLongVersionCode() != requiredInstalledVersionCode) {
+                    Slog.w(TAG, "Required installed version code was "
+                            + requiredInstalledVersionCode
+                            + " but actual installed version is "
+                            + dataOwnerPkg.getLongVersionCode());
+                    return PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION;
+                }
+            }
+
+            if (dataOwnerPkg != null) {
+                if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags,
+                        dataOwnerPkg.isDebuggable())) {
+                    try {
+                        checkDowngrade(dataOwnerPkg, pkgLite);
+                    } catch (PackageManagerException e) {
+                        Slog.w(TAG, "Downgrade detected: " + e.getMessage());
+                        return PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
+                    }
+                }
+            }
+        }
+        return PackageManager.INSTALL_SUCCEEDED;
+    }
+
     /**
      * Check and throw if the given before/after packages would be considered a
      * downgrade.
@@ -24685,7 +24921,7 @@
                 case PackageManagerInternal.PACKAGE_APP_PREDICTOR:
                     return filterOnlySystemPackages(mAppPredictionServicePackage);
                 case PackageManagerInternal.PACKAGE_COMPANION:
-                    return filterOnlySystemPackages("com.android.companiondevicemanager");
+                    return filterOnlySystemPackages(COMPANION_PACKAGE_NAME);
                 case PackageManagerInternal.PACKAGE_RETAIL_DEMO:
                     return TextUtils.isEmpty(mRetailDemoPackage)
                             ? ArrayUtils.emptyArray(String.class)
@@ -25573,7 +25809,7 @@
                         "Failed registering loading progress callback. Incremental is not enabled");
                 return false;
             }
-            return mIncrementalManager.registerCallback(ps.getPathString(),
+            return mIncrementalManager.registerLoadingProgressCallback(ps.getPathString(),
                     (IPackageLoadingProgressCallback) callback.getBinder());
         }
 
@@ -25592,7 +25828,7 @@
             if (mIncrementalManager == null) {
                 return false;
             }
-            return mIncrementalManager.unregisterCallback(ps.getPathString(),
+            return mIncrementalManager.unregisterLoadingProgressCallback(ps.getPathString(),
                     (IPackageLoadingProgressCallback) callback.getBinder());
         }
     }
@@ -25735,25 +25971,16 @@
      * @hide
      */
     @Override
-    public void logAppProcessStartIfNeeded(String processName, int uid, String seinfo,
-            String apkFile, int pid) {
+    public void logAppProcessStartIfNeeded(String packageName, String processName, int uid,
+            String seinfo, String apkFile, int pid) {
         if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
             return;
         }
         if (!SecurityLog.isLoggingEnabled()) {
             return;
         }
-        Bundle data = new Bundle();
-        data.putLong("startTimestamp", System.currentTimeMillis());
-        data.putString("processName", processName);
-        data.putInt("uid", uid);
-        data.putString("seinfo", seinfo);
-        data.putString("apkFile", apkFile);
-        data.putInt("pid", pid);
-        Message msg = mProcessLoggingHandler.obtainMessage(
-                ProcessLoggingHandler.LOG_APP_PROCESS_START_MSG);
-        msg.setData(data);
-        mProcessLoggingHandler.sendMessage(msg);
+        mProcessLoggingHandler.logAppProcessStart(mContext, this, apkFile, packageName, processName,
+                uid, seinfo, pid);
     }
 
     public CompilerStats.PackageStats getCompilerPackageStats(String pkgName) {
@@ -25774,7 +26001,7 @@
 
     @Override
     public boolean isAutoRevokeWhitelisted(String packageName) {
-        int mode = mInjector.getAppOpsManager().checkOpNoThrow(
+        int mode = mInjector.getSystemService(AppOpsManager.class).checkOpNoThrow(
                 AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
                 Binder.getCallingUid(), packageName);
         return mode == MODE_IGNORED;
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index afbf7d3..9aa1a62 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -24,6 +24,7 @@
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
 
 import android.accounts.IAccountManager;
+import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.role.IRoleManager;
@@ -96,6 +97,7 @@
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.util.ArraySet;
+import android.util.IntArray;
 import android.util.PrintWriterPrinter;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -1450,7 +1452,7 @@
         final PrintWriter pw = getOutPrintWriter();
         final int parentSessionId = Integer.parseInt(getNextArg());
 
-        List<Integer> otherSessionIds = new ArrayList<>();
+        IntArray otherSessionIds = new IntArray();
         String opt;
         while ((opt = getNextArg()) != null) {
             otherSessionIds.add(Integer.parseInt(opt));
@@ -1459,7 +1461,7 @@
             pw.println("Error: At least two sessions are required.");
             return 1;
         }
-        return doInstallAddSession(parentSessionId, ArrayUtils.convertToIntArray(otherSessionIds),
+        return doInstallAddSession(parentSessionId, otherSessionIds.toArray(),
                 true /*logSuccess*/);
     }
 
@@ -2685,9 +2687,18 @@
         }
     }
 
+    // pm remove-user [--set-ephemeral-if-in-use] USER_ID
     public int runRemoveUser() throws RemoteException {
         int userId;
-        String arg = getNextArg();
+        String arg;
+        boolean setEphemeralIfInUse = false;
+        while ((arg = getNextOption()) != null) {
+            if (arg.equals("--set-ephemeral-if-in-use")) {
+                setEphemeralIfInUse = true;
+            }
+        }
+
+        arg = getNextArg();
         if (arg == null) {
             getErrPrintWriter().println("Error: no user id specified.");
             return 1;
@@ -2695,6 +2706,15 @@
         userId = UserHandle.parseUserArg(arg);
         IUserManager um = IUserManager.Stub.asInterface(
                 ServiceManager.getService(Context.USER_SERVICE));
+        if (setEphemeralIfInUse) {
+            return removeUserOrSetEphemeral(um, userId);
+        } else {
+            return removeUser(um, userId);
+        }
+    }
+
+    private int removeUser(IUserManager um, @UserIdInt int userId) throws RemoteException {
+        Slog.i(TAG, "Removing user " + userId);
         if (um.removeUser(userId)) {
             getOutPrintWriter().println("Success: removed user");
             return 0;
@@ -2704,6 +2724,27 @@
         }
     }
 
+    private int removeUserOrSetEphemeral(IUserManager um, @UserIdInt int userId)
+            throws RemoteException {
+        Slog.i(TAG, "Removing " + userId + " or set as ephemeral if in use.");
+        int result = um.removeUserOrSetEphemeral(userId);
+        switch (result) {
+            case UserManager.REMOVE_RESULT_REMOVED:
+                getOutPrintWriter().printf("Success: user %d removed\n", userId);
+                return 0;
+            case UserManager.REMOVE_RESULT_SET_EPHEMERAL:
+                getOutPrintWriter().printf("Success: user %d set as ephemeral\n", userId);
+                return 0;
+            case UserManager.REMOVE_RESULT_ALREADY_BEING_REMOVED:
+                getOutPrintWriter().printf("Success: user %d is already being removed\n", userId);
+                return 0;
+            default:
+                getErrPrintWriter().printf("Error: couldn't remove or mark ephemeral user id %d\n",
+                        userId);
+                return 1;
+        }
+    }
+
     public int runSetUserRestriction() throws RemoteException {
         int userId = UserHandle.USER_SYSTEM;
         String opt = getNextOption();
@@ -3768,9 +3809,13 @@
         pw.println("      --restricted is shorthand for '--user-type android.os.usertype.full.RESTRICTED'.");
         pw.println("      --guest is shorthand for '--user-type android.os.usertype.full.GUEST'.");
         pw.println("");
-        pw.println("  remove-user USER_ID");
+        pw.println("  remove-user [--set-ephemeral-if-in-use] USER_ID");
         pw.println("    Remove the user with the given USER_IDENTIFIER, deleting all data");
-        pw.println("    associated with that user");
+        pw.println("    associated with that user.");
+        pw.println("      --set-ephemeral-if-in-use: If the user is currently running and");
+        pw.println("        therefore cannot be removed immediately, mark the user as ephemeral");
+        pw.println("        so that it will be automatically removed when possible (after user");
+        pw.println("        switch or reboot)");
         pw.println("");
         pw.println("  set-user-restriction [--user USER_ID] RESTRICTION VALUE");
         pw.println("");
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 2ff18f8..83f6c52 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -28,11 +28,13 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
-import com.android.server.pm.permission.AppIdPermissionState;
+import com.android.server.pm.permission.LegacyPermissionDataProvider;
+import com.android.server.pm.permission.LegacyPermissionState;
 import com.android.server.pm.pkg.PackageStateUnserialized;
 
 import java.io.File;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -214,11 +216,12 @@
         mimeGroups = updatedMimeGroups;
     }
 
+    @Deprecated
     @Override
-    public AppIdPermissionState getPermissionsState() {
+    public LegacyPermissionState getLegacyPermissionState() {
         return (sharedUser != null)
-                ? sharedUser.getPermissionsState()
-                : super.getPermissionsState();
+                ? sharedUser.getLegacyPermissionState()
+                : super.getLegacyPermissionState();
     }
 
     public int getAppId() {
@@ -299,7 +302,8 @@
         return mimeGroups != null ? mimeGroups.get(mimeGroup) : null;
     }
 
-    public void dumpDebug(ProtoOutputStream proto, long fieldId, List<UserInfo> users) {
+    public void dumpDebug(ProtoOutputStream proto, long fieldId, List<UserInfo> users,
+            LegacyPermissionDataProvider dataProvider) {
         final long packageToken = proto.start(fieldId);
         proto.write(PackageProto.NAME, (realName != null ? realName : name));
         proto.write(PackageProto.UID, appId);
@@ -336,9 +340,33 @@
         proto.write(PackageProto.StatesProto.IS_STARTABLE, incrementalStates.isStartable());
         proto.write(PackageProto.StatesProto.IS_LOADING, incrementalStates.isLoading());
         writeUsersInfoToProto(proto, PackageProto.USERS);
+        writePackageUserPermissionsProto(proto, PackageProto.USER_PERMISSIONS, users, dataProvider);
         proto.end(packageToken);
     }
 
+    /**
+     * TODO (b/170263003) refactor to dump to permissiongr proto
+     * Dumps the permissions that are granted to users for this package.
+     */
+    void writePackageUserPermissionsProto(ProtoOutputStream proto, long fieldId,
+            List<UserInfo> users, LegacyPermissionDataProvider dataProvider) {
+        Collection<LegacyPermissionState.PermissionState> runtimePermissionStates;
+        for (UserInfo user : users) {
+            final long permissionsToken = proto.start(PackageProto.USER_PERMISSIONS);
+            proto.write(PackageProto.UserPermissionsProto.ID, user.id);
+
+            runtimePermissionStates = dataProvider.getLegacyPermissionState(appId)
+                    .getRuntimePermissionStates(user.id);
+            for (LegacyPermissionState.PermissionState permission : runtimePermissionStates) {
+                if (permission.isGranted()) {
+                    proto.write(PackageProto.UserPermissionsProto.GRANTED_PERMISSIONS,
+                            permission.getName());
+                }
+            }
+            proto.end(permissionsToken);
+        }
+    }
+
     /** Updates all fields in the current setting from another. */
     public void updateFrom(PackageSetting other) {
         super.updateFrom(other);
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index d52ad46..be7c7c6 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -793,13 +793,6 @@
         incrementalStates.onStorageHealthStatusChanged(status);
     }
 
-    /**
-     * @see IncrementalStates#onStreamStatusChanged(int)
-     */
-    public void setStreamStatus(int status) {
-        incrementalStates.onStreamStatusChanged(status);
-    }
-
     protected PackageSettingBase updateFrom(PackageSettingBase other) {
         super.copyFrom(other);
         setPath(other.getPath());
diff --git a/services/core/java/com/android/server/pm/PackageSignatures.java b/services/core/java/com/android/server/pm/PackageSignatures.java
index 6bce788..b7d1eec 100644
--- a/services/core/java/com/android/server/pm/PackageSignatures.java
+++ b/services/core/java/com/android/server/pm/PackageSignatures.java
@@ -309,7 +309,7 @@
 
     @Override
     public String toString() {
-        StringBuffer buf = new StringBuffer(128);
+        StringBuilder buf = new StringBuilder(128);
         buf.append("PackageSignatures{");
         buf.append(Integer.toHexString(System.identityHashCode(this)));
         buf.append(" version:");
diff --git a/services/core/java/com/android/server/pm/PackageUsage.java b/services/core/java/com/android/server/pm/PackageUsage.java
index ef37a20..ec897798 100644
--- a/services/core/java/com/android/server/pm/PackageUsage.java
+++ b/services/core/java/com/android/server/pm/PackageUsage.java
@@ -97,7 +97,7 @@
         BufferedInputStream in = null;
         try {
             in = new BufferedInputStream(file.openRead());
-            StringBuffer sb = new StringBuffer();
+            StringBuilder sb = new StringBuilder();
 
             String firstLine = readLine(in, sb);
             if (firstLine == null) {
@@ -117,7 +117,7 @@
     }
 
     private void readVersion0LP(Map<String, PackageSetting> pkgSettings, InputStream in,
-            StringBuffer sb, String firstLine)
+            StringBuilder sb, String firstLine)
             throws IOException {
         // Initial version of the file had no version number and stored one
         // package-timestamp pair per line.
@@ -145,7 +145,7 @@
     }
 
     private void readVersion1LP(Map<String, PackageSetting> pkgSettings, InputStream in,
-            StringBuffer sb) throws IOException {
+            StringBuilder sb) throws IOException {
         // Version 1 of the file started with the corresponding version
         // number and then stored a package name and eight timestamps per line.
         String line;
@@ -178,11 +178,11 @@
         }
     }
 
-    private String readLine(InputStream in, StringBuffer sb) throws IOException {
+    private String readLine(InputStream in, StringBuilder sb) throws IOException {
         return readToken(in, sb, '\n');
     }
 
-    private String readToken(InputStream in, StringBuffer sb, char endOfToken)
+    private String readToken(InputStream in, StringBuilder sb, char endOfToken)
             throws IOException {
         sb.setLength(0);
         while (true) {
diff --git a/services/core/java/com/android/server/pm/ProcessLoggingHandler.java b/services/core/java/com/android/server/pm/ProcessLoggingHandler.java
index c47dda4..7fb34951d 100644
--- a/services/core/java/com/android/server/pm/ProcessLoggingHandler.java
+++ b/services/core/java/com/android/server/pm/ProcessLoggingHandler.java
@@ -16,29 +16,47 @@
 
 package com.android.server.pm;
 
+import static android.content.pm.PackageManager.EXTRA_CHECKSUMS;
+
 import android.app.admin.SecurityLog;
+import android.content.Context;
+import android.content.IIntentReceiver;
+import android.content.IIntentSender;
 import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.ApkChecksum;
+import android.content.pm.Checksum;
+import android.content.pm.IPackageManager;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Message;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Slog;
 
 import com.android.internal.os.BackgroundThread;
 
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.HashMap;
-import android.util.Slog;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 
 public final class ProcessLoggingHandler extends Handler {
-
     private static final String TAG = "ProcessLoggingHandler";
-    static final int LOG_APP_PROCESS_START_MSG = 1;
-    static final int INVALIDATE_BASE_APK_HASH_MSG = 2;
 
-    private final HashMap<String, String> mProcessLoggingBaseApkHashes = new HashMap();
+    private static final int LOG_APP_PROCESS_START_MSG = 1;
+
+    private static final int CHECKSUM_TYPE = Checksum.TYPE_WHOLE_SHA256;
+
+    static class LoggingInfo {
+        public String apkHash = null;
+        public List<Bundle> pendingLogEntries = new ArrayList<>();
+    }
+
+    // Apk path to logging info map.
+    private final ArrayMap<String, LoggingInfo> mLoggingInfo = new ArrayMap<>();
 
     ProcessLoggingHandler() {
         super(BackgroundThread.getHandler().getLooper());
@@ -49,64 +67,133 @@
         switch (msg.what) {
             case LOG_APP_PROCESS_START_MSG: {
                 Bundle bundle = msg.getData();
+                long startTimestamp = bundle.getLong("startTimestamp");
                 String processName = bundle.getString("processName");
                 int uid = bundle.getInt("uid");
                 String seinfo = bundle.getString("seinfo");
-                String apkFile = bundle.getString("apkFile");
                 int pid = bundle.getInt("pid");
-                long startTimestamp = bundle.getLong("startTimestamp");
-                String apkHash = computeStringHashOfApk(apkFile);
+                String apkHash = bundle.getString("apkHash");
                 SecurityLog.writeEvent(SecurityLog.TAG_APP_PROCESS_START, processName,
                         startTimestamp, uid, pid, seinfo, apkHash);
                 break;
             }
-            case INVALIDATE_BASE_APK_HASH_MSG: {
-                Bundle bundle = msg.getData();
-                mProcessLoggingBaseApkHashes.remove(bundle.getString("apkFile"));
+        }
+    }
+
+    void logAppProcessStart(Context context, IPackageManager pms, String apkFile,
+            String packageName, String processName, int uid, String seinfo, int pid) {
+        Bundle data = new Bundle();
+        data.putLong("startTimestamp", System.currentTimeMillis());
+        data.putString("processName", processName);
+        data.putInt("uid", uid);
+        data.putString("seinfo", seinfo);
+        data.putInt("pid", pid);
+
+        if (apkFile == null) {
+            enqueueSecurityLogEvent(data, "No APK");
+            return;
+        }
+
+        // Check cached apk hash.
+        boolean requestChecksums;
+        final LoggingInfo loggingInfo;
+        synchronized (mLoggingInfo) {
+            LoggingInfo cached = mLoggingInfo.get(apkFile);
+            requestChecksums = cached == null;
+            if (requestChecksums) {
+                // Create a new pending cache entry.
+                cached = new LoggingInfo();
+                mLoggingInfo.put(apkFile, cached);
+            }
+            loggingInfo = cached;
+        }
+
+        synchronized (loggingInfo) {
+            // Still pending?
+            if (!TextUtils.isEmpty(loggingInfo.apkHash)) {
+                enqueueSecurityLogEvent(data, loggingInfo.apkHash);
+                return;
+            }
+
+            loggingInfo.pendingLogEntries.add(data);
+        }
+
+        if (!requestChecksums) {
+            return;
+        }
+
+        // Request base checksums when first added entry.
+        // Capturing local loggingInfo to still log even if hash was invalidated.
+        try {
+            pms.requestChecksums(packageName, false, 0, CHECKSUM_TYPE, null,
+                    new IntentSender((IIntentSender) new IIntentSender.Stub() {
+                        @Override
+                        public void send(int code, Intent intent, String resolvedType,
+                                IBinder allowlistToken, IIntentReceiver finishedReceiver,
+                                String requiredPermission, Bundle options) {
+                            processChecksums(loggingInfo, intent);
+                        }
+                    }), context.getUserId());
+        } catch (RemoteException e) {
+            Slog.e(TAG, "requestChecksums() failed", e);
+            processChecksums(loggingInfo, null);
+        }
+    }
+
+    void processChecksums(final LoggingInfo loggingInfo, Intent intent) {
+        Parcelable[] parcelables = intent.getParcelableArrayExtra(EXTRA_CHECKSUMS);
+        ApkChecksum[] checksums = Arrays.copyOf(parcelables, parcelables.length,
+                ApkChecksum[].class);
+
+        for (ApkChecksum checksum : checksums) {
+            if (checksum.getType() == CHECKSUM_TYPE) {
+                processChecksum(loggingInfo, checksum.getValue());
                 break;
             }
         }
     }
 
-    void invalidateProcessLoggingBaseApkHash(String apkPath) {
-        Bundle data = new Bundle();
-        data.putString("apkFile", apkPath);
-        Message msg = obtainMessage(INVALIDATE_BASE_APK_HASH_MSG);
-        msg.setData(data);
-        sendMessage(msg);
-    }
-
-    private String computeStringHashOfApk(String apkFile) {
-        if (apkFile == null) {
-            return "No APK";
+    void processChecksum(final LoggingInfo loggingInfo, final byte[] hash) {
+        final String apkHash;
+        if (hash != null) {
+            StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < hash.length; i++) {
+                sb.append(String.format("%02x", hash[i]));
+            }
+            apkHash = sb.toString();
+        } else {
+            apkHash = "Failed to count APK hash";
         }
-        String apkHash = mProcessLoggingBaseApkHashes.get(apkFile);
-        if (apkHash == null) {
-            try {
-                byte[] hash = computeHashOfApkFile(apkFile);
-                StringBuilder sb = new StringBuilder();
-                for (int i = 0; i < hash.length; i++) {
-                    sb.append(String.format("%02x", hash[i]));
-                }
-                apkHash = sb.toString();
-                mProcessLoggingBaseApkHashes.put(apkFile, apkHash);
-            } catch (IOException | NoSuchAlgorithmException e) {
-                Slog.w(TAG, "computeStringHashOfApk() failed", e);
+
+        List<Bundle> pendingLogEntries;
+        synchronized (loggingInfo) {
+            if (!TextUtils.isEmpty(loggingInfo.apkHash)) {
+                return;
+            }
+            loggingInfo.apkHash = apkHash;
+
+            pendingLogEntries = loggingInfo.pendingLogEntries;
+            loggingInfo.pendingLogEntries = null;
+        }
+
+        if (pendingLogEntries != null) {
+            for (Bundle data : pendingLogEntries) {
+                enqueueSecurityLogEvent(data, apkHash);
             }
         }
-        return apkHash != null ? apkHash : "Failed to count APK hash";
     }
 
-    private byte[] computeHashOfApkFile(String packageArchiveLocation)
-            throws IOException, NoSuchAlgorithmException {
-        MessageDigest md = MessageDigest.getInstance("SHA-256");
-        FileInputStream input = new FileInputStream(new File(packageArchiveLocation));
-        byte[] buffer = new byte[65536];
-        int size;
-        while ((size = input.read(buffer)) > 0) {
-            md.update(buffer, 0, size);
+    void enqueueSecurityLogEvent(Bundle data, String apkHash) {
+        data.putString("apkHash", apkHash);
+
+        Message msg = this.obtainMessage(LOG_APP_PROCESS_START_MSG);
+        msg.setData(data);
+        this.sendMessage(msg);
+    }
+
+    void invalidateBaseApkHash(String apkFile) {
+        synchronized (mLoggingInfo) {
+            mLoggingInfo.remove(apkFile);
         }
-        input.close();
-        return md.digest();
     }
 }
diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java
index c1258b1..968c4b5 100644
--- a/services/core/java/com/android/server/pm/SettingBase.java
+++ b/services/core/java/com/android/server/pm/SettingBase.java
@@ -19,23 +19,29 @@
 import android.content.pm.ApplicationInfo;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.pm.permission.AppIdPermissionState;
+import com.android.server.pm.permission.LegacyPermissionState;
 
 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
 public abstract class SettingBase {
     int pkgFlags;
     int pkgPrivateFlags;
 
-    protected final AppIdPermissionState mPermissionsState;
+    /**
+     * The legacy permission state that is read from package settings persistence for migration.
+     * This state here can not reflect the current permission state and should not be used for
+     * purposes other than migration.
+     */
+    @Deprecated
+    protected final LegacyPermissionState mLegacyPermissionsState;
 
     SettingBase(int pkgFlags, int pkgPrivateFlags) {
         setFlags(pkgFlags);
         setPrivateFlags(pkgPrivateFlags);
-        mPermissionsState = new AppIdPermissionState();
+        mLegacyPermissionsState = new LegacyPermissionState();
     }
 
     SettingBase(SettingBase orig) {
-        mPermissionsState = new AppIdPermissionState();
+        mLegacyPermissionsState = new LegacyPermissionState();
         doCopy(orig);
     }
 
@@ -46,11 +52,12 @@
     private void doCopy(SettingBase orig) {
         pkgFlags = orig.pkgFlags;
         pkgPrivateFlags = orig.pkgPrivateFlags;
-        mPermissionsState.copyFrom(orig.mPermissionsState);
+        mLegacyPermissionsState.copyFrom(orig.mLegacyPermissionsState);
     }
 
-    public AppIdPermissionState getPermissionsState() {
-        return mPermissionsState;
+    @Deprecated
+    public LegacyPermissionState getLegacyPermissionState() {
+        return mLegacyPermissionsState;
     }
 
     void setFlags(int pkgFlags) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index c16bd5c..c7304c2 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -107,10 +107,10 @@
 import com.android.server.pm.parsing.PackageInfoUtils;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
-import com.android.server.pm.permission.AppIdPermissionState;
-import com.android.server.pm.permission.AppIdPermissionState.PermissionState;
 import com.android.server.pm.permission.BasePermission;
 import com.android.server.pm.permission.LegacyPermissionDataProvider;
+import com.android.server.pm.permission.LegacyPermissionState;
+import com.android.server.pm.permission.LegacyPermissionState.PermissionState;
 import com.android.server.pm.permission.PermissionSettings;
 import com.android.server.utils.TimingsTraceAndSlog;
 
@@ -733,7 +733,8 @@
                     pkgSetting.signatures = new PackageSignatures(disabledPkg.signatures);
                     pkgSetting.appId = disabledPkg.appId;
                     // Clone permissions
-                    pkgSetting.getPermissionsState().copyFrom(disabledPkg.getPermissionsState());
+                    pkgSetting.getLegacyPermissionState()
+                            .copyFrom(disabledPkg.getLegacyPermissionState());
                     // Clone component info
                     List<UserInfo> users = getAllUsers(userManager);
                     if (users != null) {
@@ -2114,7 +2115,7 @@
     }
 
     void readInstallPermissionsLPr(XmlPullParser parser,
-            AppIdPermissionState permissionsState) throws IOException, XmlPullParserException {
+            LegacyPermissionState permissionsState) throws IOException, XmlPullParserException {
         int outerDepth = parser.getDepth();
         int type;
         while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
@@ -2406,7 +2407,7 @@
                 serializer.attribute(null, "userId",
                         Integer.toString(usr.userId));
                 usr.signatures.writeXml(serializer, "sigs", mPastSignatures);
-                writePermissionsLPr(serializer, usr.getPermissionsState()
+                writePermissionsLPr(serializer, usr.getLegacyPermissionState()
                         .getInstallPermissionStates());
                 serializer.endTag(null, "shared-user");
             }
@@ -2734,7 +2735,7 @@
 
         // If this is a shared user, the permissions will be written there.
         if (pkg.sharedUser == null) {
-            writePermissionsLPr(serializer, pkg.getPermissionsState()
+            writePermissionsLPr(serializer, pkg.getLegacyPermissionState()
                     .getInstallPermissionStates());
         }
 
@@ -2825,7 +2826,8 @@
                     serializer, "install-initiator-sigs", mPastSignatures);
         }
 
-        writePermissionsLPr(serializer, pkg.getPermissionsState().getInstallPermissionStates());
+        writePermissionsLPr(serializer,
+                pkg.getLegacyPermissionState().getInstallPermissionStates());
 
         writeSigningKeySetLPr(serializer, pkg.keySetData);
         writeUpgradeKeySetsLPr(serializer, pkg.keySetData);
@@ -3551,7 +3553,7 @@
             }
 
             if (parser.getName().equals(TAG_PERMISSIONS)) {
-                readInstallPermissionsLPr(parser, ps.getPermissionsState());
+                readInstallPermissionsLPr(parser, ps.getLegacyPermissionState());
             } else if (parser.getName().equals(TAG_USES_STATIC_LIB)) {
                 readUsesStaticLibLPw(parser, ps);
             } else {
@@ -3848,7 +3850,7 @@
                     packageSetting.signatures.readXml(parser, mPastSignatures);
                 } else if (tagName.equals(TAG_PERMISSIONS)) {
                     readInstallPermissionsLPr(parser,
-                            packageSetting.getPermissionsState());
+                            packageSetting.getLegacyPermissionState());
                     packageSetting.installPermissionsFixed = true;
                 } else if (tagName.equals("proper-signing-keyset")) {
                     long id = Long.parseLong(parser.getAttributeValue(null, "identifier"));
@@ -4074,7 +4076,7 @@
                 if (tagName.equals("sigs")) {
                     su.signatures.readXml(parser, mPastSignatures);
                 } else if (tagName.equals("perms")) {
-                    readInstallPermissionsLPr(parser, su.getPermissionsState());
+                    readInstallPermissionsLPr(parser, su.getLegacyPermissionState());
                 } else {
                     PackageManagerService.reportSettingsProblem(Log.WARN,
                             "Unknown element under <shared-user>: " + parser.getName());
@@ -4385,7 +4387,7 @@
      */
     private static List<UserInfo> getUsers(UserManagerService userManager, boolean excludeDying,
             boolean excludePreCreated) {
-        long id = Binder.clearCallingIdentity();
+        final long id = Binder.clearCallingIdentity();
         try {
             return userManager.getUsers(/* excludePartial= */ true, excludeDying,
                     excludePreCreated);
@@ -4494,7 +4496,7 @@
 
     void dumpPackageLPr(PrintWriter pw, String prefix, String checkinTag,
             ArraySet<String> permissionNames, PackageSetting ps,
-            AppIdPermissionState permissionsState, SimpleDateFormat sdf, Date date,
+            LegacyPermissionState permissionsState, SimpleDateFormat sdf, Date date,
             List<UserInfo> users, boolean dumpAll, boolean dumpAllComponents) {
         AndroidPackage pkg = ps.pkg;
         if (checkinTag != null) {
@@ -4944,8 +4946,8 @@
                     && !packageName.equals(ps.name)) {
                 continue;
             }
-            final AppIdPermissionState permissionsState =
-                    mPermissionDataProvider.getAppIdPermissionState(ps.appId);
+            final LegacyPermissionState permissionsState =
+                    mPermissionDataProvider.getLegacyPermissionState(ps.appId);
             if (permissionNames != null
                     && !permissionsState.hasPermissionState(permissionNames)) {
                 continue;
@@ -5002,8 +5004,8 @@
                     pw.println("Hidden system packages:");
                     printedSomething = true;
                 }
-                final AppIdPermissionState permissionsState =
-                        mPermissionDataProvider.getAppIdPermissionState(ps.appId);
+                final LegacyPermissionState permissionsState =
+                        mPermissionDataProvider.getLegacyPermissionState(ps.appId);
                 dumpPackageLPr(pw, "  ", checkin ? "dis" : null, permissionNames, ps,
                         permissionsState, sdf, date, users, packageName != null, dumpAllComponents);
             }
@@ -5016,7 +5018,7 @@
         final int count = mPackages.size();
         for (int i = 0; i < count; i++) {
             final PackageSetting ps = mPackages.valueAt(i);
-            ps.dumpDebug(proto, PackageServiceDumpProto.PACKAGES, users);
+            ps.dumpDebug(proto, PackageServiceDumpProto.PACKAGES, users, mPermissionDataProvider);
         }
     }
 
@@ -5033,8 +5035,8 @@
             if (packageName != null && su != dumpState.getSharedUser()) {
                 continue;
             }
-            final AppIdPermissionState permissionsState =
-                    mPermissionDataProvider.getAppIdPermissionState(su.userId);
+            final LegacyPermissionState permissionsState =
+                    mPermissionDataProvider.getLegacyPermissionState(su.userId);
             if (permissionNames != null
                     && !permissionsState.hasPermissionState(permissionNames)) {
                 continue;
@@ -5178,7 +5180,7 @@
     }
 
     void dumpInstallPermissionsLPr(PrintWriter pw, String prefix, ArraySet<String> permissionNames,
-            AppIdPermissionState permissionsState) {
+            LegacyPermissionState permissionsState) {
         Collection<PermissionState> permissionStates =
                 permissionsState.getInstallPermissionStates();
         if (!permissionStates.isEmpty()) {
@@ -5424,7 +5426,7 @@
                 if (packageSetting.sharedUser == null) {
                     List<RuntimePermissionsState.PermissionState> permissions =
                             getPermissionsFromPermissionsState(
-                                    packageSetting.getPermissionsState(), userId);
+                                    packageSetting.getLegacyPermissionState(), userId);
                     packagePermissions.put(packageName, permissions);
                 }
             }
@@ -5437,7 +5439,7 @@
                 SharedUserSetting sharedUserSetting = mSharedUsers.valueAt(i);
                 List<RuntimePermissionsState.PermissionState> permissions =
                         getPermissionsFromPermissionsState(
-                                sharedUserSetting.getPermissionsState(), userId);
+                                sharedUserSetting.getLegacyPermissionState(), userId);
                 sharedUserPermissions.put(sharedUserName, permissions);
             }
 
@@ -5449,7 +5451,7 @@
 
         @NonNull
         private List<RuntimePermissionsState.PermissionState> getPermissionsFromPermissionsState(
-                @NonNull AppIdPermissionState permissionsState, @UserIdInt int userId) {
+                @NonNull LegacyPermissionState permissionsState, @UserIdInt int userId) {
             Collection<PermissionState> permissionStates =
                     permissionsState.getRuntimePermissionStates(userId);
             List<RuntimePermissionsState.PermissionState> permissions = new ArrayList<>();
@@ -5509,11 +5511,11 @@
                 List<RuntimePermissionsState.PermissionState> permissions =
                         packagePermissions.get(packageName);
                 if (permissions != null) {
-                    readPermissionsStateLpr(permissions, packageSetting.getPermissionsState(),
+                    readPermissionsStateLpr(permissions, packageSetting.getLegacyPermissionState(),
                             userId);
                 } else if (packageSetting.sharedUser == null && !isUpgradeToR) {
                     Slog.w(TAG, "Missing permission state for package: " + packageName);
-                    packageSetting.getPermissionsState().setMissing(true, userId);
+                    packageSetting.getLegacyPermissionState().setMissing(true, userId);
                 }
             }
 
@@ -5527,18 +5529,18 @@
                 List<RuntimePermissionsState.PermissionState> permissions =
                         sharedUserPermissions.get(sharedUserName);
                 if (permissions != null) {
-                    readPermissionsStateLpr(permissions, sharedUserSetting.getPermissionsState(),
-                            userId);
+                    readPermissionsStateLpr(permissions,
+                            sharedUserSetting.getLegacyPermissionState(), userId);
                 } else if (!isUpgradeToR) {
                     Slog.w(TAG, "Missing permission state for shared user: " + sharedUserName);
-                    sharedUserSetting.getPermissionsState().setMissing(true, userId);
+                    sharedUserSetting.getLegacyPermissionState().setMissing(true, userId);
                 }
             }
         }
 
         private void readPermissionsStateLpr(
                 @NonNull List<RuntimePermissionsState.PermissionState> permissions,
-                @NonNull AppIdPermissionState permissionsState, @UserIdInt int userId) {
+                @NonNull LegacyPermissionState permissionsState, @UserIdInt int userId) {
             int permissionsSize = permissions.size();
             for (int i = 0; i < permissionsSize; i++) {
                 RuntimePermissionsState.PermissionState permission = permissions.get(i);
@@ -5617,7 +5619,7 @@
                             XmlUtils.skipCurrentTag(parser);
                             continue;
                         }
-                        parsePermissionsLPr(parser, ps.getPermissionsState(), userId);
+                        parsePermissionsLPr(parser, ps.getLegacyPermissionState(), userId);
                     } break;
 
                     case TAG_SHARED_USER: {
@@ -5628,14 +5630,14 @@
                             XmlUtils.skipCurrentTag(parser);
                             continue;
                         }
-                        parsePermissionsLPr(parser, sus.getPermissionsState(), userId);
+                        parsePermissionsLPr(parser, sus.getLegacyPermissionState(), userId);
                     } break;
                 }
             }
         }
 
         private void parsePermissionsLPr(XmlPullParser parser,
-                AppIdPermissionState permissionsState, int userId)
+                LegacyPermissionState permissionsState, int userId)
                 throws IOException, XmlPullParserException {
             final int outerDepth = parser.getDepth();
             int type;
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 96f9982..e471ac6 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -3907,10 +3907,11 @@
         final long start = getStatStartTime();
         final long token = injectClearCallingIdentity();
         try {
-            return mContext.getPackageManager().getResourcesForApplicationAsUser(
-                    packageName, userId);
+            return mContext.createContextAsUser(UserHandle.of(userId), /* flags */ 0)
+                    .getPackageManager().getResourcesForApplication(packageName);
         } catch (NameNotFoundException e) {
-            Slog.e(TAG, "Resources for package " + packageName + " not found");
+            Slog.e(TAG, "Resources of package " + packageName + " for user " + userId
+                    + " not found");
             return null;
         } finally {
             injectRestoreCallingIdentity(token);
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 35c26d6..3bad3cb 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -17,7 +17,6 @@
 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;
@@ -46,8 +45,6 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
-import android.os.ParcelFileDescriptor;
-import android.os.ParcelableException;
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.SystemProperties;
@@ -80,7 +77,6 @@
 import java.io.File;
 import java.io.FileReader;
 import java.io.FileWriter;
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
@@ -96,7 +92,6 @@
 
     private static final String TAG = "StagingManager";
 
-    private final PackageInstallerService mPi;
     private final ApexManager mApexManager;
     private final PowerManager mPowerManager;
     private final Context mContext;
@@ -116,9 +111,7 @@
     @GuardedBy("mSuccessfulStagedSessionIds")
     private final List<Integer> mSuccessfulStagedSessionIds = new ArrayList<>();
 
-    StagingManager(PackageInstallerService pi, Context context,
-            Supplier<PackageParser2> packageParserSupplier) {
-        mPi = pi;
+    StagingManager(Context context, Supplier<PackageParser2> packageParserSupplier) {
         mContext = context;
         mPackageParserSupplier = packageParserSupplier;
 
@@ -650,118 +643,6 @@
         return "";
     }
 
-    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")) {
-                    ret.add(file.getAbsolutePath());
-                }
-            }
-        }
-        return ret;
-    }
-
-    private PackageInstallerSession createAndWriteApkSession(
-            PackageInstallerSession originalSession) throws PackageManagerException {
-        final int errorCode = SessionInfo.STAGED_SESSION_ACTIVATION_FAILED;
-        if (originalSession.stageDir == null) {
-            Slog.wtf(TAG, "Attempting to install a staged APK session with no staging dir");
-            throw new PackageManagerException(errorCode,
-                    "Attempting to install a staged APK session with no staging dir");
-        }
-        List<String> apkFilePaths = findAPKsInDir(originalSession.stageDir);
-        if (apkFilePaths.isEmpty()) {
-            Slog.w(TAG, "Can't find staged APK in " + originalSession.stageDir.getAbsolutePath());
-            throw new PackageManagerException(errorCode,
-                    "Can't find staged APK in " + originalSession.stageDir.getAbsolutePath());
-        }
-
-        PackageInstaller.SessionParams params = originalSession.params.copy();
-        params.isStaged = false;
-        params.installFlags |= PackageManager.INSTALL_STAGED;
-        params.installFlags |= PackageManager.INSTALL_DISABLE_VERIFICATION;
-        try {
-            int apkSessionId = mPi.createSession(
-                    params, originalSession.getInstallerPackageName(),
-                    originalSession.getInstallerAttributionTag(), originalSession.userId);
-            PackageInstallerSession apkSession = mPi.getSession(apkSessionId);
-            apkSession.open();
-            for (int i = 0, size = apkFilePaths.size(); i < size; i++) {
-                final String apkFilePath = apkFilePaths.get(i);
-                File apkFile = new File(apkFilePath);
-                ParcelFileDescriptor pfd = ParcelFileDescriptor.open(apkFile,
-                        ParcelFileDescriptor.MODE_READ_ONLY);
-                long sizeBytes = (pfd == null) ? -1 : pfd.getStatSize();
-                if (sizeBytes < 0) {
-                    Slog.e(TAG, "Unable to get size of: " + apkFilePath);
-                    throw new PackageManagerException(errorCode,
-                            "Unable to get size of: " + apkFilePath);
-                }
-                apkSession.write(apkFile.getName(), 0, sizeBytes, pfd);
-            }
-            return apkSession;
-        } catch (IOException | ParcelableException e) {
-            Slog.e(TAG, "Failure to install APK staged session " + originalSession.sessionId, e);
-            throw new PackageManagerException(errorCode, "Failed to create/write APK session", e);
-        }
-    }
-
-    /**
-     * Extract apks in the given session into a new session. Returns {@code null} if there is no
-     * apks in the given session. Only parent session is returned for multi-package session.
-     */
-    @Nullable
-    private PackageInstallerSession extractApksInSession(PackageInstallerSession session)
-            throws PackageManagerException {
-        if (!session.isMultiPackage() && !session.isApexSession()) {
-            return createAndWriteApkSession(session);
-        } else if (session.isMultiPackage()) {
-            // For multi-package staged sessions containing APKs, we identify which child sessions
-            // contain an APK, and with those then create a new multi-package group of sessions,
-            // carrying over all the session parameters and unmarking them as staged. On commit the
-            // sessions will be installed atomically.
-            final List<PackageInstallerSession> childSessions = new ArrayList<>();
-            for (PackageInstallerSession s : session.getChildSessions()) {
-                if (!s.isApexSession()) {
-                    childSessions.add(s);
-                }
-            }
-            if (childSessions.isEmpty()) {
-                // APEX-only multi-package staged session, nothing to do.
-                return null;
-            }
-            final PackageInstaller.SessionParams params = session.params.copy();
-            params.isStaged = false;
-            final int apkParentSessionId = mPi.createSession(
-                    params, session.getInstallerPackageName(), session.getInstallerAttributionTag(),
-                    session.userId);
-            final PackageInstallerSession apkParentSession = mPi.getSession(apkParentSessionId);
-            try {
-                apkParentSession.open();
-            } catch (IOException e) {
-                Slog.e(TAG, "Unable to prepare multi-package session for staged session "
-                        + session.sessionId);
-                throw new PackageManagerException(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
-                        "Unable to prepare multi-package session for staged session");
-            }
-
-            for (int i = 0, size = childSessions.size(); i < size; i++) {
-                final PackageInstallerSession apkChildSession = createAndWriteApkSession(
-                        childSessions.get(i));
-                try {
-                    apkParentSession.addChildSessionId(apkChildSession.sessionId);
-                } catch (IllegalStateException e) {
-                    Slog.e(TAG, "Failed to add a child session for installing the APK files", e);
-                    throw new PackageManagerException(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
-                            "Failed to add a child session " + apkChildSession.sessionId);
-                }
-            }
-            return apkParentSession;
-        }
-        return null;
-    }
-
     /**
      * Throws a PackageManagerException if there are duplicate packages in apk and apk-in-apex.
      */
@@ -794,18 +675,18 @@
 
     private void installApksInSession(PackageInstallerSession session)
             throws PackageManagerException {
-        final PackageInstallerSession apksToInstall = extractApksInSession(session);
-        if (apksToInstall == null) {
+        if (!session.containsApkSession()) {
             return;
         }
 
-        if ((apksToInstall.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
+        if ((session.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
             // If rollback is available for this session, notify the rollback
             // manager of the apk session so it can properly enable rollback.
             final RollbackManagerInternal rm =
                     LocalServices.getService(RollbackManagerInternal.class);
             try {
-                rm.notifyStagedApkSession(session.sessionId, apksToInstall.sessionId);
+                // TODO(b/136257624): extra apk session id in rollback is now redundant.
+                rm.notifyStagedApkSession(session.sessionId, session.sessionId);
             } catch (RuntimeException re) {
                 Slog.e(TAG, "Failed to notifyStagedApkSession for session: "
                         + session.sessionId, re);
@@ -813,7 +694,7 @@
         }
 
         final LocalIntentReceiverSync receiver = new LocalIntentReceiverSync();
-        apksToInstall.commit(receiver.getIntentSender(), false);
+        session.installStagedSession(receiver.getIntentSender());
         final Intent result = receiver.getResult();
         final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
                 PackageInstaller.STATUS_FAILURE);
@@ -828,6 +709,8 @@
     }
 
     void commitSession(@NonNull PackageInstallerSession session) {
+        // Store this parent session which will be used to check overlapping later
+        createSession(session);
         mPreRebootVerificationHandler.startPreRebootVerification(session);
     }
 
@@ -857,14 +740,16 @@
      * </ul>
      * @throws PackageManagerException if session fails the check
      */
-    void checkNonOverlappingWithStagedSessions(@NonNull PackageInstallerSession session)
+    private void checkNonOverlappingWithStagedSessions(@NonNull PackageInstallerSession session)
             throws PackageManagerException {
         if (session.isMultiPackage()) {
             // We cannot say a parent session overlaps until we process its children
             return;
         }
-        if (session.getPackageName() == null) {
-            throw new PackageManagerException(PackageManager.INSTALL_FAILED_INVALID_APK,
+
+        String packageName = session.getPackageName();
+        if (packageName == null) {
+            throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
                     "Cannot stage session " + session.sessionId + " with package name null");
         }
 
@@ -876,40 +761,26 @@
         synchronized (mStagedSessions) {
             for (int i = 0; i < mStagedSessions.size(); i++) {
                 final PackageInstallerSession stagedSession = mStagedSessions.valueAt(i);
-                if (!stagedSession.isCommitted() || stagedSession.isStagedAndInTerminalState()
+                if (stagedSession.hasParentSessionId() || !stagedSession.isCommitted()
+                        || stagedSession.isStagedAndInTerminalState()
                         || stagedSession.isDestroyed()) {
                     continue;
                 }
-                if (stagedSession.isMultiPackage()) {
-                    // This active parent staged session is useless as it doesn't have a package
-                    // name and the session we are checking is not a parent session either.
-                    continue;
-                }
-                // Check if stagedSession has an active parent session or not
-                if (stagedSession.hasParentSessionId()) {
-                    final int parentId = stagedSession.getParentSessionId();
-                    final PackageInstallerSession parentSession = mStagedSessions.get(parentId);
-                    if (parentSession == null || parentSession.isStagedAndInTerminalState()
-                            || parentSession.isDestroyed()) {
-                        // Parent session has been abandoned or terminated already
-                        continue;
-                    }
-                }
 
-                // From here on, stagedSession is a non-parent active staged session
+                // From here on, stagedSession is a parent active staged session
 
                 // Check if session is one of the active sessions
-                if (session.sessionId == stagedSession.sessionId) {
+                if (getSessionIdForParentOrSelf(session) == stagedSession.sessionId) {
                     Slog.w(TAG, "Session " + session.sessionId + " is already staged");
                     continue;
                 }
 
                 // New session cannot have same package name as one of the active sessions
-                if (session.getPackageName().equals(stagedSession.getPackageName())) {
+                if (stagedSession.sessionContains(s -> s.getPackageName().equals(packageName))) {
                     if (isRollback) {
                         // If the new session is a rollback, then it gets priority. The existing
                         // session is failed to unblock rollback.
-                        final PackageInstallerSession root = getParentSessionOrSelf(stagedSession);
+                        final PackageInstallerSession root = stagedSession;
                         if (!ensureActiveApexSessionIsAborted(root)) {
                             Slog.e(TAG, "Failed to abort apex session " + root.sessionId);
                             // Safe to ignore active apex session abort failure since session
@@ -923,7 +794,7 @@
                                 + "blocking rollback session: " + session.sessionId);
                     } else {
                         throw new PackageManagerException(
-                                PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
+                                SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
                                 "Package: " + session.getPackageName() + " in session: "
                                         + session.sessionId + " has been staged already by session:"
                                         + " " + stagedSession.sessionId, null);
@@ -933,17 +804,16 @@
                 // Staging multiple root sessions is not allowed if device doesn't support
                 // checkpoint. If session and stagedSession do not have common ancestor, they are
                 // from two different root sessions.
-                if (!supportsCheckpoint && getSessionIdForParentOrSelf(session)
-                        != getSessionIdForParentOrSelf(stagedSession)) {
+                if (!supportsCheckpoint) {
                     throw new PackageManagerException(
-                            PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
+                            SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
                             "Cannot stage multiple sessions without checkpoint support", null);
                 }
             }
         }
     }
 
-    void createSession(@NonNull PackageInstallerSession sessionInfo) {
+    private void createSession(@NonNull PackageInstallerSession sessionInfo) {
         synchronized (mStagedSessions) {
             mStagedSessions.append(sessionInfo.sessionId, sessionInfo);
         }
@@ -1019,16 +889,15 @@
     }
 
     void restoreSession(@NonNull PackageInstallerSession session, boolean isDeviceUpgrading) {
-        PackageInstallerSession sessionToResume = session;
-        synchronized (mStagedSessions) {
-            mStagedSessions.append(session.sessionId, session);
-            if (session.hasParentSessionId()) {
-                // Only parent sessions can be restored
-                return;
-            }
+        if (session.hasParentSessionId()) {
+            // Only parent sessions can be restored
+            return;
         }
+        // Store this parent session which will be used to check overlapping later
+        createSession(session);
         // The preconditions used during pre-reboot verification might have changed when device
         // is upgrading. Updated staged sessions to activation failed before we resume the session.
+        PackageInstallerSession sessionToResume = session;
         if (isDeviceUpgrading && !sessionToResume.isStagedAndInTerminalState()) {
             sessionToResume.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
                         "Build fingerprint has changed");
@@ -1279,6 +1148,19 @@
          * See {@link PreRebootVerificationHandler} to see all nodes of pre reboot verification
          */
         private void handlePreRebootVerification_Start(@NonNull PackageInstallerSession session) {
+            try {
+                if (session.isMultiPackage()) {
+                    for (PackageInstallerSession s : session.getChildSessions()) {
+                        checkNonOverlappingWithStagedSessions(s);
+                    }
+                } else {
+                    checkNonOverlappingWithStagedSessions(session);
+                }
+            } catch (PackageManagerException e) {
+                onPreRebootVerificationFailure(session, e.error, e.getMessage());
+                return;
+            }
+
             int rollbackId = -1;
             if ((session.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
                 // If rollback is enabled for this session, we call through to the RollbackManager
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 0a8c8f6..a0344e2 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -110,7 +110,6 @@
 import com.android.server.LocalServices;
 import com.android.server.LockGuard;
 import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
 import com.android.server.am.UserState;
 import com.android.server.storage.DeviceStorageMonitorInternal;
 import com.android.server.utils.TimingsTraceAndSlog;
@@ -253,9 +252,17 @@
 
     private final Context mContext;
     private final PackageManagerService mPm;
+
+    /**
+     * Lock for packages. If using with {@link #mUsersLock}, {@link #mPackagesLock} should be
+     * acquired first.
+     */
     private final Object mPackagesLock;
     private final UserDataPreparer mUserDataPreparer;
-    // Short-term lock for internal state, when interaction/sync with PM is not required
+    /**
+     * Short-term lock for internal state, when interaction/sync with PM is not required. If using
+     * with {@link #mPackagesLock}, {@link #mPackagesLock} should be acquired first.
+     */
     private final Object mUsersLock = LockGuard.installNewLock(LockGuard.INDEX_USER);
     private final Object mRestrictionsLock = new Object();
     // Used for serializing access to app restriction files
@@ -1660,7 +1667,7 @@
             }
         }
         if (changed) {
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
                 sendUserInfoChangedBroadcast(userId);
             } finally {
@@ -3769,7 +3776,7 @@
         if (user == null) {
             return null;
         }
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             setUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS, true, user.id);
             // Change the setting before applying the DISALLOW_SHARE_LOCATION restriction, otherwise
@@ -3822,7 +3829,7 @@
             return false;
         }
 
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             final UserData userData;
             synchronized (mPackagesLock) {
@@ -3859,16 +3866,10 @@
      */
     @Override
     public boolean removeUser(@UserIdInt int userId) {
-        Slog.i(LOG_TAG, "removeUser u" + userId);
+        Slog.i(LOG_TAG, "removeUser u" + userId, new Exception());
         checkManageOrCreateUsersPermission("Only the system can remove users");
 
-        final boolean isManagedProfile;
-        synchronized (mUsersLock) {
-            UserInfo userInfo = getUserInfoLU(userId);
-            isManagedProfile = userInfo != null && userInfo.isManagedProfile();
-        }
-        String restriction = isManagedProfile
-                ? UserManager.DISALLOW_REMOVE_MANAGED_PROFILE : UserManager.DISALLOW_REMOVE_USER;
+        final String restriction = getUserRemovalRestriction(userId);
         if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(restriction, false)) {
             Slog.w(LOG_TAG, "Cannot remove user. " + restriction + " is enabled.");
             return false;
@@ -3882,8 +3883,23 @@
         return removeUserUnchecked(userId);
     }
 
+    /**
+     * Returns the string name of the restriction to check for user removal. The restriction name
+     * varies depending on whether the user is a managed profile.
+     */
+    private String getUserRemovalRestriction(@UserIdInt int userId) {
+        final boolean isManagedProfile;
+        final UserInfo userInfo;
+        synchronized (mUsersLock) {
+            userInfo = getUserInfoLU(userId);
+        }
+        isManagedProfile = userInfo != null && userInfo.isManagedProfile();
+        return isManagedProfile
+                ? UserManager.DISALLOW_REMOVE_MANAGED_PROFILE : UserManager.DISALLOW_REMOVE_USER;
+    }
+
     private boolean removeUserUnchecked(@UserIdInt int userId) {
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             final UserData userData;
             int currentUser = ActivityManager.getCurrentUser();
@@ -3974,6 +3990,64 @@
         }
     }
 
+    @Override
+    public @UserManager.RemoveResult int removeUserOrSetEphemeral(@UserIdInt int userId) {
+        Slog.i(LOG_TAG, "removeUserOrSetEphemeral u" + userId);
+        checkManageUsersPermission("Only the system can remove users");
+        final String restriction = getUserRemovalRestriction(userId);
+        if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(restriction, false)) {
+            Slog.w(LOG_TAG, "Cannot remove user. " + restriction + " is enabled.");
+            return UserManager.REMOVE_RESULT_ERROR;
+        }
+        if (userId == UserHandle.USER_SYSTEM) {
+            Slog.e(LOG_TAG, "System user cannot be removed.");
+            return UserManager.REMOVE_RESULT_ERROR;
+        }
+
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            final UserData userData;
+            synchronized (mPackagesLock) {
+                synchronized (mUsersLock) {
+                    userData = mUsers.get(userId);
+                    if (userData == null) {
+                        Slog.e(LOG_TAG,
+                                "Cannot remove user " + userId + ", invalid user id provided.");
+                        return UserManager.REMOVE_RESULT_ERROR;
+                    }
+
+                    if (mRemovingUserIds.get(userId)) {
+                        Slog.e(LOG_TAG, "User " + userId + " is already scheduled for removal.");
+                        return UserManager.REMOVE_RESULT_ALREADY_BEING_REMOVED;
+                    }
+                }
+
+                // Attempt to immediately remove a non-current user
+                final int currentUser = ActivityManager.getCurrentUser();
+                if (currentUser != userId) {
+                    // Attempt to remove the user. This will fail if the user is the current user
+                    if (removeUser(userId)) {
+                        return UserManager.REMOVE_RESULT_REMOVED;
+                    }
+
+                    Slog.w(LOG_TAG, "Unable to immediately remove non-current user: " + userId
+                            + ". User is still set as ephemeral and will be removed on user "
+                            + "switch or reboot.");
+                }
+
+                // If the user was not immediately removed, make sure it is marked as ephemeral.
+                // Don't mark as disabled since, per UserInfo.FLAG_DISABLED documentation, an
+                // ephemeral user should only be marked as disabled when its removal is in progress.
+                userData.info.flags |= UserInfo.FLAG_EPHEMERAL;
+                writeUserLP(userData);
+
+                return UserManager.REMOVE_RESULT_SET_EPHEMERAL;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
     void finishRemoveUser(final @UserIdInt int userId) {
         if (DBG) Slog.i(LOG_TAG, "finishRemoveUser " + userId);
 
@@ -3991,7 +4065,7 @@
 
         // Let other services shutdown any activity and clean up their state before completely
         // wiping the user's system directory and removing from the user list
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             Intent removedIntent = new Intent(Intent.ACTION_USER_REMOVED);
             removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
@@ -4153,7 +4227,7 @@
     }
 
     private int getUidForPackage(String packageName) {
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             return mContext.getPackageManager().getApplicationInfo(packageName,
                     PackageManager.MATCH_ANY_USER).uid;
@@ -4708,9 +4782,12 @@
                 final UserInfo user = users.get(i);
                 final boolean running = am.isUserRunning(user.id, 0);
                 final boolean current = user.id == currentUser;
+                final boolean hasParent = user.profileGroupId != user.id
+                        && user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID;
                 if (verbose) {
-                    pw.printf("%d: id=%d, name=%s, flags=%s%s%s%s%s%s\n", i, user.id, user.name,
+                    pw.printf("%d: id=%d, name=%s, flags=%s%s%s%s%s%s%s\n", i, user.id, user.name,
                             UserInfo.flagsToString(user.flags),
+                            hasParent ? " (parentId=" + user.profileGroupId + ")" : "",
                             running ? " (running)" : "",
                             user.partial ? " (partial)" : "",
                             user.preCreated ? " (pre-created)" : "",
@@ -4791,6 +4868,11 @@
                     pw.print("  "); pw.print(userInfo);
                     pw.print(" serialNo="); pw.print(userInfo.serialNumber);
                     pw.print(" isPrimary="); pw.print(userInfo.isPrimary());
+                    if (userInfo.profileGroupId != userInfo.id
+                            &&  userInfo.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID) {
+                        pw.print(" parentId="); pw.print(userInfo.profileGroupId);
+                    }
+
                     if (mRemovingUserIds.get(userId)) {
                         pw.print(" <removing> ");
                     }
@@ -5067,7 +5149,7 @@
 
         @Override
         public void setUserIcon(@UserIdInt int userId, Bitmap bitmap) {
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
                 synchronized (mPackagesLock) {
                     UserData userData = getUserDataNoChecks(userId);
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index 3712723..f74913c 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -501,7 +501,7 @@
             }
             Log.i("PackageManager", "Compiling layouts in " + packageName + " (" + apkPath +
                     ") to " + outDexFile);
-            long callingId = Binder.clearCallingIdentity();
+            final long callingId = Binder.clearCallingIdentity();
             try {
                 synchronized (mInstallLock) {
                     return mInstaller.compileLayouts(apkPath, packageName, outDexFile,
diff --git a/services/core/java/com/android/server/pm/dex/ViewCompiler.java b/services/core/java/com/android/server/pm/dex/ViewCompiler.java
index a567266..8afe62a 100644
--- a/services/core/java/com/android/server/pm/dex/ViewCompiler.java
+++ b/services/core/java/com/android/server/pm/dex/ViewCompiler.java
@@ -46,7 +46,7 @@
             final String outDexFile = dataDir.getAbsolutePath() + "/code_cache/compiled_view.dex";
             Log.i("PackageManager", "Compiling layouts in " + packageName + " (" + apkPath +
                 ") to " + outDexFile);
-            long callingId = Binder.clearCallingIdentity();
+            final long callingId = Binder.clearCallingIdentity();
             try {
                 synchronized (mInstallLock) {
                     return mInstaller.compileLayouts(apkPath, packageName, outDexFile,
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
index eb06bf9..667414e 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -29,17 +29,17 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.PermissionInfo;
 import android.content.pm.parsing.component.ParsedPermission;
+import android.os.Build;
 import android.os.UserHandle;
 import android.util.Log;
 import android.util.Slog;
 
 import com.android.server.pm.DumpState;
 import com.android.server.pm.PackageManagerService;
-import com.android.server.pm.PackageSettingBase;
-import com.android.server.pm.parsing.PackageInfoUtils;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 
 import libcore.util.EmptyArray;
@@ -80,269 +80,287 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface ProtectionLevel {}
 
-    final String name;
+    @NonNull
+    private final String mName;
 
-    final @PermissionType int type;
+    private final @PermissionType int mType;
 
-    String sourcePackageName;
+    private String mPackageName;
 
-    int protectionLevel;
+    private int mProtectionLevel;
 
-    ParsedPermission perm;
+    @Nullable
+    private PermissionInfo mPermissionInfo;
 
-    PermissionInfo pendingPermissionInfo;
+    @Nullable
+    private PermissionInfo mPendingPermissionInfo;
 
     /** UID that owns the definition of this permission */
-    int uid;
+    private int mUid;
 
     /** Additional GIDs given to apps granted this permission */
     @NonNull
-    private int[] gids = EmptyArray.INT;
+    private int[] mGids = EmptyArray.INT;
 
     /**
-     * Flag indicating that {@link #gids} should be adjusted based on the
+     * Flag indicating that {@link #mGids} should be adjusted based on the
      * {@link UserHandle} the granted app is running as.
      */
-    private boolean perUser;
+    private boolean mGidsPerUser;
 
-    public BasePermission(String _name, String _sourcePackageName, @PermissionType int _type) {
-        name = _name;
-        sourcePackageName = _sourcePackageName;
-        type = _type;
+    public BasePermission(@NonNull String name, String packageName, @PermissionType int type) {
+        mName = name;
+        mPackageName = packageName;
+        mType = type;
         // Default to most conservative protection level.
-        protectionLevel = PermissionInfo.PROTECTION_SIGNATURE;
+        mProtectionLevel = PermissionInfo.PROTECTION_SIGNATURE;
     }
 
     @Override
     public String toString() {
-        return "BasePermission{" + Integer.toHexString(System.identityHashCode(this)) + " " + name
+        return "BasePermission{" + Integer.toHexString(System.identityHashCode(this)) + " " + mName
                 + "}";
     }
 
+    @NonNull
     public String getName() {
-        return name;
+        return mName;
     }
+
     public int getProtectionLevel() {
-        return protectionLevel;
+        return mProtectionLevel;
     }
-    public String getSourcePackageName() {
-        return sourcePackageName;
+
+    public String getPackageName() {
+        return mPackageName;
     }
+
     public int getType() {
-        return type;
+        return mType;
     }
+
     public int getUid() {
-        return uid;
+        return mUid;
     }
-    public void setGids(@NonNull int[] gids, boolean perUser) {
-        this.gids = gids;
-        this.perUser = perUser;
+
+    public void setGids(@NonNull int[] gids, boolean gidsPerUser) {
+        mGids = gids;
+        mGidsPerUser = gidsPerUser;
     }
-    public void setPermission(@Nullable ParsedPermission perm) {
-        this.perm = perm;
+
+    public void setPermissionInfo(@Nullable PermissionInfo permissionInfo) {
+        mPermissionInfo = permissionInfo;
     }
 
     public boolean hasGids() {
-        return gids.length != 0;
+        return mGids.length != 0;
     }
 
     @NonNull
     public int[] computeGids(int userId) {
-        if (perUser) {
-            final int[] userGids = new int[gids.length];
-            for (int i = 0; i < gids.length; i++) {
-                final int gid = gids[i];
+        if (mGidsPerUser) {
+            final int[] userGids = new int[mGids.length];
+            for (int i = 0; i < mGids.length; i++) {
+                final int gid = mGids[i];
                 userGids[i] = UserHandle.getUid(userId, gid);
             }
             return userGids;
         } else {
-            return gids.length != 0 ? gids.clone() : gids;
+            return mGids.length != 0 ? mGids.clone() : mGids;
         }
     }
 
     public int calculateFootprint(BasePermission perm) {
-        if (uid == perm.uid) {
-            return perm.name.length() + perm.perm.calculateFootprint();
+        if (mUid == perm.mUid) {
+            return perm.mName.length() + perm.mPermissionInfo.calculateFootprint();
         }
         return 0;
     }
 
     public boolean isPermission(ParsedPermission perm) {
-        if (this.perm == null) {
+        if (mPermissionInfo == null) {
             return false;
         }
-        return Objects.equals(this.perm.getPackageName(), perm.getPackageName())
-                && Objects.equals(this.perm.getName(), perm.getName());
+        return Objects.equals(mPermissionInfo.packageName, perm.getPackageName())
+                && Objects.equals(mPermissionInfo.name, perm.getName());
     }
 
     public boolean isDynamic() {
-        return type == TYPE_DYNAMIC;
+        return mType == TYPE_DYNAMIC;
     }
 
-
     public boolean isNormal() {
-        return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
+        return (mProtectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
                 == PermissionInfo.PROTECTION_NORMAL;
     }
     public boolean isRuntime() {
-        return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
+        return (mProtectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
                 == PermissionInfo.PROTECTION_DANGEROUS;
     }
 
+    public boolean isInstalled() {
+        return mPermissionInfo != null
+                && (mPermissionInfo.flags & PermissionInfo.FLAG_INSTALLED) != 0;
+    }
+
     public boolean isRemoved() {
-        return perm != null && (perm.getFlags() & PermissionInfo.FLAG_REMOVED) != 0;
+        return mPermissionInfo != null
+                && (mPermissionInfo.flags & PermissionInfo.FLAG_REMOVED) != 0;
     }
 
     public boolean isSoftRestricted() {
-        return perm != null && (perm.getFlags() & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0;
+        return mPermissionInfo != null
+                && (mPermissionInfo.flags & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0;
     }
 
     public boolean isHardRestricted() {
-        return perm != null && (perm.getFlags() & PermissionInfo.FLAG_HARD_RESTRICTED) != 0;
+        return mPermissionInfo != null
+                && (mPermissionInfo.flags & PermissionInfo.FLAG_HARD_RESTRICTED) != 0;
     }
 
     public boolean isHardOrSoftRestricted() {
-        return perm != null && (perm.getFlags() & (PermissionInfo.FLAG_HARD_RESTRICTED
-                | PermissionInfo.FLAG_SOFT_RESTRICTED)) != 0;
+        return mPermissionInfo != null && (mPermissionInfo.flags
+                & (PermissionInfo.FLAG_HARD_RESTRICTED | PermissionInfo.FLAG_SOFT_RESTRICTED)) != 0;
     }
 
     public boolean isImmutablyRestricted() {
-        return perm != null && (perm.getFlags() & PermissionInfo.FLAG_IMMUTABLY_RESTRICTED) != 0;
+        return mPermissionInfo != null
+                && (mPermissionInfo.flags & PermissionInfo.FLAG_IMMUTABLY_RESTRICTED) != 0;
     }
 
     public boolean isInstallerExemptIgnored() {
-        return perm != null
-                && (perm.getFlags() & PermissionInfo.FLAG_INSTALLER_EXEMPT_IGNORED) != 0;
+        return mPermissionInfo != null
+                && (mPermissionInfo.flags & PermissionInfo.FLAG_INSTALLER_EXEMPT_IGNORED) != 0;
     }
 
     public boolean isSignature() {
-        return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) ==
-                PermissionInfo.PROTECTION_SIGNATURE;
+        return (mProtectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
+                == PermissionInfo.PROTECTION_SIGNATURE;
     }
 
     public boolean isAppOp() {
-        return (protectionLevel & PermissionInfo.PROTECTION_FLAG_APPOP) != 0;
+        return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_APPOP) != 0;
     }
     public boolean isDevelopment() {
         return isSignature()
-                && (protectionLevel & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0;
+                && (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0;
     }
     public boolean isInstaller() {
-        return (protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTALLER) != 0;
+        return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_INSTALLER) != 0;
     }
     public boolean isInstant() {
-        return (protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0;
+        return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0;
     }
     public boolean isOEM() {
-        return (protectionLevel & PermissionInfo.PROTECTION_FLAG_OEM) != 0;
+        return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_OEM) != 0;
     }
     public boolean isPre23() {
-        return (protectionLevel & PermissionInfo.PROTECTION_FLAG_PRE23) != 0;
+        return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_PRE23) != 0;
     }
     public boolean isPreInstalled() {
-        return (protectionLevel & PermissionInfo.PROTECTION_FLAG_PREINSTALLED) != 0;
+        return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_PREINSTALLED) != 0;
     }
     public boolean isPrivileged() {
-        return (protectionLevel & PermissionInfo.PROTECTION_FLAG_PRIVILEGED) != 0;
+        return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_PRIVILEGED) != 0;
     }
     public boolean isRuntimeOnly() {
-        return (protectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0;
+        return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0;
     }
     public boolean isSetup() {
-        return (protectionLevel & PermissionInfo.PROTECTION_FLAG_SETUP) != 0;
+        return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_SETUP) != 0;
     }
     public boolean isVerifier() {
-        return (protectionLevel & PermissionInfo.PROTECTION_FLAG_VERIFIER) != 0;
+        return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_VERIFIER) != 0;
     }
     public boolean isVendorPrivileged() {
-        return (protectionLevel & PermissionInfo.PROTECTION_FLAG_VENDOR_PRIVILEGED) != 0;
+        return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_VENDOR_PRIVILEGED) != 0;
     }
     public boolean isSystemTextClassifier() {
-        return (protectionLevel & PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER)
+        return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER)
                 != 0;
     }
     public boolean isWellbeing() {
-        return (protectionLevel & PermissionInfo.PROTECTION_FLAG_WELLBEING) != 0;
+        return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_WELLBEING) != 0;
     }
     public boolean isDocumenter() {
-        return (protectionLevel & PermissionInfo.PROTECTION_FLAG_DOCUMENTER) != 0;
+        return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_DOCUMENTER) != 0;
     }
     public boolean isConfigurator() {
-        return (protectionLevel & PermissionInfo.PROTECTION_FLAG_CONFIGURATOR)
+        return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_CONFIGURATOR)
             != 0;
     }
     public boolean isIncidentReportApprover() {
-        return (protectionLevel & PermissionInfo.PROTECTION_FLAG_INCIDENT_REPORT_APPROVER) != 0;
+        return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_INCIDENT_REPORT_APPROVER) != 0;
     }
     public boolean isAppPredictor() {
-        return (protectionLevel & PermissionInfo.PROTECTION_FLAG_APP_PREDICTOR) != 0;
+        return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_APP_PREDICTOR) != 0;
     }
     public boolean isCompanion() {
-        return (protectionLevel & PermissionInfo.PROTECTION_FLAG_COMPANION) != 0;
+        return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_COMPANION) != 0;
     }
 
     public boolean isRetailDemo() {
-        return (protectionLevel & PermissionInfo.PROTECTION_FLAG_RETAIL_DEMO) != 0;
+        return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_RETAIL_DEMO) != 0;
     }
 
     public void transfer(@NonNull String origPackageName, @NonNull String newPackageName) {
-        if (!origPackageName.equals(sourcePackageName)) {
+        if (!origPackageName.equals(mPackageName)) {
             return;
         }
-        sourcePackageName = newPackageName;
-        perm = null;
-        if (pendingPermissionInfo != null) {
-            pendingPermissionInfo.packageName = newPackageName;
+        mPackageName = newPackageName;
+        mPermissionInfo = null;
+        if (mPendingPermissionInfo != null) {
+            mPendingPermissionInfo.packageName = newPackageName;
         }
-        uid = 0;
-        gids = EmptyArray.INT;
-        perUser = false;
+        mUid = 0;
+        mGids = EmptyArray.INT;
+        mGidsPerUser = false;
     }
 
     public boolean addToTree(@ProtectionLevel int protectionLevel,
-            @NonNull PermissionInfo info, @NonNull BasePermission tree) {
+            @NonNull PermissionInfo permissionInfo, @NonNull BasePermission tree) {
         final boolean changed =
-                (this.protectionLevel != protectionLevel
-                    || perm == null
-                    || uid != tree.uid
-                    || !Objects.equals(perm.getPackageName(), tree.perm.getPackageName())
-                    || !comparePermissionInfos(perm, info));
-        this.protectionLevel = protectionLevel;
-        info = new PermissionInfo(info);
-        info.protectionLevel = protectionLevel;
-        perm = new ParsedPermission(tree.perm);
-        uid = tree.uid;
+                (mProtectionLevel != protectionLevel
+                    || mPermissionInfo == null
+                    || mUid != tree.mUid
+                    || !Objects.equals(mPermissionInfo.packageName,
+                            tree.mPermissionInfo.packageName)
+                    || !comparePermissionInfos(mPermissionInfo, permissionInfo));
+        mProtectionLevel = protectionLevel;
+        mPermissionInfo = new PermissionInfo(permissionInfo);
+        mPermissionInfo.protectionLevel = protectionLevel;
+        mPermissionInfo.packageName = tree.mPermissionInfo.packageName;
+        mUid = tree.mUid;
         return changed;
     }
 
     public void updateDynamicPermission(Collection<BasePermission> permissionTrees) {
         if (PackageManagerService.DEBUG_SETTINGS) Log.v(TAG, "Dynamic permission: name="
-                + getName() + " pkg=" + getSourcePackageName()
-                + " info=" + pendingPermissionInfo);
-        if (pendingPermissionInfo != null) {
-            final BasePermission tree = findPermissionTree(permissionTrees, name);
-            if (tree != null && tree.perm != null) {
-                perm = new ParsedPermission(tree.perm, pendingPermissionInfo,
-                        tree.perm.getPackageName(), name);
-                uid = tree.uid;
+                + getName() + " pkg=" + getPackageName()
+                + " info=" + mPendingPermissionInfo);
+        if (mPendingPermissionInfo != null) {
+            final BasePermission tree = findPermissionTree(permissionTrees, mName);
+            if (tree != null && tree.mPermissionInfo != null) {
+                mPermissionInfo = new PermissionInfo(mPendingPermissionInfo);
+                mPermissionInfo.packageName = tree.mPermissionInfo.packageName;
+                mPermissionInfo.name = mName;
+                mUid = tree.mUid;
             }
         }
     }
 
     static BasePermission createOrUpdate(PackageManagerInternal packageManagerInternal,
-            @Nullable BasePermission bp, @NonNull ParsedPermission p,
+            @Nullable BasePermission bp, @NonNull PermissionInfo p,
             @NonNull AndroidPackage pkg, Collection<BasePermission> permissionTrees,
             boolean chatty) {
-        final PackageSettingBase pkgSetting =
-                (PackageSettingBase) packageManagerInternal.getPackageSetting(pkg.getPackageName());
         // Allow system apps to redefine non-system permissions
-        if (bp != null && !Objects.equals(bp.sourcePackageName, p.getPackageName())) {
+        if (bp != null && !Objects.equals(bp.mPackageName, p.packageName)) {
             final boolean currentOwnerIsSystem;
-            if (bp.perm == null) {
+            if (bp.mPermissionInfo == null) {
                 currentOwnerIsSystem = false;
             } else {
                 AndroidPackage currentPackage = packageManagerInternal.getPackage(
-                        bp.perm.getPackageName());
+                        bp.mPermissionInfo.packageName);
                 if (currentPackage == null) {
                     currentOwnerIsSystem = false;
                 } else {
@@ -351,52 +369,52 @@
             }
 
             if (pkg.isSystem()) {
-                if (bp.type == BasePermission.TYPE_BUILTIN && bp.perm == null) {
+                if (bp.mType == BasePermission.TYPE_BUILTIN && bp.mPermissionInfo == null) {
                     // It's a built-in permission and no owner, take ownership now
-                    p.setFlags(p.getFlags() | PermissionInfo.FLAG_INSTALLED);
-                    bp.perm = p;
-                    bp.uid = pkg.getUid();
-                    bp.sourcePackageName = p.getPackageName();
+                    p.flags |= PermissionInfo.FLAG_INSTALLED;
+                    bp.mPermissionInfo = p;
+                    bp.mUid = pkg.getUid();
+                    bp.mPackageName = p.packageName;
                 } else if (!currentOwnerIsSystem) {
                     String msg = "New decl " + pkg + " of permission  "
-                            + p.getName() + " is system; overriding " + bp.sourcePackageName;
+                            + p.name + " is system; overriding " + bp.mPackageName;
                     PackageManagerService.reportSettingsProblem(Log.WARN, msg);
                     bp = null;
                 }
             }
         }
         if (bp == null) {
-            bp = new BasePermission(p.getName(), p.getPackageName(), TYPE_NORMAL);
+            bp = new BasePermission(p.name, p.packageName, TYPE_NORMAL);
         }
         StringBuilder r = null;
-        if (bp.perm == null) {
-            if (bp.sourcePackageName == null
-                    || bp.sourcePackageName.equals(p.getPackageName())) {
-                final BasePermission tree = findPermissionTree(permissionTrees, p.getName());
+        if (bp.mPermissionInfo == null) {
+            if (bp.mPackageName == null
+                    || bp.mPackageName.equals(p.packageName)) {
+                final BasePermission tree = findPermissionTree(permissionTrees, p.name);
                 if (tree == null
-                        || tree.sourcePackageName.equals(p.getPackageName())) {
-                    p.setFlags(p.getFlags() | PermissionInfo.FLAG_INSTALLED);
-                    bp.perm = p;
-                    bp.uid = pkg.getUid();
-                    bp.sourcePackageName = p.getPackageName();
+                        || tree.mPackageName.equals(p.packageName)) {
+                    p.flags |= PermissionInfo.FLAG_INSTALLED;
+                    bp.mPermissionInfo = p;
+                    bp.mUid = pkg.getUid();
+                    bp.mPackageName = p.packageName;
                     if (chatty) {
                         if (r == null) {
                             r = new StringBuilder(256);
                         } else {
                             r.append(' ');
                         }
-                        r.append(p.getName());
+                        r.append(p.name);
                     }
                 } else {
-                    Slog.w(TAG, "Permission " + p.getName() + " from package "
-                            + p.getPackageName() + " ignored: base tree "
-                            + tree.name + " is from package "
-                            + tree.sourcePackageName);
+                    Slog.w(TAG, "Permission " + p.name + " from package "
+                            + p.packageName + " ignored: base tree "
+                            + tree.mName + " is from package "
+                            + tree.mPackageName);
                 }
             } else {
-                Slog.w(TAG, "Permission " + p.getName() + " from package "
-                        + p.getPackageName() + " ignored: original from "
-                        + bp.sourcePackageName);
+                Slog.w(TAG, "Permission " + p.name + " from package "
+                        + p.packageName + " ignored: original from "
+                        + bp.mPackageName);
             }
         } else if (chatty) {
             if (r == null) {
@@ -405,11 +423,10 @@
                 r.append(' ');
             }
             r.append("DUP:");
-            r.append(p.getName());
+            r.append(p.name);
         }
-        if (bp.perm != null && Objects.equals(bp.perm.getPackageName(), p.getPackageName())
-                && Objects.equals(bp.perm.getName(), p.getName())) {
-            bp.protectionLevel = p.getProtectionLevel();
+        if (bp.mPermissionInfo == p) {
+            bp.mProtectionLevel = p.protectionLevel;
         }
         if (PackageManagerService.DEBUG_PACKAGE_SCANNING && r != null) {
             Log.d(TAG, "  Permissions: " + r);
@@ -422,71 +439,77 @@
         if (permName != null) {
             BasePermission bp = findPermissionTree(permissionTrees, permName);
             if (bp != null) {
-                if (bp.uid == UserHandle.getAppId(callingUid)) {
+                if (bp.mUid == UserHandle.getAppId(callingUid)) {
                     return bp;
                 }
                 throw new SecurityException("Calling uid " + callingUid
                         + " is not allowed to add to permission tree "
-                        + bp.name + " owned by uid " + bp.uid);
+                        + bp.mName + " owned by uid " + bp.mUid);
             }
         }
         throw new SecurityException("No permission tree found for " + permName);
     }
 
-    public void enforceDeclaredUsedAndRuntimeOrDevelopment(AndroidPackage pkg,
-            UidPermissionState uidState) {
-        if (!uidState.hasPermissionState(name) && !pkg.getRequestedPermissions().contains(name)) {
-            throw new SecurityException("Package " + pkg.getPackageName()
-                    + " has not requested permission " + name);
-        }
-        if (!isRuntime() && !isDevelopment()) {
-            throw new SecurityException("Permission " + name + " requested by "
-                    + pkg.getPackageName() + " is not a changeable permission type");
-        }
-    }
-
     private static BasePermission findPermissionTree(
             Collection<BasePermission> permissionTrees, String permName) {
         for (BasePermission bp : permissionTrees) {
-            if (permName.startsWith(bp.name) &&
-                    permName.length() > bp.name.length() &&
-                    permName.charAt(bp.name.length()) == '.') {
+            if (permName.startsWith(bp.mName)
+                    && permName.length() > bp.mName.length()
+                    && permName.charAt(bp.mName.length()) == '.') {
                 return bp;
             }
         }
         return null;
     }
 
-    public @Nullable PermissionInfo generatePermissionInfo(@NonNull String groupName, int flags) {
-        if (groupName == null) {
-            if (perm == null || perm.getGroup() == null) {
-                return generatePermissionInfo(protectionLevel, flags);
-            }
-        } else {
-            if (perm != null && groupName.equals(perm.getGroup())) {
-                return PackageInfoUtils.generatePermissionInfo(perm, flags);
-            }
-        }
-        return null;
+    @Nullable
+    public String getBackgroundPermission() {
+        return mPermissionInfo != null ? mPermissionInfo.backgroundPermission : null;
     }
 
-    public @NonNull PermissionInfo generatePermissionInfo(int adjustedProtectionLevel, int flags) {
+    @Nullable
+    public String getGroup() {
+        return mPermissionInfo != null ? mPermissionInfo.group : null;
+    }
+
+    public int getProtection() {
+        return mProtectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
+    }
+
+    public int getProtectionFlags() {
+        return mProtectionLevel & PermissionInfo.PROTECTION_MASK_FLAGS;
+    }
+
+    @NonNull
+    public PermissionInfo generatePermissionInfo(int flags) {
+        return generatePermissionInfo(flags, Build.VERSION_CODES.CUR_DEVELOPMENT);
+    }
+
+    @NonNull
+    public PermissionInfo generatePermissionInfo(int flags, int targetSdkVersion) {
         PermissionInfo permissionInfo;
-        if (perm != null) {
-            final boolean protectionLevelChanged = protectionLevel != adjustedProtectionLevel;
-            permissionInfo = PackageInfoUtils.generatePermissionInfo(perm, flags);
-            if (protectionLevelChanged) {
-                // if we return different protection level, don't use the cached info
-                permissionInfo = new PermissionInfo(permissionInfo);
-                permissionInfo.protectionLevel = adjustedProtectionLevel;
+        if (mPermissionInfo != null) {
+            permissionInfo = new PermissionInfo(mPermissionInfo);
+            if ((flags & PackageManager.GET_META_DATA) != PackageManager.GET_META_DATA) {
+                permissionInfo.metaData = null;
             }
-            return permissionInfo;
+        } else {
+            permissionInfo = new PermissionInfo();
+            permissionInfo.name = mName;
+            permissionInfo.packageName = mPackageName;
+            permissionInfo.nonLocalizedLabel = mName;
         }
-        permissionInfo = new PermissionInfo();
-        permissionInfo.name = name;
-        permissionInfo.packageName = sourcePackageName;
-        permissionInfo.nonLocalizedLabel = name;
-        permissionInfo.protectionLevel = protectionLevel;
+        if (targetSdkVersion >= Build.VERSION_CODES.O) {
+            permissionInfo.protectionLevel = mProtectionLevel;
+        } else {
+            final int protection = mProtectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
+            if (protection == PermissionInfo.PROTECTION_SIGNATURE) {
+                // Signature permission's protection flags are always reported.
+                permissionInfo.protectionLevel = mProtectionLevel;
+            } else {
+                permissionInfo.protectionLevel = protection;
+            }
+        }
         return permissionInfo;
     }
 
@@ -508,23 +531,23 @@
         final boolean dynamic = "dynamic".equals(ptype);
         BasePermission bp = out.get(name);
         // If the permission is builtin, do not clobber it.
-        if (bp == null || bp.type != TYPE_BUILTIN) {
+        if (bp == null || bp.mType != TYPE_BUILTIN) {
             bp = new BasePermission(name.intern(), sourcePackage,
                     dynamic ? TYPE_DYNAMIC : TYPE_NORMAL);
         }
-        bp.protectionLevel = readInt(parser, null, "protection",
+        bp.mProtectionLevel = readInt(parser, null, "protection",
                 PermissionInfo.PROTECTION_NORMAL);
-        bp.protectionLevel = PermissionInfo.fixProtectionLevel(bp.protectionLevel);
+        bp.mProtectionLevel = PermissionInfo.fixProtectionLevel(bp.mProtectionLevel);
         if (dynamic) {
             final PermissionInfo pi = new PermissionInfo();
             pi.packageName = sourcePackage.intern();
             pi.name = name.intern();
             pi.icon = readInt(parser, null, "icon", 0);
             pi.nonLocalizedLabel = parser.getAttributeValue(null, "label");
-            pi.protectionLevel = bp.protectionLevel;
-            bp.pendingPermissionInfo = pi;
+            pi.protectionLevel = bp.mProtectionLevel;
+            bp.mPendingPermissionInfo = pi;
         }
-        out.put(bp.name, bp);
+        out.put(bp.mName, bp);
         return true;
     }
 
@@ -545,22 +568,23 @@
     }
 
     public void writeLPr(@NonNull XmlSerializer serializer) throws IOException {
-        if (sourcePackageName == null) {
+        if (mPackageName == null) {
             return;
         }
         serializer.startTag(null, TAG_ITEM);
-        serializer.attribute(null, ATTR_NAME, name);
-        serializer.attribute(null, ATTR_PACKAGE, sourcePackageName);
-        if (protectionLevel != PermissionInfo.PROTECTION_NORMAL) {
-            serializer.attribute(null, "protection", Integer.toString(protectionLevel));
+        serializer.attribute(null, ATTR_NAME, mName);
+        serializer.attribute(null, ATTR_PACKAGE, mPackageName);
+        if (mProtectionLevel != PermissionInfo.PROTECTION_NORMAL) {
+            serializer.attribute(null, "protection", Integer.toString(mProtectionLevel));
         }
-        if (type == BasePermission.TYPE_DYNAMIC) {
-            if (perm != null || pendingPermissionInfo != null) {
+        if (mType == BasePermission.TYPE_DYNAMIC) {
+            if (mPermissionInfo != null || mPendingPermissionInfo != null) {
                 serializer.attribute(null, "type", "dynamic");
-                int icon = perm != null ? perm.getIcon() : pendingPermissionInfo.icon;
-                CharSequence nonLocalizedLabel = perm != null
-                        ? perm.getNonLocalizedLabel()
-                        : pendingPermissionInfo.nonLocalizedLabel;
+                int icon = mPermissionInfo != null ? mPermissionInfo.icon
+                        : mPendingPermissionInfo.icon;
+                CharSequence nonLocalizedLabel = mPermissionInfo != null
+                        ? mPermissionInfo.nonLocalizedLabel
+                        : mPendingPermissionInfo.nonLocalizedLabel;
 
                 if (icon != 0) {
                     serializer.attribute(null, "icon", Integer.toString(icon));
@@ -573,27 +597,14 @@
         serializer.endTag(null, TAG_ITEM);
     }
 
-    private static boolean compareStrings(CharSequence s1, CharSequence s2) {
-        if (s1 == null) {
-            return s2 == null;
-        }
-        if (s2 == null) {
-            return false;
-        }
-        if (s1.getClass() != s2.getClass()) {
-            return false;
-        }
-        return s1.equals(s2);
-    }
-
-    private static boolean comparePermissionInfos(ParsedPermission pi1, PermissionInfo pi2) {
-        if (pi1.getIcon() != pi2.icon) return false;
-        if (pi1.getLogo() != pi2.logo) return false;
-        if (pi1.getProtectionLevel() != pi2.protectionLevel) return false;
-        if (!compareStrings(pi1.getName(), pi2.name)) return false;
-        if (!compareStrings(pi1.getNonLocalizedLabel(), pi2.nonLocalizedLabel)) return false;
+    private static boolean comparePermissionInfos(PermissionInfo pi1, PermissionInfo pi2) {
+        if (pi1.icon != pi2.icon) return false;
+        if (pi1.logo != pi2.logo) return false;
+        if (pi1.protectionLevel != pi2.protectionLevel) return false;
+        if (!Objects.equals(pi1.name, pi2.name)) return false;
+        if (!Objects.equals(pi1.nonLocalizedLabel, pi2.nonLocalizedLabel)) return false;
         // We'll take care of setting this one.
-        if (!compareStrings(pi1.getPackageName(), pi2.packageName)) return false;
+        if (!Objects.equals(pi1.packageName, pi2.packageName)) return false;
         // These are not currently stored in settings.
         //if (!compareStrings(pi1.group, pi2.group)) return false;
         //if (!compareStrings(pi1.nonLocalizedDescription, pi2.nonLocalizedDescription)) return false;
@@ -605,36 +616,34 @@
     public boolean dumpPermissionsLPr(@NonNull PrintWriter pw, @NonNull String packageName,
             @NonNull Set<String> permissionNames, boolean readEnforced,
             boolean printedSomething, @NonNull DumpState dumpState) {
-        if (packageName != null && !packageName.equals(sourcePackageName)) {
+        if (packageName != null && !packageName.equals(mPackageName)) {
             return false;
         }
-        if (permissionNames != null && !permissionNames.contains(name)) {
+        if (permissionNames != null && !permissionNames.contains(mName)) {
             return false;
         }
         if (!printedSomething) {
             if (dumpState.onTitlePrinted())
                 pw.println();
             pw.println("Permissions:");
-            printedSomething = true;
         }
-        pw.print("  Permission ["); pw.print(name); pw.print("] (");
+        pw.print("  Permission ["); pw.print(mName); pw.print("] (");
                 pw.print(Integer.toHexString(System.identityHashCode(this)));
                 pw.println("):");
-        pw.print("    sourcePackage="); pw.println(sourcePackageName);
-        pw.print("    uid="); pw.print(uid);
-                pw.print(" gids="); pw.print(Arrays.toString(
-                        computeGids(UserHandle.USER_SYSTEM)));
-                pw.print(" type="); pw.print(type);
-                pw.print(" prot=");
-                pw.println(PermissionInfo.protectionToString(protectionLevel));
-        if (perm != null) {
-            pw.print("    perm="); pw.println(perm);
-            if ((perm.getFlags() & PermissionInfo.FLAG_INSTALLED) == 0
-                    || (perm.getFlags() & PermissionInfo.FLAG_REMOVED) != 0) {
-                pw.print("    flags=0x"); pw.println(Integer.toHexString(perm.getFlags()));
+        pw.print("    sourcePackage="); pw.println(mPackageName);
+        pw.print("    uid="); pw.print(mUid);
+        pw.print(" gids="); pw.print(Arrays.toString(computeGids(UserHandle.USER_SYSTEM)));
+        pw.print(" type="); pw.print(mType);
+        pw.print(" prot=");
+        pw.println(PermissionInfo.protectionToString(mProtectionLevel));
+        if (mPermissionInfo != null) {
+            pw.print("    perm="); pw.println(mPermissionInfo);
+            if ((mPermissionInfo.flags & PermissionInfo.FLAG_INSTALLED) == 0
+                    || (mPermissionInfo.flags & PermissionInfo.FLAG_REMOVED) != 0) {
+                pw.print("    flags=0x"); pw.println(Integer.toHexString(mPermissionInfo.flags));
             }
         }
-        if (READ_EXTERNAL_STORAGE.equals(name)) {
+        if (READ_EXTERNAL_STORAGE.equals(mName)) {
             pw.print("    enforced=");
             pw.println(readEnforced);
         }
diff --git a/services/core/java/com/android/server/pm/permission/DevicePermissionState.java b/services/core/java/com/android/server/pm/permission/DevicePermissionState.java
index b9456acf..18936dd 100644
--- a/services/core/java/com/android/server/pm/permission/DevicePermissionState.java
+++ b/services/core/java/com/android/server/pm/permission/DevicePermissionState.java
@@ -21,57 +21,38 @@
 import android.annotation.UserIdInt;
 import android.util.SparseArray;
 
-import com.android.internal.annotations.GuardedBy;
-
 /**
  * Permission state for this device.
  */
 public final class DevicePermissionState {
-    @GuardedBy("mLock")
-    @NonNull
     private final SparseArray<UserPermissionState> mUserStates = new SparseArray<>();
 
-    @NonNull
-    private final Object mLock;
-
-    public DevicePermissionState(@NonNull Object lock) {
-        mLock = lock;
-    }
-
     @Nullable
     public UserPermissionState getUserState(@UserIdInt int userId) {
-        synchronized (mLock) {
-            return mUserStates.get(userId);
-        }
+        return mUserStates.get(userId);
     }
 
     @NonNull
     public UserPermissionState getOrCreateUserState(@UserIdInt int userId) {
-        synchronized (mLock) {
-            UserPermissionState userState = mUserStates.get(userId);
-            if (userState == null) {
-                userState = new UserPermissionState(mLock);
-                mUserStates.put(userId, userState);
-            }
-            return userState;
+        UserPermissionState userState = mUserStates.get(userId);
+        if (userState == null) {
+            userState = new UserPermissionState();
+            mUserStates.put(userId, userState);
         }
+        return userState;
     }
 
     public void removeUserState(@UserIdInt int userId) {
-        synchronized (mLock) {
-            mUserStates.delete(userId);
-        }
+        mUserStates.delete(userId);
     }
 
     public int[] getUserIds() {
-        synchronized (mLock) {
-            final int userStatesSize = mUserStates.size();
-            final int[] userIds = new int[userStatesSize];
-            for (int i = 0; i < userStatesSize; i++) {
-                final int userId = mUserStates.keyAt(i);
-                userIds[i] = userId;
-            }
-            return userIds;
+        final int userStatesSize = mUserStates.size();
+        final int[] userIds = new int[userStatesSize];
+        for (int i = 0; i < userStatesSize; i++) {
+            final int userId = mUserStates.keyAt(i);
+            userIds[i] = userId;
         }
+        return userIds;
     }
 }
diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermissionDataProvider.java b/services/core/java/com/android/server/pm/permission/LegacyPermissionDataProvider.java
index 7452b52..346a2c5 100644
--- a/services/core/java/com/android/server/pm/permission/LegacyPermissionDataProvider.java
+++ b/services/core/java/com/android/server/pm/permission/LegacyPermissionDataProvider.java
@@ -31,7 +31,7 @@
      * @return the legacy permission state
      */
     @NonNull
-    public abstract AppIdPermissionState getAppIdPermissionState(@AppIdInt int appId);
+    LegacyPermissionState getLegacyPermissionState(@AppIdInt int appId);
 
     /**
      * Get the GIDs computed from the permission state of a UID, either a package or a shared user.
@@ -40,5 +40,5 @@
      * @return the GIDs for the UID
      */
     @NonNull
-    public abstract int[] getGidsForUid(int uid);
+    int[] getGidsForUid(int uid);
 }
diff --git a/services/core/java/com/android/server/pm/permission/AppIdPermissionState.java b/services/core/java/com/android/server/pm/permission/LegacyPermissionState.java
similarity index 97%
rename from services/core/java/com/android/server/pm/permission/AppIdPermissionState.java
rename to services/core/java/com/android/server/pm/permission/LegacyPermissionState.java
index aabdafd..63f69ce 100644
--- a/services/core/java/com/android/server/pm/permission/AppIdPermissionState.java
+++ b/services/core/java/com/android/server/pm/permission/LegacyPermissionState.java
@@ -32,7 +32,7 @@
  * Legacy permission state that was associated with packages or shared users.
  */
 //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
-public final class AppIdPermissionState {
+public final class LegacyPermissionState {
     // Maps from user IDs to user states.
     @NonNull
     private final SparseArray<UserState> mUserStates = new SparseArray<>();
@@ -48,7 +48,7 @@
      *
      * @hide
      */
-    public void copyFrom(@NonNull AppIdPermissionState other) {
+    public void copyFrom(@NonNull LegacyPermissionState other) {
         if (other == this) {
             return;
         }
@@ -88,7 +88,7 @@
         if (getClass() != object.getClass()) {
             return false;
         }
-        final AppIdPermissionState other = (AppIdPermissionState) object;
+        final LegacyPermissionState other = (LegacyPermissionState) object;
         return Objects.equals(mUserStates, other.mUserStates)
                 && Objects.equals(mMissing, other.mMissing);
     }
diff --git a/services/core/java/com/android/server/pm/permission/OWNERS b/services/core/java/com/android/server/pm/permission/OWNERS
index 01dc01e..0e88862 100644
--- a/services/core/java/com/android/server/pm/permission/OWNERS
+++ b/services/core/java/com/android/server/pm/permission/OWNERS
@@ -1,4 +1,5 @@
 moltmann@google.com
+zhanghai@google.com
 per-file DefaultPermissionGrantPolicy.java = hackbod@android.com
 per-file DefaultPermissionGrantPolicy.java = jsharkey@android.com
 per-file DefaultPermissionGrantPolicy.java = svetoslavganov@google.com
@@ -7,3 +8,4 @@
 per-file DefaultPermissionGrantPolicy.java = patb@google.com
 per-file DefaultPermissionGrantPolicy.java = eugenesusla@google.com
 per-file DefaultPermissionGrantPolicy.java = moltmann@google.com
+per-file DefaultPermissionGrantPolicy.java = zhanghai@google.com
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 0a514fa..6c01017 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -68,7 +68,6 @@
 import android.app.AppOpsManager;
 import android.app.ApplicationPackageManager;
 import android.app.IActivityManager;
-import android.app.admin.DeviceAdminInfo;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.compat.annotation.ChangeId;
@@ -179,13 +178,6 @@
 public class PermissionManagerService extends IPermissionManager.Stub {
     private static final String TAG = "PackageManager";
 
-    /** Permission grant: not grant the permission. */
-    private static final int GRANT_DENIED = 1;
-    /** Permission grant: grant the permission as an install permission. */
-    private static final int GRANT_INSTALL = 2;
-    /** Permission grant: grant the permission as a runtime one. */
-    private static final int GRANT_RUNTIME = 3;
-
     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 */
@@ -224,8 +216,9 @@
     /** Internal connection to the user manager */
     private final UserManagerInternal mUserManagerInt;
 
+    @GuardedBy("mLock")
     @NonNull
-    private final DevicePermissionState mState;
+    private final DevicePermissionState mState = new DevicePermissionState();
 
     /** Permission controller: User space permission management */
     private PermissionControllerManager mPermissionControllerManager;
@@ -385,8 +378,9 @@
             @NonNull Injector injector) {
         mInjector = injector;
         // The package info cache is the cache for package and permission information.
+        // Disable the package info and package permission caches locally but leave the
+        // checkPermission cache active.
         mInjector.invalidatePackageInfoCache();
-        mInjector.disablePermissionCache();
         mInjector.disablePackageNamePermissionCache();
 
         mContext = context;
@@ -394,7 +388,6 @@
         mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class);
         mUserManagerInt = LocalServices.getService(UserManagerInternal.class);
         mSettings = new PermissionSettings(mLock);
-        mState = new DevicePermissionState(mLock);
         mAppOpsManager = context.getSystemService(AppOpsManager.class);
 
         mHandlerThread = new ServiceThread(TAG,
@@ -552,24 +545,37 @@
 
     @Override
     @Nullable
-    public PermissionInfo getPermissionInfo(String permName, String packageName,
+    public PermissionInfo getPermissionInfo(@NonNull String permName, @NonNull String opPackageName,
             @PermissionInfoFlags int flags) {
         final int callingUid = getCallingUid();
         if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
             return null;
         }
-        final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName);
+        final AndroidPackage opPackage = mPackageManagerInt.getPackage(opPackageName);
+        final int targetSdkVersion = getPermissionInfoCallingTargetSdkVersion(opPackage,
+                callingUid);
         synchronized (mLock) {
             final BasePermission bp = mSettings.getPermissionLocked(permName);
             if (bp == null) {
                 return null;
             }
-            final int adjustedProtectionLevel = adjustPermissionProtectionFlagsLocked(
-                    bp.getProtectionLevel(), pkg, callingUid);
-            return bp.generatePermissionInfo(adjustedProtectionLevel, flags);
+            return bp.generatePermissionInfo(flags, targetSdkVersion);
         }
     }
 
+    private int getPermissionInfoCallingTargetSdkVersion(@Nullable AndroidPackage pkg, int uid) {
+        final int appId = UserHandle.getAppId(uid);
+        if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID
+                || appId == Process.SHELL_UID) {
+            // System sees all flags.
+            return Build.VERSION_CODES.CUR_DEVELOPMENT;
+        }
+        if (pkg == null) {
+            return Build.VERSION_CODES.CUR_DEVELOPMENT;
+        }
+        return pkg.getTargetSdkVersion();
+    }
+
     @Override
     @Nullable
     public ParceledListSlice<PermissionInfo> queryPermissionsByGroup(String groupName,
@@ -584,9 +590,8 @@
             }
             final ArrayList<PermissionInfo> out = new ArrayList<PermissionInfo>(10);
             for (BasePermission bp : mSettings.mPermissions.values()) {
-                final PermissionInfo pi = bp.generatePermissionInfo(groupName, flags);
-                if (pi != null) {
-                    out.add(pi);
+                if (Objects.equals(bp.getGroup(), groupName)) {
+                    out.add(bp.generatePermissionInfo(flags));
                 }
             }
             return new ParceledListSlice<>(out);
@@ -611,7 +616,7 @@
             int fixedLevel = PermissionInfo.fixProtectionLevel(info.protectionLevel);
             if (added) {
                 enforcePermissionCapLocked(info, tree);
-                bp = new BasePermission(info.name, tree.getSourcePackageName(),
+                bp = new BasePermission(info.name, tree.getPackageName(),
                         BasePermission.TYPE_DYNAMIC);
             } else if (!bp.isDynamic()) {
                 throw new SecurityException("Not allowed to modify non-dynamic permission "
@@ -673,20 +678,23 @@
         if (pkg == null) {
             return 0;
         }
+        if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
+            return 0;
+        }
+
         synchronized (mLock) {
             if (mSettings.getPermissionLocked(permName) == null) {
                 return 0;
             }
+
+            final UidPermissionState uidState = getUidStateLocked(pkg, userId);
+            if (uidState == null) {
+                Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId);
+                return 0;
+            }
+
+            return uidState.getPermissionFlags(permName);
         }
-        if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
-            return 0;
-        }
-        final UidPermissionState uidState = getUidState(pkg, userId);
-        if (uidState == null) {
-            Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId);
-            return 0;
-        }
-        return uidState.getPermissionFlags(permName);
     }
 
     @Override
@@ -696,7 +704,7 @@
         boolean overridePolicy = false;
 
         if (callingUid != Process.SYSTEM_UID && callingUid != Process.ROOT_UID) {
-            long callingIdentity = Binder.clearCallingIdentity();
+            final long callingIdentity = Binder.clearCallingIdentity();
             try {
                 if ((flagMask & FLAG_PERMISSION_POLICY_FIXED) != 0) {
                     if (checkAdjustPolicyFlagPermission) {
@@ -780,52 +788,52 @@
             throw new IllegalArgumentException("Unknown package: " + packageName);
         }
 
-        final BasePermission bp;
-        synchronized (mLock) {
-            bp = mSettings.getPermissionLocked(permName);
+        boolean isRequested = false;
+        // Fast path, the current package has requested the permission.
+        if (pkg.getRequestedPermissions().contains(permName)) {
+            isRequested = true;
         }
-        if (bp == null) {
-            throw new IllegalArgumentException("Unknown permission: " + permName);
-        }
-
-        if (bp.isInstallerExemptIgnored()) {
-            flagValues &= ~FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
-        }
-
-        final UidPermissionState uidState = getUidState(pkg, userId);
-        if (uidState == null) {
-            Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId);
-            return;
-        }
-
-        final boolean hadState = uidState.getPermissionState(permName) != null;
-        if (!hadState) {
-            boolean isRequested = false;
-            // Fast path, the current package has requested the permission.
-            if (pkg.getRequestedPermissions().contains(permName)) {
-                isRequested = true;
-            }
-            if (!isRequested) {
-                // Slow path, go through all shared user packages.
-                String[] sharedUserPackageNames =
-                        mPackageManagerInt.getSharedUserPackagesForPackage(packageName, userId);
-                for (String sharedUserPackageName : sharedUserPackageNames) {
-                    AndroidPackage sharedUserPkg = mPackageManagerInt.getPackage(
-                            sharedUserPackageName);
-                    if (sharedUserPkg != null
-                            && sharedUserPkg.getRequestedPermissions().contains(permName)) {
-                        isRequested = true;
-                        break;
-                    }
+        if (!isRequested) {
+            // Slow path, go through all shared user packages.
+            String[] sharedUserPackageNames =
+                    mPackageManagerInt.getSharedUserPackagesForPackage(packageName, userId);
+            for (String sharedUserPackageName : sharedUserPackageNames) {
+                AndroidPackage sharedUserPkg = mPackageManagerInt.getPackage(
+                        sharedUserPackageName);
+                if (sharedUserPkg != null
+                        && sharedUserPkg.getRequestedPermissions().contains(permName)) {
+                    isRequested = true;
+                    break;
                 }
             }
-            if (!isRequested) {
+        }
+
+        final BasePermission bp;
+        final boolean permissionUpdated;
+        synchronized (mLock) {
+            bp = mSettings.getPermissionLocked(permName);
+            if (bp == null) {
+                throw new IllegalArgumentException("Unknown permission: " + permName);
+            }
+
+            if (bp.isInstallerExemptIgnored()) {
+                flagValues &= ~FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
+            }
+
+            final UidPermissionState uidState = getUidStateLocked(pkg, userId);
+            if (uidState == null) {
+                Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId);
+                return;
+            }
+
+            if (!uidState.hasPermissionState(permName) && !isRequested) {
                 Log.e(TAG, "Permission " + permName + " isn't requested by package " + packageName);
                 return;
             }
+
+            permissionUpdated = uidState.updatePermissionFlags(bp, flagMask, flagValues);
         }
-        final boolean permissionUpdated =
-                uidState.updatePermissionFlags(bp, flagMask, flagValues);
+
         if (permissionUpdated && bp.isRuntime()) {
             notifyRuntimePermissionStateChanged(packageName, userId);
         }
@@ -869,14 +877,17 @@
 
         final boolean[] changed = new boolean[1];
         mPackageManagerInt.forEachPackage(pkg -> {
-            final UidPermissionState uidState = getUidState(pkg, userId);
-            if (uidState == null) {
-                Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
-                        + userId);
-                return;
+            synchronized (mLock) {
+                final UidPermissionState uidState = getUidStateLocked(pkg, userId);
+                if (uidState == null) {
+                    Slog.e(TAG,
+                            "Missing permissions state for " + pkg.getPackageName() + " and user "
+                                    + userId);
+                    return;
+                }
+                changed[0] |= uidState.updatePermissionFlagsForAllPermissions(
+                        effectiveFlagMask, effectiveFlagValues);
             }
-            changed[0] |= uidState.updatePermissionFlagsForAllPermissions(
-                    effectiveFlagMask, effectiveFlagValues);
             mOnPermissionChangeListeners.onPermissionsChanged(pkg.getUid());
         });
 
@@ -928,33 +939,37 @@
         }
 
         final int uid = UserHandle.getUid(userId, pkg.getUid());
-        final UidPermissionState uidState = getUidState(pkg, userId);
-        if (uidState == null) {
-            Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
-                    + userId);
-            return PackageManager.PERMISSION_DENIED;
-        }
+        final boolean isInstantApp = mPackageManagerInt.getInstantAppPackageName(uid) != null;
 
-        if (checkSinglePermissionInternal(uid, uidState, permissionName)) {
-            return PackageManager.PERMISSION_GRANTED;
-        }
+        synchronized (mLock) {
+            final UidPermissionState uidState = getUidStateLocked(pkg, userId);
+            if (uidState == null) {
+                Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
+                        + userId);
+                return PackageManager.PERMISSION_DENIED;
+            }
 
-        final String fullerPermissionName = FULLER_PERMISSION_MAP.get(permissionName);
-        if (fullerPermissionName != null
-                && checkSinglePermissionInternal(uid, uidState, fullerPermissionName)) {
-            return PackageManager.PERMISSION_GRANTED;
+            if (checkSinglePermissionInternalLocked(uidState, permissionName, isInstantApp)) {
+                return PackageManager.PERMISSION_GRANTED;
+            }
+
+            final String fullerPermissionName = FULLER_PERMISSION_MAP.get(permissionName);
+            if (fullerPermissionName != null && checkSinglePermissionInternalLocked(uidState,
+                    fullerPermissionName, isInstantApp)) {
+                return PackageManager.PERMISSION_GRANTED;
+            }
         }
 
         return PackageManager.PERMISSION_DENIED;
     }
 
-    private boolean checkSinglePermissionInternal(int uid,
-            @NonNull UidPermissionState uidState, @NonNull String permissionName) {
+    private boolean checkSinglePermissionInternalLocked(@NonNull UidPermissionState uidState,
+            @NonNull String permissionName, boolean isInstantApp) {
         if (!uidState.isPermissionGranted(permissionName)) {
             return false;
         }
 
-        if (mPackageManagerInt.getInstantAppPackageName(uid) != null) {
+        if (isInstantApp) {
             return mSettings.isPermissionInstant(permissionName);
         }
 
@@ -1002,24 +1017,25 @@
             return checkPermissionInternal(pkg, false, permissionName, userId);
         }
 
-        if (checkSingleUidPermissionInternal(uid, permissionName)) {
-            return PackageManager.PERMISSION_GRANTED;
-        }
+        synchronized (mLock) {
+            if (checkSingleUidPermissionInternalLocked(uid, permissionName)) {
+                return PackageManager.PERMISSION_GRANTED;
+            }
 
-        final String fullerPermissionName = FULLER_PERMISSION_MAP.get(permissionName);
-        if (fullerPermissionName != null
-                && checkSingleUidPermissionInternal(uid, fullerPermissionName)) {
-            return PackageManager.PERMISSION_GRANTED;
+            final String fullerPermissionName = FULLER_PERMISSION_MAP.get(permissionName);
+            if (fullerPermissionName != null
+                    && checkSingleUidPermissionInternalLocked(uid, fullerPermissionName)) {
+                return PackageManager.PERMISSION_GRANTED;
+            }
         }
 
         return PackageManager.PERMISSION_DENIED;
     }
 
-    private boolean checkSingleUidPermissionInternal(int uid, @NonNull String permissionName) {
-        synchronized (mLock) {
-            ArraySet<String> permissions = mSystemPermissions.get(uid);
-            return permissions != null && permissions.contains(permissionName);
-        }
+    private boolean checkSingleUidPermissionInternalLocked(int uid,
+            @NonNull String permissionName) {
+        ArraySet<String> permissions = mSystemPermissions.get(uid);
+        return permissions != null && permissions.contains(permissionName);
     }
 
     @Override
@@ -1052,7 +1068,7 @@
         // check.
         if (packageName != null) {
             // Allow access to a package that has been granted the READ_DEVICE_IDENTIFIERS appop.
-            long token = mInjector.clearCallingIdentity();
+            final long token = mInjector.clearCallingIdentity();
             AppOpsManager appOpsManager = (AppOpsManager) mInjector.getSystemService(
                     Context.APP_OPS_SERVICE);
             try {
@@ -1145,42 +1161,45 @@
 
         final long identity = Binder.clearCallingIdentity();
         try {
-            final UidPermissionState uidState = getUidState(pkg, userId);
-            if (uidState == null) {
-                Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId);
-                return null;
-            }
-
-            int queryFlags = 0;
-            if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM) != 0) {
-                queryFlags |= FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
-            }
-            if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE) != 0) {
-                queryFlags |= FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
-            }
-            if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER) != 0) {
-                queryFlags |=  FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
-            }
-            if ((flags & PackageManager.FLAG_PERMISSION_ALLOWLIST_ROLE) != 0) {
-                queryFlags |=  FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT;
-            }
-
-            ArrayList<String> whitelistedPermissions = null;
-
-            final int permissionCount = ArrayUtils.size(pkg.getRequestedPermissions());
-            for (int i = 0; i < permissionCount; i++) {
-                final String permissionName = pkg.getRequestedPermissions().get(i);
-                final int currentFlags =
-                        uidState.getPermissionFlags(permissionName);
-                if ((currentFlags & queryFlags) != 0) {
-                    if (whitelistedPermissions == null) {
-                        whitelistedPermissions = new ArrayList<>();
-                    }
-                    whitelistedPermissions.add(permissionName);
+            synchronized (mLock) {
+                final UidPermissionState uidState = getUidStateLocked(pkg, userId);
+                if (uidState == null) {
+                    Slog.e(TAG, "Missing permissions state for " + packageName + " and user "
+                            + userId);
+                    return null;
                 }
-            }
 
-            return whitelistedPermissions;
+                int queryFlags = 0;
+                if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM) != 0) {
+                    queryFlags |= FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
+                }
+                if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE) != 0) {
+                    queryFlags |= FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
+                }
+                if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER) != 0) {
+                    queryFlags |= FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
+                }
+                if ((flags & PackageManager.FLAG_PERMISSION_ALLOWLIST_ROLE) != 0) {
+                    queryFlags |=  FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT;
+                }
+
+                ArrayList<String> whitelistedPermissions = null;
+
+                final int permissionCount = ArrayUtils.size(pkg.getRequestedPermissions());
+                for (int i = 0; i < permissionCount; i++) {
+                    final String permissionName = pkg.getRequestedPermissions().get(i);
+                    final int currentFlags =
+                            uidState.getPermissionFlags(permissionName);
+                    if ((currentFlags & queryFlags) != 0) {
+                        if (whitelistedPermissions == null) {
+                            whitelistedPermissions = new ArrayList<>();
+                        }
+                        whitelistedPermissions.add(permissionName);
+                    }
+                }
+
+                return whitelistedPermissions;
+            }
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -1445,12 +1464,15 @@
                 "grantRuntimePermission");
 
         final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName);
-        final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting(
-                packageName);
+        final PackageSetting ps = mPackageManagerInt.getPackageSetting(packageName);
         if (pkg == null || ps == null) {
             Log.e(TAG, "Unknown package: " + packageName);
             return;
         }
+        if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
+            throw new IllegalArgumentException("Unknown package: " + packageName);
+        }
+
         final BasePermission bp;
         synchronized (mLock) {
             bp = mSettings.getPermissionLocked(permName);
@@ -1458,46 +1480,17 @@
         if (bp == null) {
             throw new IllegalArgumentException("Unknown permission: " + permName);
         }
-        if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
-            throw new IllegalArgumentException("Unknown package: " + packageName);
-        }
 
-        final UidPermissionState uidState = getUidState(pkg, userId);
-        if (uidState == null) {
-            Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
-                    + userId);
-            return;
+        if (!(bp.isRuntime() || bp.isDevelopment())) {
+            throw new SecurityException("Permission " + permName + " requested by "
+                    + pkg.getPackageName() + " is not a changeable permission type");
         }
 
-        bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, uidState);
-
         // 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 (pkg.getTargetSdkVersion() < Build.VERSION_CODES.M
-                && bp.isRuntime()) {
-            return;
-        }
-
-        final int uid = UserHandle.getUid(userId, UserHandle.getAppId(pkg.getUid()));
-
-        final int flags = uidState.getPermissionFlags(permName);
-        if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
-            Log.e(TAG, "Cannot grant system fixed permission "
-                    + permName + " for package " + packageName);
-            return;
-        }
-        if (!overridePolicy && (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
-            Log.e(TAG, "Cannot grant policy fixed permission "
-                    + permName + " for package " + packageName);
-            return;
-        }
-
-        if (bp.isHardRestricted()
-                && (flags & PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) == 0) {
-            Log.e(TAG, "Cannot grant hard restricted non-exempt permission "
-                    + permName + " for package " + packageName);
+        if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.M && bp.isRuntime()) {
             return;
         }
 
@@ -1509,36 +1502,61 @@
             return;
         }
 
-        if (bp.isDevelopment()) {
-            // Development permissions must be handled specially, since they are not
-            // normal runtime permissions.  For now they apply to all users.
-            // TODO(zhanghai): We are breaking the behavior above by making all permission state
-            //  per-user. It isn't documented behavior and relatively rarely used anyway.
-            if (uidState.grantPermission(bp)) {
-                if (callback != null) {
-                    callback.onInstallPermissionGranted();
-                }
+        synchronized (mLock) {
+            final UidPermissionState uidState = getUidStateLocked(pkg, userId);
+            if (uidState == null) {
+                Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
+                        + userId);
+                return;
             }
-            return;
-        }
 
-        if (ps.getInstantApp(userId) && !bp.isInstant()) {
-            throw new SecurityException("Cannot grant non-ephemeral permission"
-                    + permName + " for package " + packageName);
-        }
+            if (!(uidState.hasPermissionState(permName)
+                    || pkg.getRequestedPermissions().contains(permName))) {
+                throw new SecurityException("Package " + pkg.getPackageName()
+                        + " has not requested permission " + permName);
+            }
 
-        if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.M) {
-            Slog.w(TAG, "Cannot grant runtime permission to a legacy app");
-            return;
-        }
+            final int flags = uidState.getPermissionFlags(permName);
+            if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
+                Log.e(TAG, "Cannot grant system fixed permission "
+                        + permName + " for package " + packageName);
+                return;
+            }
+            if (!overridePolicy && (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
+                Log.e(TAG, "Cannot grant policy fixed permission "
+                        + permName + " for package " + packageName);
+                return;
+            }
 
-        if (!uidState.grantPermission(bp)) {
-            return;
-        }
+            if (bp.isHardRestricted()
+                    && (flags & PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) == 0) {
+                Log.e(TAG, "Cannot grant hard restricted non-exempt permission "
+                        + permName + " for package " + packageName);
+                return;
+            }
 
-        if (bp.hasGids()) {
-            if (callback != null) {
-                callback.onGidsChanged(UserHandle.getAppId(pkg.getUid()), userId);
+            if (bp.isDevelopment()) {
+                // Development permissions must be handled specially, since they are not
+                // normal runtime permissions.  For now they apply to all users.
+                // TODO(zhanghai): We are breaking the behavior above by making all permission state
+                //  per-user. It isn't documented behavior and relatively rarely used anyway.
+                if (!uidState.grantPermission(bp)) {
+                    return;
+                }
+            } else {
+                if (ps.getInstantApp(userId) && !bp.isInstant()) {
+                    throw new SecurityException("Cannot grant non-ephemeral permission" + permName
+                            + " for package " + packageName);
+                }
+
+                if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.M) {
+                    Slog.w(TAG, "Cannot grant runtime permission to a legacy app");
+                    return;
+                }
+
+                if (!uidState.grantPermission(bp)) {
+                    return;
+                }
             }
         }
 
@@ -1546,8 +1564,16 @@
             logPermission(MetricsEvent.ACTION_PERMISSION_GRANTED, permName, packageName);
         }
 
+        final int uid = UserHandle.getUid(userId, UserHandle.getAppId(pkg.getUid()));
         if (callback != null) {
-            callback.onPermissionGranted(uid, userId);
+            if (bp.isDevelopment()) {
+                callback.onInstallPermissionGranted();
+            } else {
+                callback.onPermissionGranted(uid, userId);
+            }
+            if (bp.hasGids()) {
+                callback.onGidsChanged(UserHandle.getAppId(pkg.getUid()), userId);
+            }
         }
 
         if (bp.isRuntime()) {
@@ -1601,56 +1627,57 @@
         if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
             throw new IllegalArgumentException("Unknown package: " + packageName);
         }
-        final BasePermission bp = mSettings.getPermissionLocked(permName);
+        final BasePermission bp = mSettings.getPermission(permName);
         if (bp == null) {
             throw new IllegalArgumentException("Unknown permission: " + permName);
         }
 
-        final UidPermissionState uidState = getUidState(pkg, userId);
-        if (uidState == null) {
-            Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
-                    + userId);
-            return;
+        if (!(bp.isRuntime() || bp.isDevelopment())) {
+            throw new SecurityException("Permission " + permName + " requested by "
+                    + pkg.getPackageName() + " is not a changeable permission type");
         }
 
-        bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, uidState);
+        synchronized (mLock) {
+            final UidPermissionState uidState = getUidStateLocked(pkg, userId);
+            if (uidState == null) {
+                Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
+                        + userId);
+                return;
+            }
 
-        // 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 (pkg.getTargetSdkVersion() < Build.VERSION_CODES.M
-                && bp.isRuntime()) {
-            return;
-        }
+            if (!(uidState.hasPermissionState(permName)
+                    || pkg.getRequestedPermissions().contains(permName))) {
+                throw new SecurityException("Package " + pkg.getPackageName()
+                        + " has not requested permission " + permName);
+            }
 
-        final int flags = uidState.getPermissionFlags(permName);
-        // Only the system may revoke SYSTEM_FIXED permissions.
-        if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0
-                && UserHandle.getCallingAppId() != Process.SYSTEM_UID) {
-            throw new SecurityException("Non-System UID cannot revoke system fixed permission "
-                    + permName + " for package " + packageName);
-        }
-        if (!overridePolicy && (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
-            throw new SecurityException("Cannot revoke policy fixed permission "
-                    + permName + " for package " + packageName);
-        }
+            // 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 (pkg.getTargetSdkVersion() < Build.VERSION_CODES.M && bp.isRuntime()) {
+                return;
+            }
 
-        if (bp.isDevelopment()) {
+            final int flags = uidState.getPermissionFlags(permName);
+            // Only the system may revoke SYSTEM_FIXED permissions.
+            if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0
+                    && UserHandle.getCallingAppId() != Process.SYSTEM_UID) {
+                throw new SecurityException("Non-System UID cannot revoke system fixed permission "
+                        + permName + " for package " + packageName);
+            }
+            if (!overridePolicy && (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
+                throw new SecurityException("Cannot revoke policy fixed permission "
+                        + permName + " for package " + packageName);
+            }
+
             // Development permissions must be handled specially, since they are not
             // normal runtime permissions.  For now they apply to all users.
             // TODO(zhanghai): We are breaking the behavior above by making all permission state
             //  per-user. It isn't documented behavior and relatively rarely used anyway.
-            if (uidState.revokePermission(bp)) {
-                if (callback != null) {
-                    mDefaultPermissionCallback.onInstallPermissionRevoked();
-                }
+            if (!uidState.revokePermission(bp)) {
+                return;
             }
-            return;
-        }
-
-        if (!uidState.revokePermission(bp)) {
-            return;
         }
 
         if (bp.isRuntime()) {
@@ -1658,8 +1685,12 @@
         }
 
         if (callback != null) {
-            callback.onPermissionRevoked(UserHandle.getUid(userId,
-                    UserHandle.getAppId(pkg.getUid())), userId, reason);
+            if (bp.isDevelopment()) {
+                mDefaultPermissionCallback.onInstallPermissionRevoked();
+            } else {
+                callback.onPermissionRevoked(UserHandle.getUid(userId,
+                        UserHandle.getAppId(pkg.getUid())), userId, reason);
+            }
         }
 
         if (bp.isRuntime()) {
@@ -1694,7 +1725,6 @@
      * @param pkg The package for which to reset.
      * @param userId The device user for which to do a reset.
      */
-    @GuardedBy("mLock")
     private void resetRuntimePermissionsInternal(final AndroidPackage pkg,
             final int userId) {
         final String packageName = pkg.getPackageName();
@@ -2218,32 +2248,6 @@
         }
     }
 
-    private int adjustPermissionProtectionFlagsLocked(int protectionLevel,
-            @Nullable AndroidPackage pkg, int uid) {
-        // Signature permission flags area always reported
-        final int protectionLevelMasked = protectionLevel
-                & (PermissionInfo.PROTECTION_NORMAL
-                | PermissionInfo.PROTECTION_DANGEROUS
-                | PermissionInfo.PROTECTION_SIGNATURE);
-        if (protectionLevelMasked == PermissionInfo.PROTECTION_SIGNATURE) {
-            return protectionLevel;
-        }
-        // System sees all flags.
-        final int appId = UserHandle.getAppId(uid);
-        if (appId == Process.SYSTEM_UID || appId == Process.ROOT_UID
-                || appId == Process.SHELL_UID) {
-            return protectionLevel;
-        }
-        if (pkg == null) {
-            return protectionLevel;
-        }
-        if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.O) {
-            return protectionLevelMasked;
-        }
-        // Apps that target O see flags for all protection levels.
-        return protectionLevel;
-    }
-
     /**
      * We might auto-grant permissions if any permission of the group is already granted. Hence if
      * the group of a granted permission changes we need to revoke it to avoid having permissions of
@@ -2331,7 +2335,7 @@
             // Assume by default that we did not install this permission into the system.
             p.setFlags(p.getFlags() & ~PermissionInfo.FLAG_INSTALLED);
 
-            synchronized (PermissionManagerService.this.mLock) {
+            synchronized (mLock) {
                 // Now that permission groups have a special meaning, we ignore permission
                 // groups for legacy apps to prevent unexpected behavior. In particular,
                 // permissions for one app being granted to someone just because they happen
@@ -2346,19 +2350,25 @@
                     }
                 }
 
+                final PermissionInfo permissionInfo = PackageInfoUtils.generatePermissionInfo(p,
+                        PackageManager.GET_META_DATA);
+                final BasePermission bp;
                 if (p.isTree()) {
-                    final BasePermission bp = BasePermission.createOrUpdate(
+                    bp = BasePermission.createOrUpdate(
                             mPackageManagerInt,
-                            mSettings.getPermissionTreeLocked(p.getName()), p, pkg,
+                            mSettings.getPermissionTreeLocked(p.getName()), permissionInfo, pkg,
                             mSettings.getAllPermissionTreesLocked(), chatty);
                     mSettings.putPermissionTreeLocked(p.getName(), bp);
                 } else {
-                    final BasePermission bp = BasePermission.createOrUpdate(
+                    bp = BasePermission.createOrUpdate(
                             mPackageManagerInt,
                             mSettings.getPermissionLocked(p.getName()),
-                            p, pkg, mSettings.getAllPermissionTreesLocked(), chatty);
+                            permissionInfo, pkg, mSettings.getAllPermissionTreesLocked(), chatty);
                     mSettings.putPermissionLocked(p.getName(), bp);
                 }
+                if (bp.isInstalled()) {
+                    p.setFlags(p.getFlags() | PermissionInfo.FLAG_INSTALLED);
+                }
             }
         }
     }
@@ -2416,7 +2426,7 @@
                     bp = mSettings.mPermissionTrees.get(p.getName());
                 }
                 if (bp != null && bp.isPermission(p)) {
-                    bp.setPermission(null);
+                    bp.setPermissionInfo(null);
                     if (DEBUG_REMOVE && chatty) {
                         if (r == null) {
                             r = new StringBuilder(256);
@@ -2471,31 +2481,35 @@
         if (ps == null) {
             return Collections.emptySet();
         }
-        final UidPermissionState uidState = getUidState(ps, userId);
-        if (uidState == null) {
-            Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId);
-            return Collections.emptySet();
-        }
-        if (!ps.getInstantApp(userId)) {
-            return uidState.getGrantedPermissions();
-        } else {
-            // Install permission state is shared among all users, but instant app state is
-            // per-user, so we can only filter it here unless we make install permission state
-            // per-user as well.
-            final Set<String> instantPermissions = new ArraySet<>(uidState.getGrantedPermissions());
-            instantPermissions.removeIf(permissionName -> {
-                BasePermission permission = mSettings.getPermission(permissionName);
-                if (permission == null) {
-                    return true;
-                }
-                if (!permission.isInstant()) {
-                    EventLog.writeEvent(0x534e4554, "140256621", UserHandle.getUid(userId,
-                            ps.getAppId()), permissionName);
-                    return true;
-                }
-                return false;
-            });
-            return instantPermissions;
+
+        synchronized (mLock) {
+            final UidPermissionState uidState = getUidStateLocked(ps, userId);
+            if (uidState == null) {
+                Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId);
+                return Collections.emptySet();
+            }
+            if (!ps.getInstantApp(userId)) {
+                return uidState.getGrantedPermissions();
+            } else {
+                // Install permission state is shared among all users, but instant app state is
+                // per-user, so we can only filter it here unless we make install permission state
+                // per-user as well.
+                final Set<String> instantPermissions =
+                        new ArraySet<>(uidState.getGrantedPermissions());
+                instantPermissions.removeIf(permissionName -> {
+                    BasePermission permission = mSettings.getPermission(permissionName);
+                    if (permission == null) {
+                        return true;
+                    }
+                    if (!permission.isInstant()) {
+                        EventLog.writeEvent(0x534e4554, "140256621", UserHandle.getUid(userId,
+                                ps.getAppId()), permissionName);
+                        return true;
+                    }
+                    return false;
+                });
+                return instantPermissions;
+            }
         }
     }
 
@@ -2548,349 +2562,352 @@
         boolean runtimePermissionsRevoked = false;
         int[] updatedUserIds = EMPTY_INT_ARRAY;
 
-        for (final int userId : userIds) {
-            final UserPermissionState userState = mState.getOrCreateUserState(userId);
-            final UidPermissionState uidState = userState.getOrCreateUidState(ps.getAppId());
+        final ArraySet<String> shouldGrantSignaturePermission = new ArraySet<>();
+        final List<String> requestedPermissions = pkg.getRequestedPermissions();
+        final int requestedPermissionsSize = requestedPermissions.size();
+        for (int i = 0; i < requestedPermissionsSize; i++) {
+            final String permissionName = pkg.getRequestedPermissions().get(i);
 
-            if (uidState.isMissing()) {
-                Collection<String> requestedPermissions;
-                int targetSdkVersion;
-                if (!ps.isSharedUser()) {
-                    requestedPermissions = pkg.getRequestedPermissions();
-                    targetSdkVersion = pkg.getTargetSdkVersion();
-                } else {
-                    requestedPermissions = new ArraySet<>();
-                    targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
-                    List<AndroidPackage> packages = ps.getSharedUser().getPackages();
-                    int packagesSize = packages.size();
-                    for (int i = 0; i < packagesSize; i++) {
-                        AndroidPackage sharedUserPackage = packages.get(i);
-                        requestedPermissions.addAll(sharedUserPackage.getRequestedPermissions());
-                        targetSdkVersion = Math.min(targetSdkVersion,
-                                sharedUserPackage.getTargetSdkVersion());
-                    }
-                }
-
-                for (String permissionName : requestedPermissions) {
-                    BasePermission permission = mSettings.getPermission(permissionName);
-                    if (permission == null) {
-                        continue;
-                    }
-                    if (Objects.equals(permission.getSourcePackageName(), PLATFORM_PACKAGE_NAME)
-                            && permission.isRuntime() && !permission.isRemoved()) {
-                        if (permission.isHardOrSoftRestricted()
-                                || permission.isImmutablyRestricted()) {
-                            uidState.updatePermissionFlags(permission,
-                                    FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT,
-                                    FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT);
-                        }
-                        if (targetSdkVersion < Build.VERSION_CODES.M) {
-                            uidState.updatePermissionFlags(permission,
-                                    PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED
-                                            | PackageManager.FLAG_PERMISSION_REVOKED_COMPAT,
-                                    PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED
-                                            | PackageManager.FLAG_PERMISSION_REVOKED_COMPAT);
-                            uidState.grantPermission(permission);
-                        }
-                    }
-                }
-
-                uidState.setMissing(false);
-                updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
+            final BasePermission permission = mSettings.getPermission(permissionName);
+            if (permission == null) {
+                continue;
             }
+            if (permission.isSignature() && shouldGrantSignaturePermission(pkg, ps,
+                    permission)) {
+                shouldGrantSignaturePermission.add(permissionName);
+            }
+        }
 
-            UidPermissionState origState = uidState;
-
-            boolean changedInstallPermission = false;
-
-            if (replace) {
-                userState.setInstallPermissionsFixed(ps.name, false);
-                if (!ps.isSharedUser()) {
-                    origState = new UidPermissionState(uidState);
-                    uidState.reset();
-                } else {
-                    // We need to know only about runtime permission changes since the
-                    // calling code always writes the install permissions state but
-                    // the runtime ones are written only if changed. The only cases of
-                    // changed runtime permissions here are promotion of an install to
-                    // runtime and revocation of a runtime from a shared user.
-                    synchronized (mLock) {
-                        if (revokeUnusedSharedUserPermissionsLocked(
-                                ps.getSharedUser().getPackages(), uidState)) {
-                            updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
-                            runtimePermissionsRevoked = true;
-                        }
-                    }
+        final SparseBooleanArray isPermissionPolicyInitialized = new SparseBooleanArray();
+        if (mPermissionPolicyInternal != null) {
+            for (final int userId : userIds) {
+                if (mPermissionPolicyInternal.isInitialized(userId)) {
+                    isPermissionPolicyInitialized.put(userId, true);
                 }
             }
+        }
 
-            ArraySet<String> newImplicitPermissions = new ArraySet<>();
-            final String friendlyName = pkg.getPackageName() + "(" + pkg.getUid() + ")";
+        synchronized (mLock) {
+            for (final int userId : userIds) {
+                final UserPermissionState userState = mState.getOrCreateUserState(userId);
+                final UidPermissionState uidState = userState.getOrCreateUidState(ps.getAppId());
 
-            final int N = pkg.getRequestedPermissions().size();
-            for (int i = 0; i < N; i++) {
-                final String permName = pkg.getRequestedPermissions().get(i);
-                final BasePermission bp = mSettings.getPermission(permName);
-                final boolean appSupportsRuntimePermissions =
-                        pkg.getTargetSdkVersion() >= Build.VERSION_CODES.M;
-                String upgradedActivityRecognitionPermission = null;
-
-                if (DEBUG_INSTALL && bp != null) {
-                    Log.i(TAG, "Package " + friendlyName
-                            + " checking " + permName + ": " + bp);
-                }
-
-                if (bp == null || getSourcePackageSetting(bp) == null) {
-                    if (packageOfInterest == null || packageOfInterest.equals(
-                            pkg.getPackageName())) {
-                        if (DEBUG_PERMISSIONS) {
-                            Slog.i(TAG, "Unknown permission " + permName
-                                    + " in package " + friendlyName);
-                        }
-                    }
-                    continue;
-                }
-
-                // Cache newImplicitPermissions before modifing permissionsState as for the shared
-                // uids the original and new state are the same object
-                if (!origState.hasPermissionState(permName)
-                        && (pkg.getImplicitPermissions().contains(permName)
-                                || (permName.equals(Manifest.permission.ACTIVITY_RECOGNITION)))) {
-                    if (pkg.getImplicitPermissions().contains(permName)) {
-                        // If permName is an implicit permission, try to auto-grant
-                        newImplicitPermissions.add(permName);
-
-                        if (DEBUG_PERMISSIONS) {
-                            Slog.i(TAG, permName + " is newly added for " + friendlyName);
-                        }
+                if (uidState.isMissing()) {
+                    Collection<String> uidRequestedPermissions;
+                    int targetSdkVersion;
+                    if (!ps.isSharedUser()) {
+                        uidRequestedPermissions = pkg.getRequestedPermissions();
+                        targetSdkVersion = pkg.getTargetSdkVersion();
                     } else {
-                        // Special case for Activity Recognition permission. Even if AR permission
-                        // is not an implicit permission we want to add it to the list (try to
-                        // auto-grant it) if the app was installed on a device before AR permission
-                        // was split, regardless of if the app now requests the new AR permission
-                        // or has updated its target SDK and AR is no longer implicit to it.
-                        // This is a compatibility workaround for apps when AR permission was
-                        // split in Q.
-                        final List<SplitPermissionInfoParcelable> permissionList =
-                                getSplitPermissions();
-                        int numSplitPerms = permissionList.size();
-                        for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) {
-                            SplitPermissionInfoParcelable sp = permissionList.get(splitPermNum);
-                            String splitPermName = sp.getSplitPermission();
-                            if (sp.getNewPermissions().contains(permName)
-                                    && origState.isPermissionGranted(splitPermName)) {
-                                upgradedActivityRecognitionPermission = splitPermName;
-                                newImplicitPermissions.add(permName);
+                        uidRequestedPermissions = new ArraySet<>();
+                        targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
+                        List<AndroidPackage> packages = ps.getSharedUser().getPackages();
+                        int packagesSize = packages.size();
+                        for (int i = 0; i < packagesSize; i++) {
+                            AndroidPackage sharedUserPackage = packages.get(i);
+                            uidRequestedPermissions.addAll(
+                                    sharedUserPackage.getRequestedPermissions());
+                            targetSdkVersion = Math.min(targetSdkVersion,
+                                    sharedUserPackage.getTargetSdkVersion());
+                        }
+                    }
 
-                                if (DEBUG_PERMISSIONS) {
-                                    Slog.i(TAG, permName + " is newly added for "
-                                            + friendlyName);
-                                }
-                                break;
+                    for (String permissionName : uidRequestedPermissions) {
+                        BasePermission permission = mSettings.getPermission(permissionName);
+                        if (permission == null) {
+                            continue;
+                        }
+                        if (Objects.equals(permission.getPackageName(), PLATFORM_PACKAGE_NAME)
+                                && permission.isRuntime() && !permission.isRemoved()) {
+                            if (permission.isHardOrSoftRestricted()
+                                    || permission.isImmutablyRestricted()) {
+                                uidState.updatePermissionFlags(permission,
+                                        FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT,
+                                        FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT);
+                            }
+                            if (targetSdkVersion < Build.VERSION_CODES.M) {
+                                uidState.updatePermissionFlags(permission,
+                                        PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED
+                                                | PackageManager.FLAG_PERMISSION_REVOKED_COMPAT,
+                                        PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED
+                                                | PackageManager.FLAG_PERMISSION_REVOKED_COMPAT);
+                                uidState.grantPermission(permission);
+                            }
+                        }
+                    }
+
+                    uidState.setMissing(false);
+                    updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
+                }
+
+                UidPermissionState origState = uidState;
+
+                boolean changedInstallPermission = false;
+
+                if (replace) {
+                    userState.setInstallPermissionsFixed(ps.name, false);
+                    if (!ps.isSharedUser()) {
+                        origState = new UidPermissionState(uidState);
+                        uidState.reset();
+                    } else {
+                        // We need to know only about runtime permission changes since the
+                        // calling code always writes the install permissions state but
+                        // the runtime ones are written only if changed. The only cases of
+                        // changed runtime permissions here are promotion of an install to
+                        // runtime and revocation of a runtime from a shared user.
+                        synchronized (mLock) {
+                            if (revokeUnusedSharedUserPermissionsLocked(
+                                    ps.getSharedUser().getPackages(), uidState)) {
+                                updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
+                                runtimePermissionsRevoked = true;
                             }
                         }
                     }
                 }
 
-                // TODO(b/140256621): The package instant app method has been removed
-                //  as part of work in b/135203078, so this has been commented out in the meantime
-                // Limit ephemeral apps to ephemeral allowed permissions.
-    //            if (/*pkg.isInstantApp()*/ false && !bp.isInstant()) {
-    //                if (DEBUG_PERMISSIONS) {
-    //                    Log.i(TAG, "Denying non-ephemeral permission " + bp.getName()
-    //                            + " for package " + pkg.getPackageName());
-    //                }
-    //                continue;
-    //            }
+                ArraySet<String> newImplicitPermissions = new ArraySet<>();
+                final String friendlyName = pkg.getPackageName() + "(" + pkg.getUid() + ")";
 
-                if (bp.isRuntimeOnly() && !appSupportsRuntimePermissions) {
-                    if (DEBUG_PERMISSIONS) {
-                        Log.i(TAG, "Denying runtime-only permission " + bp.getName()
-                                + " for package " + friendlyName);
+                for (int i = 0; i < requestedPermissionsSize; i++) {
+                    final String permName = requestedPermissions.get(i);
+
+                    final BasePermission bp = mSettings.getPermission(permName);
+                    final boolean appSupportsRuntimePermissions =
+                            pkg.getTargetSdkVersion() >= Build.VERSION_CODES.M;
+                    String legacyActivityRecognitionPermission = null;
+
+                    if (DEBUG_INSTALL && bp != null) {
+                        Log.i(TAG, "Package " + friendlyName
+                                + " checking " + permName + ": " + bp);
                     }
-                    continue;
-                }
 
-                final String perm = bp.getName();
-                boolean allowedSig = false;
-                int grant = GRANT_DENIED;
-
-                // Keep track of app op permissions.
-                if (bp.isAppOp()) {
-                    mSettings.addAppOpPackage(perm, pkg.getPackageName());
-                }
-
-                if (bp.isNormal()) {
-                    // For all apps normal permissions are install time ones.
-                    grant = GRANT_INSTALL;
-                } else if (bp.isRuntime()) {
-                    // For modern apps keep runtime permissions unchanged.
-                    grant = GRANT_RUNTIME;
-                } else if (bp.isSignature()) {
-                    // For all apps signature permissions are install time ones.
-                    allowedSig = shouldGrantSignaturePermission(perm, pkg, ps, bp, origState);
-                    if (allowedSig) {
-                        grant = GRANT_INSTALL;
+                    // TODO(zhanghai): I don't think we need to check source package setting if
+                    //  permission is present, because otherwise the permission should have been
+                    //  removed.
+                    if (bp == null /*|| getSourcePackageSetting(bp) == null*/) {
+                        if (packageOfInterest == null || packageOfInterest.equals(
+                                pkg.getPackageName())) {
+                            if (DEBUG_PERMISSIONS) {
+                                Slog.i(TAG, "Unknown permission " + permName
+                                        + " in package " + friendlyName);
+                            }
+                        }
+                        continue;
                     }
-                }
 
-                if (grant == GRANT_INSTALL && !allowedSig && !origState.isPermissionGranted(perm)) {
-                    // If this is an existing, non-system package, then
-                    // we can't add any new permissions to it. Runtime
-                    // permissions can be added any time - they are dynamic.
-                    if (!ps.isSystem() && userState.areInstallPermissionsFixed(ps.name)) {
-                        // Except...  if this is a permission that was added
-                        // to the platform (note: need to only do this when
-                        // updating the platform).
-                        if (!isNewPlatformPermissionForPackage(perm, pkg)) {
-                            grant = GRANT_DENIED;
+                    // Cache newImplicitPermissions before modifing permissionsState as for the
+                    // shared uids the original and new state are the same object
+                    if (!origState.hasPermissionState(permName)
+                            && (pkg.getImplicitPermissions().contains(permName)
+                            || (permName.equals(Manifest.permission.ACTIVITY_RECOGNITION)))) {
+                        if (pkg.getImplicitPermissions().contains(permName)) {
+                            // If permName is an implicit permission, try to auto-grant
+                            newImplicitPermissions.add(permName);
+
+                            if (DEBUG_PERMISSIONS) {
+                                Slog.i(TAG, permName + " is newly added for " + friendlyName);
+                            }
+                        } else {
+                            // Special case for Activity Recognition permission. Even if AR
+                            // permission is not an implicit permission we want to add it to the
+                            // list (try to auto-grant it) if the app was installed on a device
+                            // before AR permission was split, regardless of if the app now requests
+                            // the new AR permission or has updated its target SDK and AR is no
+                            // longer implicit to it. This is a compatibility workaround for apps
+                            // when AR permission was split in Q.
+                            // TODO(zhanghai): This calls into SystemConfig, which generally
+                            //  shouldn't  cause deadlock, but maybe we should keep a cache of the
+                            //  split permission  list and just eliminate the possibility.
+                            final List<SplitPermissionInfoParcelable> permissionList =
+                                    getSplitPermissions();
+                            int numSplitPerms = permissionList.size();
+                            for (int splitPermNum = 0; splitPermNum < numSplitPerms;
+                                    splitPermNum++) {
+                                SplitPermissionInfoParcelable sp = permissionList.get(splitPermNum);
+                                String splitPermName = sp.getSplitPermission();
+                                if (sp.getNewPermissions().contains(permName)
+                                        && origState.isPermissionGranted(splitPermName)) {
+                                    legacyActivityRecognitionPermission = splitPermName;
+                                    newImplicitPermissions.add(permName);
+
+                                    if (DEBUG_PERMISSIONS) {
+                                        Slog.i(TAG, permName + " is newly added for "
+                                                + friendlyName);
+                                    }
+                                    break;
+                                }
+                            }
                         }
                     }
-                }
 
-                if (DEBUG_PERMISSIONS) {
-                    Slog.i(TAG, "Considering granting permission " + perm + " to package "
-                            + pkg.getPackageName());
-                }
+                    // TODO(b/140256621): The package instant app method has been removed
+                    //  as part of work in b/135203078, so this has been commented out in the
+                    //  meantime
+                    // Limit ephemeral apps to ephemeral allowed permissions.
+        //            if (/*pkg.isInstantApp()*/ false && !bp.isInstant()) {
+        //                if (DEBUG_PERMISSIONS) {
+        //                    Log.i(TAG, "Denying non-ephemeral permission " + bp.getName()
+        //                            + " for package " + pkg.getPackageName());
+        //                }
+        //                continue;
+        //            }
 
-                synchronized (mLock) {
-                    if (grant != GRANT_DENIED) {
-                        switch (grant) {
-                            case GRANT_INSTALL: {
-                                // Grant an install permission.
-                                if (uidState.grantPermission(bp)) {
-                                    changedInstallPermission = true;
-                                }
-                            } break;
+                    if (bp.isRuntimeOnly() && !appSupportsRuntimePermissions) {
+                        if (DEBUG_PERMISSIONS) {
+                            Log.i(TAG, "Denying runtime-only permission " + bp.getName()
+                                    + " for package " + friendlyName);
+                        }
+                        continue;
+                    }
 
-                            case GRANT_RUNTIME: {
-                                boolean hardRestricted = bp.isHardRestricted();
-                                boolean softRestricted = bp.isSoftRestricted();
+                    final String perm = bp.getName();
 
-                                // If permission policy is not ready we don't deal with restricted
-                                // permissions as the policy may whitelist some permissions. Once
-                                // the policy is initialized we would re-evaluate permissions.
-                                final boolean permissionPolicyInitialized =
-                                        mPermissionPolicyInternal != null
-                                                && mPermissionPolicyInternal.isInitialized(userId);
+                    // Keep track of app op permissions.
+                    if (bp.isAppOp()) {
+                        mSettings.addAppOpPackage(perm, pkg.getPackageName());
+                    }
 
-                                PermissionState origPermState = origState.getPermissionState(perm);
-                                int flags = origPermState != null ? origPermState.getFlags() : 0;
+                    boolean shouldGrantNormalPermission = true;
+                    if (bp.isNormal() && !origState.isPermissionGranted(perm)) {
+                        // If this is an existing, non-system package, then
+                        // we can't add any new permissions to it. Runtime
+                        // permissions can be added any time - they are dynamic.
+                        if (!ps.isSystem() && userState.areInstallPermissionsFixed(ps.name)) {
+                            // Except...  if this is a permission that was added
+                            // to the platform (note: need to only do this when
+                            // updating the platform).
+                            if (!isNewPlatformPermissionForPackage(perm, pkg)) {
+                                shouldGrantNormalPermission = false;
+                            }
+                        }
+                    }
 
-                                boolean wasChanged = false;
+                    if (DEBUG_PERMISSIONS) {
+                        Slog.i(TAG, "Considering granting permission " + perm + " to package "
+                                + pkg.getPackageName());
+                    }
 
-                                boolean restrictionExempt =
-                                        (origState.getPermissionFlags(bp.name)
-                                                & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
-                                boolean restrictionApplied = (origState.getPermissionFlags(
-                                        bp.name) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
+                    if ((bp.isNormal() && shouldGrantNormalPermission) || (bp.isSignature()
+                            && (shouldGrantSignaturePermission.contains(permName)
+                            || (bp.isDevelopment() && origState.isPermissionGranted(permName))))) {
+                        // Grant an install permission.
+                        if (uidState.grantPermission(bp)) {
+                            changedInstallPermission = true;
+                        }
+                    } else if (bp.isRuntime()) {
+                        boolean hardRestricted = bp.isHardRestricted();
+                        boolean softRestricted = bp.isSoftRestricted();
 
-                                if (appSupportsRuntimePermissions) {
-                                    // If hard restricted we don't allow holding it
-                                    if (permissionPolicyInitialized && hardRestricted) {
-                                        if (!restrictionExempt) {
-                                            if (origPermState != null && origPermState.isGranted()
-                                                    && uidState.revokePermission(bp)) {
-                                                wasChanged = true;
-                                            }
-                                            if (!restrictionApplied) {
-                                                flags |= FLAG_PERMISSION_APPLY_RESTRICTION;
-                                                wasChanged = true;
-                                            }
-                                        }
-                                    // If soft restricted we allow holding in a restricted form
-                                    } else if (permissionPolicyInitialized && softRestricted) {
-                                        // Regardless if granted set the restriction flag as it
-                                        // may affect app treatment based on this permission.
-                                        if (!restrictionExempt && !restrictionApplied) {
-                                            flags |= FLAG_PERMISSION_APPLY_RESTRICTION;
-                                            wasChanged = true;
-                                        }
-                                    }
+                        // If permission policy is not ready we don't deal with restricted
+                        // permissions as the policy may whitelist some permissions. Once
+                        // the policy is initialized we would re-evaluate permissions.
+                        final boolean permissionPolicyInitialized =
+                                isPermissionPolicyInitialized.get(userId);
 
-                                    // Remove review flag as it is not necessary anymore
-                                    if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
-                                        flags &= ~FLAG_PERMISSION_REVIEW_REQUIRED;
+                        PermissionState origPermState = origState.getPermissionState(perm);
+                        int flags = origPermState != null ? origPermState.getFlags() : 0;
+
+                        boolean wasChanged = false;
+
+                        boolean restrictionExempt =
+                                (origState.getPermissionFlags(bp.getName())
+                                        & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
+                        boolean restrictionApplied = (origState.getPermissionFlags(
+                                bp.getName()) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
+
+                        if (appSupportsRuntimePermissions) {
+                            // If hard restricted we don't allow holding it
+                            if (permissionPolicyInitialized && hardRestricted) {
+                                if (!restrictionExempt) {
+                                    if (origPermState != null && origPermState.isGranted()
+                                            && uidState.revokePermission(bp)) {
                                         wasChanged = true;
                                     }
-
-                                    if ((flags & FLAG_PERMISSION_REVOKED_COMPAT) != 0) {
-                                        flags &= ~FLAG_PERMISSION_REVOKED_COMPAT;
-                                        wasChanged = true;
-                                    // Hard restricted permissions cannot be held.
-                                    } else if (!permissionPolicyInitialized
-                                            || (!hardRestricted || restrictionExempt)) {
-                                        if ((origPermState != null && origPermState.isGranted())
-                                                || upgradedActivityRecognitionPermission != null) {
-                                            if (!uidState.grantPermission(bp)) {
-                                                wasChanged = true;
-                                            }
-                                        }
-                                    }
-                                } else {
-                                    if (origPermState == null) {
-                                        // New permission
-                                        if (PLATFORM_PACKAGE_NAME.equals(
-                                                bp.getSourcePackageName())) {
-                                            if (!bp.isRemoved()) {
-                                                flags |= FLAG_PERMISSION_REVIEW_REQUIRED
-                                                        | FLAG_PERMISSION_REVOKED_COMPAT;
-                                                wasChanged = true;
-                                            }
-                                        }
-                                    }
-
-                                    if (!uidState.isPermissionGranted(bp.name)
-                                            && uidState.grantPermission(bp)) {
-                                        wasChanged = true;
-                                    }
-
-                                    // If legacy app always grant the permission but if restricted
-                                    // and not exempt take a note a restriction should be applied.
-                                    if (permissionPolicyInitialized
-                                            && (hardRestricted || softRestricted)
-                                                    && !restrictionExempt && !restrictionApplied) {
+                                    if (!restrictionApplied) {
                                         flags |= FLAG_PERMISSION_APPLY_RESTRICTION;
                                         wasChanged = true;
                                     }
                                 }
+                            // If soft restricted we allow holding in a restricted form
+                            } else if (permissionPolicyInitialized && softRestricted) {
+                                // Regardless if granted set the restriction flag as it
+                                // may affect app treatment based on this permission.
+                                if (!restrictionExempt && !restrictionApplied) {
+                                    flags |= FLAG_PERMISSION_APPLY_RESTRICTION;
+                                    wasChanged = true;
+                                }
+                            }
 
-                                // If unrestricted or restriction exempt, don't apply restriction.
-                                if (permissionPolicyInitialized) {
-                                    if (!(hardRestricted || softRestricted) || restrictionExempt) {
-                                        if (restrictionApplied) {
-                                            flags &= ~FLAG_PERMISSION_APPLY_RESTRICTION;
-                                            // Dropping restriction on a legacy app implies a review
-                                            if (!appSupportsRuntimePermissions) {
-                                                flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
-                                            }
-                                            wasChanged = true;
-                                        }
+                            // 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_REVOKED_COMPAT) != 0) {
+                                flags &= ~FLAG_PERMISSION_REVOKED_COMPAT;
+                                wasChanged = true;
+                            // Hard restricted permissions cannot be held.
+                            } else if (!permissionPolicyInitialized
+                                    || (!hardRestricted || restrictionExempt)) {
+                                if ((origPermState != null && origPermState.isGranted())
+                                        || legacyActivityRecognitionPermission != null) {
+                                    if (!uidState.grantPermission(bp)) {
+                                        wasChanged = true;
                                     }
                                 }
-
-                                if (wasChanged) {
-                                    updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
-                                }
-
-                                uidState.updatePermissionFlags(bp, MASK_PERMISSION_FLAGS_ALL,
-                                        flags);
-                            } break;
-
-                            default: {
-                                if (packageOfInterest == null
-                                        || packageOfInterest.equals(pkg.getPackageName())) {
-                                    if (DEBUG_PERMISSIONS) {
-                                        Slog.i(TAG, "Not granting permission " + perm
-                                                + " to package " + friendlyName
-                                                + " because it was previously installed without");
+                            }
+                        } else {
+                            if (origPermState == null) {
+                                // New permission
+                                if (PLATFORM_PACKAGE_NAME.equals(
+                                        bp.getPackageName())) {
+                                    if (!bp.isRemoved()) {
+                                        flags |= FLAG_PERMISSION_REVIEW_REQUIRED
+                                                | FLAG_PERMISSION_REVOKED_COMPAT;
+                                        wasChanged = true;
                                     }
                                 }
-                            } break;
+                            }
+
+                            if (!uidState.isPermissionGranted(bp.getName())
+                                    && uidState.grantPermission(bp)) {
+                                wasChanged = true;
+                            }
+
+                            // If legacy app always grant the permission but if restricted
+                            // and not exempt take a note a restriction should be applied.
+                            if (permissionPolicyInitialized
+                                    && (hardRestricted || softRestricted)
+                                            && !restrictionExempt && !restrictionApplied) {
+                                flags |= FLAG_PERMISSION_APPLY_RESTRICTION;
+                                wasChanged = true;
+                            }
                         }
+
+                        // If unrestricted or restriction exempt, don't apply restriction.
+                        if (permissionPolicyInitialized) {
+                            if (!(hardRestricted || softRestricted) || restrictionExempt) {
+                                if (restrictionApplied) {
+                                    flags &= ~FLAG_PERMISSION_APPLY_RESTRICTION;
+                                    // Dropping restriction on a legacy app implies a review
+                                    if (!appSupportsRuntimePermissions) {
+                                        flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
+                                    }
+                                    wasChanged = true;
+                                }
+                            }
+                        }
+
+                        if (wasChanged) {
+                            updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
+                        }
+
+                        uidState.updatePermissionFlags(bp, MASK_PERMISSION_FLAGS_ALL,
+                                flags);
                     } else {
                         if (DEBUG_PERMISSIONS) {
-                            boolean wasGranted = uidState.isPermissionGranted(bp.name);
+                            boolean wasGranted = uidState.isPermissionGranted(bp.getName());
                             if (wasGranted || bp.isAppOp()) {
                                 Slog.i(TAG, (wasGranted ? "Un-granting" : "Not granting")
                                         + " permission " + perm
@@ -2902,23 +2919,21 @@
                                         + ")");
                             }
                         }
-                        if (uidState.removePermissionState(bp.name)) {
+                        if (uidState.removePermissionState(bp.getName())) {
                             changedInstallPermission = true;
                         }
                     }
                 }
-            }
 
-            if ((changedInstallPermission || replace)
-                    && !userState.areInstallPermissionsFixed(ps.name)
-                    && !ps.isSystem() || ps.getPkgState().isUpdatedSystemApp()) {
-                // This is the first that we have heard about this package, so the
-                // permissions we have now selected are fixed until explicitly
-                // changed.
-                userState.setInstallPermissionsFixed(ps.name, true);
-            }
+                if ((changedInstallPermission || replace)
+                        && !userState.areInstallPermissionsFixed(ps.name)
+                        && !ps.isSystem() || ps.getPkgState().isUpdatedSystemApp()) {
+                    // This is the first that we have heard about this package, so the
+                    // permissions we have now selected are fixed until explicitly
+                    // changed.
+                    userState.setInstallPermissionsFixed(ps.name, true);
+                }
 
-            synchronized (mLock) {
                 updatedUserIds = revokePermissionsNoLongerImplicitLocked(uidState, pkg,
                         userId, updatedUserIds);
                 updatedUserIds = setInitialGrantForNewImplicitPermissionsLocked(origState,
@@ -2973,7 +2988,7 @@
         for (String permission : ps.getGrantedPermissions()) {
             if (!pkg.getImplicitPermissions().contains(permission)) {
                 BasePermission bp = mSettings.getPermissionLocked(permission);
-                if (bp.isRuntime()) {
+                if (bp != null && bp.isRuntime()) {
                     int flags = ps.getPermissionFlags(permission);
 
                     if ((flags & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED) != 0) {
@@ -3193,7 +3208,7 @@
                         + " to register permissions as one time.");
         Objects.requireNonNull(packageName);
 
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             getOneTimePermissionUserManager(userId).startPackageOneTimeSession(packageName,
                     timeoutMillis, importanceToResetTimer, importanceToKeepSessionAlive);
@@ -3209,7 +3224,7 @@
                         + " to remove permissions as one time.");
         Objects.requireNonNull(packageName);
 
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             getOneTimePermissionUserManager(userId).stopPackageOneTimeSession(packageName);
         } finally {
@@ -3241,7 +3256,7 @@
         return result;
     }
 
-    private boolean isNewPlatformPermissionForPackage(String perm, AndroidPackage pkg) {
+    private static boolean isNewPlatformPermissionForPackage(String perm, AndroidPackage pkg) {
         boolean allowed = false;
         final int NP = PackageParser.NEW_PERMISSIONS.length;
         for (int ip=0; ip<NP; ip++) {
@@ -3258,102 +3273,13 @@
         return allowed;
     }
 
-    /**
-     * Determines whether a package is whitelisted for a particular privapp permission.
-     *
-     * <p>Does NOT check whether the package is a privapp, just whether it's whitelisted.
-     *
-     * <p>This handles parent/child apps.
-     */
-    private boolean hasPrivappWhitelistEntry(String perm, AndroidPackage pkg) {
-        ArraySet<String> wlPermissions;
-        if (pkg.isVendor()) {
-            wlPermissions =
-                    SystemConfig.getInstance().getVendorPrivAppPermissions(pkg.getPackageName());
-        } else if (pkg.isProduct()) {
-            wlPermissions =
-                    SystemConfig.getInstance().getProductPrivAppPermissions(pkg.getPackageName());
-        } else if (pkg.isSystemExt()) {
-            wlPermissions =
-                    SystemConfig.getInstance().getSystemExtPrivAppPermissions(
-                            pkg.getPackageName());
-        } else {
-            wlPermissions = SystemConfig.getInstance().getPrivAppPermissions(pkg.getPackageName());
-        }
-
-        return wlPermissions != null && wlPermissions.contains(perm);
-    }
-
-    private boolean shouldGrantSignaturePermission(String perm, AndroidPackage pkg,
-            PackageSetting pkgSetting, BasePermission bp, UidPermissionState origPermissions) {
-        boolean oemPermission = bp.isOEM();
-        boolean vendorPrivilegedPermission = bp.isVendorPrivileged();
-        boolean privilegedPermission = bp.isPrivileged() || bp.isVendorPrivileged();
-        boolean privappPermissionsDisable =
-                RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_DISABLE;
-        boolean platformPermission = PLATFORM_PACKAGE_NAME.equals(bp.getSourcePackageName());
-        boolean platformPackage = PLATFORM_PACKAGE_NAME.equals(pkg.getPackageName());
-        if (!privappPermissionsDisable && privilegedPermission && pkg.isPrivileged()
-                && !platformPackage && platformPermission) {
-            if (!hasPrivappWhitelistEntry(perm, pkg)) {
-                // Only enforce whitelist this on boot
-                if (!mSystemReady
-                        // Updated system apps do not need to be whitelisted
-                        && !pkgSetting.getPkgState().isUpdatedSystemApp()) {
-                    ApexManager apexMgr = ApexManager.getInstance();
-                    String apexContainingPkg = apexMgr.getActiveApexPackageNameContainingPackage(
-                            pkg);
-
-                    // Apps that are in updated apexs' do not need to be whitelisted
-                    if (apexContainingPkg == null || apexMgr.isFactory(
-                            apexMgr.getPackageInfo(apexContainingPkg, MATCH_ACTIVE_PACKAGE))) {
-                        // it's only a reportable violation if the permission isn't explicitly
-                        // denied
-                        ArraySet<String> deniedPermissions = null;
-                        if (pkg.isVendor()) {
-                            deniedPermissions = SystemConfig.getInstance()
-                                    .getVendorPrivAppDenyPermissions(pkg.getPackageName());
-                        } else if (pkg.isProduct()) {
-                            deniedPermissions = SystemConfig.getInstance()
-                                    .getProductPrivAppDenyPermissions(pkg.getPackageName());
-                        } else if (pkg.isSystemExt()) {
-                            deniedPermissions = SystemConfig.getInstance()
-                                    .getSystemExtPrivAppDenyPermissions(pkg.getPackageName());
-                        } else {
-                            deniedPermissions = SystemConfig.getInstance()
-                                    .getPrivAppDenyPermissions(pkg.getPackageName());
-                        }
-                        final boolean permissionViolation =
-                                deniedPermissions == null || !deniedPermissions.contains(perm);
-                        if (permissionViolation) {
-                            Slog.w(TAG, "Privileged permission " + perm + " for package "
-                                    + pkg.getPackageName() + " (" + pkg.getPath()
-                                    + ") not in privapp-permissions whitelist");
-
-                            if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE) {
-                                if (mPrivappPermissionsViolations == null) {
-                                    mPrivappPermissionsViolations = new ArraySet<>();
-                                }
-                                mPrivappPermissionsViolations.add(
-                                        pkg.getPackageName() + " (" + pkg.getPath() + "): "
-                                                + perm);
-                            }
-                        } else {
-                            return false;
-                        }
-                    }
-                }
-                if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE) {
-                    return false;
-                }
-            }
-        }
+    private boolean shouldGrantSignaturePermission(@NonNull AndroidPackage pkg,
+            @NonNull PackageSetting pkgSetting, @NonNull BasePermission bp) {
         // expect single system package
         String systemPackageName = ArrayUtils.firstOrNull(mPackageManagerInt.getKnownPackageNames(
                 PackageManagerInternal.PACKAGE_SYSTEM, UserHandle.USER_SYSTEM));
         final AndroidPackage systemPackage =
                 mPackageManagerInt.getPackage(systemPackageName);
-
         // check if the package is allow to use this signature permission.  A package is allowed to
         // use a signature permission if:
         //     - it has the same set of signing certificates as the source package
@@ -3372,143 +3298,142 @@
                 || systemPackage.getSigningDetails().checkCapability(
                         pkg.getSigningDetails(),
                         PackageParser.SigningDetails.CertCapabilities.PERMISSION);
-        if (!allowed && (privilegedPermission || oemPermission)) {
-            if (pkg.isSystem()) {
-                // For updated system applications, a privileged/oem permission
-                // is granted only if it had been defined by the original application.
-                if (pkgSetting.getPkgState().isUpdatedSystemApp()) {
-                    final PackageSetting disabledPs = mPackageManagerInt
-                            .getDisabledSystemPackage(pkg.getPackageName());
-                    final AndroidPackage disabledPkg = disabledPs == null ? null : disabledPs.pkg;
-                    if (disabledPkg != null && isPackageRequestingPermission(disabledPkg, perm)
-                            && ((privilegedPermission && disabledPkg.isPrivileged())
-                                    || (oemPermission && canGrantOemPermission(disabledPkg,
-                                            perm)))) {
-                        allowed = true;
-                    }
-                } else {
-                    allowed = (privilegedPermission && pkg.isPrivileged())
-                            || (oemPermission && canGrantOemPermission(pkg, perm));
+        final boolean isVendorPrivilegedPermission = bp.isVendorPrivileged();
+        final boolean isPrivilegedPermission = bp.isPrivileged() || isVendorPrivilegedPermission;
+        final boolean isOemPermission = bp.isOEM();
+        if (!allowed && (isPrivilegedPermission || isOemPermission) && pkg.isSystem()) {
+            final String permissionName = bp.getName();
+            // For updated system applications, a privileged/oem permission
+            // is granted only if it had been defined by the original application.
+            if (pkgSetting.getPkgState().isUpdatedSystemApp()) {
+                final PackageSetting disabledPs = mPackageManagerInt
+                        .getDisabledSystemPackage(pkg.getPackageName());
+                final AndroidPackage disabledPkg = disabledPs == null ? null : disabledPs.pkg;
+                if (disabledPkg != null && disabledPkg.getRequestedPermissions().contains(
+                        permissionName)) {
+                    allowed = (isPrivilegedPermission && canGrantPrivilegedPermission(disabledPkg,
+                            true, bp)) || (isOemPermission && canGrantOemPermission(disabledPkg,
+                            permissionName));
                 }
-                // In any case, don't grant a privileged permission to privileged vendor apps, if
-                // the permission's protectionLevel does not have the extra 'vendorPrivileged'
-                // flag.
-                if (allowed && privilegedPermission &&
-                        !vendorPrivilegedPermission && pkg.isVendor()) {
-                   Slog.w(TAG, "Permission " + perm + " cannot be granted to privileged vendor apk "
-                           + pkg.getPackageName()
-                           + " because it isn't a 'vendorPrivileged' permission.");
-                   allowed = false;
-                }
+            } else {
+                allowed = (isPrivilegedPermission && canGrantPrivilegedPermission(pkg, false, bp))
+                        || (isOemPermission && canGrantOemPermission(pkg, permissionName));
+            }
+            // In any case, don't grant a privileged permission to privileged vendor apps, if
+            // the permission's protectionLevel does not have the extra 'vendorPrivileged'
+            // flag.
+            if (allowed && isPrivilegedPermission && !isVendorPrivilegedPermission
+                    && pkg.isVendor()) {
+                Slog.w(TAG, "Permission " + permissionName
+                        + " cannot be granted to privileged vendor apk " + pkg.getPackageName()
+                        + " because it isn't a 'vendorPrivileged' permission.");
+                allowed = false;
             }
         }
-        if (!allowed) {
-            if (!allowed
-                    && bp.isPre23()
-                    && pkg.getTargetSdkVersion() < Build.VERSION_CODES.M) {
-                // If this was a previously normal/dangerous permission that got moved
-                // to a system permission as part of the runtime permission redesign, then
-                // we still want to blindly grant it to old apps.
-                allowed = true;
-            }
-            // TODO (moltmann): The installer now shares the platforms signature. Hence it does not
-            //                  need a separate flag anymore. Hence we need to check which
-            //                  permissions are needed by the permission controller
-            if (!allowed && bp.isInstaller()
-                    && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
-                            PackageManagerInternal.PACKAGE_INSTALLER, UserHandle.USER_SYSTEM),
-                    pkg.getPackageName()) || ArrayUtils.contains(
-                            mPackageManagerInt.getKnownPackageNames(
-                                    PackageManagerInternal.PACKAGE_PERMISSION_CONTROLLER,
-                    UserHandle.USER_SYSTEM), pkg.getPackageName())) {
-                // If this permission is to be granted to the system installer and
-                // this app is an installer, then it gets the permission.
-                allowed = true;
-            }
-            if (!allowed && bp.isVerifier()
-                    && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
-                            PackageManagerInternal.PACKAGE_VERIFIER, UserHandle.USER_SYSTEM),
-                    pkg.getPackageName())) {
-                // If this permission is to be granted to the system verifier and
-                // this app is a verifier, then it gets the permission.
-                allowed = true;
-            }
-            if (!allowed && bp.isPreInstalled()
-                    && pkg.isSystem()) {
-                // Any pre-installed system app is allowed to get this permission.
-                allowed = true;
-            }
-            if (!allowed && bp.isDevelopment()) {
-                // For development permissions, a development permission
-                // is granted only if it was already granted.
-                allowed = origPermissions.isPermissionGranted(perm);
-            }
-            if (!allowed && bp.isSetup()
-                    && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
-                            PackageManagerInternal.PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM),
-                    pkg.getPackageName())) {
-                // If this permission is to be granted to the system setup wizard and
-                // this app is a setup wizard, then it gets the permission.
-                allowed = true;
-            }
-            if (!allowed && bp.isSystemTextClassifier()
-                    && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
-                            PackageManagerInternal.PACKAGE_SYSTEM_TEXT_CLASSIFIER,
-                    UserHandle.USER_SYSTEM), pkg.getPackageName())) {
-                // Special permissions for the system default text classifier.
-                allowed = true;
-            }
-            if (!allowed && bp.isConfigurator()
-                    && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
-                            PackageManagerInternal.PACKAGE_CONFIGURATOR,
-                    UserHandle.USER_SYSTEM), pkg.getPackageName())) {
-                // Special permissions for the device configurator.
-                allowed = true;
-            }
-            if (!allowed && bp.isWellbeing()
-                    && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
-                            PackageManagerInternal.PACKAGE_WELLBEING, UserHandle.USER_SYSTEM),
-                    pkg.getPackageName())) {
-                // Special permission granted only to the OEM specified wellbeing app
-                allowed = true;
-            }
-            if (!allowed && bp.isDocumenter()
-                    && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
-                            PackageManagerInternal.PACKAGE_DOCUMENTER, UserHandle.USER_SYSTEM),
-                    pkg.getPackageName())) {
-                // If this permission is to be granted to the documenter and
-                // this app is the documenter, then it gets the permission.
-                allowed = true;
-            }
-            if (!allowed && bp.isIncidentReportApprover()
-                    && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
-                            PackageManagerInternal.PACKAGE_INCIDENT_REPORT_APPROVER,
-                    UserHandle.USER_SYSTEM), pkg.getPackageName())) {
-                // If this permission is to be granted to the incident report approver and
-                // this app is the incident report approver, then it gets the permission.
-                allowed = true;
-            }
-            if (!allowed && bp.isAppPredictor()
-                    && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
-                            PackageManagerInternal.PACKAGE_APP_PREDICTOR, UserHandle.USER_SYSTEM),
-                    pkg.getPackageName())) {
-                // Special permissions for the system app predictor.
-                allowed = true;
-            }
-            if (!allowed && bp.isCompanion()
-                    && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
-                        PackageManagerInternal.PACKAGE_COMPANION, UserHandle.USER_SYSTEM),
-                    pkg.getPackageName())) {
-                // Special permissions for the system companion device manager.
-                allowed = true;
-            }
-            if (!allowed && bp.isRetailDemo()
-                    && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
-                            PackageManagerInternal.PACKAGE_RETAIL_DEMO, UserHandle.USER_SYSTEM),
-                    pkg.getPackageName()) && isProfileOwner(pkg.getUid())) {
-                // Special permission granted only to the OEM specified retail demo app
-                allowed = true;
-            }
+        if (!allowed && bp.isPre23() && pkg.getTargetSdkVersion() < Build.VERSION_CODES.M) {
+            // If this was a previously normal/dangerous permission that got moved
+            // to a system permission as part of the runtime permission redesign, then
+            // we still want to blindly grant it to old apps.
+            allowed = true;
+        }
+        // TODO (moltmann): The installer now shares the platforms signature. Hence it does not
+        //                  need a separate flag anymore. Hence we need to check which
+        //                  permissions are needed by the permission controller
+        if (!allowed && bp.isInstaller()
+                && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+                        PackageManagerInternal.PACKAGE_INSTALLER, UserHandle.USER_SYSTEM),
+                pkg.getPackageName()) || ArrayUtils.contains(
+                        mPackageManagerInt.getKnownPackageNames(
+                                PackageManagerInternal.PACKAGE_PERMISSION_CONTROLLER,
+                UserHandle.USER_SYSTEM), pkg.getPackageName())) {
+            // If this permission is to be granted to the system installer and
+            // this app is an installer, then it gets the permission.
+            allowed = true;
+        }
+        if (!allowed && bp.isVerifier()
+                && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+                        PackageManagerInternal.PACKAGE_VERIFIER, UserHandle.USER_SYSTEM),
+                pkg.getPackageName())) {
+            // If this permission is to be granted to the system verifier and
+            // this app is a verifier, then it gets the permission.
+            allowed = true;
+        }
+        if (!allowed && bp.isPreInstalled()
+                && pkg.isSystem()) {
+            // Any pre-installed system app is allowed to get this permission.
+            allowed = true;
+        }
+        // Deferred to be checked under permission data lock inside restorePermissionState().
+        //if (!allowed && bp.isDevelopment()) {
+        //    // For development permissions, a development permission
+        //    // is granted only if it was already granted.
+        //    allowed = origPermissions.isPermissionGranted(permissionName);
+        //}
+        if (!allowed && bp.isSetup()
+                && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+                        PackageManagerInternal.PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM),
+                pkg.getPackageName())) {
+            // If this permission is to be granted to the system setup wizard and
+            // this app is a setup wizard, then it gets the permission.
+            allowed = true;
+        }
+        if (!allowed && bp.isSystemTextClassifier()
+                && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+                        PackageManagerInternal.PACKAGE_SYSTEM_TEXT_CLASSIFIER,
+                UserHandle.USER_SYSTEM), pkg.getPackageName())) {
+            // Special permissions for the system default text classifier.
+            allowed = true;
+        }
+        if (!allowed && bp.isConfigurator()
+                && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+                        PackageManagerInternal.PACKAGE_CONFIGURATOR,
+                UserHandle.USER_SYSTEM), pkg.getPackageName())) {
+            // Special permissions for the device configurator.
+            allowed = true;
+        }
+        if (!allowed && bp.isWellbeing()
+                && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+                        PackageManagerInternal.PACKAGE_WELLBEING, UserHandle.USER_SYSTEM),
+                pkg.getPackageName())) {
+            // Special permission granted only to the OEM specified wellbeing app
+            allowed = true;
+        }
+        if (!allowed && bp.isDocumenter()
+                && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+                        PackageManagerInternal.PACKAGE_DOCUMENTER, UserHandle.USER_SYSTEM),
+                pkg.getPackageName())) {
+            // If this permission is to be granted to the documenter and
+            // this app is the documenter, then it gets the permission.
+            allowed = true;
+        }
+        if (!allowed && bp.isIncidentReportApprover()
+                && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+                        PackageManagerInternal.PACKAGE_INCIDENT_REPORT_APPROVER,
+                UserHandle.USER_SYSTEM), pkg.getPackageName())) {
+            // If this permission is to be granted to the incident report approver and
+            // this app is the incident report approver, then it gets the permission.
+            allowed = true;
+        }
+        if (!allowed && bp.isAppPredictor()
+                && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+                        PackageManagerInternal.PACKAGE_APP_PREDICTOR, UserHandle.USER_SYSTEM),
+                pkg.getPackageName())) {
+            // Special permissions for the system app predictor.
+            allowed = true;
+        }
+        if (!allowed && bp.isCompanion()
+                && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+                    PackageManagerInternal.PACKAGE_COMPANION, UserHandle.USER_SYSTEM),
+                pkg.getPackageName())) {
+            // Special permissions for the system companion device manager.
+            allowed = true;
+        }
+        if (!allowed && bp.isRetailDemo()
+                && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+                        PackageManagerInternal.PACKAGE_RETAIL_DEMO, UserHandle.USER_SYSTEM),
+                pkg.getPackageName()) && isProfileOwner(pkg.getUid())) {
+            // Special permission granted only to the OEM specified retail demo app
+            allowed = true;
         }
         return allowed;
     }
@@ -3525,18 +3450,90 @@
 
     @Nullable
     private PackageSetting getSourcePackageSetting(@NonNull BasePermission bp) {
-        final String sourcePackageName = bp.getSourcePackageName();
+        final String sourcePackageName = bp.getPackageName();
         return mPackageManagerInt.getPackageSetting(sourcePackageName);
     }
 
-    private static boolean isProfileOwner(int uid) {
-        DevicePolicyManagerInternal dpmInternal =
-                LocalServices.getService(DevicePolicyManagerInternal.class);
-        if (dpmInternal != null) {
-            return dpmInternal
-                    .isActiveAdminWithPolicy(uid, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+    private boolean canGrantPrivilegedPermission(@NonNull AndroidPackage pkg,
+            boolean isUpdatedSystemApp, @NonNull BasePermission permission) {
+        if (!pkg.isPrivileged()) {
+            return false;
         }
-        return false;
+        final boolean isPlatformPermission = PLATFORM_PACKAGE_NAME.equals(
+                permission.getPackageName());
+        if (!isPlatformPermission) {
+            return true;
+        }
+        if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_DISABLE) {
+            return true;
+        }
+        final String permissionName = permission.getName();
+        if (isInSystemConfigPrivAppPermissions(pkg, permissionName)) {
+            return true;
+        }
+        // Only enforce whitelist this on boot
+        if (!mSystemReady
+                // Updated system apps do not need to be whitelisted
+                && !isUpdatedSystemApp) {
+            final ApexManager apexManager = ApexManager.getInstance();
+            final String packageName = pkg.getPackageName();
+            final String containingApexPackageName =
+                    apexManager.getActiveApexPackageNameContainingPackage(packageName);
+            final boolean isInUpdatedApex = containingApexPackageName != null
+                    && !apexManager.isFactory(apexManager.getPackageInfo(containingApexPackageName,
+                    MATCH_ACTIVE_PACKAGE));
+            // Apps that are in updated apexs' do not need to be whitelisted
+            if (!isInUpdatedApex) {
+                // it's only a reportable violation if the permission isn't explicitly
+                // denied
+                if (isInSystemConfigPrivAppDenyPermissions(pkg, permissionName)) {
+                    return false;
+                }
+                Slog.w(TAG, "Privileged permission " + permissionName + " for package "
+                        + packageName + " (" + pkg.getPath()
+                        + ") not in privapp-permissions whitelist");
+                if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE) {
+                    if (mPrivappPermissionsViolations == null) {
+                        mPrivappPermissionsViolations = new ArraySet<>();
+                    }
+                    mPrivappPermissionsViolations.add(packageName + " (" + pkg.getPath() + "): "
+                            + permissionName);
+                }
+            }
+        }
+        return !RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE;
+    }
+
+    private boolean isInSystemConfigPrivAppPermissions(@NonNull AndroidPackage pkg,
+            @NonNull String permission) {
+        final SystemConfig systemConfig = SystemConfig.getInstance();
+        final Set<String> permissions;
+        if (pkg.isVendor()) {
+            permissions = systemConfig.getVendorPrivAppPermissions(pkg.getPackageName());
+        } else if (pkg.isProduct()) {
+            permissions = systemConfig.getProductPrivAppPermissions(pkg.getPackageName());
+        } else if (pkg.isSystemExt()) {
+            permissions = systemConfig.getSystemExtPrivAppPermissions(pkg.getPackageName());
+        } else {
+            permissions = systemConfig.getPrivAppPermissions(pkg.getPackageName());
+        }
+        return permissions != null && permissions.contains(permission);
+    }
+
+    private boolean isInSystemConfigPrivAppDenyPermissions(@NonNull AndroidPackage pkg,
+            @NonNull String permission) {
+        final SystemConfig systemConfig = SystemConfig.getInstance();
+        final Set<String> permissions;
+        if (pkg.isVendor()) {
+            permissions = systemConfig.getVendorPrivAppDenyPermissions(pkg.getPackageName());
+        } else if (pkg.isProduct()) {
+            permissions = systemConfig.getProductPrivAppDenyPermissions(pkg.getPackageName());
+        } else if (pkg.isSystemExt()) {
+            permissions = systemConfig.getSystemExtPrivAppDenyPermissions(pkg.getPackageName());
+        } else {
+            permissions = systemConfig.getPrivAppDenyPermissions(pkg.getPackageName());
+        }
+        return permissions != null && permissions.contains(permission);
     }
 
     private static boolean canGrantOemPermission(AndroidPackage pkg, String permission) {
@@ -3553,6 +3550,16 @@
         return Boolean.TRUE == granted;
     }
 
+    private static boolean isProfileOwner(int uid) {
+        DevicePolicyManagerInternal dpmInternal =
+                LocalServices.getService(DevicePolicyManagerInternal.class);
+        //TODO(b/169395065) Figure out if this flow makes sense in Device Owner mode.
+        if (dpmInternal != null) {
+            return dpmInternal.isActiveProfileOwner(uid) || dpmInternal.isActiveDeviceOwner(uid);
+        }
+        return false;
+    }
+
     private boolean isPermissionsReviewRequired(@NonNull AndroidPackage pkg,
             @UserIdInt int userId) {
         // Permission review applies only to apps not supporting the new permission model.
@@ -3561,24 +3568,15 @@
         }
 
         // Legacy apps have the permission and get user consent on launch.
-        final UidPermissionState uidState = getUidState(pkg, userId);
-        if (uidState == null) {
-            Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
-                    + userId);
-            return false;
-        }
-        return uidState.isPermissionReviewRequired();
-    }
-
-    private boolean isPackageRequestingPermission(AndroidPackage pkg, String permission) {
-        final int permCount = pkg.getRequestedPermissions().size();
-        for (int j = 0; j < permCount; j++) {
-            String requestedPermission = pkg.getRequestedPermissions().get(j);
-            if (permission.equals(requestedPermission)) {
-                return true;
+        synchronized (mLock) {
+            final UidPermissionState uidState = getUidStateLocked(pkg, userId);
+            if (uidState == null) {
+                Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
+                        + userId);
+                return false;
             }
+            return uidState.isPermissionReviewRequired();
         }
-        return false;
     }
 
     private void grantRequestedRuntimePermissions(AndroidPackage pkg, int[] userIds,
@@ -3591,13 +3589,6 @@
 
     private void grantRequestedRuntimePermissionsForUser(AndroidPackage pkg, int userId,
             String[] grantedPermissions, int callingUid, PermissionCallback callback) {
-        final UidPermissionState uidState = getUidState(pkg, userId);
-        if (uidState == null) {
-            Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
-                    + userId);
-            return;
-        }
-
         final int immutableFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED
                 | PackageManager.FLAG_PERMISSION_POLICY_FIXED;
 
@@ -3619,7 +3610,8 @@
                     && (supportsRuntimePermissions || !bp.isRuntimeOnly())
                     && (grantedPermissions == null
                            || ArrayUtils.contains(grantedPermissions, permission))) {
-                final int flags = uidState.getPermissionFlags(permission);
+                final int flags = getPermissionFlagsInternal(permission, pkg.getPackageName(),
+                        callingUid, userId);
                 if (supportsRuntimePermissions) {
                     // Installer cannot change immutable permissions.
                     if ((flags & immutableFlags) == 0) {
@@ -3647,12 +3639,6 @@
 
         for (int i = 0; i < userIds.length; i++) {
             int userId = userIds[i];
-            final UidPermissionState uidState = getUidState(pkg, userId);
-            if (uidState == null) {
-                Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
-                        + userId);
-                continue;
-            }
 
             for (int j = 0; j < permissionCount; j++) {
                 final String permissionName = pkg.getRequestedPermissions().get(j);
@@ -3663,14 +3649,26 @@
                     continue;
                 }
 
-                if (uidState.isPermissionGranted(permissionName)) {
+                final boolean isGranted;
+                synchronized (mLock) {
+                    final UidPermissionState uidState = getUidStateLocked(pkg, userId);
+                    if (uidState == null) {
+                        Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()
+                                + " and user " + userId);
+                        continue;
+                    }
+                    isGranted = uidState.isPermissionGranted(permissionName);
+                }
+
+                if (isGranted) {
                     if (oldGrantedRestrictedPermissions.get(userId) == null) {
                         oldGrantedRestrictedPermissions.put(userId, new ArraySet<>());
                     }
                     oldGrantedRestrictedPermissions.get(userId).add(permissionName);
                 }
 
-                final int oldFlags = uidState.getPermissionFlags(permissionName);
+                final int oldFlags = getPermissionFlagsInternal(permissionName,
+                        pkg.getPackageName(), callingUid, userId);
 
                 int newFlags = oldFlags;
                 int mask = 0;
@@ -3734,7 +3732,6 @@
                 // as whitelisting trumps policy i.e. policy cannot grant a non
                 // grantable permission.
                 if ((oldFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
-                    final boolean isGranted = uidState.isPermissionGranted(permissionName);
                     if (!isWhitelisted && isGranted) {
                         mask |= PackageManager.FLAG_PERMISSION_POLICY_FIXED;
                         newFlags &= ~PackageManager.FLAG_PERMISSION_POLICY_FIXED;
@@ -3768,15 +3765,19 @@
 
                 final int oldGrantedCount = oldPermsForUser.size();
                 for (int j = 0; j < oldGrantedCount; j++) {
-                    final String permission = oldPermsForUser.valueAt(j);
+                    final String permissionName = oldPermsForUser.valueAt(j);
                     // Sometimes we create a new permission state instance during update.
-                    final UidPermissionState newUidState = getUidState(pkg, userId);
-                    if (newUidState == null) {
-                        Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()
-                                + " and user " + userId);
-                        continue;
+                    final boolean isGranted;
+                    synchronized (mLock) {
+                        final UidPermissionState uidState = getUidStateLocked(pkg, userId);
+                        if (uidState == null) {
+                            Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()
+                                    + " and user " + userId);
+                            continue;
+                        }
+                        isGranted = uidState.isPermissionGranted(permissionName);
                     }
-                    if (!newUidState.isPermissionGranted(permission)) {
+                    if (!isGranted) {
                         callback.onPermissionRevoked(pkg.getUid(), userId, null);
                         break;
                     }
@@ -3825,13 +3826,6 @@
                 continue;
             }
 
-            UidPermissionState uidState = getUidState(deletedPs.pkg, userId);
-            if (uidState == null) {
-                Slog.e(TAG, "Missing permissions state for " + deletedPs.pkg.getPackageName()
-                        + " and user " + userId);
-                continue;
-            }
-
             PackageSetting disabledPs = mPackageManagerInt.getDisabledSystemPackage(
                     deletedPs.pkg.getPackageName());
 
@@ -3850,10 +3844,19 @@
                 }
             }
 
-            // TODO(zhanghai): Why are we only killing the UID when GIDs changed, instead of any
-            //  permission change?
-            if (uidState.removePermissionState(bp.name) && bp.hasGids()) {
-                affectedUserId = userId;
+            synchronized (mLock) {
+                UidPermissionState uidState = getUidStateLocked(deletedPs.pkg, userId);
+                if (uidState == null) {
+                    Slog.e(TAG, "Missing permissions state for " + deletedPs.pkg.getPackageName()
+                            + " and user " + userId);
+                    continue;
+                }
+
+                // TODO(zhanghai): Why are we only killing the UID when GIDs changed, instead of any
+                //  permission change?
+                if (uidState.removePermissionState(bp.getName()) && bp.hasGids()) {
+                    affectedUserId = userId;
+                }
             }
         }
 
@@ -3892,7 +3895,8 @@
             if (!usedPermissions.contains(permissionState.getName())) {
                 BasePermission bp = mSettings.getPermissionLocked(permissionState.getName());
                 if (bp != null) {
-                    if (uidState.removePermissionState(bp.name) && permissionState.isRuntime()) {
+                    if (uidState.removePermissionState(bp.getName())
+                            && permissionState.isRuntime()) {
                         runtimePermissionChanged = true;
                     }
                 }
@@ -3963,9 +3967,9 @@
                 // Only system declares background permissions, hence mapping does never change.
                 mBackgroundPermissions = new ArrayMap<>();
                 for (BasePermission bp : mSettings.getAllPermissionsLocked()) {
-                    if (bp.perm != null && bp.perm.getBackgroundPermission() != null) {
-                        String fgPerm = bp.name;
-                        String bgPerm = bp.perm.getBackgroundPermission();
+                    if (bp.getBackgroundPermission() != null) {
+                        String fgPerm = bp.getName();
+                        String bgPerm = bp.getBackgroundPermission();
 
                         List<String> fgPerms = mBackgroundPermissions.get(bgPerm);
                         if (fgPerms == null) {
@@ -4114,7 +4118,7 @@
                 if (bp.isDynamic()) {
                     bp.updateDynamicPermission(mSettings.mPermissionTrees.values());
                 }
-                if (!packageName.equals(bp.getSourcePackageName())) {
+                if (!packageName.equals(bp.getPackageName())) {
                     // Not checking sourcePackageSetting because it can be null when
                     // the permission source package is the target package and the target package is
                     // being uninstalled,
@@ -4135,7 +4139,7 @@
                 // From all other packages
                 if (pkg == null || !hasPermission(pkg, bp.getName())) {
                     Slog.i(TAG, "Removing permission " + bp.getName()
-                            + " that used to be declared by " + bp.getSourcePackageName());
+                            + " that used to be declared by " + bp.getPackageName());
                     if (bp.isRuntime()) {
                         final int[] userIds = mUserManagerInt.getUserIds();
                         final int numUserIds = userIds.length;
@@ -4148,14 +4152,17 @@
                     } else {
                         mPackageManagerInt.forEachPackage(p -> {
                             final int[] userIds = mUserManagerInt.getUserIds();
-                            for (final int userId : userIds) {
-                                final UidPermissionState uidState = getUidState(p, userId);
-                                if (uidState == null) {
-                                    Slog.e(TAG, "Missing permissions state for "
-                                            + p.getPackageName() + " and user " + userId);
-                                    return;
+                            synchronized (mLock) {
+                                for (final int userId : userIds) {
+                                    final UidPermissionState uidState = getUidStateLocked(p,
+                                            userId);
+                                    if (uidState == null) {
+                                        Slog.e(TAG, "Missing permissions state for "
+                                                + p.getPackageName() + " and user " + userId);
+                                        continue;
+                                    }
+                                    uidState.removePermissionState(bp.getName());
                                 }
-                                uidState.removePermissionState(bp.name);
                             }
                         });
                     }
@@ -4163,16 +4170,16 @@
                     continue;
                 }
                 final AndroidPackage sourcePkg =
-                        mPackageManagerInt.getPackage(bp.getSourcePackageName());
+                        mPackageManagerInt.getPackage(bp.getPackageName());
                 final PackageSetting sourcePs =
                         (PackageSetting) mPackageManagerInt.getPackageSetting(
-                                bp.getSourcePackageName());
+                                bp.getPackageName());
                 synchronized (mLock) {
                     if (sourcePkg != null && sourcePs != null) {
                         continue;
                     }
                     Slog.w(TAG, "Removing dangling permission: " + bp.getName()
-                            + " from package " + bp.getSourcePackageName());
+                            + " from package " + bp.getPackageName());
                     mSettings.removePermissionLocked(bp.getName());
                 }
             }
@@ -4244,7 +4251,7 @@
             final Iterator<BasePermission> it = mSettings.mPermissionTrees.values().iterator();
             while (it.hasNext()) {
                 final BasePermission bp = it.next();
-                if (!packageName.equals(bp.getSourcePackageName())) {
+                if (!packageName.equals(bp.getPackageName())) {
                     // Not checking sourcePackageSetting because it can be null when
                     // the permission source package is the target package and the target package is
                     // being uninstalled,
@@ -4255,7 +4262,7 @@
                 changed = true;
                 if (pkg == null || !hasPermission(pkg, bp.getName())) {
                     Slog.i(TAG, "Removing permission tree " + bp.getName()
-                            + " that used to be declared by " + bp.getSourcePackageName());
+                            + " that used to be declared by " + bp.getPackageName());
                     it.remove();
                 }
                 if (needsUpdate == null) {
@@ -4267,16 +4274,16 @@
         if (needsUpdate != null) {
             for (final BasePermission bp : needsUpdate) {
                 final AndroidPackage sourcePkg =
-                        mPackageManagerInt.getPackage(bp.getSourcePackageName());
+                        mPackageManagerInt.getPackage(bp.getPackageName());
                 final PackageSetting sourcePs =
                         (PackageSetting) mPackageManagerInt.getPackageSetting(
-                                bp.getSourcePackageName());
+                                bp.getPackageName());
                 synchronized (mLock) {
                     if (sourcePkg != null && sourcePs != null) {
                         continue;
                     }
                     Slog.w(TAG, "Removing dangling permission tree: " + bp.getName()
-                            + " from package " + bp.getSourcePackageName());
+                            + " from package " + bp.getPackageName());
                     mSettings.removePermissionLocked(bp.getName());
                 }
             }
@@ -4563,26 +4570,24 @@
     }
 
     @Nullable
-    private UidPermissionState getUidState(@NonNull PackageSetting ps,
+    private UidPermissionState getUidStateLocked(@NonNull PackageSetting ps,
             @UserIdInt int userId) {
-        return getUidState(ps.getAppId(), userId);
+        return getUidStateLocked(ps.getAppId(), userId);
     }
 
     @Nullable
-    private UidPermissionState getUidState(@NonNull AndroidPackage pkg,
+    private UidPermissionState getUidStateLocked(@NonNull AndroidPackage pkg,
             @UserIdInt int userId) {
-        return getUidState(pkg.getUid(), userId);
+        return getUidStateLocked(pkg.getUid(), userId);
     }
 
     @Nullable
-    private UidPermissionState getUidState(int appId, @UserIdInt int userId) {
-        synchronized (mLock) {
-            final UserPermissionState userState = mState.getUserState(userId);
-            if (userState == null) {
-                return null;
-            }
-            return userState.getUidState(appId);
+    private UidPermissionState getUidStateLocked(@AppIdInt int appId, @UserIdInt int userId) {
+        final UserPermissionState userState = mState.getUserState(userId);
+        if (userState == null) {
+            return null;
         }
+        return userState.getUidState(appId);
     }
 
     private void removeAppIdState(@AppIdInt int appId) {
@@ -4599,7 +4604,7 @@
         final int[] userIds = getAllUserIds();
         mPackageManagerInt.forEachPackageSetting(ps -> {
             final int appId = ps.getAppId();
-            final AppIdPermissionState appIdState = ps.getPermissionsState();
+            final LegacyPermissionState legacyState = ps.getLegacyPermissionState();
 
             synchronized (mLock) {
                 for (final int userId : userIds) {
@@ -4608,30 +4613,33 @@
                     userState.setInstallPermissionsFixed(ps.name, ps.areInstallPermissionsFixed());
                     final UidPermissionState uidState = userState.getOrCreateUidState(appId);
                     uidState.reset();
-                    uidState.setMissing(appIdState.isMissing(userId));
+                    uidState.setMissing(legacyState.isMissing(userId));
                     readStateFromPermissionStates(uidState,
-                            appIdState.getInstallPermissionStates());
+                            legacyState.getInstallPermissionStates());
                     readStateFromPermissionStates(uidState,
-                            appIdState.getRuntimePermissionStates(userId));
+                            legacyState.getRuntimePermissionStates(userId));
                 }
             }
         });
     }
 
     private void readStateFromPermissionStates(@NonNull UidPermissionState uidState,
-            @NonNull Collection<AppIdPermissionState.PermissionState> permissionStates) {
-        for (final AppIdPermissionState.PermissionState permissionState : permissionStates) {
+            @NonNull Collection<LegacyPermissionState.PermissionState> permissionStates) {
+        for (final LegacyPermissionState.PermissionState permissionState : permissionStates) {
             uidState.putPermissionState(permissionState.getPermission(),
                     permissionState.isGranted(), permissionState.getFlags());
         }
     }
 
     private void writeStateToPackageSettings() {
-        final int[] userIds = mState.getUserIds();
+        final int[] userIds;
+        synchronized (mLock) {
+            userIds = mState.getUserIds();
+        }
         mPackageManagerInt.forEachPackageSetting(ps -> {
             ps.setInstallPermissionsFixed(false);
-            final AppIdPermissionState appIdState = ps.getPermissionsState();
-            appIdState.reset();
+            final LegacyPermissionState legacyState = ps.getLegacyPermissionState();
+            legacyState.reset();
             final int appId = ps.getAppId();
 
             synchronized (mLock) {
@@ -4653,21 +4661,21 @@
                         continue;
                     }
 
-                    appIdState.setMissing(uidState.isMissing(), userId);
+                    legacyState.setMissing(uidState.isMissing(), userId);
                     final List<PermissionState> permissionStates = uidState.getPermissionStates();
                     final int permissionStatesSize = permissionStates.size();
                     for (int i = 0; i < permissionStatesSize; i++) {
                         final PermissionState permissionState = permissionStates.get(i);
 
-                        final AppIdPermissionState.PermissionState legacyPermissionState =
-                                new AppIdPermissionState.PermissionState(
+                        final LegacyPermissionState.PermissionState legacyPermissionState =
+                                new LegacyPermissionState.PermissionState(
                                         permissionState.getPermission(),
                                         permissionState.isGranted(), permissionState.getFlags());
                         if (permissionState.isRuntime()) {
-                            appIdState.putRuntimePermissionState(legacyPermissionState,
+                            legacyState.putRuntimePermissionState(legacyPermissionState,
                                     userId);
                         } else {
-                            appIdState.putInstallPermissionState(legacyPermissionState);
+                            legacyState.putInstallPermissionState(legacyPermissionState);
                         }
                     }
                 }
@@ -4676,45 +4684,51 @@
     }
 
     @NonNull
-    private AppIdPermissionState getAppIdPermissionState(@AppIdInt int appId) {
-        final AppIdPermissionState appIdState = new AppIdPermissionState();
-        final int[] userIds = mState.getUserIds();
-        for (final int userId : userIds) {
-            final UidPermissionState uidState = getUidState(appId, userId);
-            if (uidState == null) {
-                Slog.e(TAG, "Missing permissions state for app ID " + appId + " and user ID "
-                        + userId);
-                continue;
-            }
+    private LegacyPermissionState getLegacyPermissionState(@AppIdInt int appId) {
+        final LegacyPermissionState legacyState = new LegacyPermissionState();
+        synchronized (mLock) {
+            final int[] userIds = mState.getUserIds();
+            for (final int userId : userIds) {
+                final UidPermissionState uidState = getUidStateLocked(appId, userId);
+                if (uidState == null) {
+                    Slog.e(TAG, "Missing permissions state for app ID " + appId + " and user ID "
+                            + userId);
+                    continue;
+                }
 
-            final List<PermissionState> permissionStates = uidState.getPermissionStates();
-            final int permissionStatesSize = permissionStates.size();
-            for (int i = 0; i < permissionStatesSize; i++) {
-                final PermissionState permissionState = permissionStates.get(i);
+                final List<PermissionState> permissionStates = uidState.getPermissionStates();
+                final int permissionStatesSize = permissionStates.size();
+                for (int i = 0; i < permissionStatesSize; i++) {
+                    final PermissionState permissionState = permissionStates.get(i);
 
-                final AppIdPermissionState.PermissionState legacyPermissionState =
-                        new AppIdPermissionState.PermissionState(permissionState.getPermission(),
-                                permissionState.isGranted(), permissionState.getFlags());
-                if (permissionState.isRuntime()) {
-                    appIdState.putRuntimePermissionState(legacyPermissionState, userId);
-                } else if (userId == UserHandle.USER_SYSTEM) {
-                    appIdState.putInstallPermissionState(legacyPermissionState);
+                    final LegacyPermissionState.PermissionState legacyPermissionState =
+                            new LegacyPermissionState.PermissionState(
+                                    permissionState.getPermission(), permissionState.isGranted(),
+                                    permissionState.getFlags());
+                    if (permissionState.isRuntime()) {
+                        legacyState.putRuntimePermissionState(legacyPermissionState, userId);
+                    } else if (userId == UserHandle.USER_SYSTEM) {
+                        legacyState.putInstallPermissionState(legacyPermissionState);
+                    }
                 }
             }
         }
-        return appIdState;
+        return legacyState;
     }
 
     @NonNull
     private int[] getGidsForUid(int uid) {
         final int appId = UserHandle.getAppId(uid);
         final int userId = UserHandle.getUserId(uid);
-        final UidPermissionState uidState = getUidState(appId, userId);
-        if (uidState == null) {
-            Slog.e(TAG, "Missing permissions state for app ID " + appId + " and user ID " + userId);
-            return EMPTY_INT_ARRAY;
+        synchronized (mLock) {
+            final UidPermissionState uidState = getUidStateLocked(appId, userId);
+            if (uidState == null) {
+                Slog.e(TAG, "Missing permissions state for app ID " + appId + " and user ID "
+                        + userId);
+                return EMPTY_INT_ARRAY;
+            }
+            return uidState.computeGids(mGlobalGids, userId);
         }
-        return uidState.computeGids(mGlobalGids, userId);
     }
 
     private class PermissionManagerServiceInternalImpl extends PermissionManagerServiceInternal {
@@ -4882,9 +4896,8 @@
                 for (int i = 0; i < numTotalPermissions; i++) {
                     BasePermission bp = mSettings.mPermissions.valueAt(i);
 
-                    if (bp.perm != null && bp.perm.getProtection() == protection) {
-                        matchingPermissions.add(
-                                PackageInfoUtils.generatePermissionInfo(bp.perm, 0));
+                    if (bp.getProtection() == protection) {
+                        matchingPermissions.add(bp.generatePermissionInfo(0));
                     }
                 }
             }
@@ -4903,10 +4916,8 @@
                 for (int i = 0; i < numTotalPermissions; i++) {
                     BasePermission bp = mSettings.mPermissions.valueAt(i);
 
-                    if (bp.perm != null && (bp.perm.getProtectionFlags() & protectionFlags)
-                            == protectionFlags) {
-                        matchingPermissions.add(
-                                PackageInfoUtils.generatePermissionInfo(bp.perm, 0));
+                    if ((bp.getProtectionFlags() & protectionFlags) == protectionFlags) {
+                        matchingPermissions.add(bp.generatePermissionInfo(0));
                     }
                 }
             }
@@ -5111,8 +5122,8 @@
         }
 
         @NonNull
-        public AppIdPermissionState getAppIdPermissionState(@AppIdInt int appId) {
-            return PermissionManagerService.this.getAppIdPermissionState(appId);
+        public LegacyPermissionState getLegacyPermissionState(@AppIdInt int appId) {
+            return PermissionManagerService.this.getLegacyPermissionState(appId);
         }
 
         @NonNull
diff --git a/services/core/java/com/android/server/pm/permission/UidPermissionState.java b/services/core/java/com/android/server/pm/permission/UidPermissionState.java
index 82ab954..9727a54 100644
--- a/services/core/java/com/android/server/pm/permission/UidPermissionState.java
+++ b/services/core/java/com/android/server/pm/permission/UidPermissionState.java
@@ -24,8 +24,6 @@
 import android.util.ArraySet;
 import android.util.IntArray;
 
-import com.android.internal.annotations.GuardedBy;
-
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -35,29 +33,23 @@
  * Permission state for a UID.
  */
 public final class UidPermissionState {
-    @NonNull
-    private final Object mLock = new Object();
-
     private boolean mMissing;
 
-    @GuardedBy("mLock")
     @Nullable
     private ArrayMap<String, PermissionState> mPermissions;
 
     public UidPermissionState() {}
 
     public UidPermissionState(@NonNull UidPermissionState other) {
-        synchronized (mLock) {
-            mMissing = other.mMissing;
+        mMissing = other.mMissing;
 
-            if (other.mPermissions != null) {
-                mPermissions = new ArrayMap<>();
-                final int permissionsSize = other.mPermissions.size();
-                for (int i = 0; i < permissionsSize; i++) {
-                    final String name = other.mPermissions.keyAt(i);
-                    final PermissionState permissionState = other.mPermissions.valueAt(i);
-                    mPermissions.put(name, new PermissionState(permissionState));
-                }
+        if (other.mPermissions != null) {
+            mPermissions = new ArrayMap<>();
+            final int permissionsSize = other.mPermissions.size();
+            for (int i = 0; i < permissionsSize; i++) {
+                final String name = other.mPermissions.keyAt(i);
+                final PermissionState permissionState = other.mPermissions.valueAt(i);
+                mPermissions.put(name, new PermissionState(permissionState));
             }
         }
     }
@@ -66,11 +58,9 @@
      * Reset the internal state of this object.
      */
     public void reset() {
-        synchronized (mLock) {
-            mMissing = false;
-            mPermissions = null;
-            invalidateCache();
-        }
+        mMissing = false;
+        mPermissions = null;
+        invalidateCache();
     }
 
     /**
@@ -97,9 +87,7 @@
      */
     @Deprecated
     public boolean hasPermissionState(@NonNull String name) {
-        synchronized (mLock) {
-            return mPermissions != null && mPermissions.containsKey(name);
-        }
+        return mPermissions != null && mPermissions.containsKey(name);
     }
 
     /**
@@ -109,19 +97,17 @@
      */
     @Deprecated
     public boolean hasPermissionState(@NonNull ArraySet<String> names) {
-        synchronized (mLock) {
-            if (mPermissions == null) {
-                return false;
-            }
-            final int namesSize = names.size();
-            for (int i = 0; i < namesSize; i++) {
-                final String name = names.valueAt(i);
-                if (mPermissions.containsKey(name)) {
-                    return true;
-                }
-            }
+        if (mPermissions == null) {
             return false;
         }
+        final int namesSize = names.size();
+        for (int i = 0; i < namesSize; i++) {
+            final String name = names.valueAt(i);
+            if (mPermissions.containsKey(name)) {
+                return true;
+            }
+        }
+        return false;
     }
 
     /**
@@ -132,28 +118,24 @@
      */
     @Nullable
     public PermissionState getPermissionState(@NonNull String name) {
-        synchronized (mLock) {
-            if (mPermissions == null) {
-                return null;
-            }
-            return mPermissions.get(name);
+        if (mPermissions == null) {
+            return null;
         }
+        return mPermissions.get(name);
     }
 
     @NonNull
     private PermissionState getOrCreatePermissionState(@NonNull BasePermission permission) {
-        synchronized (mLock) {
-            if (mPermissions == null) {
-                mPermissions = new ArrayMap<>();
-            }
-            final String name = permission.getName();
-            PermissionState permissionState = mPermissions.get(name);
-            if (permissionState == null) {
-                permissionState = new PermissionState(permission);
-                mPermissions.put(name, permissionState);
-            }
-            return permissionState;
+        if (mPermissions == null) {
+            mPermissions = new ArrayMap<>();
         }
+        final String name = permission.getName();
+        PermissionState permissionState = mPermissions.get(name);
+        if (permissionState == null) {
+            permissionState = new PermissionState(permission);
+            mPermissions.put(name, permissionState);
+        }
+        return permissionState;
     }
 
     /**
@@ -163,12 +145,10 @@
      */
     @NonNull
     public List<PermissionState> getPermissionStates() {
-        synchronized (mLock) {
-            if (mPermissions == null) {
-                return Collections.emptyList();
-            }
-            return new ArrayList<>(mPermissions.values());
+        if (mPermissions == null) {
+            return Collections.emptyList();
         }
+        return new ArrayList<>(mPermissions.values());
     }
 
     /**
@@ -179,20 +159,18 @@
      * @param flags the permission flags
      */
     public void putPermissionState(@NonNull BasePermission permission, boolean granted, int flags) {
-        synchronized (mLock) {
-            final String name = permission.getName();
-            if (mPermissions == null) {
-                mPermissions = new ArrayMap<>();
-            } else {
-                mPermissions.remove(name);
-            }
-            final PermissionState permissionState = new PermissionState(permission);
-            if (granted) {
-                permissionState.grant();
-            }
-            permissionState.updateFlags(flags, flags);
-            mPermissions.put(name, permissionState);
+        final String name = permission.getName();
+        if (mPermissions == null) {
+            mPermissions = new ArrayMap<>();
+        } else {
+            mPermissions.remove(name);
         }
+        final PermissionState permissionState = new PermissionState(permission);
+        if (granted) {
+            permissionState.grant();
+        }
+        permissionState.updateFlags(flags, flags);
+        mPermissions.put(name, permissionState);
     }
 
     /**
@@ -202,16 +180,14 @@
      * @return whether the permission state changed
      */
     public boolean removePermissionState(@NonNull String name) {
-        synchronized (mLock) {
-            if (mPermissions == null) {
-                return false;
-            }
-            boolean changed = mPermissions.remove(name) != null;
-            if (changed && mPermissions.isEmpty()) {
-                mPermissions = null;
-            }
-            return changed;
+        if (mPermissions == null) {
+            return false;
         }
+        final boolean changed = mPermissions.remove(name) != null;
+        if (changed && mPermissions.isEmpty()) {
+            mPermissions = null;
+        }
+        return changed;
     }
 
     /**
@@ -232,22 +208,20 @@
      */
     @NonNull
     public Set<String> getGrantedPermissions() {
-        synchronized (mLock) {
-            if (mPermissions == null) {
-                return Collections.emptySet();
-            }
-
-            Set<String> permissions = new ArraySet<>(mPermissions.size());
-            final int permissionsSize = mPermissions.size();
-            for (int i = 0; i < permissionsSize; i++) {
-                PermissionState permissionState = mPermissions.valueAt(i);
-
-                if (permissionState.isGranted()) {
-                    permissions.add(permissionState.getName());
-                }
-            }
-            return permissions;
+        if (mPermissions == null) {
+            return Collections.emptySet();
         }
+
+        final Set<String> permissions = new ArraySet<>(mPermissions.size());
+        final int permissionsSize = mPermissions.size();
+        for (int i = 0; i < permissionsSize; i++) {
+            final PermissionState permissionState = mPermissions.valueAt(i);
+
+            if (permissionState.isGranted()) {
+                permissions.add(permissionState.getName());
+            }
+        }
+        return permissions;
     }
 
     /**
@@ -257,7 +231,7 @@
      * @return whether the permission grant state changed
      */
     public boolean grantPermission(@NonNull BasePermission permission) {
-        PermissionState permissionState = getOrCreatePermissionState(permission);
+        final PermissionState permissionState = getOrCreatePermissionState(permission);
         return permissionState.grant();
     }
 
@@ -310,7 +284,7 @@
         final PermissionState permissionState = getOrCreatePermissionState(permission);
         final boolean changed = permissionState.updateFlags(flagMask, flagValues);
         if (changed && permissionState.isDefault()) {
-            removePermissionState(permission.name);
+            removePermissionState(permission.getName());
         }
         return changed;
     }
@@ -319,37 +293,33 @@
         if (flagMask == 0) {
             return false;
         }
-        synchronized (mLock) {
-            if (mPermissions == null) {
-                return false;
-            }
-            boolean anyChanged = false;
-            for (int i = mPermissions.size() - 1; i >= 0; i--) {
-                final PermissionState permissionState = mPermissions.valueAt(i);
-                final boolean changed = permissionState.updateFlags(flagMask, flagValues);
-                if (changed && permissionState.isDefault()) {
-                    mPermissions.removeAt(i);
-                }
-                anyChanged |= changed;
-            }
-            return anyChanged;
+        if (mPermissions == null) {
+            return false;
         }
+        boolean anyChanged = false;
+        for (int i = mPermissions.size() - 1; i >= 0; i--) {
+            final PermissionState permissionState = mPermissions.valueAt(i);
+            final boolean changed = permissionState.updateFlags(flagMask, flagValues);
+            if (changed && permissionState.isDefault()) {
+                mPermissions.removeAt(i);
+            }
+            anyChanged |= changed;
+        }
+        return anyChanged;
     }
 
     public boolean isPermissionReviewRequired() {
-        synchronized (mLock) {
-            if (mPermissions == null) {
-                return false;
-            }
-            final int permissionsSize = mPermissions.size();
-            for (int i = 0; i < permissionsSize; i++) {
-                final PermissionState permission = mPermissions.valueAt(i);
-                if ((permission.getFlags() & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
-                    return true;
-                }
-            }
+        if (mPermissions == null) {
             return false;
         }
+        final int permissionsSize = mPermissions.size();
+        for (int i = 0; i < permissionsSize; i++) {
+            final PermissionState permission = mPermissions.valueAt(i);
+            if ((permission.getFlags() & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
+                return true;
+            }
+        }
+        return false;
     }
 
     /**
@@ -360,24 +330,22 @@
      */
     @NonNull
     public int[] computeGids(@NonNull int[] globalGids, @UserIdInt int userId) {
-        synchronized (mLock) {
-            IntArray gids = IntArray.wrap(globalGids);
-            if (mPermissions == null) {
-                return gids.toArray();
-            }
-            final int permissionsSize = mPermissions.size();
-            for (int i = 0; i < permissionsSize; i++) {
-                PermissionState permissionState = mPermissions.valueAt(i);
-                if (!permissionState.isGranted()) {
-                    continue;
-                }
-                final int[] permissionGids = permissionState.computeGids(userId);
-                if (permissionGids.length != 0) {
-                    gids.addAll(permissionGids);
-                }
-            }
+        IntArray gids = IntArray.wrap(globalGids);
+        if (mPermissions == null) {
             return gids.toArray();
         }
+        final int permissionsSize = mPermissions.size();
+        for (int i = 0; i < permissionsSize; i++) {
+            PermissionState permissionState = mPermissions.valueAt(i);
+            if (!permissionState.isGranted()) {
+                continue;
+            }
+            final int[] permissionGids = permissionState.computeGids(userId);
+            if (permissionGids.length != 0) {
+                gids.addAll(permissionGids);
+            }
+        }
+        return gids.toArray();
     }
 
     static void invalidateCache() {
diff --git a/services/core/java/com/android/server/pm/permission/UserPermissionState.java b/services/core/java/com/android/server/pm/permission/UserPermissionState.java
index 7f55cb1..2c741cf 100644
--- a/services/core/java/com/android/server/pm/permission/UserPermissionState.java
+++ b/services/core/java/com/android/server/pm/permission/UserPermissionState.java
@@ -23,8 +23,6 @@
 import android.util.ArraySet;
 import android.util.SparseArray;
 
-import com.android.internal.annotations.GuardedBy;
-
 /**
  * Permission state for a user.
  */
@@ -33,71 +31,52 @@
      * Whether the install permissions have been granted to a package, so that no install
      * permissions should be added to it unless the package is upgraded.
      */
-    @GuardedBy("mLock")
     @NonNull
     private final ArraySet<String> mInstallPermissionsFixed = new ArraySet<>();
 
     /**
      * Maps from app ID to {@link UidPermissionState}.
      */
-    @GuardedBy("mLock")
     @NonNull
     private final SparseArray<UidPermissionState> mUidStates = new SparseArray<>();
 
-    @NonNull
-    private final Object mLock;
-
-    public UserPermissionState(@NonNull Object lock) {
-        mLock = lock;
-    }
-
     public boolean areInstallPermissionsFixed(@NonNull String packageName) {
-        synchronized (mLock) {
-            return mInstallPermissionsFixed.contains(packageName);
-        }
+        return mInstallPermissionsFixed.contains(packageName);
     }
 
     public void setInstallPermissionsFixed(@NonNull String packageName, boolean fixed) {
-        synchronized (mLock) {
-            if (fixed) {
-                mInstallPermissionsFixed.add(packageName);
-            } else {
-                mInstallPermissionsFixed.remove(packageName);
-            }
+        if (fixed) {
+            mInstallPermissionsFixed.add(packageName);
+        } else {
+            mInstallPermissionsFixed.remove(packageName);
         }
     }
 
     @Nullable
     public UidPermissionState getUidState(@AppIdInt int appId) {
         checkAppId(appId);
-        synchronized (mLock) {
-            return mUidStates.get(appId);
-        }
+        return mUidStates.get(appId);
     }
 
     @NonNull
     public UidPermissionState getOrCreateUidState(@AppIdInt int appId) {
         checkAppId(appId);
-        synchronized (mLock) {
-            UidPermissionState uidState = mUidStates.get(appId);
-            if (uidState == null) {
-                uidState = new UidPermissionState();
-                mUidStates.put(appId, uidState);
-            }
-            return uidState;
+        UidPermissionState uidState = mUidStates.get(appId);
+        if (uidState == null) {
+            uidState = new UidPermissionState();
+            mUidStates.put(appId, uidState);
         }
+        return uidState;
     }
 
     public void removeUidState(@AppIdInt int appId) {
         checkAppId(appId);
-        synchronized (mLock) {
-            mUidStates.delete(appId);
-        }
+        mUidStates.delete(appId);
     }
 
     private void checkAppId(@AppIdInt int appId) {
         if (UserHandle.getUserId(appId) != 0) {
-            throw new IllegalArgumentException(appId + " is not an app ID");
+            throw new IllegalArgumentException("Invalid app ID " + appId);
         }
     }
 }
diff --git a/services/core/java/com/android/server/policy/DeviceStatePolicyImpl.java b/services/core/java/com/android/server/policy/DeviceStatePolicyImpl.java
new file mode 100644
index 0000000..54f6183
--- /dev/null
+++ b/services/core/java/com/android/server/policy/DeviceStatePolicyImpl.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.policy;
+
+import android.annotation.NonNull;
+
+import com.android.server.devicestate.DeviceStatePolicy;
+import com.android.server.devicestate.DeviceStateProvider;
+
+/**
+ * Default empty implementation of {@link DeviceStatePolicy}.
+ *
+ * @see DeviceStateProviderImpl
+ */
+public final class DeviceStatePolicyImpl implements DeviceStatePolicy {
+    private final DeviceStateProvider mProvider;
+
+    public DeviceStatePolicyImpl() {
+        mProvider = new DeviceStateProviderImpl();
+    }
+
+    public DeviceStateProvider getDeviceStateProvider() {
+        return mProvider;
+    }
+
+    @Override
+    public void configureDeviceForState(int state, @NonNull Runnable onComplete) {
+        onComplete.run();
+    }
+}
diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
new file mode 100644
index 0000000..85ab0bc
--- /dev/null
+++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.policy;
+
+import android.annotation.Nullable;
+
+import com.android.server.devicestate.DeviceStateProvider;
+
+/**
+ * Default implementation of {@link DeviceStateProvider}. Currently only supports
+ * {@link #DEFAULT_DEVICE_STATE}.
+ *
+ * @see DeviceStatePolicyImpl
+ */
+final class DeviceStateProviderImpl implements DeviceStateProvider {
+    private static final int DEFAULT_DEVICE_STATE = 0;
+
+    @Nullable
+    private Listener mListener = null;
+
+    @Override
+    public void setListener(Listener listener) {
+        if (mListener != null) {
+            throw new RuntimeException("Provider already has a listener set.");
+        }
+
+        mListener = listener;
+        mListener.onSupportedDeviceStatesChanged(new int[]{ DEFAULT_DEVICE_STATE });
+        mListener.onStateChanged(DEFAULT_DEVICE_STATE);
+    }
+}
diff --git a/services/core/java/com/android/server/policy/DisplayFoldController.java b/services/core/java/com/android/server/policy/DisplayFoldController.java
index 0db3d78..f8e26fc 100644
--- a/services/core/java/com/android/server/policy/DisplayFoldController.java
+++ b/services/core/java/com/android/server/policy/DisplayFoldController.java
@@ -83,32 +83,39 @@
         if (mFolded != null && mFolded == folded) {
             return;
         }
-        if (folded) {
-            Rect foldedArea;
-            if (!mOverrideFoldedArea.isEmpty()) {
-                foldedArea = mOverrideFoldedArea;
-            } else if (!mFoldedArea.isEmpty()) {
-                foldedArea = mFoldedArea;
-            } else {
-                return;
-            }
 
-            mDisplayManagerInternal.getNonOverrideDisplayInfo(mDisplayId, mNonOverrideDisplayInfo);
-            final int dx = (mNonOverrideDisplayInfo.logicalWidth - foldedArea.width()) / 2
-                    - foldedArea.left;
-            final int dy = (mNonOverrideDisplayInfo.logicalHeight - foldedArea.height()) / 2
-                    - foldedArea.top;
-
-            // Bypass scaling otherwise LogicalDisplay will scale contents by default.
-            mDisplayManagerInternal.setDisplayScalingDisabled(mDisplayId, true);
-            mWindowManagerInternal.setForcedDisplaySize(mDisplayId,
-                    foldedArea.width(), foldedArea.height());
-            mDisplayManagerInternal.setDisplayOffsets(mDisplayId, -dx, -dy);
+        final Rect foldedArea;
+        if (!mOverrideFoldedArea.isEmpty()) {
+            foldedArea = mOverrideFoldedArea;
+        } else if (!mFoldedArea.isEmpty()) {
+            foldedArea = mFoldedArea;
         } else {
-            mDisplayManagerInternal.setDisplayScalingDisabled(mDisplayId, false);
-            mWindowManagerInternal.clearForcedDisplaySize(mDisplayId);
-            mDisplayManagerInternal.setDisplayOffsets(mDisplayId, 0, 0);
+            foldedArea = null;
         }
+
+        // Only do display scaling/cropping if it has been configured to do so
+        if (foldedArea != null) {
+            if (folded) {
+
+                mDisplayManagerInternal.getNonOverrideDisplayInfo(
+                        mDisplayId, mNonOverrideDisplayInfo);
+                final int dx = (mNonOverrideDisplayInfo.logicalWidth - foldedArea.width()) / 2
+                        - foldedArea.left;
+                final int dy = (mNonOverrideDisplayInfo.logicalHeight - foldedArea.height()) / 2
+                        - foldedArea.top;
+
+                // Bypass scaling otherwise LogicalDisplay will scale contents by default.
+                mDisplayManagerInternal.setDisplayScalingDisabled(mDisplayId, true);
+                mWindowManagerInternal.setForcedDisplaySize(mDisplayId,
+                        foldedArea.width(), foldedArea.height());
+                mDisplayManagerInternal.setDisplayOffsets(mDisplayId, -dx, -dy);
+            } else {
+                mDisplayManagerInternal.setDisplayScalingDisabled(mDisplayId, false);
+                mWindowManagerInternal.clearForcedDisplaySize(mDisplayId);
+                mDisplayManagerInternal.setDisplayOffsets(mDisplayId, 0, 0);
+            }
+        }
+
         mDurationLogger.setDeviceFolded(folded);
         mDurationLogger.logFocusedAppWithFoldState(folded, mFocusedApp);
         mFolded = folded;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index df283e2..0983e10 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -117,6 +117,7 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerInternal;
 import android.hardware.hdmi.HdmiAudioSystemClient;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiPlaybackClient;
@@ -366,6 +367,7 @@
     StatusBarManagerInternal mStatusBarManagerInternal;
     AudioManagerInternal mAudioManagerInternal;
     DisplayManager mDisplayManager;
+    DisplayManagerInternal mDisplayManagerInternal;
     boolean mPreloadedRecentApps;
     final Object mServiceAquireLock = new Object();
     Vibrator mVibrator; // Vibrator for giving feedback of orientation changes
@@ -1752,6 +1754,7 @@
         mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
         mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
         mDisplayManager = mContext.getSystemService(DisplayManager.class);
+        mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
         mPackageManager = mContext.getPackageManager();
         mHasFeatureWatch = mPackageManager.hasSystemFeature(FEATURE_WATCH);
         mHasFeatureLeanback = mPackageManager.hasSystemFeature(FEATURE_LEANBACK);
@@ -4506,7 +4509,11 @@
 
     // Called on the DisplayManager's DisplayPowerController thread.
     @Override
-    public void screenTurnedOff() {
+    public void screenTurnedOff(int displayId) {
+        if (displayId != DEFAULT_DISPLAY) {
+            return;
+        }
+
         if (DEBUG_WAKEUP) Slog.i(TAG, "Screen turned off...");
 
         updateScreenOffSleepToken(true);
@@ -4529,7 +4536,11 @@
 
     // Called on the DisplayManager's DisplayPowerController thread.
     @Override
-    public void screenTurningOn(final ScreenOnListener screenOnListener) {
+    public void screenTurningOn(int displayId, final ScreenOnListener screenOnListener) {
+        if (displayId != DEFAULT_DISPLAY) {
+            return;
+        }
+
         if (DEBUG_WAKEUP) Slog.i(TAG, "Screen turning on...");
 
         Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "screenTurningOn", 0 /* cookie */);
@@ -4552,7 +4563,11 @@
 
     // Called on the DisplayManager's DisplayPowerController thread.
     @Override
-    public void screenTurnedOn() {
+    public void screenTurnedOn(int displayId) {
+        if (displayId != DEFAULT_DISPLAY) {
+            return;
+        }
+
         synchronized (mLock) {
             if (mKeyguardDelegate != null) {
                 mKeyguardDelegate.onScreenTurnedOn();
@@ -4562,7 +4577,11 @@
     }
 
     @Override
-    public void screenTurningOff(ScreenOffListener screenOffListener) {
+    public void screenTurningOff(int displayId, ScreenOffListener screenOffListener) {
+        if (displayId != DEFAULT_DISPLAY) {
+            return;
+        }
+
         mWindowManagerFuncs.screenTurningOff(screenOffListener);
         synchronized (mLock) {
             if (mKeyguardDelegate != null) {
@@ -4824,8 +4843,8 @@
         }
         startedWakingUp(ON_BECAUSE_OF_UNKNOWN);
         finishedWakingUp(ON_BECAUSE_OF_UNKNOWN);
-        screenTurningOn(null);
-        screenTurnedOn();
+        screenTurningOn(DEFAULT_DISPLAY, null);
+        screenTurnedOn(DEFAULT_DISPLAY);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index b96d65c..0d8d347 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -831,7 +831,7 @@
     public void finishedGoingToSleep(int why);
 
     /**
-     * Called when the device is about to turn on the screen to show content.
+     * Called when the display is about to turn on to show content.
      * When waking up, this method will be called once after the call to wakingUp().
      * When dozing, the method will be called sometime after the call to goingToSleep() and
      * may be called repeatedly in the case where the screen is pulsing on and off.
@@ -839,13 +839,13 @@
      * Must call back on the listener to tell it when the higher-level system
      * is ready for the screen to go on (i.e. the lock screen is shown).
      */
-    public void screenTurningOn(ScreenOnListener screenOnListener);
+    public void screenTurningOn(int displayId, ScreenOnListener screenOnListener);
 
     /**
-     * Called when the device has actually turned on the screen, i.e. the display power state has
-     * been set to ON and the screen is unblocked.
+     * Called when the display has actually turned on, i.e. the display power state has been set to
+     * ON and the screen is unblocked.
      */
-    public void screenTurnedOn();
+    public void screenTurnedOn(int displayId);
 
     /**
      * Called when the display would like to be turned off. This gives policy a chance to do some
@@ -854,12 +854,12 @@
      * @param screenOffListener Must be called to tell that the display power state can actually be
      *                          changed now after policy has done its work.
      */
-    public void screenTurningOff(ScreenOffListener screenOffListener);
+    public void screenTurningOff(int displayId, ScreenOffListener screenOffListener);
 
     /**
-     * Called when the device has turned the screen off.
+     * Called when the display has turned off.
      */
-    public void screenTurnedOff();
+    public void screenTurnedOff(int displayId);
 
     public interface ScreenOnListener {
         void onScreenOn();
diff --git a/services/core/java/com/android/server/power/AttentionDetector.java b/services/core/java/com/android/server/power/AttentionDetector.java
index cbdfc56..06edd19 100644
--- a/services/core/java/com/android/server/power/AttentionDetector.java
+++ b/services/core/java/com/android/server/power/AttentionDetector.java
@@ -18,14 +18,12 @@
 
 import static android.provider.DeviceConfig.NAMESPACE_ATTENTION_MANAGER_SERVICE;
 
-import android.Manifest;
 import android.app.ActivityManager;
 import android.app.SynchronousUserSwitchObserver;
 import android.attention.AttentionManagerInternal;
 import android.attention.AttentionManagerInternal.AttentionCallbackInternal;
 import android.content.ContentResolver;
 import android.content.Context;
-import android.content.pm.PackageManager;
 import android.database.ContentObserver;
 import android.os.Handler;
 import android.os.PowerManager;
@@ -124,9 +122,6 @@
     protected WindowManagerInternal mWindowManager;
 
     @VisibleForTesting
-    protected PackageManager mPackageManager;
-
-    @VisibleForTesting
     protected ContentResolver mContentResolver;
 
     /**
@@ -164,7 +159,6 @@
     public void systemReady(Context context) {
         mContext = context;
         updateEnabledFromSettings(context);
-        mPackageManager = context.getPackageManager();
         mContentResolver = context.getContentResolver();
         mAttentionManager = LocalServices.getService(AttentionManagerInternal.class);
         mWindowManager = LocalServices.getService(WindowManagerInternal.class);
@@ -192,14 +186,11 @@
     public long updateUserActivity(long nextScreenDimming, long dimDurationMillis) {
         if (nextScreenDimming == mLastActedOnNextScreenDimming
                 || !mIsSettingEnabled
+                || !isAttentionServiceSupported()
                 || mWindowManager.isKeyguardShowingAndNotOccluded()) {
             return nextScreenDimming;
         }
 
-        if (!isAttentionServiceSupported() || !serviceHasSufficientPermissions()) {
-            return nextScreenDimming;
-        }
-
         final long now = SystemClock.uptimeMillis();
         final long whenToCheck = nextScreenDimming - getPreDimCheckDurationMillis();
         final long whenToStopExtending = mLastUserActivityTime + getMaxExtensionMillis();
@@ -300,18 +291,6 @@
         return mAttentionManager != null && mAttentionManager.isAttentionServiceSupported();
     }
 
-    /**
-     * Returns {@code true} if the attention service has sufficient permissions, disables the
-     * depending features otherwise.
-     */
-    @VisibleForTesting
-    boolean serviceHasSufficientPermissions() {
-        final String attentionPackage = mPackageManager.getAttentionServicePackageName();
-        return attentionPackage != null && mPackageManager.checkPermission(
-                Manifest.permission.CAMERA, attentionPackage)
-                == PackageManager.PERMISSION_GRANTED;
-    }
-
     public void dump(PrintWriter pw) {
         pw.println("AttentionDetector:");
         pw.println(" mIsSettingEnabled=" + mIsSettingEnabled);
diff --git a/services/core/java/com/android/server/power/batterysaver/TEST_MAPPING b/services/core/java/com/android/server/power/batterysaver/TEST_MAPPING
new file mode 100644
index 0000000..17dba7d
--- /dev/null
+++ b/services/core/java/com/android/server/power/batterysaver/TEST_MAPPING
@@ -0,0 +1,19 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsLocationCoarseTestCases"
+    },
+    {
+      "name": "CtsLocationFineTestCases"
+    },
+    {
+      "name": "CtsLocationNoneTestCases"
+    },
+    {
+      "name": "FrameworksMockingServicesTests",
+      "options": [
+        {"include-filter": "com.android.server.location"}
+      ]
+    }
+  ]
+}
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsData.java b/services/core/java/com/android/server/powerstats/PowerStatsData.java
deleted file mode 100644
index 755bd5f..0000000
--- a/services/core/java/com/android/server/powerstats/PowerStatsData.java
+++ /dev/null
@@ -1,286 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.powerstats;
-
-import android.util.Log;
-import android.util.proto.ProtoInputStream;
-import android.util.proto.ProtoOutputStream;
-import android.util.proto.ProtoUtils;
-import android.util.proto.WireTypeMismatchException;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * PowerStatsData is a class that performs two operations:
- * 1) Unpacks serialized protobuf byte arrays, as defined in powerstatsservice.proto,
- *    into RailInfo or EnergyData object arrays.
- *
- * 2) Packs RailInfo or EnergyData object arrays in protobuf byte arrays as
- *    defined in powerstatsservice.proto.
- *
- * Inside frameworks, proto source is generated with the genstream option
- * and therefore the getter/setter helper functions are not available.
- * The protos need to be packed/unpacked in a more manual way using
- * ProtoOutputStream/ProtoInputStream.
- */
-public class PowerStatsData {
-    private static final String TAG = PowerStatsData.class.getSimpleName();
-
-    private List<Data> mDataList;
-
-    public PowerStatsData(ProtoInputStream pis) throws IOException {
-        mDataList = new ArrayList<Data>();
-        unpackProto(pis);
-    }
-
-    public PowerStatsData(Data[] data) {
-        mDataList = new ArrayList<Data>(Arrays.asList(data));
-    }
-
-    private void unpackProto(ProtoInputStream pis) throws IOException {
-        long token;
-
-        while (true) {
-            try {
-                switch (pis.nextField()) {
-                    case (int) PowerStatsServiceProto.RAIL_INFO:
-                        token = pis.start(PowerStatsServiceProto.RAIL_INFO);
-                        mDataList.add(new RailInfo(pis));
-                        pis.end(token);
-                        break;
-
-                    case (int) PowerStatsServiceProto.ENERGY_DATA:
-                        token = pis.start(PowerStatsServiceProto.ENERGY_DATA);
-                        mDataList.add(new EnergyData(pis));
-                        pis.end(token);
-                        break;
-
-                    case ProtoInputStream.NO_MORE_FIELDS:
-                        return;
-
-                    default:
-                        Log.e(TAG, "Unhandled field in proto: "
-                                + ProtoUtils.currentFieldToString(pis));
-                        break;
-                }
-            } catch (WireTypeMismatchException wtme) {
-                Log.e(TAG, "Wire Type mismatch in proto: " + ProtoUtils.currentFieldToString(pis));
-            }
-        }
-    }
-
-    /**
-     * Write this object to an output stream in protobuf format.
-     *
-     * @param pos ProtoOutputStream of file where data is to be written.  Data is
-     *            written in protobuf format as defined by powerstatsservice.proto.
-     */
-    public void toProto(ProtoOutputStream pos) {
-        long token;
-
-        for (Data data : mDataList) {
-            if (data instanceof RailInfo) {
-                token = pos.start(PowerStatsServiceProto.RAIL_INFO);
-            } else {
-                token = pos.start(PowerStatsServiceProto.ENERGY_DATA);
-            }
-            data.toProto(pos);
-            pos.end(token);
-        }
-    }
-
-    /**
-     * Convert mDataList to proto format and return the serialized byte array.
-     *
-     * @return byte array containing a serialized protobuf of mDataList.
-     */
-    public byte[] getProtoBytes() {
-        ProtoOutputStream pos = new ProtoOutputStream();
-        long token;
-
-        for (Data data : mDataList) {
-            if (data instanceof RailInfo) {
-                token = pos.start(PowerStatsServiceProto.RAIL_INFO);
-            } else {
-                token = pos.start(PowerStatsServiceProto.ENERGY_DATA);
-            }
-            data.toProto(pos);
-            pos.end(token);
-        }
-        return pos.getBytes();
-    }
-
-    /**
-     * Print this object to logcat.
-     */
-    public void print() {
-        for (Data data : mDataList) {
-            Log.d(TAG, data.toString());
-        }
-    }
-
-    /**
-     * RailInfo is a class that stores a description for an individual ODPM
-     * rail.  It provides functionality to unpack a RailInfo object from a
-     * serialized protobuf byte array, and to pack a RailInfo object into
-     * a ProtoOutputStream.
-     */
-    public static class RailInfo extends Data {
-        public String mRailName;
-        public String mSubSysName;
-        public long mSamplingRate;
-
-        public RailInfo(ProtoInputStream pis) throws IOException {
-            unpackProto(pis);
-        }
-
-        public RailInfo(long index, String railName, String subSysName, long samplingRate) {
-            mIndex = index;
-            mRailName = railName;
-            mSubSysName = subSysName;
-            mSamplingRate = samplingRate;
-        }
-
-        @Override
-        protected void unpackProto(ProtoInputStream pis) throws IOException {
-            while (true) {
-                try {
-                    switch (pis.nextField()) {
-                        case (int) RailInfoProto.INDEX:
-                            mIndex = pis.readInt(RailInfoProto.INDEX);
-                            break;
-
-                        case (int) RailInfoProto.RAIL_NAME:
-                            mRailName = pis.readString(RailInfoProto.RAIL_NAME);
-                            break;
-
-                        case (int) RailInfoProto.SUBSYS_NAME:
-                            mSubSysName = pis.readString(RailInfoProto.SUBSYS_NAME);
-                            break;
-
-                        case (int) RailInfoProto.SAMPLING_RATE:
-                            mSamplingRate = pis.readInt(RailInfoProto.SAMPLING_RATE);
-                            break;
-
-                        case ProtoInputStream.NO_MORE_FIELDS:
-                            return;
-
-                        default:
-                            Log.e(TAG, "Unhandled field in RailInfoProto: "
-                                    + ProtoUtils.currentFieldToString(pis));
-                            break;
-                    }
-                } catch (WireTypeMismatchException wtme) {
-                    Log.e(TAG, "Wire Type mismatch in RailInfoProto: "
-                            + ProtoUtils.currentFieldToString(pis));
-                }
-            }
-        }
-
-        @Override
-        public void toProto(ProtoOutputStream pos) {
-            pos.write(RailInfoProto.INDEX, mIndex);
-            pos.write(RailInfoProto.RAIL_NAME, mRailName);
-            pos.write(RailInfoProto.SUBSYS_NAME, mSubSysName);
-            pos.write(RailInfoProto.SAMPLING_RATE, mSamplingRate);
-        }
-
-        @Override
-        public String toString() {
-            return String.format("Index = " + mIndex
-                + ", RailName = " + mRailName
-                + ", SubSysName = " + mSubSysName
-                + ", SamplingRate = " + mSamplingRate);
-        }
-    }
-
-    /**
-     * EnergyData is a class that stores an energy (uWs) data reading for an
-     * individual ODPM rail.  It provides functionality to unpack an EnergyData
-     * object from a serialized protobuf byte array, and to pack an EnergyData
-     * object into a ProtoOutputStream.
-     */
-    public static class EnergyData extends Data {
-        public long mTimestampMs;
-        public long mEnergyUWs;
-
-        public EnergyData(ProtoInputStream pis) throws IOException {
-            unpackProto(pis);
-        }
-
-        public EnergyData(long index, long timestampMs, long energyUWs) {
-            mIndex = index;
-            mTimestampMs = timestampMs;
-            mEnergyUWs = energyUWs;
-        }
-
-        @Override
-        protected void unpackProto(ProtoInputStream pis) throws IOException {
-            while (true) {
-                try {
-                    switch (pis.nextField()) {
-                        case (int) EnergyDataProto.INDEX:
-                            mIndex = pis.readInt(EnergyDataProto.INDEX);
-                            break;
-
-                        case (int) EnergyDataProto.TIMESTAMP_MS:
-                            mTimestampMs = pis.readLong(EnergyDataProto.TIMESTAMP_MS);
-                            break;
-
-                        case (int) EnergyDataProto.ENERGY_UWS:
-                            mEnergyUWs = pis.readLong(EnergyDataProto.ENERGY_UWS);
-                            break;
-
-                        case ProtoInputStream.NO_MORE_FIELDS:
-                            return;
-
-                        default:
-                            Log.e(TAG, "Unhandled field in EnergyDataProto: "
-                                    + ProtoUtils.currentFieldToString(pis));
-                            break;
-                    }
-                } catch (WireTypeMismatchException wtme) {
-                    Log.e(TAG, "Wire Type mismatch in EnergyDataProto: "
-                            + ProtoUtils.currentFieldToString(pis));
-                }
-            }
-        }
-
-        @Override
-        protected void toProto(ProtoOutputStream pos) {
-            pos.write(EnergyDataProto.INDEX, mIndex);
-            pos.write(EnergyDataProto.TIMESTAMP_MS, mTimestampMs);
-            pos.write(EnergyDataProto.ENERGY_UWS, mEnergyUWs);
-        }
-
-        @Override
-        public String toString() {
-            return String.format("Index = " + mIndex
-                + ", Timestamp (ms) = " + mTimestampMs
-                + ", Energy (uWs) = " + mEnergyUWs);
-        }
-    }
-
-    private abstract static class Data {
-        public long mIndex;
-        protected abstract void unpackProto(ProtoInputStream pis) throws IOException;
-        protected abstract void toProto(ProtoOutputStream pos);
-    }
-}
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java b/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java
index 84a6fc9..5d8afd4 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java
@@ -198,19 +198,21 @@
      *             array and written to on-device storage.
      */
     public void write(byte[] data) {
-        mLock.lock();
+        if (data.length > 0) {
+            mLock.lock();
 
-        long currentTimeMillis = System.currentTimeMillis();
-        try {
-            DataElement dataElement = new DataElement(data);
-            mFileRotator.rewriteActive(new DataRewriter(dataElement.toByteArray()),
-                    currentTimeMillis);
-            mFileRotator.maybeRotate(currentTimeMillis);
-        } catch (IOException e) {
-            Log.e(TAG, "Failed to write to on-device storage: " + e);
+            long currentTimeMillis = System.currentTimeMillis();
+            try {
+                DataElement dataElement = new DataElement(data);
+                mFileRotator.rewriteActive(new DataRewriter(dataElement.toByteArray()),
+                        currentTimeMillis);
+                mFileRotator.maybeRotate(currentTimeMillis);
+            } catch (IOException e) {
+                Log.e(TAG, "Failed to write to on-device storage: " + e);
+            }
+
+            mLock.unlock();
         }
-
-        mLock.unlock();
     }
 
     /**
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java b/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java
index dc996a3..18646b9 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java
@@ -16,6 +16,17 @@
 
 package com.android.server.powerstats;
 
+import android.hardware.power.stats.IPowerStats;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.function.Supplier;
+
 /**
  * PowerStatsHALWrapper is a wrapper class for the PowerStats HAL API calls.
  */
@@ -27,30 +38,50 @@
      */
     public interface IPowerStatsHALWrapper {
         /**
-         * Returns rail info for all available ODPM rails.
+         * Returns the energy consumer IDs for all available energy consumers (power models) on the
+         *         device.  Examples of subsystems for which energy consumer results (power models)
+         *         may be available are GPS, display, wifi, etc.  The default list of energy
+         *         consumers can be found in the PowerStats HAL definition (EnergyConsumerId.aidl).
+         *         The availability of energy consumer IDs is hardware dependent.
          *
-         * @return array of RailInfo objects containing rail info for all
-         *         available rails.
+         * @return List of EnergyConsumerIds all available energy consumers.
          */
-        PowerStatsData.RailInfo[] readRailInfo();
+        int[] getEnergyConsumerInfo();
 
         /**
-         * Returns energy data for all available ODPM rails.  Available rails can
-         *         be retrieved by calling nativeGetRailInfo.  Energy data and
-         *         rail info can be linked through the index field.
+         * Returns the energy consumer result for all available energy consumers (power models).
+         *         Available consumers can be retrieved by calling getEnergyConsumerInfo().  The
+         *         subsystem corresponding to the energy consumer result is defined by the energy
+         *         consumer ID.
          *
-         * @return array of EnergyData objects containing energy data for all
-         *         available rails.
+         * @return List of EnergyConsumerResult objects containing energy consumer results for all
+         *         available energy consumers (power models).
          */
-        PowerStatsData.EnergyData[] readEnergyData();
+        android.hardware.power.stats.EnergyConsumerResult[] getEnergyConsumed();
+
+        /**
+         * Returns channel info for all available energy meters.
+         *
+         * @return List of ChannelInfo objects containing channel info for all available energy
+         *         meters.
+         */
+        android.hardware.power.stats.ChannelInfo[] getEnergyMeterInfo();
+
+        /**
+         * Returns energy measurements for all available energy meters.  Available channels can be
+         *         retrieved by calling getEnergyMeterInfo().  Energy measurements and channel info
+         *         can be linked through the channelId field.
+         *
+         * @return List of EnergyMeasurement objects containing energy measurements for all
+         *         available energy meters.
+         */
+        android.hardware.power.stats.EnergyMeasurement[] readEnergyMeters();
 
         /**
          * Returns boolean indicating if connection to power stats HAL was
          *         established.
          *
-         * @return true if connection to power stats HAL was correctly established
-         *         and that energy data and rail info can be read from the interface.
-         *         false otherwise
+         * @return true if connection to power stats HAL was correctly established.
          */
         boolean initialize();
     }
@@ -61,45 +92,108 @@
      * framework and will be passed into the PowerStatsService through an injector.
      */
     public static final class PowerStatsHALWrapperImpl implements IPowerStatsHALWrapper {
-        private static native boolean nativeInit();
-        private static native PowerStatsData.RailInfo[] nativeGetRailInfo();
-        private static native PowerStatsData.EnergyData[] nativeGetEnergyData();
+        private static Supplier<IPowerStats> sVintfPowerStats;
 
-        /**
-         * Returns rail info for all available ODPM rails.
-         *
-         * @return array of RailInfo objects containing rail info for all
-         *         available rails.
-         */
         @Override
-        public PowerStatsData.RailInfo[] readRailInfo() {
-            return nativeGetRailInfo();
+        public int[] getEnergyConsumerInfo() {
+            int[] energyConsumerInfoHAL = null;
+
+            if (sVintfPowerStats != null) {
+                try {
+                    energyConsumerInfoHAL = sVintfPowerStats.get().getEnergyConsumerInfo();
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Failed to get energy consumer info from PowerStats HAL");
+                }
+            }
+
+            return energyConsumerInfoHAL;
         }
 
-        /**
-         * Returns energy data for all available ODPM rails.  Available rails can
-         *         be retrieved by calling nativeGetRailInfo.  Energy data and
-         *         rail info can be linked through the index field.
-         *
-         * @return array of EnergyData objects containing energy data for all
-         *         available rails.
-         */
         @Override
-        public PowerStatsData.EnergyData[] readEnergyData() {
-            return nativeGetEnergyData();
+        public android.hardware.power.stats.EnergyConsumerResult[] getEnergyConsumed() {
+            android.hardware.power.stats.EnergyConsumerResult[] energyConsumedHAL = null;
+
+            if (sVintfPowerStats != null) {
+                try {
+                    energyConsumedHAL =
+                        sVintfPowerStats.get().getEnergyConsumed(new int[0]);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Failed to get energy consumer results from PowerStats HAL");
+                }
+            }
+
+            return energyConsumedHAL;
         }
 
-        /**
-         * Returns boolean indicating if connection to power stats HAL was
-         *         established.
-         *
-         * @return true if connection to power stats HAL was correctly established
-         *         and that energy data and rail info can be read from the interface.
-         *         false otherwise
-         */
+        @Override
+        public android.hardware.power.stats.ChannelInfo[] getEnergyMeterInfo() {
+            android.hardware.power.stats.ChannelInfo[] energyMeterInfoHAL = null;
+
+            if (sVintfPowerStats != null) {
+                try {
+                    energyMeterInfoHAL = sVintfPowerStats.get().getEnergyMeterInfo();
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Failed to get energy meter info from PowerStats HAL");
+                }
+            }
+
+            return energyMeterInfoHAL;
+        }
+
+        @Override
+        public android.hardware.power.stats.EnergyMeasurement[] readEnergyMeters() {
+            android.hardware.power.stats.EnergyMeasurement[] energyMeasurementHAL = null;
+
+            if (sVintfPowerStats != null) {
+                try {
+                    energyMeasurementHAL =
+                        sVintfPowerStats.get().readEnergyMeters(new int[0]);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Failed to get energy measurements from PowerStats HAL");
+                }
+            }
+
+            return energyMeasurementHAL;
+        }
+
         @Override
         public boolean initialize() {
-            return nativeInit();
+            Supplier<IPowerStats> service = new VintfHalCache();
+
+            if (service.get() == null) {
+                sVintfPowerStats = null;
+                return false;
+            } else {
+                sVintfPowerStats = service;
+                return true;
+            }
+        }
+    }
+
+    private static class VintfHalCache implements Supplier<IPowerStats>, IBinder.DeathRecipient {
+        @GuardedBy("this")
+        private IPowerStats mInstance = null;
+
+        @Override
+        public synchronized IPowerStats get() {
+            if (mInstance == null) {
+                IBinder binder = Binder.allowBlocking(ServiceManager.waitForDeclaredService(
+                        "android.hardware.power.stats.IPowerStats/default"));
+                if (binder != null) {
+                    mInstance = IPowerStats.Stub.asInterface(binder);
+                    try {
+                        binder.linkToDeath(this, 0);
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "Unable to register DeathRecipient for " + mInstance);
+                    }
+                }
+            }
+            return mInstance;
+        }
+
+        @Override
+        public synchronized void binderDied() {
+            mInstance = null;
         }
     }
 }
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
index 71a34a4..bec99bc 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
@@ -17,6 +17,9 @@
 package com.android.server.powerstats;
 
 import android.content.Context;
+import android.hardware.power.stats.ChannelInfo;
+import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyMeasurement;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -25,6 +28,10 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper;
+import com.android.server.powerstats.ProtoStreamUtils.ChannelInfoUtils;
+import com.android.server.powerstats.ProtoStreamUtils.EnergyConsumerIdUtils;
+import com.android.server.powerstats.ProtoStreamUtils.EnergyConsumerResultUtils;
+import com.android.server.powerstats.ProtoStreamUtils.EnergyMeasurementUtils;
 
 import java.io.ByteArrayInputStream;
 import java.io.File;
@@ -32,18 +39,19 @@
 import java.io.IOException;
 
 /**
- * PowerStatsLogger is responsible for logging energy data to on-device
- * storage.  Messages are sent to its message handler to request that energy
- * data be logged, at which time it queries the PowerStats HAL and logs the
- * data to on-device storage.  The on-device storage is dumped to file by
- * calling writeToFile with a file descriptor that points to the output file.
+ * PowerStatsLogger is responsible for logging model and meter energy data to on-device storage.
+ * Messages are sent to its message handler to request that energy data be logged, at which time it
+ * queries the PowerStats HAL and logs the data to on-device storage.  The on-device storage is
+ * dumped to file by calling writeModelDataToFile or writeMeterDataToFile with a file descriptor
+ * that points to the output file.
  */
 public final class PowerStatsLogger extends Handler {
     private static final String TAG = PowerStatsLogger.class.getSimpleName();
     private static final boolean DEBUG = false;
     protected static final int MSG_LOG_TO_DATA_STORAGE = 0;
 
-    private final PowerStatsDataStorage mPowerStatsDataStorage;
+    private final PowerStatsDataStorage mPowerStatsMeterStorage;
+    private final PowerStatsDataStorage mPowerStatsModelStorage;
     private final IPowerStatsHALWrapper mPowerStatsHALWrapper;
 
     @Override
@@ -51,31 +59,40 @@
         switch (msg.what) {
             case MSG_LOG_TO_DATA_STORAGE:
                 if (DEBUG) Log.d(TAG, "Logging to data storage");
-                PowerStatsData energyData =
-                        new PowerStatsData(mPowerStatsHALWrapper.readEnergyData());
-                mPowerStatsDataStorage.write(energyData.getProtoBytes());
+
+                // Log power meter data.
+                EnergyMeasurement[] energyMeasurements = mPowerStatsHALWrapper.readEnergyMeters();
+                mPowerStatsMeterStorage.write(
+                        EnergyMeasurementUtils.getProtoBytes(energyMeasurements));
+                if (DEBUG) EnergyMeasurementUtils.print(energyMeasurements);
+
+                // Log power model data.
+                EnergyConsumerResult[] energyConsumerResults =
+                    mPowerStatsHALWrapper.getEnergyConsumed();
+                mPowerStatsModelStorage.write(
+                        EnergyConsumerResultUtils.getProtoBytes(energyConsumerResults));
+                if (DEBUG) EnergyConsumerResultUtils.print(energyConsumerResults);
                 break;
         }
     }
 
     /**
-     * Writes data stored in PowerStatsDataStorage to a file descriptor.
+     * Writes meter data stored in PowerStatsDataStorage to a file descriptor.
      *
-     * @param fd FileDescriptor where data stored in PowerStatsDataStorage is
-     *           written.  Data is written in protobuf format as defined by
-     *           powerstatsservice.proto.
+     * @param fd FileDescriptor where meter data stored in PowerStatsDataStorage is written.  Data
+     *           is written in protobuf format as defined by powerstatsservice.proto.
      */
-    public void writeToFile(FileDescriptor fd) {
-        if (DEBUG) Log.d(TAG, "Writing to file");
+    public void writeMeterDataToFile(FileDescriptor fd) {
+        if (DEBUG) Log.d(TAG, "Writing meter data to file");
 
         final ProtoOutputStream pos = new ProtoOutputStream(fd);
 
         try {
-            PowerStatsData railInfo = new PowerStatsData(mPowerStatsHALWrapper.readRailInfo());
-            railInfo.toProto(pos);
-            if (DEBUG) railInfo.print();
+            ChannelInfo[] channelInfo = mPowerStatsHALWrapper.getEnergyMeterInfo();
+            ChannelInfoUtils.packProtoMessage(channelInfo, pos);
+            if (DEBUG) ChannelInfoUtils.print(channelInfo);
 
-            mPowerStatsDataStorage.read(new PowerStatsDataStorage.DataElementReadCallback() {
+            mPowerStatsMeterStorage.read(new PowerStatsDataStorage.DataElementReadCallback() {
                 @Override
                 public void onReadDataElement(byte[] data) {
                     try {
@@ -84,26 +101,70 @@
                         // TODO(b/166535853): ProtoOutputStream doesn't provide a method to write
                         // a byte array that already contains a serialized proto, so I have to
                         // deserialize, then re-serialize.  This is computationally inefficient.
-                        final PowerStatsData energyData = new PowerStatsData(pis);
-                        energyData.toProto(pos);
-                        if (DEBUG) energyData.print();
+                        EnergyMeasurement[] energyMeasurement =
+                            EnergyMeasurementUtils.unpackProtoMessage(data);
+                        EnergyMeasurementUtils.packProtoMessage(energyMeasurement, pos);
+                        if (DEBUG) EnergyMeasurementUtils.print(energyMeasurement);
                     } catch (IOException e) {
-                        Log.e(TAG, "Failed to write energy data to incident report.");
+                        Log.e(TAG, "Failed to write energy meter data to incident report.");
                     }
                 }
             });
         } catch (IOException e) {
-            Log.e(TAG, "Failed to write rail info to incident report.");
+            Log.e(TAG, "Failed to write energy meter info to incident report.");
         }
 
         pos.flush();
     }
 
-    public PowerStatsLogger(Context context, File dataStoragePath, String dataStorageFilename,
-            IPowerStatsHALWrapper powerStatsHALWrapper) {
+    /**
+     * Writes model data stored in PowerStatsDataStorage to a file descriptor.
+     *
+     * @param fd FileDescriptor where model data stored in PowerStatsDataStorage is written.  Data
+     *           is written in protobuf format as defined by powerstatsservice.proto.
+     */
+    public void writeModelDataToFile(FileDescriptor fd) {
+        if (DEBUG) Log.d(TAG, "Writing model data to file");
+
+        final ProtoOutputStream pos = new ProtoOutputStream(fd);
+
+        try {
+            int[] energyConsumerId = mPowerStatsHALWrapper.getEnergyConsumerInfo();
+            EnergyConsumerIdUtils.packProtoMessage(energyConsumerId, pos);
+            if (DEBUG) EnergyConsumerIdUtils.print(energyConsumerId);
+
+            mPowerStatsModelStorage.read(new PowerStatsDataStorage.DataElementReadCallback() {
+                @Override
+                public void onReadDataElement(byte[] data) {
+                    try {
+                        final ProtoInputStream pis =
+                                new ProtoInputStream(new ByteArrayInputStream(data));
+                        // TODO(b/166535853): ProtoOutputStream doesn't provide a method to write
+                        // a byte array that already contains a serialized proto, so I have to
+                        // deserialize, then re-serialize.  This is computationally inefficient.
+                        EnergyConsumerResult[] energyConsumerResult =
+                            EnergyConsumerResultUtils.unpackProtoMessage(data);
+                        EnergyConsumerResultUtils.packProtoMessage(energyConsumerResult, pos);
+                        if (DEBUG) EnergyConsumerResultUtils.print(energyConsumerResult);
+                    } catch (IOException e) {
+                        Log.e(TAG, "Failed to write energy model data to incident report.");
+                    }
+                }
+            });
+        } catch (IOException e) {
+            Log.e(TAG, "Failed to write energy model info to incident report.");
+        }
+
+        pos.flush();
+    }
+
+    public PowerStatsLogger(Context context, File dataStoragePath, String meterFilename,
+            String modelFilename, IPowerStatsHALWrapper powerStatsHALWrapper) {
         super(Looper.getMainLooper());
         mPowerStatsHALWrapper = powerStatsHALWrapper;
-        mPowerStatsDataStorage = new PowerStatsDataStorage(context, dataStoragePath,
-            dataStorageFilename);
+        mPowerStatsMeterStorage = new PowerStatsDataStorage(context, dataStoragePath,
+            meterFilename);
+        mPowerStatsModelStorage = new PowerStatsDataStorage(context, dataStoragePath,
+            modelFilename);
     }
 }
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java
index fd609c1..b89464f 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsService.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.powerstats;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.os.Binder;
 import android.os.Environment;
@@ -42,14 +43,18 @@
     private static final boolean DEBUG = false;
     private static final String DATA_STORAGE_SUBDIR = "powerstats";
     private static final int DATA_STORAGE_VERSION = 0;
-    private static final String DATA_STORAGE_FILENAME = "log.powerstats." + DATA_STORAGE_VERSION;
+    private static final String METER_FILENAME = "log.powerstats.meter." + DATA_STORAGE_VERSION;
+    private static final String MODEL_FILENAME = "log.powerstats.model." + DATA_STORAGE_VERSION;
 
     private final Injector mInjector;
 
     private Context mContext;
     private IPowerStatsHALWrapper mPowerStatsHALWrapper;
+    @Nullable
     private PowerStatsLogger mPowerStatsLogger;
+    @Nullable
     private BatteryTrigger mBatteryTrigger;
+    @Nullable
     private TimerTrigger mTimerTrigger;
 
     @VisibleForTesting
@@ -59,8 +64,12 @@
                 DATA_STORAGE_SUBDIR);
         }
 
-        String createDataStorageFilename() {
-            return DATA_STORAGE_FILENAME;
+        String createMeterFilename() {
+            return METER_FILENAME;
+        }
+
+        String createModelFilename() {
+            return MODEL_FILENAME;
         }
 
         IPowerStatsHALWrapper createPowerStatsHALWrapperImpl() {
@@ -68,9 +77,10 @@
         }
 
         PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath,
-                String dataStorageFilename, IPowerStatsHALWrapper powerStatsHALWrapper) {
-            return new PowerStatsLogger(context, dataStoragePath, dataStorageFilename,
-                powerStatsHALWrapper);
+                String meterFilename, String modelFilename,
+                IPowerStatsHALWrapper powerStatsHALWrapper) {
+            return new PowerStatsLogger(context, dataStoragePath, meterFilename,
+                modelFilename, powerStatsHALWrapper);
         }
 
         BatteryTrigger createBatteryTrigger(Context context, PowerStatsLogger powerStatsLogger) {
@@ -87,8 +97,16 @@
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
 
-            if (args.length > 0 && "--proto".equals(args[0])) {
-                mPowerStatsLogger.writeToFile(fd);
+            if (mPowerStatsLogger == null) {
+                Log.e(TAG, "PowerStats HAL is not initialized.  No data available.");
+            } else {
+                if (args.length > 0 && "--proto".equals(args[0])) {
+                    if ("model".equals(args[1])) {
+                        mPowerStatsLogger.writeModelDataToFile(fd);
+                    } else if ("meter".equals(args[1])) {
+                        mPowerStatsLogger.writeMeterDataToFile(fd);
+                    }
+                }
             }
         }
     }
@@ -113,8 +131,8 @@
 
             // Only start logger and triggers if initialization is successful.
             mPowerStatsLogger = mInjector.createPowerStatsLogger(mContext,
-                mInjector.createDataStoragePath(), mInjector.createDataStorageFilename(),
-                mPowerStatsHALWrapper);
+                mInjector.createDataStoragePath(), mInjector.createMeterFilename(),
+                mInjector.createModelFilename(), mPowerStatsHALWrapper);
             mBatteryTrigger = mInjector.createBatteryTrigger(mContext, mPowerStatsLogger);
             mTimerTrigger = mInjector.createTimerTrigger(mContext, mPowerStatsLogger);
         } else {
diff --git a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java
new file mode 100644
index 0000000..c29c5da
--- /dev/null
+++ b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.powerstats;
+
+import android.hardware.power.stats.ChannelInfo;
+import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyMeasurement;
+import android.util.Log;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+import android.util.proto.ProtoUtils;
+import android.util.proto.WireTypeMismatchException;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * ProtoStreamUtils provides helper functions for the PowerStats HAL objects returned from calls
+ * to the PowerStats HAL APIs.  It provides functions to pack/unpack object arrays to/from protobuf
+ * format.  These helper functions are required since frameworks code uses the genstream option
+ * when generating source code and therefore, getter/setter helper functions are not available.  The
+ * protobufs need to be packed/unpacked in a more manual way using
+ * ProtoOutputStream/ProtoInputStream.  It also provides print() functions for debugging purposes.
+ */
+public class ProtoStreamUtils {
+    private static final String TAG = ProtoStreamUtils.class.getSimpleName();
+
+    static class ChannelInfoUtils {
+        public static void packProtoMessage(ChannelInfo[] channelInfo, ProtoOutputStream pos) {
+            long token;
+
+            for (int i = 0; i < channelInfo.length; i++) {
+                token = pos.start(PowerStatsServiceMeterProto.CHANNEL_INFO);
+                pos.write(ChannelInfoProto.CHANNEL_ID, channelInfo[i].channelId);
+                pos.write(ChannelInfoProto.CHANNEL_NAME, channelInfo[i].channelName);
+                pos.end(token);
+            }
+
+        }
+
+        public static void print(ChannelInfo[] channelInfo) {
+            for (int i = 0; i < channelInfo.length; i++) {
+                Log.d(TAG, "ChannelId = " + channelInfo[i].channelId
+                        + ", ChannelName = " + channelInfo[i].channelName);
+            }
+        }
+    }
+
+    static class EnergyMeasurementUtils {
+        public static byte[] getProtoBytes(EnergyMeasurement[] energyMeasurement) {
+            ProtoOutputStream pos = new ProtoOutputStream();
+            packProtoMessage(energyMeasurement, pos);
+            return pos.getBytes();
+        }
+
+        public static void packProtoMessage(EnergyMeasurement[] energyMeasurement,
+                ProtoOutputStream pos) {
+            long token;
+
+            for (int i = 0; i < energyMeasurement.length; i++) {
+                token = pos.start(PowerStatsServiceMeterProto.ENERGY_MEASUREMENT);
+                pos.write(EnergyMeasurementProto.CHANNEL_ID, energyMeasurement[i].channelId);
+                pos.write(EnergyMeasurementProto.TIMESTAMP_MS, energyMeasurement[i].timestampMs);
+                pos.write(EnergyMeasurementProto.ENERGY_UWS, energyMeasurement[i].energyUWs);
+                pos.end(token);
+            }
+        }
+
+        public static EnergyMeasurement[] unpackProtoMessage(byte[] data) throws IOException {
+            final ProtoInputStream pis = new ProtoInputStream(new ByteArrayInputStream(data));
+            List<EnergyMeasurement> energyMeasurementList = new ArrayList<EnergyMeasurement>();
+            long token;
+
+            while (true) {
+                try {
+                    int nextField = pis.nextField();
+                    EnergyMeasurement energyMeasurement = new EnergyMeasurement();
+
+                    if (nextField == (int) PowerStatsServiceMeterProto.ENERGY_MEASUREMENT) {
+                        token = pis.start(PowerStatsServiceMeterProto.ENERGY_MEASUREMENT);
+                        energyMeasurementList.add(unpackProtoMessage(pis));
+                        pis.end(token);
+                    } else if (nextField == ProtoInputStream.NO_MORE_FIELDS) {
+                        return energyMeasurementList.toArray(
+                            new EnergyMeasurement[energyMeasurementList.size()]);
+                    } else {
+                        Log.e(TAG, "Unhandled field in proto: "
+                                + ProtoUtils.currentFieldToString(pis));
+                    }
+                } catch (WireTypeMismatchException wtme) {
+                    Log.e(TAG, "Wire Type mismatch in proto: "
+                            + ProtoUtils.currentFieldToString(pis));
+                }
+            }
+        }
+
+        private static EnergyMeasurement unpackProtoMessage(ProtoInputStream pis)
+                throws IOException {
+            EnergyMeasurement energyMeasurement = new EnergyMeasurement();
+
+            while (true) {
+                try {
+                    switch (pis.nextField()) {
+                        case (int) EnergyMeasurementProto.CHANNEL_ID:
+                            energyMeasurement.channelId =
+                                pis.readInt(EnergyMeasurementProto.CHANNEL_ID);
+                            break;
+
+                        case (int) EnergyMeasurementProto.TIMESTAMP_MS:
+                            energyMeasurement.timestampMs =
+                                pis.readLong(EnergyMeasurementProto.TIMESTAMP_MS);
+                            break;
+
+                        case (int) EnergyMeasurementProto.ENERGY_UWS:
+                            energyMeasurement.energyUWs =
+                                pis.readLong(EnergyMeasurementProto.ENERGY_UWS);
+                            break;
+
+                        case ProtoInputStream.NO_MORE_FIELDS:
+                            return energyMeasurement;
+
+                        default:
+                            Log.e(TAG, "Unhandled field in EnergyMeasurementProto: "
+                                    + ProtoUtils.currentFieldToString(pis));
+                            break;
+                    }
+                } catch (WireTypeMismatchException wtme) {
+                    Log.e(TAG, "Wire Type mismatch in EnergyMeasurementProto: "
+                            + ProtoUtils.currentFieldToString(pis));
+                }
+            }
+        }
+
+        public static void print(EnergyMeasurement[] energyMeasurement) {
+            for (int i = 0; i < energyMeasurement.length; i++) {
+                Log.d(TAG, "ChannelId = " + energyMeasurement[i].channelId
+                        + ", Timestamp (ms) = " + energyMeasurement[i].timestampMs
+                        + ", Energy (uWs) = " + energyMeasurement[i].energyUWs);
+            }
+        }
+    }
+
+    static class EnergyConsumerIdUtils {
+        public static void packProtoMessage(int[] energyConsumerId, ProtoOutputStream pos) {
+            long token;
+
+            for (int i = 0; i < energyConsumerId.length; i++) {
+                token = pos.start(PowerStatsServiceModelProto.ENERGY_CONSUMER_ID);
+                pos.write(EnergyConsumerIdProto.ENERGY_CONSUMER_ID, energyConsumerId[i]);
+                pos.end(token);
+            }
+        }
+
+        public static void print(int[] energyConsumerId) {
+            for (int i = 0; i < energyConsumerId.length; i++) {
+                Log.d(TAG, "EnergyConsumerId = " + energyConsumerId[i]);
+            }
+        }
+    }
+
+    static class EnergyConsumerResultUtils {
+        public static byte[] getProtoBytes(EnergyConsumerResult[] energyConsumerResult) {
+            ProtoOutputStream pos = new ProtoOutputStream();
+            packProtoMessage(energyConsumerResult, pos);
+            return pos.getBytes();
+        }
+
+        public static void packProtoMessage(EnergyConsumerResult[] energyConsumerResult,
+                ProtoOutputStream pos) {
+            long token;
+
+            for (int i = 0; i < energyConsumerResult.length; i++) {
+                token = pos.start(PowerStatsServiceModelProto.ENERGY_CONSUMER_RESULT);
+                pos.write(EnergyConsumerResultProto.ENERGY_CONSUMER_ID,
+                        energyConsumerResult[i].energyConsumerId);
+                pos.write(EnergyConsumerResultProto.TIMESTAMP_MS,
+                        energyConsumerResult[i].timestampMs);
+                pos.write(EnergyConsumerResultProto.ENERGY_UWS, energyConsumerResult[i].energyUWs);
+                pos.end(token);
+            }
+        }
+
+        public static EnergyConsumerResult[] unpackProtoMessage(byte[] data) throws IOException {
+            final ProtoInputStream pis = new ProtoInputStream(new ByteArrayInputStream(data));
+            List<EnergyConsumerResult> energyConsumerResultList =
+                    new ArrayList<EnergyConsumerResult>();
+            long token;
+
+            while (true) {
+                try {
+                    int nextField = pis.nextField();
+                    EnergyConsumerResult energyConsumerResult = new EnergyConsumerResult();
+
+                    if (nextField == (int) PowerStatsServiceModelProto.ENERGY_CONSUMER_RESULT) {
+                        token = pis.start(PowerStatsServiceModelProto.ENERGY_CONSUMER_RESULT);
+                        energyConsumerResultList.add(unpackProtoMessage(pis));
+                        pis.end(token);
+                    } else if (nextField == ProtoInputStream.NO_MORE_FIELDS) {
+                        return energyConsumerResultList.toArray(
+                            new EnergyConsumerResult[energyConsumerResultList.size()]);
+                    } else {
+                        Log.e(TAG, "Unhandled field in proto: "
+                                + ProtoUtils.currentFieldToString(pis));
+                    }
+                } catch (WireTypeMismatchException wtme) {
+                    Log.e(TAG, "Wire Type mismatch in proto: "
+                            + ProtoUtils.currentFieldToString(pis));
+                }
+            }
+        }
+
+        private static EnergyConsumerResult unpackProtoMessage(ProtoInputStream pis)
+                throws IOException {
+            EnergyConsumerResult energyConsumerResult = new EnergyConsumerResult();
+
+            while (true) {
+                try {
+                    switch (pis.nextField()) {
+                        case (int) EnergyConsumerResultProto.ENERGY_CONSUMER_ID:
+                            energyConsumerResult.energyConsumerId =
+                                pis.readInt(EnergyConsumerResultProto.ENERGY_CONSUMER_ID);
+                            break;
+
+                        case (int) EnergyConsumerResultProto.TIMESTAMP_MS:
+                            energyConsumerResult.timestampMs =
+                                pis.readLong(EnergyConsumerResultProto.TIMESTAMP_MS);
+                            break;
+
+                        case (int) EnergyConsumerResultProto.ENERGY_UWS:
+                            energyConsumerResult.energyUWs =
+                                pis.readLong(EnergyConsumerResultProto.ENERGY_UWS);
+                            break;
+
+                        case ProtoInputStream.NO_MORE_FIELDS:
+                            return energyConsumerResult;
+
+                        default:
+                            Log.e(TAG, "Unhandled field in EnergyConsumerResultProto: "
+                                    + ProtoUtils.currentFieldToString(pis));
+                            break;
+                    }
+                } catch (WireTypeMismatchException wtme) {
+                    Log.e(TAG, "Wire Type mismatch in EnergyConsumerResultProto: "
+                            + ProtoUtils.currentFieldToString(pis));
+                }
+            }
+        }
+
+        public static void print(EnergyConsumerResult[] energyConsumerResult) {
+            for (int i = 0; i < energyConsumerResult.length; i++) {
+                Log.d(TAG, "EnergyConsumerId = " + energyConsumerResult[i].energyConsumerId
+                        + ", Timestamp (ms) = " + energyConsumerResult[i].timestampMs
+                        + ", Energy (uWs) = " + energyConsumerResult[i].energyUWs);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index a291cef..e7b1b4e 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -660,7 +660,7 @@
 
         @Override
         public String getDefaultSmsPackage(int userId) {
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 return CollectionUtils.firstOrNull(
                         getRoleHoldersAsUser(RoleManager.ROLE_SMS, userId));
@@ -699,7 +699,7 @@
         }
 
         private int getUidForPackage(String packageName) {
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
                 return getContext().getPackageManager().getApplicationInfo(packageName,
                         PackageManager.MATCH_ANY_USER).uid;
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 9350edf..1e89e06 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -28,7 +28,6 @@
 import android.content.IntentFilter;
 import android.content.IntentSender;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.ModuleInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
@@ -1089,8 +1088,8 @@
                 Manifest.permission.TEST_MANAGE_ROLLBACKS,
                 installerPackageName) == PackageManager.PERMISSION_GRANTED;
 
-        // For now only allow rollbacks for modules or for testing.
-        return (isRollbackWhitelisted(packageName) && manageRollbacksGranted)
+        // For now only allow rollbacks for allowlisted packages or for testing.
+        return (isRollbackAllowlisted(packageName) && manageRollbacksGranted)
             || testManageRollbacksGranted;
     }
 
@@ -1098,25 +1097,8 @@
      * Returns true is this package is eligible for enabling rollback.
      */
     @AnyThread
-    private boolean isRollbackWhitelisted(String packageName) {
-        // TODO: Remove #isModule when the allowlist is ready.
-        return SystemConfig.getInstance().getRollbackWhitelistedPackages().contains(packageName)
-                || isModule(packageName);
-    }
-    /**
-     * Returns true if the package name is the name of a module.
-     */
-    @AnyThread
-    private boolean isModule(String packageName) {
-        PackageManager pm = mContext.getPackageManager();
-        final ModuleInfo moduleInfo;
-        try {
-            moduleInfo = pm.getModuleInfo(packageName, 0);
-        } catch (PackageManager.NameNotFoundException e) {
-            return false;
-        }
-
-        return moduleInfo != null;
+    private boolean isRollbackAllowlisted(String packageName) {
+        return SystemConfig.getInstance().getRollbackWhitelistedPackages().contains(packageName);
     }
 
     /**
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index c07b94d..ba1401d 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -27,7 +27,6 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
 import android.content.pm.VersionedPackage;
 import android.content.rollback.PackageRollbackInfo;
 import android.content.rollback.RollbackInfo;
@@ -44,13 +43,11 @@
 
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.Preconditions;
-import com.android.server.LocalServices;
 import com.android.server.PackageWatchdog;
 import com.android.server.PackageWatchdog.FailureReasons;
 import com.android.server.PackageWatchdog.PackageHealthObserver;
 import com.android.server.PackageWatchdog.PackageHealthObserverImpact;
 import com.android.server.pm.ApexManager;
-import com.android.server.pm.parsing.pkg.AndroidPackage;
 
 import java.io.BufferedReader;
 import java.io.File;
@@ -342,14 +339,10 @@
     private boolean isModule(String packageName) {
         // Check if the package is an APK inside an APEX. If it is, use the parent APEX package when
         // querying PackageManager.
-        PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
-        AndroidPackage apkPackage = pmi.getPackage(packageName);
-        if (apkPackage != null) {
-            String apexPackageName = mApexManager.getActiveApexPackageNameContainingPackage(
-                    apkPackage);
-            if (apexPackageName != null) {
-                packageName = apexPackageName;
-            }
+        String apexPackageName = mApexManager.getActiveApexPackageNameContainingPackage(
+                packageName);
+        if (apexPackageName != null) {
+            packageName = apexPackageName;
         }
 
         PackageManager pm = mContext.getPackageManager();
diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java
index a4fa745..44a6336 100644
--- a/services/core/java/com/android/server/rollback/RollbackStore.java
+++ b/services/core/java/com/android/server/rollback/RollbackStore.java
@@ -25,6 +25,7 @@
 import android.content.rollback.PackageRollbackInfo.RestoreInfo;
 import android.content.rollback.RollbackInfo;
 import android.os.UserHandle;
+import android.util.AtomicFile;
 import android.util.Slog;
 import android.util.SparseIntArray;
 
@@ -37,6 +38,7 @@
 import org.json.JSONObject;
 
 import java.io.File;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.nio.file.Files;
@@ -66,8 +68,6 @@
     //
     // * XXX, YYY are the rollbackIds for the corresponding rollbacks.
     // * rollback.json contains all relevant metadata for the rollback.
-    //
-    // TODO: Use AtomicFile for all the .json files?
     private final File mRollbackDataDir;
 
     RollbackStore(File rollbackDataDir) {
@@ -259,6 +259,8 @@
      * Saves the given rollback to persistent storage.
      */
     static void saveRollback(Rollback rollback) {
+        FileOutputStream fos = null;
+        AtomicFile file = new AtomicFile(new File(rollback.getBackupDir(), "rollback.json"));
         try {
             JSONObject dataJson = new JSONObject();
             dataJson.put("info", rollbackInfoToJson(rollback.info));
@@ -272,11 +274,16 @@
             dataJson.putOpt(
                     "extensionVersions", extensionVersionsToJson(rollback.getExtensionVersions()));
 
-            PrintWriter pw = new PrintWriter(new File(rollback.getBackupDir(), "rollback.json"));
+            fos = file.startWrite();
+            PrintWriter pw = new PrintWriter(fos);
             pw.println(dataJson.toString());
             pw.close();
+            file.finishWrite(fos);
         } catch (JSONException | IOException e) {
             Slog.e(TAG, "Unable to save rollback for: " + rollback.info.getRollbackId(), e);
+            if (fos != null) {
+                file.failWrite(fos);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/search/Searchables.java b/services/core/java/com/android/server/search/Searchables.java
index ca7f036..6e1e979 100644
--- a/services/core/java/com/android/server/search/Searchables.java
+++ b/services/core/java/com/android/server/search/Searchables.java
@@ -234,7 +234,7 @@
         List<ResolveInfo> searchList;
         final Intent intent = new Intent(Intent.ACTION_SEARCH);
 
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             searchList = queryIntentActivities(intent,
                     PackageManager.GET_META_DATA | PackageManager.MATCH_DEBUG_TRIAGED_MISSING);
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index 2a74b3d..ee9694f 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -281,7 +281,7 @@
             String providerPkg = getProviderPkg(grantUri, providerUser);
             mPermissions.grantSliceAccess(pkg, userId, providerPkg, providerUser, grantUri);
         }
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             mContext.getContentResolver().notifyChange(uri, null);
         } finally {
@@ -402,7 +402,7 @@
     }
 
     private String getProviderPkg(Uri uri, int user) {
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             String providerName = getUriWithoutUserId(uri).getAuthority();
             ProviderInfo provider = mContext.getPackageManager().resolveContentProviderAsUser(
@@ -438,7 +438,7 @@
     }
 
     private boolean hasFullSliceAccess(String pkg, int userId) {
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             boolean ret = isDefaultHomeApp(pkg, userId) || isAssistant(pkg, userId)
                     || isGrantedFullAccess(pkg, userId);
diff --git a/services/core/java/com/android/server/slice/SliceShellCommand.java b/services/core/java/com/android/server/slice/SliceShellCommand.java
index 9137a3b..bdc8bbd 100644
--- a/services/core/java/com/android/server/slice/SliceShellCommand.java
+++ b/services/core/java/com/android/server/slice/SliceShellCommand.java
@@ -69,7 +69,7 @@
             return -1;
         }
         Context context = mService.getContext();
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             Uri uri = new Uri.Builder()
                     .scheme(ContentResolver.SCHEME_CONTENT)
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java b/services/core/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java
index 7977e93..9404904 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java
@@ -16,11 +16,6 @@
 
 package com.android.server.soundtrigger_middleware;
 
-import android.media.ICaptureStateListener;
-import android.os.IBinder;
-import android.os.Parcel;
-import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.util.Log;
 
 import java.util.concurrent.Semaphore;
@@ -78,7 +73,11 @@
      * @param active true when external capture is active.
      */
     private void setCaptureState(boolean active) {
-        mListener.accept(active);
+        try {
+            mListener.accept(active);
+        } catch (Exception e) {
+            Log.e(TAG, "Exception caught while setting capture state", e);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
index 7b6c656..51b00fa 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
@@ -236,7 +236,8 @@
 
         @Override
         public void unloadModel(int modelHandle) throws RemoteException {
-            enforcePermissions();
+            // Unloading a model does not require special permissions. Having a handle to the
+            // session is sufficient.
             mDelegate.unloadModel(modelHandle);
 
         }
@@ -250,7 +251,8 @@
 
         @Override
         public void stopRecognition(int modelHandle) throws RemoteException {
-            enforcePermissions();
+            // Stopping a model does not require special permissions. Having a handle to the
+            // session is sufficient.
             mDelegate.stopRecognition(modelHandle);
         }
 
@@ -284,7 +286,8 @@
 
         @Override
         public void detach() throws RemoteException {
-            enforcePermissions();
+            // Detaching does not require special permissions. Having a handle to the session is
+            // sufficient.
             mDelegate.detach();
         }
 
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index c871a5e..f43a4ce 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -1574,7 +1574,7 @@
     }
 
     int pullWifiActivityInfoLocked(int atomTag, List<StatsEvent> pulledData) {
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             SynchronousResultReceiver wifiReceiver = new SynchronousResultReceiver("wifi");
             mWifiManager.getWifiActivityEnergyInfoAsync(
@@ -1622,7 +1622,7 @@
     }
 
     int pullModemActivityInfoLocked(int atomTag, List<StatsEvent> pulledData) {
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             SynchronousResultReceiver modemReceiver = new SynchronousResultReceiver("telephony");
             mTelephony.requestModemActivityInfo(modemReceiver);
@@ -1630,13 +1630,14 @@
             if (modemInfo == null) {
                 return StatsManager.PULL_SKIP;
             }
-            pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, modemInfo.getTimestamp(),
+            pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag,
+                    modemInfo.getTimestampMillis(),
                     modemInfo.getSleepTimeMillis(), modemInfo.getIdleTimeMillis(),
-                    modemInfo.getTransmitPowerInfo().get(0).getTimeInMillis(),
-                    modemInfo.getTransmitPowerInfo().get(1).getTimeInMillis(),
-                    modemInfo.getTransmitPowerInfo().get(2).getTimeInMillis(),
-                    modemInfo.getTransmitPowerInfo().get(3).getTimeInMillis(),
-                    modemInfo.getTransmitPowerInfo().get(4).getTimeInMillis(),
+                    modemInfo.getTransmitDurationMillisAtPowerLevel(0),
+                    modemInfo.getTransmitDurationMillisAtPowerLevel(1),
+                    modemInfo.getTransmitDurationMillisAtPowerLevel(2),
+                    modemInfo.getTransmitDurationMillisAtPowerLevel(3),
+                    modemInfo.getTransmitDurationMillisAtPowerLevel(4),
                     modemInfo.getReceiveTimeMillis(),
                     -1 /*`energy_used` field name deprecated, use -1 to indicate as unused.*/));
         } finally {
@@ -2723,7 +2724,7 @@
 
     // Add a RoleHolder atom for each package that holds a role.
     int pullRoleHolderLocked(int atomTag, List<StatsEvent> pulledData) {
-        long callingToken = Binder.clearCallingIdentity();
+        final long callingToken = Binder.clearCallingIdentity();
         try {
             PackageManager pm = mContext.getPackageManager();
             RoleManagerInternal rmi = LocalServices.getService(RoleManagerInternal.class);
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index e9215f9..ebfffec 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -86,6 +86,13 @@
     void toggleSplitScreen();
     void appTransitionFinished(int displayId);
 
+    /**
+     * Notifies the status bar that a Emergency Action launch gesture has been detected.
+     *
+     * TODO (b/169175022) Update method name and docs when feature name is locked.
+     */
+    void onEmergencyActionLaunchGestureDetected();
+
     void toggleRecentApps();
 
     void setCurrentUser(int newUserId);
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index d1fd0ad..55cb7f3 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -264,6 +264,23 @@
             }
         }
 
+        /**
+         * Notifies the status bar that a Emergency Action launch gesture has been detected.
+         *
+         * TODO (b/169175022) Update method name and docs when feature name is locked.
+         */
+        @Override
+        public void onEmergencyActionLaunchGestureDetected() {
+            if (SPEW) Slog.d(TAG, "Launching emergency action");
+            if (mBar != null) {
+                try {
+                    mBar.onEmergencyActionLaunchGestureDetected();
+                } catch (RemoteException e) {
+                    if (SPEW) Slog.d(TAG, "Failed to launch emergency action");
+                }
+            }
+        }
+
         @Override
         public void topAppWindowChanged(int displayId, boolean isFullscreen, boolean isImmersive) {
             StatusBarManagerService.this.topAppWindowChanged(displayId, isFullscreen, isImmersive);
@@ -1158,7 +1175,7 @@
     @Override
     public void onPanelRevealed(boolean clearNotificationEffects, int numItems) {
         enforceStatusBarService();
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             mNotificationDelegate.onPanelRevealed(clearNotificationEffects, numItems);
         } finally {
@@ -1169,7 +1186,7 @@
     @Override
     public void clearNotificationEffects() throws RemoteException {
         enforceStatusBarService();
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             mNotificationDelegate.clearEffects();
         } finally {
@@ -1180,7 +1197,7 @@
     @Override
     public void onPanelHidden() throws RemoteException {
         enforceStatusBarService();
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             mNotificationDelegate.onPanelHidden();
         } finally {
@@ -1196,7 +1213,7 @@
         enforceStatusBarService();
         String reason = PowerManager.SHUTDOWN_USER_REQUESTED;
         ShutdownCheckPoints.recordCheckPoint(Binder.getCallingPid(), reason);
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             mNotificationDelegate.prepareForPossibleShutdown();
             // ShutdownThread displays UI, so give it a UI context.
@@ -1217,7 +1234,7 @@
                 ? PowerManager.REBOOT_SAFE_MODE
                 : PowerManager.SHUTDOWN_USER_REQUESTED;
         ShutdownCheckPoints.recordCheckPoint(Binder.getCallingPid(), reason);
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             mNotificationDelegate.prepareForPossibleShutdown();
             mHandler.post(() -> {
@@ -1236,7 +1253,7 @@
     @Override
     public void onGlobalActionsShown() {
         enforceStatusBarService();
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             if (mGlobalActionListener == null) return;
             mGlobalActionListener.onGlobalActionsShown();
@@ -1248,7 +1265,7 @@
     @Override
     public void onGlobalActionsHidden() {
         enforceStatusBarService();
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             if (mGlobalActionListener == null) return;
             mGlobalActionListener.onGlobalActionsDismissed();
@@ -1262,7 +1279,7 @@
         enforceStatusBarService();
         final int callingUid = Binder.getCallingUid();
         final int callingPid = Binder.getCallingPid();
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             mNotificationDelegate.onNotificationClick(callingUid, callingPid, key, nv);
         } finally {
@@ -1277,7 +1294,7 @@
         enforceStatusBarService();
         final int callingUid = Binder.getCallingUid();
         final int callingPid = Binder.getCallingPid();
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             mNotificationDelegate.onNotificationActionClick(callingUid, callingPid, key,
                     actionIndex, action, nv, generatedByAssistant);
@@ -1292,7 +1309,7 @@
         enforceStatusBarService();
         final int callingUid = Binder.getCallingUid();
         final int callingPid = Binder.getCallingPid();
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             // WARNING: this will call back into us to do the remove.  Don't hold any locks.
             mNotificationDelegate.onNotificationError(callingUid, callingPid,
@@ -1310,7 +1327,7 @@
         enforceStatusBarService();
         final int callingUid = Binder.getCallingUid();
         final int callingPid = Binder.getCallingPid();
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             mNotificationDelegate.onNotificationClear(callingUid, callingPid, pkg, tag, id, userId,
                     key, dismissalSurface, dismissalSentiment, nv);
@@ -1324,7 +1341,7 @@
             NotificationVisibility[] newlyVisibleKeys, NotificationVisibility[] noLongerVisibleKeys)
             throws RemoteException {
         enforceStatusBarService();
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             mNotificationDelegate.onNotificationVisibilityChanged(
                     newlyVisibleKeys, noLongerVisibleKeys);
@@ -1337,7 +1354,7 @@
     public void onNotificationExpansionChanged(String key, boolean userAction, boolean expanded,
             int location) throws RemoteException {
         enforceStatusBarService();
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             mNotificationDelegate.onNotificationExpansionChanged(
                     key, userAction, expanded, location);
@@ -1349,7 +1366,7 @@
     @Override
     public void onNotificationDirectReplied(String key) throws RemoteException {
         enforceStatusBarService();
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             mNotificationDelegate.onNotificationDirectReplied(key);
         } finally {
@@ -1361,7 +1378,7 @@
     public void onNotificationSmartSuggestionsAdded(String key, int smartReplyCount,
             int smartActionCount, boolean generatedByAssistant, boolean editBeforeSending) {
         enforceStatusBarService();
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             mNotificationDelegate.onNotificationSmartSuggestionsAdded(key, smartReplyCount,
                     smartActionCount, generatedByAssistant, editBeforeSending);
@@ -1375,7 +1392,7 @@
             String key, int replyIndex, CharSequence reply, int notificationLocation,
             boolean modifiedBeforeSending) throws RemoteException {
         enforceStatusBarService();
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             mNotificationDelegate.onNotificationSmartReplySent(key, replyIndex, reply,
                     notificationLocation, modifiedBeforeSending);
@@ -1387,7 +1404,7 @@
     @Override
     public void onNotificationSettingsViewed(String key) throws RemoteException {
         enforceStatusBarService();
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             mNotificationDelegate.onNotificationSettingsViewed(key);
         } finally {
@@ -1400,7 +1417,7 @@
         enforceStatusBarService();
         final int callingUid = Binder.getCallingUid();
         final int callingPid = Binder.getCallingPid();
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             mNotificationDelegate.onClearAll(callingUid, callingPid, userId);
         } finally {
@@ -1411,7 +1428,7 @@
     @Override
     public void onNotificationBubbleChanged(String key, boolean isBubble, int flags) {
         enforceStatusBarService();
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             mNotificationDelegate.onNotificationBubbleChanged(key, isBubble, flags);
         } finally {
@@ -1422,7 +1439,7 @@
     @Override
     public void onBubbleNotificationSuppressionChanged(String key, boolean isNotifSuppressed) {
         enforceStatusBarService();
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             mNotificationDelegate.onBubbleNotificationSuppressionChanged(key, isNotifSuppressed);
         } finally {
@@ -1446,7 +1463,7 @@
             String packageName) {
         enforceStatusBarService();
         int callingUid = Binder.getCallingUid();
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             mNotificationDelegate.grantInlineReplyUriPermission(key, uri, user, packageName,
                     callingUid);
@@ -1459,7 +1476,7 @@
     public void clearInlineReplyUriPermissions(String key) {
         enforceStatusBarService();
         int callingUid = Binder.getCallingUid();
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             mNotificationDelegate.clearInlineReplyUriPermissions(key, callingUid);
         } finally {
diff --git a/services/core/java/com/android/server/testharness/TestHarnessModeService.java b/services/core/java/com/android/server/testharness/TestHarnessModeService.java
index 5311369..52236a8 100644
--- a/services/core/java/com/android/server/testharness/TestHarnessModeService.java
+++ b/services/core/java/com/android/server/testharness/TestHarnessModeService.java
@@ -25,7 +25,6 @@
 import android.content.Intent;
 import android.content.pm.UserInfo;
 import android.debug.AdbManagerInternal;
-import android.debug.AdbTransportType;
 import android.location.LocationManager;
 import android.os.BatteryManager;
 import android.os.Binder;
@@ -162,12 +161,11 @@
     private void configureSettings() {
         ContentResolver cr = getContext().getContentResolver();
 
-        // Stop ADB before we enable it, otherwise on userdebug/eng builds, the keys won't have
-        // registered with adbd, and it will prompt the user to confirm the keys.
-        Settings.Global.putInt(cr, Settings.Global.ADB_ENABLED, 0);
-        AdbManagerInternal adbManager = LocalServices.getService(AdbManagerInternal.class);
-        if (adbManager.isAdbEnabled(AdbTransportType.USB)) {
-            adbManager.stopAdbdForTransport(AdbTransportType.USB);
+        // If adb is already enabled, then we need to restart the daemon to pick up the change in
+        // keys. This is only really useful for userdebug/eng builds.
+        if (Settings.Global.getInt(cr, Settings.Global.ADB_ENABLED, 0) == 1) {
+            SystemProperties.set("ctl.restart", "adbd");
+            Slog.d(TAG, "Restarted adbd");
         }
 
         // Disable the TTL for ADB keys before enabling ADB
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index 70ab48b..59cebf7 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -114,7 +114,7 @@
         enforceSuggestManualTimePermission();
         Objects.requireNonNull(timeSignal);
 
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             return mTimeDetectorStrategy.suggestManualTime(timeSignal);
         } finally {
diff --git a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
index 9e76bc1..1f73977 100644
--- a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
+++ b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
@@ -28,6 +28,8 @@
 import android.app.time.TimeZoneConfiguration;
 import android.os.UserHandle;
 
+import com.android.internal.util.Preconditions;
+
 import java.util.Objects;
 
 /**
@@ -40,6 +42,7 @@
     private final @UserIdInt int mUserId;
     private final boolean mUserConfigAllowed;
     private final boolean mAutoDetectionSupported;
+    private final boolean mGeoDetectionSupported;
     private final boolean mAutoDetectionEnabled;
     private final boolean mLocationEnabled;
     private final boolean mGeoDetectionEnabled;
@@ -48,9 +51,13 @@
         mUserId = builder.mUserId;
         mUserConfigAllowed = builder.mUserConfigAllowed;
         mAutoDetectionSupported = builder.mAutoDetectionSupported;
+        mGeoDetectionSupported = builder.mGeoDetectionSupported;
         mAutoDetectionEnabled = builder.mAutoDetectionEnabled;
         mLocationEnabled = builder.mLocationEnabled;
         mGeoDetectionEnabled = builder.mGeoDetectionEnabled;
+        // if mGeoDetectionSupported then mAutoDetectionSupported, i.e. mGeoDetectionSupported
+        // cannot be true if mAutoDetectionSupported == false
+        Preconditions.checkState(mAutoDetectionSupported || !mGeoDetectionSupported);
     }
 
     /** Returns the ID of the user this configuration is associated with. */
@@ -69,11 +76,16 @@
         return mUserConfigAllowed;
     }
 
-    /** Returns true if the device supports some form of auto time zone detection. */
+    /** Returns true if the device supports any form of auto time zone detection. */
     public boolean isAutoDetectionSupported() {
         return mAutoDetectionSupported;
     }
 
+    /** Returns true if the device supports geolocation time zone detection. */
+    public boolean isGeoDetectionSupported() {
+        return mGeoDetectionSupported;
+    }
+
     /** Returns the value of the auto time zone detection enabled setting. */
     public boolean getAutoDetectionEnabledSetting() {
         return mAutoDetectionEnabled;
@@ -101,10 +113,10 @@
      * distinct from the raw setting value.
      */
     public boolean getGeoDetectionEnabledBehavior() {
-        if (getAutoDetectionEnabledBehavior()) {
-            return mLocationEnabled && mGeoDetectionEnabled;
-        }
-        return false;
+        return getAutoDetectionEnabledBehavior()
+                && isGeoDetectionSupported()
+                && isLocationEnabled()
+                && getGeoDetectionEnabledSetting();
     }
 
     /** Creates a {@link TimeZoneCapabilitiesAndConfig} object using the configuration values. */
@@ -121,10 +133,10 @@
 
         // Automatic time zone detection is only supported on devices if there is a telephony
         // network available or geolocation time zone detection is possible.
-        boolean deviceHasTimeZoneDetection = isAutoDetectionSupported();
+        boolean deviceHasAutoTimeZoneDetection = isAutoDetectionSupported();
 
         final int configureAutoDetectionEnabledCapability;
-        if (!deviceHasTimeZoneDetection) {
+        if (!deviceHasAutoTimeZoneDetection) {
             configureAutoDetectionEnabledCapability = CAPABILITY_NOT_SUPPORTED;
         } else if (!allowConfigDateTime) {
             configureAutoDetectionEnabledCapability = CAPABILITY_NOT_ALLOWED;
@@ -133,12 +145,13 @@
         }
         builder.setConfigureAutoDetectionEnabledCapability(configureAutoDetectionEnabledCapability);
 
+        boolean deviceHasLocationTimeZoneDetection = isGeoDetectionSupported();
         final int configureGeolocationDetectionEnabledCapability;
-        if (!deviceHasTimeZoneDetection) {
+        if (!deviceHasLocationTimeZoneDetection) {
             configureGeolocationDetectionEnabledCapability = CAPABILITY_NOT_SUPPORTED;
         } else if (!allowConfigDateTime) {
             configureGeolocationDetectionEnabledCapability = CAPABILITY_NOT_ALLOWED;
-        } else if (!isLocationEnabled()) {
+        } else if (!mAutoDetectionEnabled || !isLocationEnabled()) {
             configureGeolocationDetectionEnabledCapability = CAPABILITY_NOT_APPLICABLE;
         } else {
             configureGeolocationDetectionEnabledCapability = CAPABILITY_POSSESSED;
@@ -199,6 +212,7 @@
         return mUserId == that.mUserId
                 && mUserConfigAllowed == that.mUserConfigAllowed
                 && mAutoDetectionSupported == that.mAutoDetectionSupported
+                && mGeoDetectionSupported == that.mGeoDetectionSupported
                 && mAutoDetectionEnabled == that.mAutoDetectionEnabled
                 && mLocationEnabled == that.mLocationEnabled
                 && mGeoDetectionEnabled == that.mGeoDetectionEnabled;
@@ -207,7 +221,8 @@
     @Override
     public int hashCode() {
         return Objects.hash(mUserId, mUserConfigAllowed, mAutoDetectionSupported,
-                mAutoDetectionEnabled, mLocationEnabled, mGeoDetectionEnabled);
+                mGeoDetectionSupported, mAutoDetectionEnabled, mLocationEnabled,
+                mGeoDetectionEnabled);
     }
 
     @Override
@@ -216,6 +231,7 @@
                 + "mUserId=" + mUserId
                 + ", mUserConfigAllowed=" + mUserConfigAllowed
                 + ", mAutoDetectionSupported=" + mAutoDetectionSupported
+                + ", mGeoDetectionSupported=" + mGeoDetectionSupported
                 + ", mAutoDetectionEnabled=" + mAutoDetectionEnabled
                 + ", mLocationEnabled=" + mLocationEnabled
                 + ", mGeoDetectionEnabled=" + mGeoDetectionEnabled
@@ -228,8 +244,10 @@
     public static class Builder {
 
         private final @UserIdInt int mUserId;
+
         private boolean mUserConfigAllowed;
         private boolean mAutoDetectionSupported;
+        private boolean mGeoDetectionSupported;
         private boolean mAutoDetectionEnabled;
         private boolean mLocationEnabled;
         private boolean mGeoDetectionEnabled;
@@ -248,6 +266,7 @@
             this.mUserId = toCopy.mUserId;
             this.mUserConfigAllowed = toCopy.mUserConfigAllowed;
             this.mAutoDetectionSupported = toCopy.mAutoDetectionSupported;
+            this.mGeoDetectionSupported = toCopy.mGeoDetectionSupported;
             this.mAutoDetectionEnabled = toCopy.mAutoDetectionEnabled;
             this.mLocationEnabled = toCopy.mLocationEnabled;
             this.mGeoDetectionEnabled = toCopy.mGeoDetectionEnabled;
@@ -262,7 +281,7 @@
         }
 
         /**
-         * Sets whether automatic time zone detection is supported on this device.
+         * Sets whether any form of automatic time zone detection is supported on this device.
          */
         public Builder setAutoDetectionSupported(boolean supported) {
             mAutoDetectionSupported = supported;
@@ -270,6 +289,14 @@
         }
 
         /**
+         * Sets whether geolocation time zone detection is supported on this device.
+         */
+        public Builder setGeoDetectionSupported(boolean supported) {
+            mGeoDetectionSupported = supported;
+            return this;
+        }
+
+        /**
          * Sets the value of the automatic time zone detection enabled setting for this device.
          */
         public Builder setAutoDetectionEnabled(boolean enabled) {
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
index 964dbec..941be0e 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
@@ -119,13 +119,13 @@
 
     @Override
     public ConfigurationInternal getConfigurationInternal(@UserIdInt int userId) {
-        boolean geoDetectionEnabled = mGeoDetectionFeatureEnabled && isGeoDetectionEnabled(userId);
         return new ConfigurationInternal.Builder(userId)
                 .setUserConfigAllowed(isUserConfigAllowed(userId))
                 .setAutoDetectionSupported(isAutoDetectionSupported())
+                .setGeoDetectionSupported(isGeoDetectionSupported())
                 .setAutoDetectionEnabled(isAutoDetectionEnabled())
                 .setLocationEnabled(isLocationEnabled(userId))
-                .setGeoDetectionEnabled(geoDetectionEnabled)
+                .setGeoDetectionEnabled(isGeoDetectionEnabled(userId))
                 .build();
     }
 
@@ -170,7 +170,11 @@
             final boolean autoDetectionEnabled = configuration.isAutoDetectionEnabled();
             setAutoDetectionEnabled(autoDetectionEnabled);
 
-            if (mGeoDetectionFeatureEnabled) {
+            // Avoid writing the geo detection enabled setting for devices that do not support geo
+            // time zone detection: if we wrote it down then we'd set the value explicitly, which
+            // would prevent detecting "default" later. That might influence what happens on later
+            // releases that support geo detection on the same hardware.
+            if (isGeoDetectionSupported()) {
                 final boolean geoTzDetectionEnabled = configuration.isGeoDetectionEnabled();
                 setGeoDetectionEnabled(userId, geoTzDetectionEnabled);
             }
@@ -183,7 +187,11 @@
     }
 
     private boolean isAutoDetectionSupported() {
-        return deviceHasTelephonyNetwork() || mGeoDetectionFeatureEnabled;
+        return deviceHasTelephonyNetwork() || isGeoDetectionSupported();
+    }
+
+    private boolean isGeoDetectionSupported() {
+        return mGeoDetectionFeatureEnabled;
     }
 
     private boolean isAutoDetectionEnabled() {
@@ -199,10 +207,10 @@
     }
 
     private boolean isGeoDetectionEnabled(@UserIdInt int userId) {
-        final boolean locationEnabled = isLocationEnabled(userId);
+        final boolean geoDetectionEnabledByDefault = false;
         return Settings.Secure.getIntForUser(mCr,
                 Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED,
-                locationEnabled ? 1 : 0 /* defaultValue */, userId) != 0;
+                (geoDetectionEnabledByDefault ? 1 : 0) /* defaultValue */, userId) != 0;
     }
 
     private void setGeoDetectionEnabled(@UserIdInt int userId, boolean enabled) {
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index 6e1f89b..68a086d 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -30,6 +30,7 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
+import android.os.SystemProperties;
 import android.util.ArrayMap;
 import android.util.IndentingPrintWriter;
 import android.util.Slog;
@@ -58,11 +59,14 @@
     private static final String TAG = "TimeZoneDetectorService";
 
     /**
-     * A compile time constant "feature switch" for enabling / disabling location-based time zone
-     * detection on Android. If this is {@code false}, there should be few / little changes in
-     * behavior with previous releases and little overhead associated with geolocation components.
+     * A "feature switch" for enabling / disabling location-based time zone detection. If this is
+     * {@code false}, there should be few / little changes in behavior with previous releases and
+     * little overhead associated with geolocation components.
+     * TODO(b/151304765) Remove this when the feature is on for all.
      */
-    public static final boolean GEOLOCATION_TIME_ZONE_DETECTION_ENABLED = false;
+    public static final boolean GEOLOCATION_TIME_ZONE_DETECTION_ENABLED =
+            SystemProperties.getBoolean(
+                    "persist.sys.location_time_zone_detection_feature_enabled", false);
 
     /**
      * Handles the service lifecycle for {@link TimeZoneDetectorService} and
@@ -148,7 +152,7 @@
         enforceManageTimeZoneDetectorPermission();
 
         int userId = mCallerIdentityInjector.getCallingUserId();
-        long token = mCallerIdentityInjector.clearCallingIdentity();
+        final long token = mCallerIdentityInjector.clearCallingIdentity();
         try {
             ConfigurationInternal configurationInternal =
                     mTimeZoneDetectorStrategy.getConfigurationInternal(userId);
@@ -164,7 +168,7 @@
         Objects.requireNonNull(configuration);
 
         int callingUserId = mCallerIdentityInjector.getCallingUserId();
-        long token = mCallerIdentityInjector.clearCallingIdentity();
+        final long token = mCallerIdentityInjector.clearCallingIdentity();
         try {
             return mTimeZoneDetectorStrategy.updateConfiguration(callingUserId, configuration);
         } finally {
@@ -278,7 +282,7 @@
         Objects.requireNonNull(timeZoneSuggestion);
 
         int userId = mCallerIdentityInjector.getCallingUserId();
-        long token = mCallerIdentityInjector.clearCallingIdentity();
+        final long token = mCallerIdentityInjector.clearCallingIdentity();
         try {
             return mTimeZoneDetectorStrategy.suggestManualTimeZone(userId, timeZoneSuggestion);
         } finally {
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 89b108c..0e8fd8f 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -1125,7 +1125,7 @@
             userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
                     false /* allowAll */, true /* requireFull */, "isDeviceLocked", null);
 
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 if (!mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
                     userId = resolveProfileParent(userId);
@@ -1141,7 +1141,7 @@
             userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
                     false /* allowAll */, true /* requireFull */, "isDeviceSecure", null);
 
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 if (!mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
                     userId = resolveProfileParent(userId);
@@ -1328,7 +1328,7 @@
     }
 
     private int resolveProfileParent(int userId) {
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             UserInfo parent = mUserManager.getProfileParent(userId);
             if (parent != null) {
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 7b044ed..149dbd0 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -54,7 +54,7 @@
 import android.media.tv.ITvInputServiceCallback;
 import android.media.tv.ITvInputSession;
 import android.media.tv.ITvInputSessionCallback;
-import android.media.tv.TvChannelInfo;
+import android.media.tv.TunedInfo;
 import android.media.tv.TvContentRating;
 import android.media.tv.TvContentRatingSystemInfo;
 import android.media.tv.TvContract;
@@ -90,7 +90,6 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.IoThread;
 import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
 
 import java.io.File;
 import java.io.FileDescriptor;
@@ -114,7 +113,7 @@
     private static final boolean DEBUG = false;
     private static final String TAG = "TvInputManagerService";
     private static final String DVB_DIRECTORY = "/dev/dvb";
-    private static final int APP_TAG_SELF = TvChannelInfo.APP_TAG_SELF;
+    private static final int APP_TAG_SELF = TunedInfo.APP_TAG_SELF;
     private static final String PERMISSION_ACCESS_WATCHED_PROGRAMS =
             "com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS";
 
@@ -860,9 +859,9 @@
             try {
                 ITvInputManagerCallback callback = userState.mCallbacks.getBroadcastItem(i);
                 Pair<Integer, Integer> pidUid = userState.callbackPidUidMap.get(callback);
-                List<TvChannelInfo> infos = getCurrentTvChannelInfosInternalLocked(
+                List<TunedInfo> infos = getCurrentTunedInfosInternalLocked(
                         userState, pidUid.first, pidUid.second);
-                callback.onCurrentTvChannelInfosUpdated(infos);
+                callback.onCurrentTunedInfosUpdated(infos);
             } catch (RemoteException e) {
                 Slog.e(TAG, "failed to report updated current channel infos to callback", e);
             }
@@ -2097,14 +2096,14 @@
         }
 
         @Override
-        public List<TvChannelInfo> getCurrentTvChannelInfos(@UserIdInt int userId) {
+        public List<TunedInfo> getCurrentTunedInfos(@UserIdInt int userId) {
             int callingPid = Binder.getCallingPid();
             int callingUid = Binder.getCallingUid();
             final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId,
                     "getTvCurrentChannelInfos");
             synchronized (mLock) {
                 UserState userState = getOrCreateUserStateLocked(resolvedUserId);
-                return getCurrentTvChannelInfosInternalLocked(userState, callingPid, callingUid);
+                return getCurrentTunedInfosInternalLocked(userState, callingPid, callingUid);
             }
         }
 
@@ -2278,9 +2277,9 @@
         }
     }
 
-    private List<TvChannelInfo> getCurrentTvChannelInfosInternalLocked(
+    private List<TunedInfo> getCurrentTunedInfosInternalLocked(
             UserState userState, int callingPid, int callingUid) {
-        List<TvChannelInfo> channelInfos = new ArrayList<>();
+        List<TunedInfo> channelInfos = new ArrayList<>();
         boolean watchedProgramsAccess = hasAccessWatchedProgramsPermission(callingPid, callingUid);
         for (SessionState state : userState.sessionStateMap.values()) {
             if (state.isCurrent) {
@@ -2288,7 +2287,7 @@
                 int appType;
                 if (state.callingUid == callingUid) {
                     appTag = APP_TAG_SELF;
-                    appType = TvChannelInfo.APP_TYPE_SELF;
+                    appType = TunedInfo.APP_TYPE_SELF;
                 } else {
                     appTag = userState.mAppTagMap.get(state.callingUid);
                     if (appTag == null) {
@@ -2296,10 +2295,10 @@
                         userState.mAppTagMap.put(state.callingUid, appTag);
                     }
                     appType = isSystemApp(state.componentName.getPackageName())
-                            ? TvChannelInfo.APP_TYPE_SYSTEM
-                            : TvChannelInfo.APP_TYPE_NON_SYSTEM;
+                            ? TunedInfo.APP_TYPE_SYSTEM
+                            : TunedInfo.APP_TYPE_NON_SYSTEM;
                 }
-                channelInfos.add(new TvChannelInfo(
+                channelInfos.add(new TunedInfo(
                         state.inputId,
                         watchedProgramsAccess ? state.currentChannel : null,
                         state.isRecordingSession,
diff --git a/services/core/java/com/android/server/vcn/OWNERS b/services/core/java/com/android/server/vcn/OWNERS
new file mode 100644
index 0000000..33b9f0f
--- /dev/null
+++ b/services/core/java/com/android/server/vcn/OWNERS
@@ -0,0 +1,7 @@
+set noparent
+
+benedictwong@google.com
+ckesting@google.com
+evitayan@google.com
+nharold@google.com
+jchalard@google.com
\ No newline at end of file
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 5f4d46c..8b0963b 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1444,7 +1444,7 @@
         public void engineShown(IWallpaperEngine engine) {
             synchronized (mLock) {
                 if (mReply != null) {
-                    long ident = Binder.clearCallingIdentity();
+                    final long ident = Binder.clearCallingIdentity();
                     try {
                         mReply.sendResult(null);
                     } catch (RemoteException e) {
@@ -2009,7 +2009,7 @@
     public boolean hasNamedWallpaper(String name) {
         synchronized (mLock) {
             List<UserInfo> users;
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
                 users = ((UserManager) mContext.getSystemService(Context.USER_SERVICE)).getUsers();
             } finally {
diff --git a/services/core/java/com/android/server/webkit/SystemImpl.java b/services/core/java/com/android/server/webkit/SystemImpl.java
index 2018940..68f554c 100644
--- a/services/core/java/com/android/server/webkit/SystemImpl.java
+++ b/services/core/java/com/android/server/webkit/SystemImpl.java
@@ -197,19 +197,6 @@
     }
 
     @Override
-    public boolean isFallbackLogicEnabled() {
-        // Note that this is enabled by default (i.e. if the setting hasn't been set).
-        return Settings.Global.getInt(AppGlobals.getInitialApplication().getContentResolver(),
-                Settings.Global.WEBVIEW_FALLBACK_LOGIC_ENABLED, 1) == 1;
-    }
-
-    @Override
-    public void enableFallbackLogic(boolean enable) {
-        Settings.Global.putInt(AppGlobals.getInitialApplication().getContentResolver(),
-                Settings.Global.WEBVIEW_FALLBACK_LOGIC_ENABLED, enable ? 1 : 0);
-    }
-
-    @Override
     public void enablePackageForAllUsers(Context context, String packageName, boolean enable) {
         UserManager userManager = (UserManager)context.getSystemService(Context.USER_SERVICE);
         for(UserInfo userInfo : userManager.getUsers()) {
diff --git a/services/core/java/com/android/server/webkit/SystemInterface.java b/services/core/java/com/android/server/webkit/SystemInterface.java
index 743740d..09c23a7 100644
--- a/services/core/java/com/android/server/webkit/SystemInterface.java
+++ b/services/core/java/com/android/server/webkit/SystemInterface.java
@@ -41,9 +41,6 @@
     public void updateUserSetting(Context context, String newProviderName);
     public void killPackageDependents(String packageName);
 
-    public boolean isFallbackLogicEnabled();
-    public void enableFallbackLogic(boolean enable);
-
     public void enablePackageForAllUsers(Context context, String packageName, boolean enable);
 
     public boolean systemIsDebuggable();
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index 90a153b..ee46ce1 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -171,7 +171,7 @@
                 return;
             }
 
-            long callingId = Binder.clearCallingIdentity();
+            final long callingId = Binder.clearCallingIdentity();
             try {
                 WebViewUpdateService.this.mImpl.notifyRelroCreationCompleted();
             } finally {
@@ -232,7 +232,7 @@
                 throw new SecurityException(msg);
             }
 
-            long callingId = Binder.clearCallingIdentity();
+            final long callingId = Binder.clearCallingIdentity();
             try {
                 return WebViewUpdateService.this.mImpl.changeProviderAndSetting(
                         newProvider);
@@ -285,7 +285,7 @@
                 throw new SecurityException(msg);
             }
 
-            long callingId = Binder.clearCallingIdentity();
+            final long callingId = Binder.clearCallingIdentity();
             try {
                 WebViewUpdateService.this.mImpl.enableMultiProcess(enable);
             } finally {
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
index 11fd795..4d670f1 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
@@ -15,15 +15,22 @@
  */
 package com.android.server.webkit;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.Signature;
 import android.os.AsyncTask;
 import android.os.UserHandle;
 import android.util.Slog;
+import android.webkit.UserPackage;
+import android.webkit.WebViewFactory;
 import android.webkit.WebViewProviderInfo;
 import android.webkit.WebViewProviderResponse;
 
 import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Implementation of the WebViewUpdateService.
@@ -33,18 +40,17 @@
  * This class keeps track of and prepares the current WebView implementation, and needs to keep
  * track of a couple of different things such as what package is used as WebView implementation.
  *
- * The public methods in this class are accessed from WebViewUpdateService either on the UI thread
- * or on one of multiple Binder threads. The WebView preparation code shares state between threads
- * meaning that code that chooses a new WebView implementation or checks which implementation is
- * being used needs to hold a lock.
+ * The package-visible methods in this class are accessed from WebViewUpdateService either on the UI
+ * thread or on one of multiple Binder threads. The WebView preparation code shares state between
+ * threads meaning that code that chooses a new WebView implementation or checks which
+ * implementation is being used needs to hold a lock.
  *
  * The WebViewUpdateService can be accessed in a couple of different ways.
  * 1. It is started from the SystemServer at boot - at that point we just initiate some state such
  * as the WebView preparation class.
  * 2. The SystemServer calls WebViewUpdateService.prepareWebViewInSystemServer. This happens at boot
  * and the WebViewUpdateService should not have been accessed before this call. In this call we
- * migrate away from the old fallback logic if necessary and then choose WebView implementation for
- * the first time.
+ * choose WebView implementation for the first time.
  * 3. The update service listens for Intents related to package installs and removals. These intents
  * are received and processed on the UI thread. Each intent can result in changing WebView
  * implementation.
@@ -56,37 +62,133 @@
  *
  * @hide
  */
-public class WebViewUpdateServiceImpl {
+class WebViewUpdateServiceImpl {
     private static final String TAG = WebViewUpdateServiceImpl.class.getSimpleName();
 
-    private SystemInterface mSystemInterface;
-    private WebViewUpdater mWebViewUpdater;
-    final private Context mContext;
+    private static class WebViewPackageMissingException extends Exception {
+        WebViewPackageMissingException(String message) {
+            super(message);
+        }
 
-    private final static int MULTIPROCESS_SETTING_ON_VALUE = Integer.MAX_VALUE;
-    private final static int MULTIPROCESS_SETTING_OFF_VALUE = Integer.MIN_VALUE;
+        WebViewPackageMissingException(Exception e) {
+            super(e);
+        }
+    }
 
-    public WebViewUpdateServiceImpl(Context context, SystemInterface systemInterface) {
+    private static final int WAIT_TIMEOUT_MS = 1000; // KEY_DISPATCHING_TIMEOUT is 5000.
+    private static final long NS_PER_MS = 1000000;
+
+    private static final int VALIDITY_OK = 0;
+    private static final int VALIDITY_INCORRECT_SDK_VERSION = 1;
+    private static final int VALIDITY_INCORRECT_VERSION_CODE = 2;
+    private static final int VALIDITY_INCORRECT_SIGNATURE = 3;
+    private static final int VALIDITY_NO_LIBRARY_FLAG = 4;
+
+    private static final int MULTIPROCESS_SETTING_ON_VALUE = Integer.MAX_VALUE;
+    private static final int MULTIPROCESS_SETTING_OFF_VALUE = Integer.MIN_VALUE;
+
+    private final SystemInterface mSystemInterface;
+    private final Context mContext;
+
+    private long mMinimumVersionCode = -1;
+
+    // Keeps track of the number of running relro creations
+    private int mNumRelroCreationsStarted = 0;
+    private int mNumRelroCreationsFinished = 0;
+    // Implies that we need to rerun relro creation because we are using an out-of-date package
+    private boolean mWebViewPackageDirty = false;
+    private boolean mAnyWebViewInstalled = false;
+
+    private static final int NUMBER_OF_RELROS_UNKNOWN = Integer.MAX_VALUE;
+
+    // The WebView package currently in use (or the one we are preparing).
+    private PackageInfo mCurrentWebViewPackage = null;
+
+    private final Object mLock = new Object();
+
+    WebViewUpdateServiceImpl(Context context, SystemInterface systemInterface) {
         mContext = context;
         mSystemInterface = systemInterface;
-        mWebViewUpdater = new WebViewUpdater(mContext, mSystemInterface);
     }
 
     void packageStateChanged(String packageName, int changedState, int userId) {
         // We don't early out here in different cases where we could potentially early-out (e.g. if
         // we receive PACKAGE_CHANGED for another user than the system user) since that would
         // complicate this logic further and open up for more edge cases.
-        mWebViewUpdater.packageStateChanged(packageName, changedState);
+        for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) {
+            String webviewPackage = provider.packageName;
+
+            if (webviewPackage.equals(packageName)) {
+                boolean updateWebView = false;
+                boolean removedOrChangedOldPackage = false;
+                String oldProviderName = null;
+                PackageInfo newPackage = null;
+                synchronized (mLock) {
+                    try {
+                        newPackage = findPreferredWebViewPackage();
+                        if (mCurrentWebViewPackage != null) {
+                            oldProviderName = mCurrentWebViewPackage.packageName;
+                        }
+                        // Only trigger update actions if the updated package is the one
+                        // that will be used, or the one that was in use before the
+                        // update, or if we haven't seen a valid WebView package before.
+                        updateWebView =
+                            provider.packageName.equals(newPackage.packageName)
+                            || provider.packageName.equals(oldProviderName)
+                            || mCurrentWebViewPackage == null;
+                        // We removed the old package if we received an intent to remove
+                        // or replace the old package.
+                        removedOrChangedOldPackage =
+                            provider.packageName.equals(oldProviderName);
+                        if (updateWebView) {
+                            onWebViewProviderChanged(newPackage);
+                        }
+                    } catch (WebViewPackageMissingException e) {
+                        mCurrentWebViewPackage = null;
+                        Slog.e(TAG, "Could not find valid WebView package to create relro with "
+                                + e);
+                    }
+                }
+                if (updateWebView && !removedOrChangedOldPackage
+                        && oldProviderName != null) {
+                    // If the provider change is the result of adding or replacing a
+                    // package that was not the previous provider then we must kill
+                    // packages dependent on the old package ourselves. The framework
+                    // only kills dependents of packages that are being removed.
+                    mSystemInterface.killPackageDependents(oldProviderName);
+                }
+                return;
+            }
+        }
     }
 
     void prepareWebViewInSystemServer() {
-        migrateFallbackStateOnBoot();
-        mWebViewUpdater.prepareWebViewInSystemServer();
+        try {
+            synchronized (mLock) {
+                mCurrentWebViewPackage = findPreferredWebViewPackage();
+                String userSetting = mSystemInterface.getUserChosenWebViewProvider(mContext);
+                if (userSetting != null
+                        && !userSetting.equals(mCurrentWebViewPackage.packageName)) {
+                    // Don't persist the user-chosen setting across boots if the package being
+                    // chosen is not used (could be disabled or uninstalled) so that the user won't
+                    // be surprised by the device switching to using a certain webview package,
+                    // that was uninstalled/disabled a long time ago, if it is installed/enabled
+                    // again.
+                    mSystemInterface.updateUserSetting(mContext,
+                            mCurrentWebViewPackage.packageName);
+                }
+                onWebViewProviderChanged(mCurrentWebViewPackage);
+            }
+        } catch (Throwable t) {
+            // Log and discard errors at this stage as we must not crash the system server.
+            Slog.e(TAG, "error preparing webview provider from system server", t);
+        }
+
         if (getCurrentWebViewPackage() == null) {
             // We didn't find a valid WebView implementation. Try explicitly re-enabling the
             // fallback package for all users in case it was disabled, even if we already did the
-            // one-time migration before. If this actually changes the state, WebViewUpdater will
-            // see the PackageManager broadcast shortly and try again.
+            // one-time migration before. If this actually changes the state, we will see the
+            // PackageManager broadcast shortly and try again.
             WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages();
             WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders);
             if (fallbackProvider != null) {
@@ -105,7 +207,7 @@
         }
     }
 
-    void startZygoteWhenReady() {
+    private void startZygoteWhenReady() {
         // Wait on a background thread for RELRO creation to be done. We ignore the return value
         // because even if RELRO creation failed we still want to start the zygote.
         waitForAndGetProvider();
@@ -131,23 +233,231 @@
      */
     private void handleUserChange() {
         // Potentially trigger package-changing logic.
-        mWebViewUpdater.updateCurrentWebViewPackage(null);
+        updateCurrentWebViewPackage(null);
     }
 
     void notifyRelroCreationCompleted() {
-        mWebViewUpdater.notifyRelroCreationCompleted();
+        synchronized (mLock) {
+            mNumRelroCreationsFinished++;
+            checkIfRelrosDoneLocked();
+        }
     }
 
     WebViewProviderResponse waitForAndGetProvider() {
-        return mWebViewUpdater.waitForAndGetProvider();
+        PackageInfo webViewPackage = null;
+        final long timeoutTimeMs = System.nanoTime() / NS_PER_MS + WAIT_TIMEOUT_MS;
+        boolean webViewReady = false;
+        int webViewStatus = WebViewFactory.LIBLOAD_SUCCESS;
+        synchronized (mLock) {
+            webViewReady = webViewIsReadyLocked();
+            while (!webViewReady) {
+                final long timeNowMs = System.nanoTime() / NS_PER_MS;
+                if (timeNowMs >= timeoutTimeMs) break;
+                try {
+                    mLock.wait(timeoutTimeMs - timeNowMs);
+                } catch (InterruptedException e) {
+                    // ignore
+                }
+                webViewReady = webViewIsReadyLocked();
+            }
+            // Make sure we return the provider that was used to create the relro file
+            webViewPackage = mCurrentWebViewPackage;
+            if (webViewReady) {
+                // success
+            } else if (!mAnyWebViewInstalled) {
+                webViewStatus = WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES;
+            } else {
+                // Either the current relro creation  isn't done yet, or the new relro creatioin
+                // hasn't kicked off yet (the last relro creation used an out-of-date WebView).
+                webViewStatus = WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO;
+                Slog.e(TAG, "Timed out waiting for relro creation, relros started "
+                        + mNumRelroCreationsStarted
+                        + " relros finished " + mNumRelroCreationsFinished
+                        + " package dirty? " + mWebViewPackageDirty);
+            }
+        }
+        if (!webViewReady) Slog.w(TAG, "creating relro file timed out");
+        return new WebViewProviderResponse(webViewPackage, webViewStatus);
     }
 
-    String changeProviderAndSetting(String newProvider) {
-        return mWebViewUpdater.changeProviderAndSetting(newProvider);
+    /**
+     * Change WebView provider and provider setting and kill packages using the old provider.
+     * Return the new provider (in case we are in the middle of creating relro files, or
+     * replacing that provider it will not be in use directly, but will be used when the relros
+     * or the replacement are done).
+     */
+    String changeProviderAndSetting(String newProviderName) {
+        PackageInfo newPackage = updateCurrentWebViewPackage(newProviderName);
+        if (newPackage == null) return "";
+        return newPackage.packageName;
     }
 
+    /**
+     * Update the current WebView package.
+     * @param newProviderName the package to switch to, null if no package has been explicitly
+     * chosen.
+     */
+    private PackageInfo updateCurrentWebViewPackage(@Nullable String newProviderName) {
+        PackageInfo oldPackage = null;
+        PackageInfo newPackage = null;
+        boolean providerChanged = false;
+        synchronized (mLock) {
+            oldPackage = mCurrentWebViewPackage;
+
+            if (newProviderName != null) {
+                mSystemInterface.updateUserSetting(mContext, newProviderName);
+            }
+
+            try {
+                newPackage = findPreferredWebViewPackage();
+                providerChanged = (oldPackage == null)
+                        || !newPackage.packageName.equals(oldPackage.packageName);
+            } catch (WebViewPackageMissingException e) {
+                // If updated the Setting but don't have an installed WebView package, the
+                // Setting will be used when a package is available.
+                mCurrentWebViewPackage = null;
+                Slog.e(TAG, "Couldn't find WebView package to use " + e);
+                return null;
+            }
+            // Perform the provider change if we chose a new provider
+            if (providerChanged) {
+                onWebViewProviderChanged(newPackage);
+            }
+        }
+        // Kill apps using the old provider only if we changed provider
+        if (providerChanged && oldPackage != null) {
+            mSystemInterface.killPackageDependents(oldPackage.packageName);
+        }
+        // Return the new provider, this is not necessarily the one we were asked to switch to,
+        // but the persistent setting will now be pointing to the provider we were asked to
+        // switch to anyway.
+        return newPackage;
+    }
+
+    /**
+     * This is called when we change WebView provider, either when the current provider is
+     * updated or a new provider is chosen / takes precedence.
+     */
+    private void onWebViewProviderChanged(PackageInfo newPackage) {
+        synchronized (mLock) {
+            mAnyWebViewInstalled = true;
+            if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
+                mCurrentWebViewPackage = newPackage;
+
+                // The relro creations might 'finish' (not start at all) before
+                // WebViewFactory.onWebViewProviderChanged which means we might not know the
+                // number of started creations before they finish.
+                mNumRelroCreationsStarted = NUMBER_OF_RELROS_UNKNOWN;
+                mNumRelroCreationsFinished = 0;
+                mNumRelroCreationsStarted =
+                    mSystemInterface.onWebViewProviderChanged(newPackage);
+                // If the relro creations finish before we know the number of started creations
+                // we will have to do any cleanup/notifying here.
+                checkIfRelrosDoneLocked();
+            } else {
+                mWebViewPackageDirty = true;
+            }
+        }
+    }
+
+    /**
+     * Fetch only the currently valid WebView packages.
+     **/
     WebViewProviderInfo[] getValidWebViewPackages() {
-        return mWebViewUpdater.getValidWebViewPackages();
+        ProviderAndPackageInfo[] providersAndPackageInfos = getValidWebViewPackagesAndInfos();
+        WebViewProviderInfo[] providers =
+            new WebViewProviderInfo[providersAndPackageInfos.length];
+        for (int n = 0; n < providersAndPackageInfos.length; n++) {
+            providers[n] = providersAndPackageInfos[n].provider;
+        }
+        return providers;
+    }
+
+    private static class ProviderAndPackageInfo {
+        public final WebViewProviderInfo provider;
+        public final PackageInfo packageInfo;
+
+        ProviderAndPackageInfo(WebViewProviderInfo provider, PackageInfo packageInfo) {
+            this.provider = provider;
+            this.packageInfo = packageInfo;
+        }
+    }
+
+    private ProviderAndPackageInfo[] getValidWebViewPackagesAndInfos() {
+        WebViewProviderInfo[] allProviders = mSystemInterface.getWebViewPackages();
+        List<ProviderAndPackageInfo> providers = new ArrayList<>();
+        for (int n = 0; n < allProviders.length; n++) {
+            try {
+                PackageInfo packageInfo =
+                        mSystemInterface.getPackageInfoForProvider(allProviders[n]);
+                if (validityResult(allProviders[n], packageInfo) == VALIDITY_OK) {
+                    providers.add(new ProviderAndPackageInfo(allProviders[n], packageInfo));
+                }
+            } catch (NameNotFoundException e) {
+                // Don't add non-existent packages
+            }
+        }
+        return providers.toArray(new ProviderAndPackageInfo[providers.size()]);
+    }
+
+    /**
+     * Returns either the package info of the WebView provider determined in the following way:
+     * If the user has chosen a provider then use that if it is valid,
+     * otherwise use the first package in the webview priority list that is valid.
+     *
+     */
+    private PackageInfo findPreferredWebViewPackage() throws WebViewPackageMissingException {
+        ProviderAndPackageInfo[] providers = getValidWebViewPackagesAndInfos();
+
+        String userChosenProvider = mSystemInterface.getUserChosenWebViewProvider(mContext);
+
+        // If the user has chosen provider, use that (if it's installed and enabled for all
+        // users).
+        for (ProviderAndPackageInfo providerAndPackage : providers) {
+            if (providerAndPackage.provider.packageName.equals(userChosenProvider)) {
+                // userPackages can contain null objects.
+                List<UserPackage> userPackages =
+                        mSystemInterface.getPackageInfoForProviderAllUsers(mContext,
+                                providerAndPackage.provider);
+                if (isInstalledAndEnabledForAllUsers(userPackages)) {
+                    return providerAndPackage.packageInfo;
+                }
+            }
+        }
+
+        // User did not choose, or the choice failed; use the most stable provider that is
+        // installed and enabled for all users, and available by default (not through
+        // user choice).
+        for (ProviderAndPackageInfo providerAndPackage : providers) {
+            if (providerAndPackage.provider.availableByDefault) {
+                // userPackages can contain null objects.
+                List<UserPackage> userPackages =
+                        mSystemInterface.getPackageInfoForProviderAllUsers(mContext,
+                                providerAndPackage.provider);
+                if (isInstalledAndEnabledForAllUsers(userPackages)) {
+                    return providerAndPackage.packageInfo;
+                }
+            }
+        }
+
+        // This should never happen during normal operation (only with modified system images).
+        mAnyWebViewInstalled = false;
+        throw new WebViewPackageMissingException("Could not find a loadable WebView package");
+    }
+
+    /**
+     * Return true iff {@param packageInfos} point to only installed and enabled packages.
+     * The given packages {@param packageInfos} should all be pointing to the same package, but each
+     * PackageInfo representing a different user's package.
+     */
+    private static boolean isInstalledAndEnabledForAllUsers(
+            List<UserPackage> userPackages) {
+        for (UserPackage userPackage : userPackages) {
+            if (!userPackage.isInstalledPackage() || !userPackage.isEnabledPackage()) {
+                return false;
+            }
+        }
+        return true;
     }
 
     WebViewProviderInfo[] getWebViewPackages() {
@@ -155,28 +465,143 @@
     }
 
     PackageInfo getCurrentWebViewPackage() {
-        return mWebViewUpdater.getCurrentWebViewPackage();
+        synchronized (mLock) {
+            return mCurrentWebViewPackage;
+        }
     }
 
     /**
-     * If the fallback logic is enabled, re-enable any fallback package for all users, then
-     * disable the fallback logic.
-     *
-     * This migrates away from the old fallback mechanism to the new state where packages are never
-     * automatically enableenableisabled.
+     * Returns whether WebView is ready and is not going to go through its preparation phase
+     * again directly.
      */
-    private void migrateFallbackStateOnBoot() {
-        if (!mSystemInterface.isFallbackLogicEnabled()) return;
+    private boolean webViewIsReadyLocked() {
+        return !mWebViewPackageDirty
+            && (mNumRelroCreationsStarted == mNumRelroCreationsFinished)
+            // The current package might be replaced though we haven't received an intent
+            // declaring this yet, the following flag makes anyone loading WebView to wait in
+            // this case.
+            && mAnyWebViewInstalled;
+    }
 
-        WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages();
-        WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders);
-        if (fallbackProvider != null) {
-            Slog.i(TAG, "One-time migration: enabling " + fallbackProvider.packageName);
-            mSystemInterface.enablePackageForAllUsers(mContext, fallbackProvider.packageName, true);
-        } else {
-            Slog.i(TAG, "Skipping one-time migration: no fallback provider");
+    private void checkIfRelrosDoneLocked() {
+        if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
+            if (mWebViewPackageDirty) {
+                mWebViewPackageDirty = false;
+                // If we have changed provider since we started the relro creation we need to
+                // redo the whole process using the new package instead.
+                try {
+                    PackageInfo newPackage = findPreferredWebViewPackage();
+                    onWebViewProviderChanged(newPackage);
+                } catch (WebViewPackageMissingException e) {
+                    mCurrentWebViewPackage = null;
+                    // If we can't find any valid WebView package we are now in a state where
+                    // mAnyWebViewInstalled is false, so loading WebView will be blocked and we
+                    // should simply wait until we receive an intent declaring a new package was
+                    // installed.
+                }
+            } else {
+                mLock.notifyAll();
+            }
         }
-        mSystemInterface.enableFallbackLogic(false);
+    }
+
+    private int validityResult(WebViewProviderInfo configInfo, PackageInfo packageInfo) {
+        // Ensure the provider targets this framework release (or a later one).
+        if (!UserPackage.hasCorrectTargetSdkVersion(packageInfo)) {
+            return VALIDITY_INCORRECT_SDK_VERSION;
+        }
+        if (!versionCodeGE(packageInfo.getLongVersionCode(), getMinimumVersionCode())
+                && !mSystemInterface.systemIsDebuggable()) {
+            // Webview providers may be downgraded arbitrarily low, prevent that by enforcing
+            // minimum version code. This check is only enforced for user builds.
+            return VALIDITY_INCORRECT_VERSION_CODE;
+        }
+        if (!providerHasValidSignature(configInfo, packageInfo, mSystemInterface)) {
+            return VALIDITY_INCORRECT_SIGNATURE;
+        }
+        if (WebViewFactory.getWebViewLibrary(packageInfo.applicationInfo) == null) {
+            return VALIDITY_NO_LIBRARY_FLAG;
+        }
+        return VALIDITY_OK;
+    }
+
+    /**
+     * Both versionCodes should be from a WebView provider package implemented by Chromium.
+     * VersionCodes from other kinds of packages won't make any sense in this method.
+     *
+     * An introduction to Chromium versionCode scheme:
+     * "BBBBPPPXX"
+     * BBBB: 4 digit branch number. It monotonically increases over time.
+     * PPP: patch number in the branch. It is padded with zeroes to the left. These three digits
+     * may change their meaning in the future.
+     * XX: Digits to differentiate different APK builds of the same source version.
+     *
+     * This method takes the "BBBB" of versionCodes and compare them.
+     *
+     * https://www.chromium.org/developers/version-numbers describes general Chromium versioning;
+     * https://source.chromium.org/chromium/chromium/src/+/master:build/util/android_chrome_version.py
+     * is the canonical source for how Chromium versionCodes are calculated.
+     *
+     * @return true if versionCode1 is higher than or equal to versionCode2.
+     */
+    private static boolean versionCodeGE(long versionCode1, long versionCode2) {
+        long v1 = versionCode1 / 100000;
+        long v2 = versionCode2 / 100000;
+
+        return v1 >= v2;
+    }
+
+    /**
+     * Gets the minimum version code allowed for a valid provider. It is the minimum versionCode
+     * of all available-by-default WebView provider packages. If there is no such WebView provider
+     * package on the system, then return -1, which means all positive versionCode WebView packages
+     * are accepted.
+     *
+     * Note that this is a private method that handles a variable (mMinimumVersionCode) which is
+     * shared between threads. Furthermore, this method does not hold mLock meaning that we must
+     * take extra care to ensure this method is thread-safe.
+     */
+    private long getMinimumVersionCode() {
+        if (mMinimumVersionCode > 0) {
+            return mMinimumVersionCode;
+        }
+
+        long minimumVersionCode = -1;
+        for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) {
+            if (provider.availableByDefault) {
+                try {
+                    long versionCode =
+                            mSystemInterface.getFactoryPackageVersion(provider.packageName);
+                    if (minimumVersionCode < 0 || versionCode < minimumVersionCode) {
+                        minimumVersionCode = versionCode;
+                    }
+                } catch (NameNotFoundException e) {
+                    // Safe to ignore.
+                }
+            }
+        }
+
+        mMinimumVersionCode = minimumVersionCode;
+        return mMinimumVersionCode;
+    }
+
+    private static boolean providerHasValidSignature(WebViewProviderInfo provider,
+            PackageInfo packageInfo, SystemInterface systemInterface) {
+        // Skip checking signatures on debuggable builds, for development purposes.
+        if (systemInterface.systemIsDebuggable()) return true;
+
+        // Allow system apps to be valid providers regardless of signature.
+        if (packageInfo.applicationInfo.isSystemApp()) return true;
+
+        // We don't support packages with multiple signatures.
+        if (packageInfo.signatures.length != 1) return false;
+
+        // If any of the declared signatures match the package signature, it's valid.
+        for (Signature signature : provider.signatures) {
+            if (signature.equals(packageInfo.signatures[0])) return true;
+        }
+
+        return false;
     }
 
     /**
@@ -217,9 +642,89 @@
      */
     void dumpState(PrintWriter pw) {
         pw.println("Current WebView Update Service state");
-        pw.println(String.format("  Fallback logic enabled: %b",
-                mSystemInterface.isFallbackLogicEnabled()));
         pw.println(String.format("  Multiprocess enabled: %b", isMultiProcessEnabled()));
-        mWebViewUpdater.dumpState(pw);
+        synchronized (mLock) {
+            if (mCurrentWebViewPackage == null) {
+                pw.println("  Current WebView package is null");
+            } else {
+                pw.println(String.format("  Current WebView package (name, version): (%s, %s)",
+                        mCurrentWebViewPackage.packageName,
+                        mCurrentWebViewPackage.versionName));
+            }
+            pw.println(String.format("  Minimum targetSdkVersion: %d",
+                    UserPackage.MINIMUM_SUPPORTED_SDK));
+            pw.println(String.format("  Minimum WebView version code: %d",
+                    mMinimumVersionCode));
+            pw.println(String.format("  Number of relros started: %d",
+                    mNumRelroCreationsStarted));
+            pw.println(String.format("  Number of relros finished: %d",
+                        mNumRelroCreationsFinished));
+            pw.println(String.format("  WebView package dirty: %b", mWebViewPackageDirty));
+            pw.println(String.format("  Any WebView package installed: %b",
+                    mAnyWebViewInstalled));
+
+            try {
+                PackageInfo preferredWebViewPackage = findPreferredWebViewPackage();
+                pw.println(String.format(
+                        "  Preferred WebView package (name, version): (%s, %s)",
+                        preferredWebViewPackage.packageName,
+                        preferredWebViewPackage.versionName));
+            } catch (WebViewPackageMissingException e) {
+                pw.println(String.format("  Preferred WebView package: none"));
+            }
+
+            dumpAllPackageInformationLocked(pw);
+        }
+    }
+
+    private void dumpAllPackageInformationLocked(PrintWriter pw) {
+        WebViewProviderInfo[] allProviders = mSystemInterface.getWebViewPackages();
+        pw.println("  WebView packages:");
+        for (WebViewProviderInfo provider : allProviders) {
+            List<UserPackage> userPackages =
+                    mSystemInterface.getPackageInfoForProviderAllUsers(mContext, provider);
+            PackageInfo systemUserPackageInfo =
+                    userPackages.get(UserHandle.USER_SYSTEM).getPackageInfo();
+            if (systemUserPackageInfo == null) {
+                pw.println(String.format("    %s is NOT installed.", provider.packageName));
+                continue;
+            }
+
+            int validity = validityResult(provider, systemUserPackageInfo);
+            String packageDetails = String.format(
+                    "versionName: %s, versionCode: %d, targetSdkVersion: %d",
+                    systemUserPackageInfo.versionName,
+                    systemUserPackageInfo.getLongVersionCode(),
+                    systemUserPackageInfo.applicationInfo.targetSdkVersion);
+            if (validity == VALIDITY_OK) {
+                boolean installedForAllUsers = isInstalledAndEnabledForAllUsers(
+                        mSystemInterface.getPackageInfoForProviderAllUsers(mContext, provider));
+                pw.println(String.format(
+                        "    Valid package %s (%s) is %s installed/enabled for all users",
+                        systemUserPackageInfo.packageName,
+                        packageDetails,
+                        installedForAllUsers ? "" : "NOT"));
+            } else {
+                pw.println(String.format("    Invalid package %s (%s), reason: %s",
+                        systemUserPackageInfo.packageName,
+                        packageDetails,
+                        getInvalidityReason(validity)));
+            }
+        }
+    }
+
+    private static String getInvalidityReason(int invalidityReason) {
+        switch (invalidityReason) {
+            case VALIDITY_INCORRECT_SDK_VERSION:
+                return "SDK version too low";
+            case VALIDITY_INCORRECT_VERSION_CODE:
+                return "Version code too low";
+            case VALIDITY_INCORRECT_SIGNATURE:
+                return "Incorrect signature";
+            case VALIDITY_NO_LIBRARY_FLAG:
+                return "No WebView-library manifest flag";
+            default:
+                return "Unexcepted validity-reason";
+        }
     }
 }
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdater.java b/services/core/java/com/android/server/webkit/WebViewUpdater.java
deleted file mode 100644
index 3b58af2..0000000
--- a/services/core/java/com/android/server/webkit/WebViewUpdater.java
+++ /dev/null
@@ -1,599 +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.webkit;
-
-import android.content.Context;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.Signature;
-import android.os.UserHandle;
-import android.util.Slog;
-import android.webkit.UserPackage;
-import android.webkit.WebViewFactory;
-import android.webkit.WebViewProviderInfo;
-import android.webkit.WebViewProviderResponse;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Class that decides what WebView implementation to use and prepares that implementation for
- * use.
- */
-class WebViewUpdater {
-    private static final String TAG = WebViewUpdater.class.getSimpleName();
-
-    private static class WebViewPackageMissingException extends Exception {
-        public WebViewPackageMissingException(String message) { super(message); }
-        public WebViewPackageMissingException(Exception e) { super(e); }
-    }
-
-    private static final int WAIT_TIMEOUT_MS = 1000; // KEY_DISPATCHING_TIMEOUT is 5000.
-
-    private final static int VALIDITY_OK = 0;
-    private final static int VALIDITY_INCORRECT_SDK_VERSION = 1;
-    private final static int VALIDITY_INCORRECT_VERSION_CODE = 2;
-    private final static int VALIDITY_INCORRECT_SIGNATURE = 3;
-    private final static int VALIDITY_NO_LIBRARY_FLAG = 4;
-
-    private Context mContext;
-    private SystemInterface mSystemInterface;
-    private long mMinimumVersionCode = -1;
-
-    // Keeps track of the number of running relro creations
-    private int mNumRelroCreationsStarted = 0;
-    private int mNumRelroCreationsFinished = 0;
-    // Implies that we need to rerun relro creation because we are using an out-of-date package
-    private boolean mWebViewPackageDirty = false;
-    private boolean mAnyWebViewInstalled = false;
-
-    private int NUMBER_OF_RELROS_UNKNOWN = Integer.MAX_VALUE;
-
-    // The WebView package currently in use (or the one we are preparing).
-    private PackageInfo mCurrentWebViewPackage = null;
-
-    private final Object mLock = new Object();
-
-    WebViewUpdater(Context context, SystemInterface systemInterface) {
-        mContext = context;
-        mSystemInterface = systemInterface;
-    }
-
-    void packageStateChanged(String packageName, int changedState) {
-        for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) {
-            String webviewPackage = provider.packageName;
-
-            if (webviewPackage.equals(packageName)) {
-                boolean updateWebView = false;
-                boolean removedOrChangedOldPackage = false;
-                String oldProviderName = null;
-                PackageInfo newPackage = null;
-                synchronized(mLock) {
-                    try {
-                        newPackage = findPreferredWebViewPackage();
-                        if (mCurrentWebViewPackage != null) {
-                            oldProviderName = mCurrentWebViewPackage.packageName;
-                        }
-                        // Only trigger update actions if the updated package is the one
-                        // that will be used, or the one that was in use before the
-                        // update, or if we haven't seen a valid WebView package before.
-                        updateWebView =
-                            provider.packageName.equals(newPackage.packageName)
-                            || provider.packageName.equals(oldProviderName)
-                            || mCurrentWebViewPackage == null;
-                        // We removed the old package if we received an intent to remove
-                        // or replace the old package.
-                        removedOrChangedOldPackage =
-                            provider.packageName.equals(oldProviderName);
-                        if (updateWebView) {
-                            onWebViewProviderChanged(newPackage);
-                        }
-                    } catch (WebViewPackageMissingException e) {
-                        mCurrentWebViewPackage = null;
-                        Slog.e(TAG, "Could not find valid WebView package to create " +
-                                "relro with " + e);
-                    }
-                }
-                if(updateWebView && !removedOrChangedOldPackage
-                        && oldProviderName != null) {
-                    // If the provider change is the result of adding or replacing a
-                    // package that was not the previous provider then we must kill
-                    // packages dependent on the old package ourselves. The framework
-                    // only kills dependents of packages that are being removed.
-                    mSystemInterface.killPackageDependents(oldProviderName);
-                }
-                return;
-            }
-        }
-    }
-
-    void prepareWebViewInSystemServer() {
-        try {
-            synchronized(mLock) {
-                mCurrentWebViewPackage = findPreferredWebViewPackage();
-                String userSetting = mSystemInterface.getUserChosenWebViewProvider(mContext);
-                if (userSetting != null
-                        && !userSetting.equals(mCurrentWebViewPackage.packageName)) {
-                    // Don't persist the user-chosen setting across boots if the package being
-                    // chosen is not used (could be disabled or uninstalled) so that the user won't
-                    // be surprised by the device switching to using a certain webview package,
-                    // that was uninstalled/disabled a long time ago, if it is installed/enabled
-                    // again.
-                    mSystemInterface.updateUserSetting(mContext,
-                            mCurrentWebViewPackage.packageName);
-                }
-                onWebViewProviderChanged(mCurrentWebViewPackage);
-            }
-        } catch (Throwable t) {
-            // Log and discard errors at this stage as we must not crash the system server.
-            Slog.e(TAG, "error preparing webview provider from system server", t);
-        }
-    }
-
-    /**
-     * Change WebView provider and provider setting and kill packages using the old provider.
-     * Return the new provider (in case we are in the middle of creating relro files, or
-     * replacing that provider it will not be in use directly, but will be used when the relros
-     * or the replacement are done).
-     */
-    String changeProviderAndSetting(String newProviderName) {
-        PackageInfo newPackage = updateCurrentWebViewPackage(newProviderName);
-        if (newPackage == null) return "";
-        return newPackage.packageName;
-    }
-
-    /**
-     * Update the current WebView package.
-     * @param newProviderName the package to switch to, null if no package has been explicitly
-     * chosen.
-     */
-    PackageInfo updateCurrentWebViewPackage(String newProviderName) {
-        PackageInfo oldPackage = null;
-        PackageInfo newPackage = null;
-        boolean providerChanged = false;
-        synchronized(mLock) {
-            oldPackage = mCurrentWebViewPackage;
-
-            if (newProviderName != null) {
-                mSystemInterface.updateUserSetting(mContext, newProviderName);
-            }
-
-            try {
-                newPackage = findPreferredWebViewPackage();
-                providerChanged = (oldPackage == null)
-                        || !newPackage.packageName.equals(oldPackage.packageName);
-            } catch (WebViewPackageMissingException e) {
-                // If updated the Setting but don't have an installed WebView package, the
-                // Setting will be used when a package is available.
-                mCurrentWebViewPackage = null;
-                Slog.e(TAG, "Couldn't find WebView package to use " + e);
-                return null;
-            }
-            // Perform the provider change if we chose a new provider
-            if (providerChanged) {
-                onWebViewProviderChanged(newPackage);
-            }
-        }
-        // Kill apps using the old provider only if we changed provider
-        if (providerChanged && oldPackage != null) {
-            mSystemInterface.killPackageDependents(oldPackage.packageName);
-        }
-        // Return the new provider, this is not necessarily the one we were asked to switch to,
-        // but the persistent setting will now be pointing to the provider we were asked to
-        // switch to anyway.
-        return newPackage;
-    }
-
-    /**
-     * This is called when we change WebView provider, either when the current provider is
-     * updated or a new provider is chosen / takes precedence.
-     */
-    private void onWebViewProviderChanged(PackageInfo newPackage) {
-        synchronized(mLock) {
-            mAnyWebViewInstalled = true;
-            if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
-                mCurrentWebViewPackage = newPackage;
-
-                // The relro creations might 'finish' (not start at all) before
-                // WebViewFactory.onWebViewProviderChanged which means we might not know the
-                // number of started creations before they finish.
-                mNumRelroCreationsStarted = NUMBER_OF_RELROS_UNKNOWN;
-                mNumRelroCreationsFinished = 0;
-                mNumRelroCreationsStarted =
-                    mSystemInterface.onWebViewProviderChanged(newPackage);
-                // If the relro creations finish before we know the number of started creations
-                // we will have to do any cleanup/notifying here.
-                checkIfRelrosDoneLocked();
-            } else {
-                mWebViewPackageDirty = true;
-            }
-        }
-    }
-
-    /**
-     * Fetch only the currently valid WebView packages.
-     **/
-    WebViewProviderInfo[] getValidWebViewPackages() {
-        ProviderAndPackageInfo[] providersAndPackageInfos = getValidWebViewPackagesAndInfos();
-        WebViewProviderInfo[] providers =
-            new WebViewProviderInfo[providersAndPackageInfos.length];
-        for(int n = 0; n < providersAndPackageInfos.length; n++) {
-            providers[n] = providersAndPackageInfos[n].provider;
-        }
-        return providers;
-    }
-
-    private static class ProviderAndPackageInfo {
-        public final WebViewProviderInfo provider;
-        public final PackageInfo packageInfo;
-
-        public ProviderAndPackageInfo(WebViewProviderInfo provider, PackageInfo packageInfo) {
-            this.provider = provider;
-            this.packageInfo = packageInfo;
-        }
-    }
-
-    private ProviderAndPackageInfo[] getValidWebViewPackagesAndInfos() {
-        WebViewProviderInfo[] allProviders = mSystemInterface.getWebViewPackages();
-        List<ProviderAndPackageInfo> providers = new ArrayList<>();
-        for(int n = 0; n < allProviders.length; n++) {
-            try {
-                PackageInfo packageInfo =
-                    mSystemInterface.getPackageInfoForProvider(allProviders[n]);
-                if (isValidProvider(allProviders[n], packageInfo)) {
-                    providers.add(new ProviderAndPackageInfo(allProviders[n], packageInfo));
-                }
-            } catch (NameNotFoundException e) {
-                // Don't add non-existent packages
-            }
-        }
-        return providers.toArray(new ProviderAndPackageInfo[providers.size()]);
-    }
-
-    /**
-     * Returns either the package info of the WebView provider determined in the following way:
-     * If the user has chosen a provider then use that if it is valid,
-     * otherwise use the first package in the webview priority list that is valid.
-     *
-     */
-    private PackageInfo findPreferredWebViewPackage() throws WebViewPackageMissingException {
-        ProviderAndPackageInfo[] providers = getValidWebViewPackagesAndInfos();
-
-        String userChosenProvider = mSystemInterface.getUserChosenWebViewProvider(mContext);
-
-        // If the user has chosen provider, use that (if it's installed and enabled for all
-        // users).
-        for (ProviderAndPackageInfo providerAndPackage : providers) {
-            if (providerAndPackage.provider.packageName.equals(userChosenProvider)) {
-                // userPackages can contain null objects.
-                List<UserPackage> userPackages =
-                        mSystemInterface.getPackageInfoForProviderAllUsers(mContext,
-                                providerAndPackage.provider);
-                if (isInstalledAndEnabledForAllUsers(userPackages)) {
-                    return providerAndPackage.packageInfo;
-                }
-            }
-        }
-
-        // User did not choose, or the choice failed; use the most stable provider that is
-        // installed and enabled for all users, and available by default (not through
-        // user choice).
-        for (ProviderAndPackageInfo providerAndPackage : providers) {
-            if (providerAndPackage.provider.availableByDefault) {
-                // userPackages can contain null objects.
-                List<UserPackage> userPackages =
-                        mSystemInterface.getPackageInfoForProviderAllUsers(mContext,
-                                providerAndPackage.provider);
-                if (isInstalledAndEnabledForAllUsers(userPackages)) {
-                    return providerAndPackage.packageInfo;
-                }
-            }
-        }
-
-        // This should never happen during normal operation (only with modified system images).
-        mAnyWebViewInstalled = false;
-        throw new WebViewPackageMissingException("Could not find a loadable WebView package");
-    }
-
-    /**
-     * Return true iff {@param packageInfos} point to only installed and enabled packages.
-     * The given packages {@param packageInfos} should all be pointing to the same package, but each
-     * PackageInfo representing a different user's package.
-     */
-    static boolean isInstalledAndEnabledForAllUsers(
-            List<UserPackage> userPackages) {
-        for (UserPackage userPackage : userPackages) {
-            if (!userPackage.isInstalledPackage() || !userPackage.isEnabledPackage()) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    void notifyRelroCreationCompleted() {
-        synchronized (mLock) {
-            mNumRelroCreationsFinished++;
-            checkIfRelrosDoneLocked();
-        }
-    }
-
-    WebViewProviderResponse waitForAndGetProvider() {
-        PackageInfo webViewPackage = null;
-        final long NS_PER_MS = 1000000;
-        final long timeoutTimeMs = System.nanoTime() / NS_PER_MS + WAIT_TIMEOUT_MS;
-        boolean webViewReady = false;
-        int webViewStatus = WebViewFactory.LIBLOAD_SUCCESS;
-        synchronized (mLock) {
-            webViewReady = webViewIsReadyLocked();
-            while (!webViewReady) {
-                final long timeNowMs = System.nanoTime() / NS_PER_MS;
-                if (timeNowMs >= timeoutTimeMs) break;
-                try {
-                    mLock.wait(timeoutTimeMs - timeNowMs);
-                } catch (InterruptedException e) {}
-                webViewReady = webViewIsReadyLocked();
-            }
-            // Make sure we return the provider that was used to create the relro file
-            webViewPackage = mCurrentWebViewPackage;
-            if (webViewReady) {
-            } else if (!mAnyWebViewInstalled) {
-                webViewStatus = WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES;
-            } else {
-                // Either the current relro creation  isn't done yet, or the new relro creatioin
-                // hasn't kicked off yet (the last relro creation used an out-of-date WebView).
-                webViewStatus = WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO;
-                Slog.e(TAG, "Timed out waiting for relro creation, relros started "
-                        + mNumRelroCreationsStarted
-                        + " relros finished " + mNumRelroCreationsFinished
-                        + " package dirty? " + mWebViewPackageDirty);
-            }
-        }
-        if (!webViewReady) Slog.w(TAG, "creating relro file timed out");
-        return new WebViewProviderResponse(webViewPackage, webViewStatus);
-    }
-
-    PackageInfo getCurrentWebViewPackage() {
-        synchronized(mLock) {
-            return mCurrentWebViewPackage;
-        }
-    }
-
-    /**
-     * Returns whether WebView is ready and is not going to go through its preparation phase
-     * again directly.
-     */
-    private boolean webViewIsReadyLocked() {
-        return !mWebViewPackageDirty
-            && (mNumRelroCreationsStarted == mNumRelroCreationsFinished)
-            // The current package might be replaced though we haven't received an intent
-            // declaring this yet, the following flag makes anyone loading WebView to wait in
-            // this case.
-            && mAnyWebViewInstalled;
-    }
-
-    private void checkIfRelrosDoneLocked() {
-        if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
-            if (mWebViewPackageDirty) {
-                mWebViewPackageDirty = false;
-                // If we have changed provider since we started the relro creation we need to
-                // redo the whole process using the new package instead.
-                try {
-                    PackageInfo newPackage = findPreferredWebViewPackage();
-                    onWebViewProviderChanged(newPackage);
-                } catch (WebViewPackageMissingException e) {
-                    mCurrentWebViewPackage = null;
-                    // If we can't find any valid WebView package we are now in a state where
-                    // mAnyWebViewInstalled is false, so loading WebView will be blocked and we
-                    // should simply wait until we receive an intent declaring a new package was
-                    // installed.
-                }
-            } else {
-                mLock.notifyAll();
-            }
-        }
-    }
-
-    /**
-     * Returns whether this provider is valid for use as a WebView provider.
-     */
-    boolean isValidProvider(WebViewProviderInfo configInfo, PackageInfo packageInfo) {
-        return VALIDITY_OK == validityResult(configInfo, packageInfo);
-    }
-
-    private int validityResult(WebViewProviderInfo configInfo, PackageInfo packageInfo) {
-        // Ensure the provider targets this framework release (or a later one).
-        if (!UserPackage.hasCorrectTargetSdkVersion(packageInfo)) {
-            return VALIDITY_INCORRECT_SDK_VERSION;
-        }
-        if (!versionCodeGE(packageInfo.getLongVersionCode(), getMinimumVersionCode())
-                && !mSystemInterface.systemIsDebuggable()) {
-            // Webview providers may be downgraded arbitrarily low, prevent that by enforcing
-            // minimum version code. This check is only enforced for user builds.
-            return VALIDITY_INCORRECT_VERSION_CODE;
-        }
-        if (!providerHasValidSignature(configInfo, packageInfo, mSystemInterface)) {
-            return VALIDITY_INCORRECT_SIGNATURE;
-        }
-        if (WebViewFactory.getWebViewLibrary(packageInfo.applicationInfo) == null) {
-            return VALIDITY_NO_LIBRARY_FLAG;
-        }
-        return VALIDITY_OK;
-    }
-
-    /**
-     * Both versionCodes should be from a WebView provider package implemented by Chromium.
-     * VersionCodes from other kinds of packages won't make any sense in this method.
-     *
-     * An introduction to Chromium versionCode scheme:
-     * "BBBBPPPAX"
-     * BBBB: 4 digit branch number. It monotonically increases over time.
-     * PPP: patch number in the branch. It is padded with zeroes to the left. These three digits
-     * may change their meaning in the future.
-     * A: architecture digit.
-     * X: A digit to differentiate APKs for other reasons.
-     *
-     * This method takes the "BBBB" of versionCodes and compare them.
-     *
-     * @return true if versionCode1 is higher than or equal to versionCode2.
-     */
-    private static boolean versionCodeGE(long versionCode1, long versionCode2) {
-        long v1 = versionCode1 / 100000;
-        long v2 = versionCode2 / 100000;
-
-        return v1 >= v2;
-    }
-
-    /**
-     * Gets the minimum version code allowed for a valid provider. It is the minimum versionCode
-     * of all available-by-default WebView provider packages. If there is no such WebView provider
-     * package on the system, then return -1, which means all positive versionCode WebView packages
-     * are accepted.
-     *
-     * Note that this is a private method in WebViewUpdater that handles a variable
-     * (mMinimumVersionCode) which is shared between threads. Furthermore, this method does not
-     * hold mLock meaning that we must take extra care to ensure this method is thread-safe.
-     */
-    private long getMinimumVersionCode() {
-        if (mMinimumVersionCode > 0) {
-            return mMinimumVersionCode;
-        }
-
-        long minimumVersionCode = -1;
-        for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) {
-            if (provider.availableByDefault) {
-                try {
-                    long versionCode =
-                        mSystemInterface.getFactoryPackageVersion(provider.packageName);
-                    if (minimumVersionCode < 0 || versionCode < minimumVersionCode) {
-                        minimumVersionCode = versionCode;
-                    }
-                } catch (NameNotFoundException e) {
-                    // Safe to ignore.
-                }
-            }
-        }
-
-        mMinimumVersionCode = minimumVersionCode;
-        return mMinimumVersionCode;
-    }
-
-    private static boolean providerHasValidSignature(WebViewProviderInfo provider,
-            PackageInfo packageInfo, SystemInterface systemInterface) {
-        // Skip checking signatures on debuggable builds, for development purposes.
-        if (systemInterface.systemIsDebuggable()) return true;
-
-        // Allow system apps to be valid providers regardless of signature.
-        if (packageInfo.applicationInfo.isSystemApp()) return true;
-
-        // We don't support packages with multiple signatures.
-        if (packageInfo.signatures.length != 1) return false;
-
-        // If any of the declared signatures match the package signature, it's valid.
-        for (Signature signature : provider.signatures) {
-            if (signature.equals(packageInfo.signatures[0])) return true;
-        }
-
-        return false;
-    }
-
-    void dumpState(PrintWriter pw) {
-        synchronized (mLock) {
-            if (mCurrentWebViewPackage == null) {
-                pw.println("  Current WebView package is null");
-            } else {
-                pw.println(String.format("  Current WebView package (name, version): (%s, %s)",
-                        mCurrentWebViewPackage.packageName,
-                        mCurrentWebViewPackage.versionName));
-            }
-            pw.println(String.format("  Minimum targetSdkVersion: %d",
-                    UserPackage.MINIMUM_SUPPORTED_SDK));
-            pw.println(String.format("  Minimum WebView version code: %d",
-                  mMinimumVersionCode));
-            pw.println(String.format("  Number of relros started: %d",
-                    mNumRelroCreationsStarted));
-            pw.println(String.format("  Number of relros finished: %d",
-                        mNumRelroCreationsFinished));
-            pw.println(String.format("  WebView package dirty: %b", mWebViewPackageDirty));
-            pw.println(String.format("  Any WebView package installed: %b",
-                    mAnyWebViewInstalled));
-
-            try {
-                PackageInfo preferredWebViewPackage = findPreferredWebViewPackage();
-                pw.println(String.format(
-                        "  Preferred WebView package (name, version): (%s, %s)",
-                        preferredWebViewPackage.packageName,
-                        preferredWebViewPackage.versionName));
-            } catch (WebViewPackageMissingException e) {
-                pw.println(String.format("  Preferred WebView package: none"));
-            }
-
-            dumpAllPackageInformationLocked(pw);
-        }
-    }
-
-    private void dumpAllPackageInformationLocked(PrintWriter pw) {
-        WebViewProviderInfo[] allProviders = mSystemInterface.getWebViewPackages();
-        pw.println("  WebView packages:");
-        for (WebViewProviderInfo provider : allProviders) {
-            List<UserPackage> userPackages =
-                    mSystemInterface.getPackageInfoForProviderAllUsers(mContext, provider);
-            PackageInfo systemUserPackageInfo =
-                    userPackages.get(UserHandle.USER_SYSTEM).getPackageInfo();
-            if (systemUserPackageInfo == null) {
-                pw.println(String.format("    %s is NOT installed.", provider.packageName));
-                continue;
-            }
-
-            int validity = validityResult(provider, systemUserPackageInfo);
-            String packageDetails = String.format(
-                    "versionName: %s, versionCode: %d, targetSdkVersion: %d",
-                    systemUserPackageInfo.versionName,
-                    systemUserPackageInfo.getLongVersionCode(),
-                    systemUserPackageInfo.applicationInfo.targetSdkVersion);
-            if (validity == VALIDITY_OK) {
-                boolean installedForAllUsers = isInstalledAndEnabledForAllUsers(
-                        mSystemInterface.getPackageInfoForProviderAllUsers(mContext, provider));
-                pw.println(String.format(
-                        "    Valid package %s (%s) is %s installed/enabled for all users",
-                        systemUserPackageInfo.packageName,
-                        packageDetails,
-                        installedForAllUsers ? "" : "NOT"));
-            } else {
-                pw.println(String.format("    Invalid package %s (%s), reason: %s",
-                        systemUserPackageInfo.packageName,
-                        packageDetails,
-                        getInvalidityReason(validity)));
-            }
-        }
-    }
-
-    private static String getInvalidityReason(int invalidityReason) {
-        switch (invalidityReason) {
-            case VALIDITY_INCORRECT_SDK_VERSION:
-                return "SDK version too low";
-            case VALIDITY_INCORRECT_VERSION_CODE:
-                return "Version code too low";
-            case VALIDITY_INCORRECT_SIGNATURE:
-                return "Incorrect signature";
-            case VALIDITY_NO_LIBRARY_FLAG:
-                return "No WebView-library manifest flag";
-            default:
-                return "Unexcepted validity-reason";
-        }
-    }
-
-}
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index fb06a9c..9d08b1b 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -397,6 +397,13 @@
                     return -1;
             }
         }
+
+        PackageOptimizationInfo getPackageOptimizationInfo(ArtManagerInternal artManagerInternal) {
+            return artManagerInternal == null || launchedActivityAppRecordRequiredAbi == null
+                    ? PackageOptimizationInfo.createWithNoInfo()
+                    : artManagerInternal.getPackageOptimizationInfo(applicationInfo,
+                            launchedActivityAppRecordRequiredAbi, launchedActivityName);
+        }
     }
 
     ActivityMetricsLogger(ActivityStackSupervisor supervisor, Looper looper) {
@@ -857,14 +864,8 @@
                     info.bindApplicationDelayMs);
         }
         builder.addTaggedData(APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS, info.windowsDrawnDelayMs);
-        final ArtManagerInternal artManagerInternal = getArtManagerInternal();
         final PackageOptimizationInfo packageOptimizationInfo =
-                (artManagerInternal == null) || (info.launchedActivityAppRecordRequiredAbi == null)
-                ? PackageOptimizationInfo.createWithNoInfo()
-                : artManagerInternal.getPackageOptimizationInfo(
-                        info.applicationInfo,
-                        info.launchedActivityAppRecordRequiredAbi,
-                        info.launchedActivityName);
+                info.getPackageOptimizationInfo(getArtManagerInternal());
         builder.addTaggedData(PACKAGE_OPTIMIZATION_COMPILATION_REASON,
                 packageOptimizationInfo.getCompilationReason());
         builder.addTaggedData(PACKAGE_OPTIMIZATION_COMPILATION_FILTER,
@@ -985,6 +986,8 @@
         builder.addTaggedData(APP_TRANSITION_PROCESS_RUNNING,
                 info.mProcessRunning ? 1 : 0);
         mMetricsLogger.write(builder);
+        final PackageOptimizationInfo packageOptimizationInfo =
+                infoSnapshot.getPackageOptimizationInfo(getArtManagerInternal());
         FrameworkStatsLog.write(
                 FrameworkStatsLog.APP_START_FULLY_DRAWN,
                 info.mLastLaunchedActivity.info.applicationInfo.uid,
@@ -995,6 +998,8 @@
                 info.mLastLaunchedActivity.info.name,
                 info.mProcessRunning,
                 startupTimeMs,
+                packageOptimizationInfo.getCompilationReason(),
+                packageOptimizationInfo.getCompilationFilter(),
                 info.mSourceType,
                 info.mSourceEventDelayMs);
 
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 9868fc1..504973c 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -179,6 +179,7 @@
 import static com.android.server.wm.IdentifierProto.TITLE;
 import static com.android.server.wm.IdentifierProto.USER_ID;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
 import static com.android.server.wm.Task.ActivityState.DESTROYED;
 import static com.android.server.wm.Task.ActivityState.DESTROYING;
@@ -621,6 +622,7 @@
      * 1920x1080, and the actually size on the screen is 960x540, then the scale is 0.5.
      */
     private float mSizeCompatScale = 1f;
+
     /**
      * The bounds in global coordinates for activity in size compatibility mode.
      * @see ActivityRecord#hasSizeCompatBounds()
@@ -701,6 +703,9 @@
     // Token for targeting this activity for assist purposes.
     final Binder assistToken = new Binder();
 
+    // Tracking cookie for the launch of this activity and it's task.
+    IBinder mLaunchCookie;
+
     private final Runnable mPauseTimeoutRunnable = new Runnable() {
         @Override
         public void run() {
@@ -1643,6 +1648,7 @@
             mHandoverTaskDisplayArea = daToken != null
                     ? (TaskDisplayArea) WindowContainer.fromBinder(daToken.asBinder()) : null;
             mHandoverLaunchDisplayId = options.getLaunchDisplayId();
+            mLaunchCookie = options.getLaunchCookie();
         }
     }
 
@@ -2586,7 +2592,11 @@
 
                 // When finishing the activity preemptively take the snapshot before the app window
                 // is marked as hidden and any configuration changes take place
-                if (mAtmService.mWindowManager.mTaskSnapshotController != null) {
+                // Note that RecentsAnimation will handle task snapshot while switching apps with
+                // the best capture timing (e.g. IME window capture),
+                // No need additional task capture while task is controlled by RecentsAnimation.
+                if (mAtmService.mWindowManager.mTaskSnapshotController != null
+                        && !task.isAnimatingByRecents()) {
                     final ArraySet<Task> tasks = Sets.newArraySet(task);
                     mAtmService.mWindowManager.mTaskSnapshotController.snapshotTasks(tasks);
                     mAtmService.mWindowManager.mTaskSnapshotController
@@ -3222,6 +3232,7 @@
         getDisplayContent().mUnknownAppVisibilityController.appRemovedOrHidden(this);
         mWmService.mTaskSnapshotController.onAppRemoved(this);
         mStackSupervisor.getActivityMetricsLogger().notifyActivityRemoved(this);
+        mStackSupervisor.mStoppingActivities.remove(this);
         waitingToShow = false;
 
         // TODO(b/169035022): move to a more-appropriate place.
@@ -3985,10 +3996,6 @@
         }
     }
 
-    ActivityOptions getOptionsForTargetActivityLocked() {
-        return pendingOptions != null ? pendingOptions.forTargetActivity() : null;
-    }
-
     void clearOptionsLocked() {
         clearOptionsLocked(true /* withAbort */);
     }
@@ -4215,7 +4222,9 @@
         // Note that we ignore display frozen since we want the opening / closing transition type
         // can be updated correctly even display frozen, and it's safe since in applyAnimation will
         // still check DC#okToAnimate again if the transition animation is fine to apply.
-        if (okToAnimate(true /* ignoreFrozen */) && appTransition.isTransitionSet()) {
+        final boolean recentsAnimating = isAnimating(PARENTS, ANIMATION_TYPE_RECENTS);
+        if (okToAnimate(true /* ignoreFrozen */) && (appTransition.isTransitionSet()
+                || (recentsAnimating && !isActivityTypeHome()))) {
             if (visible) {
                 displayContent.mOpeningApps.add(this);
                 mEnteringAnimation = true;
@@ -6084,7 +6093,8 @@
 
     @Override
     void prepareSurfaces() {
-        final boolean show = isVisible() || isAnimating(PARENTS, ANIMATION_TYPE_APP_TRANSITION);
+        final boolean show = isVisible() || isAnimating(PARENTS,
+                ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS);
 
         if (mSurfaceControl != null) {
             if (show && !mLastSurfaceShowing) {
@@ -6400,6 +6410,10 @@
         mLastReportedConfiguration.setConfiguration(global, override);
     }
 
+    boolean hasCompatDisplayInsets() {
+        return mCompatDisplayInsets != null;
+    }
+
     /**
      * @return {@code true} if this activity is in size compatibility mode that uses the different
      *         density than its parent or its bounds don't fit in parent naturally.
@@ -6490,6 +6504,7 @@
                 && !mAtmService.mForceResizableActivities;
     }
 
+    @Override
     boolean hasSizeCompatBounds() {
         return mSizeCompatBounds != null;
     }
@@ -6537,7 +6552,9 @@
         mSizeCompatScale = 1f;
         mSizeCompatBounds = null;
         mCompatDisplayInsets = null;
-        onRequestedOverrideConfigurationChanged(EMPTY);
+
+        // Recompute from Task because letterbox can also happen on Task level.
+        task.onRequestedOverrideConfigurationChanged(task.getRequestedOverrideConfiguration());
     }
 
     @Override
@@ -6646,9 +6663,10 @@
                 ? requestedOrientation
                 : newParentConfiguration.orientation;
         int rotation = newParentConfiguration.windowConfiguration.getRotation();
-        final boolean canChangeOrientation = handlesOrientationChangeFromDescendant();
-        if (canChangeOrientation && !mCompatDisplayInsets.mIsFloating) {
-            // Use parent rotation because the original display can rotate by requested orientation.
+        final boolean isFixedToUserRotation = mDisplayContent == null
+                || mDisplayContent.getDisplayRotation().isFixedToUserRotation();
+        if (!isFixedToUserRotation && !mCompatDisplayInsets.mIsFloating) {
+            // Use parent rotation because the original display can be rotated.
             resolvedConfig.windowConfiguration.setRotation(rotation);
         } else {
             final int overrideRotation = resolvedConfig.windowConfiguration.getRotation();
@@ -6664,7 +6682,7 @@
         final Rect containingAppBounds = new Rect();
         final Rect containingBounds = mTmpBounds;
         mCompatDisplayInsets.getContainerBounds(containingAppBounds, containingBounds, rotation,
-                orientation, orientationRequested, canChangeOrientation);
+                orientation, orientationRequested, isFixedToUserRotation);
         resolvedBounds.set(containingBounds);
         // The size of floating task is fixed (only swap), so the aspect ratio is already correct.
         if (!mCompatDisplayInsets.mIsFloating) {
@@ -7733,7 +7751,7 @@
         final Rect[] mStableInsets = new Rect[4];
 
         /** Constructs the environment to simulate the bounds behavior of the given container. */
-        CompatDisplayInsets(DisplayContent display, WindowContainer container) {
+        CompatDisplayInsets(DisplayContent display, ActivityRecord container) {
             mIsFloating = container.getWindowConfiguration().tasksAreFloating();
             if (mIsFloating) {
                 final Rect containerBounds = container.getWindowConfiguration().getBounds();
@@ -7749,16 +7767,23 @@
                 return;
             }
 
-            // If the activity is not floating, assume it fills the display.
-            mWidth = display.mBaseDisplayWidth;
-            mHeight = display.mBaseDisplayHeight;
+            if (container.getTask().isTaskLetterboxed()) {
+                // For apps in Task letterbox, it should fill the task bounds.
+                final Rect taskBounds = container.getTask().getBounds();
+                mWidth = taskBounds.width();
+                mHeight = taskBounds.height();
+            } else {
+                // If the activity is not floating nor letterboxed, assume it fills the display.
+                mWidth = display.mBaseDisplayWidth;
+                mHeight = display.mBaseDisplayHeight;
+            }
             final DisplayPolicy policy = display.getDisplayPolicy();
             for (int rotation = 0; rotation < 4; rotation++) {
                 mNonDecorInsets[rotation] = new Rect();
                 mStableInsets[rotation] = new Rect();
                 final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
-                final int dw = rotated ? mHeight : mWidth;
-                final int dh = rotated ? mWidth : mHeight;
+                final int dw = rotated ? display.mBaseDisplayHeight : display.mBaseDisplayWidth;
+                final int dh = rotated ? display.mBaseDisplayWidth : display.mBaseDisplayHeight;
                 final DisplayCutout cutout = display.calculateDisplayCutoutForRotation(rotation)
                         .getDisplayCutout();
                 policy.getNonDecorInsetsLw(rotation, dw, dh, cutout, mNonDecorInsets[rotation]);
@@ -7784,7 +7809,7 @@
 
         /** Gets the horizontal centered container bounds for size compatibility mode. */
         void getContainerBounds(Rect outAppBounds, Rect outBounds, int rotation, int orientation,
-                boolean orientationRequested, boolean canChangeOrientation) {
+                boolean orientationRequested, boolean isFixedToUserRotation) {
             getFrameByOrientation(outBounds, orientation);
             if (mIsFloating) {
                 outAppBounds.set(outBounds);
@@ -7797,7 +7822,7 @@
             final boolean isOrientationMismatched =
                     ((outBounds.width() > outBounds.height()) != (dW > dH));
 
-            if (isOrientationMismatched && !canChangeOrientation && orientationRequested) {
+            if (isOrientationMismatched && isFixedToUserRotation && orientationRequested) {
                 // The orientation is mismatched but the display cannot rotate. The bounds will fit
                 // to the short side of container.
                 if (orientation == ORIENTATION_LANDSCAPE) {
@@ -7875,4 +7900,17 @@
         pictureInPictureArgs.copyOnlySet(p);
         getTask().getRootTask().onPictureInPictureParamsChanged();
     }
+
+    @Override
+    boolean isSyncFinished() {
+        if (!super.isSyncFinished()) return false;
+        if (!isVisibleRequested()) return true;
+        // If visibleRequested, wait for at-least one visible child.
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            if (mChildren.get(i).isVisibleRequested()) {
+                return true;
+            }
+        }
+        return false;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index e2c1303..a8079cf 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -1468,13 +1468,13 @@
         }
     }
 
-    private void removeStackInSurfaceTransaction(Task stack) {
-        if (stack.getWindowingMode() == WINDOWING_MODE_PINNED) {
-            removePinnedStackInSurfaceTransaction(stack);
+    private void removeRootTaskInSurfaceTransaction(Task rootTask) {
+        if (rootTask.getWindowingMode() == WINDOWING_MODE_PINNED) {
+            removePinnedStackInSurfaceTransaction(rootTask);
         } else {
             final PooledConsumer c = PooledLambda.obtainConsumer(
                     ActivityStackSupervisor::processRemoveTask, this, PooledLambda.__(Task.class));
-            stack.forAllLeafTasks(c, true /* traverseTopToBottom */);
+            rootTask.forAllLeafTasks(c, true /* traverseTopToBottom */);
             c.recycle();
         }
     }
@@ -1484,12 +1484,12 @@
     }
 
     /**
-     * Removes the stack associated with the given {@param stack}. If the {@param stack} is the
-     * pinned stack, then its tasks are not explicitly removed when the stack is destroyed, but
-     * instead moved back onto the fullscreen stack.
+     * Removes the root task associated with the given {@param task}. If the {@param task} is the
+     * pinned task, then its child tasks are not explicitly removed when the root task is
+     * destroyed, but instead moved back onto the TaskDisplayArea.
      */
-    void removeStack(Task stack) {
-        mWindowManager.inSurfaceTransaction(() -> removeStackInSurfaceTransaction(stack));
+    void removeRootTask(Task task) {
+        mWindowManager.inSurfaceTransaction(() -> removeRootTaskInSurfaceTransaction(task));
     }
 
     /**
@@ -2040,6 +2040,11 @@
         final ActivityRecord prevTopActivity = mTopResumedActivity;
         final Task topStack = mRootWindowContainer.getTopDisplayFocusedStack();
         if (topStack == null || topStack.mResumedActivity == prevTopActivity) {
+            if (mService.isSleepingLocked()) {
+                // There won't be a next resumed activity. The top process should still be updated
+                // according to the current top focused activity.
+                mService.updateTopApp(null /* topResumedActivity */);
+            }
             return;
         }
 
@@ -2058,6 +2063,8 @@
         // Update the current top activity.
         mTopResumedActivity = topStack.mResumedActivity;
         scheduleTopResumedActivityStateIfNeeded();
+
+        mService.updateTopApp(mTopResumedActivity);
     }
 
     /** Schedule top resumed state change if previous top activity already reported back. */
@@ -2157,21 +2164,7 @@
             }
 
             final DisplayContent preferredDisplay = preferredTaskDisplayArea.mDisplayContent;
-
-            final boolean singleTaskInstance = preferredDisplay != null
-                    && preferredDisplay.isSingleTaskInstance();
-
             if (preferredDisplay != task.getDisplayContent()) {
-                // 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.
-                if (singleTaskInstance) {
-                    mService.getTaskChangeNotificationController()
-                            .notifyActivityLaunchOnSecondaryDisplayRerouted(task.getTaskInfo(),
-                                    preferredDisplay.mDisplayId);
-                    return;
-                }
-
                 Slog.w(TAG, "Failed to put " + task + " on display " + preferredDisplay.mDisplayId);
                 // Display a warning toast that we failed to put a task on a secondary display.
                 mService.getTaskChangeNotificationController()
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 28e71fa..c8a8f81 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1358,6 +1358,12 @@
             }
             return false;
         }
+        // don't abort if the callingUid has SYSTEM_ALERT_WINDOW permission
+        if (mService.hasSystemAlertWindowPermission(callingUid, callingPid, callingPackage)) {
+            Slog.w(TAG, "Background activity start for " + callingPackage
+                    + " allowed because SYSTEM_ALERT_WINDOW permission is granted.");
+            return false;
+        }
         // If we don't have callerApp at this point, no caller was provided to startActivity().
         // That's the case for PendingIntent-based starts, since the creator's process might not be
         // up and alive. If that's the case, we retrieve the WindowProcessController for the send()
@@ -1394,12 +1400,6 @@
                 }
             }
         }
-        // don't abort if the callingUid has SYSTEM_ALERT_WINDOW permission
-        if (mService.hasSystemAlertWindowPermission(callingUid, callingPid, callingPackage)) {
-            Slog.w(TAG, "Background activity start for " + callingPackage
-                    + " allowed because SYSTEM_ALERT_WINDOW permission is granted.");
-            return false;
-        }
         // anything that has fallen through would currently be aborted
         Slog.w(TAG, "Background activity start [callingPackage: " + callingPackage
                 + "; callingUid: " + callingUid
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index e65be41..c7d716d82 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -173,13 +173,6 @@
     public abstract boolean hasResumedActivity(int uid);
 
     /**
-     * Notify listeners that contents are drawn for the first time on a single task display.
-     *
-     * @param displayId An ID of the display on which contents are drawn.
-     */
-    public abstract void notifySingleTaskDisplayDrawn(int displayId);
-
-    /**
      * Start activity {@code intents} as if {@code packageName/featureId} on user {@code userId} did
      * it.
      *
@@ -283,7 +276,7 @@
     /**
      * Cancels any currently running recents animation.
      */
-    public abstract void cancelRecentsAnimation(boolean restoreHomeStackPosition);
+    public abstract void cancelRecentsAnimation(boolean restoreHomeRootTaskPosition);
 
     /**
      * This enforces {@code func} can only be called if either the caller is Recents activity or
@@ -324,7 +317,6 @@
     public abstract void onProcessRemoved(String name, int uid);
     public abstract void onCleanUpApplicationRecord(WindowProcessController proc);
     public abstract int getTopProcessState();
-    public abstract boolean isHeavyWeightProcess(WindowProcessController proc);
     public abstract void clearHeavyWeightProcessIfEquals(WindowProcessController proc);
     public abstract void finishHeavyWeightApp();
 
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 0938d22..49d9e95 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -386,16 +386,18 @@
     /** All processes we currently have running mapped by pid and uid */
     final WindowProcessControllerMap mProcessMap = new WindowProcessControllerMap();
     /** This is the process holding what we currently consider to be the "home" activity. */
-    WindowProcessController mHomeProcess;
+    volatile WindowProcessController mHomeProcess;
     /** The currently running heavy-weight process, if any. */
-    WindowProcessController mHeavyWeightProcess = null;
+    volatile WindowProcessController mHeavyWeightProcess;
     boolean mHasHeavyWeightFeature;
     boolean mHasLeanbackFeature;
+    /** The process of the top most activity. */
+    volatile WindowProcessController mTopApp;
     /**
      * This is the process holding the activity the user last visited that is in a different process
      * from the one they are currently in.
      */
-    WindowProcessController mPreviousProcess;
+    volatile WindowProcessController mPreviousProcess;
     /** The time at which the previous process was last visible. */
     long mPreviousProcessVisibleTime;
 
@@ -593,7 +595,7 @@
      * Whether mSleeping can quickly toggled between true/false without the device actually
      * display changing states is undefined.
      */
-    private boolean mSleeping = false;
+    private volatile boolean mSleeping;
 
     /**
      * The mDreaming state is set by the {@link DreamManagerService} when it receives a request to
@@ -606,7 +608,7 @@
      * The process state used for processes that are running the top activities.
      * This changes between TOP and TOP_SLEEPING to following mSleeping.
      */
-    int mTopProcessState = ActivityManager.PROCESS_STATE_TOP;
+    volatile int mTopProcessState = ActivityManager.PROCESS_STATE_TOP;
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({
@@ -2172,7 +2174,7 @@
     @Override
     public RootTaskInfo getFocusedRootTaskInfo() throws RemoteException {
         enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getFocusedRootTaskInfo()");
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
                 Task focusedStack = getTopDisplayFocusedStack();
@@ -2187,19 +2189,19 @@
     }
 
     @Override
-    public void setFocusedStack(int stackId) {
-        mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setFocusedStack()");
-        ProtoLog.d(WM_DEBUG_FOCUS, "setFocusedStack: stackId=%d", stackId);
+    public void setFocusedRootTask(int taskId) {
+        mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setFocusedRootTask()");
+        ProtoLog.d(WM_DEBUG_FOCUS, "setFocusedRootTask: taskId=%d", taskId);
         final long callingId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
-                final Task stack = mRootWindowContainer.getStack(stackId);
-                if (stack == null) {
-                    Slog.w(TAG, "setFocusedStack: No stack with id=" + stackId);
+                final Task task = mRootWindowContainer.getStack(taskId);
+                if (task == null) {
+                    Slog.w(TAG, "setFocusedRootTask: No task with id=" + taskId);
                     return;
                 }
-                final ActivityRecord r = stack.topRunningActivity();
-                if (r != null && r.moveFocusableActivityToTop("setFocusedStack")) {
+                final ActivityRecord r = task.topRunningActivity();
+                if (r != null && r.moveFocusableActivityToTop("setFocusedRootTask")) {
                     mRootWindowContainer.resumeFocusedStacksTopActivities();
                 }
             }
@@ -2253,8 +2255,19 @@
         synchronized (mGlobalLock) {
             final long ident = Binder.clearCallingIdentity();
             try {
-                return mStackSupervisor.removeTaskById(taskId, true, REMOVE_FROM_RECENTS,
-                        "remove-task");
+                final Task task = mRootWindowContainer.anyTaskForId(taskId,
+                        MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
+                if (task == null) {
+                    Slog.w(TAG, "removeTask: No task remove with id=" + taskId);
+                    return false;
+                }
+
+                if (task.isLeafTask()) {
+                    mStackSupervisor.removeTask(task, true, REMOVE_FROM_RECENTS, "remove-task");
+                } else {
+                    mStackSupervisor.removeRootTask(task);
+                }
+                return true;
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -2346,7 +2359,7 @@
     @Override
     public Rect getTaskBounds(int taskId) {
         mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "getTaskBounds()");
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         Rect rect = new Rect();
         try {
             synchronized (mGlobalLock) {
@@ -2494,13 +2507,6 @@
             if (taskOrgController.handleInterceptBackPressedOnTaskRoot(stack)) {
                 // This task is handled by a task organizer that has requested the back pressed
                 // callback
-            } else if (stack != null && (stack.isSingleTaskInstance())) {
-                // Single-task stacks are used for activities which are presented in floating
-                // windows above full screen activities. A task change listener is used to notify
-                // SystemUI so the back action can be handled specially.
-                final Task task = r.getTask();
-                mTaskChangeNotificationController
-                        .notifyBackPressedOnTaskRoot(task.getTaskInfo());
             } else {
                 moveActivityTaskToBack(token, false /* nonRoot */);
             }
@@ -2750,31 +2756,31 @@
     }
 
     @Override
-    public void moveTaskToStack(int taskId, int stackId, boolean toTop) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToStack()");
+    public void moveTaskToRootTask(int taskId, int rootTaskId, boolean toTop) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToRootTask()");
         synchronized (mGlobalLock) {
             final long ident = Binder.clearCallingIdentity();
             try {
                 final Task task = mRootWindowContainer.anyTaskForId(taskId);
                 if (task == null) {
-                    Slog.w(TAG, "moveTaskToStack: No task for id=" + taskId);
+                    Slog.w(TAG, "moveTaskToRootTask: No task for id=" + taskId);
                     return;
                 }
 
-                ProtoLog.d(WM_DEBUG_TASKS, "moveTaskToStack: moving task=%d to "
-                        + "stackId=%d toTop=%b", taskId, stackId, toTop);
+                ProtoLog.d(WM_DEBUG_TASKS, "moveTaskToRootTask: moving task=%d to "
+                        + "rootTaskId=%d toTop=%b", taskId, rootTaskId, toTop);
 
-                final Task stack = mRootWindowContainer.getStack(stackId);
-                if (stack == null) {
+                final Task rootTask = mRootWindowContainer.getStack(rootTaskId);
+                if (rootTask == null) {
                     throw new IllegalStateException(
-                            "moveTaskToStack: No stack for stackId=" + stackId);
+                            "moveTaskToRootTask: No rootTask for rootTaskId=" + rootTaskId);
                 }
-                if (!stack.isActivityTypeStandardOrUndefined()) {
-                    throw new IllegalArgumentException("moveTaskToStack: Attempt to move task "
-                            + taskId + " to stack " + stackId);
+                if (!rootTask.isActivityTypeStandardOrUndefined()) {
+                    throw new IllegalArgumentException("moveTaskToRootTask: Attempt to move task "
+                            + taskId + " to rootTask " + rootTaskId);
                 }
-                task.reparent(stack, toTop, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME,
-                        "moveTaskToStack");
+                task.reparent(rootTask, toTop, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME,
+                        "moveTaskToRootTask");
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -2862,18 +2868,18 @@
     }
 
     /**
-     * Removes stacks in the input windowing modes from the system if they are of activity type
+     * Removes root tasks in the input windowing modes from the system if they are of activity type
      * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
      */
     @Override
-    public void removeStacksInWindowingModes(int[] windowingModes) {
+    public void removeRootTasksInWindowingModes(int[] windowingModes) {
         enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
-                "removeStacksInWindowingModes()");
+                "removeRootTasksInWindowingModes()");
 
         synchronized (mGlobalLock) {
             final long ident = Binder.clearCallingIdentity();
             try {
-                mRootWindowContainer.removeStacksInWindowingModes(windowingModes);
+                mRootWindowContainer.removeRootTasksInWindowingModes(windowingModes);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -2881,14 +2887,14 @@
     }
 
     @Override
-    public void removeStacksWithActivityTypes(int[] activityTypes) {
+    public void removeRootTasksWithActivityTypes(int[] activityTypes) {
         enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
-                "removeStacksWithActivityTypes()");
+                "removeRootTasksWithActivityTypes()");
 
         synchronized (mGlobalLock) {
             final long ident = Binder.clearCallingIdentity();
             try {
-                mRootWindowContainer.removeStacksWithActivityTypes(activityTypes);
+                mRootWindowContainer.removeRootTasksWithActivityTypes(activityTypes);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -2910,7 +2916,7 @@
     @Override
     public List<RootTaskInfo> getAllRootTaskInfos() {
         enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getAllRootTaskInfos()");
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
                 return mRootWindowContainer.getAllRootTaskInfos(INVALID_DISPLAY);
@@ -2923,7 +2929,7 @@
     @Override
     public RootTaskInfo getRootTaskInfo(int windowingMode, int activityType) {
         enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getRootTaskInfo()");
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
                 return mRootWindowContainer.getRootTaskInfo(windowingMode, activityType);
@@ -2937,7 +2943,7 @@
     public List<RootTaskInfo> getAllRootTaskInfosOnDisplay(int displayId) {
         enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
                 "getAllRootTaskInfosOnDisplay()");
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
                 return mRootWindowContainer.getAllRootTaskInfos(displayId);
@@ -2951,7 +2957,7 @@
     public RootTaskInfo getRootTaskInfoOnDisplay(int windowingMode, int activityType,
             int displayId) {
         enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getRootTaskInfoOnDisplay()");
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
                 return mRootWindowContainer.getRootTaskInfo(windowingMode, activityType, displayId);
@@ -2962,14 +2968,14 @@
     }
 
     @Override
-    public void cancelRecentsAnimation(boolean restoreHomeStackPosition) {
+    public void cancelRecentsAnimation(boolean restoreHomeRootTaskPosition) {
         enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "cancelRecentsAnimation()");
         final long callingUid = Binder.getCallingUid();
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
                 // Cancel the recents animation synchronously (do not hold the WM lock)
-                mWindowManager.cancelRecentsAnimation(restoreHomeStackPosition
+                mWindowManager.cancelRecentsAnimation(restoreHomeRootTaskPosition
                         ? REORDER_MOVE_TO_ORIGINAL_POSITION
                         : REORDER_KEEP_IN_PLACE, "cancelRecentsAnimation/uid=" + callingUid);
             }
@@ -2993,7 +2999,7 @@
     public void startSystemLockTaskMode(int taskId) {
         mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "startSystemLockTaskMode");
         // This makes inner call to look as if it was initiated by system.
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
                 final Task task = mRootWindowContainer.anyTaskForId(taskId,
@@ -3044,10 +3050,10 @@
         //   - will put the device in fully locked mode (LockTask), if the app is allowlisted
         //   - will start the pinned mode, otherwise
         final int callingUid = Binder.getCallingUid();
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             // When a task is locked, dismiss the pinned stack if it exists
-            mRootWindowContainer.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
+            mRootWindowContainer.removeRootTasksInWindowingModes(WINDOWING_MODE_PINNED);
 
             getLockTaskController().startLockTaskMode(task, isSystemCaller, callingUid);
         } finally {
@@ -3057,7 +3063,7 @@
 
     private void stopLockTaskModeInternal(@Nullable IBinder token, boolean isSystemCaller) {
         final int callingUid = Binder.getCallingUid();
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
                 Task task = null;
@@ -3138,7 +3144,7 @@
     public List<IBinder> getAppTasks(String callingPackage) {
         int callingUid = Binder.getCallingUid();
         assertPackageMatchesCallingUid(callingPackage);
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
                 return mRecentTasks.getAppTasksList(callingUid, callingPackage);
@@ -3357,7 +3363,7 @@
     @Override
     public boolean resizeTask(int taskId, Rect bounds, int resizeMode) {
         mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "resizeTask()");
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
                 final Task task = mRootWindowContainer.anyTaskForId(taskId,
@@ -3422,7 +3428,7 @@
         }
 
         synchronized (mGlobalLock) {
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             if (mKeyguardShown != keyguardShowing) {
                 mKeyguardShown = keyguardShowing;
                 final Message msg = PooledLambda.obtainMessage(
@@ -3469,37 +3475,15 @@
     }
 
     @Override
-    public void removeStack(int stackId) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "removeStack()");
-        synchronized (mGlobalLock) {
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                final Task stack = mRootWindowContainer.getStack(stackId);
-                if (stack == null) {
-                    Slog.w(TAG, "removeStack: No stack with id=" + stackId);
-                    return;
-                }
-                if (!stack.isActivityTypeStandardOrUndefined()) {
-                    throw new IllegalArgumentException(
-                            "Removing non-standard stack is not allowed.");
-                }
-                mStackSupervisor.removeStack(stack);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-    }
-
-    @Override
-    public void moveStackToDisplay(int stackId, int displayId) {
-        mAmInternal.enforceCallingPermission(INTERNAL_SYSTEM_WINDOW, "moveStackToDisplay()");
+    public void moveRootTaskToDisplay(int taskId, int displayId) {
+        mAmInternal.enforceCallingPermission(INTERNAL_SYSTEM_WINDOW, "moveRootTaskToDisplay()");
 
         synchronized (mGlobalLock) {
             final long ident = Binder.clearCallingIdentity();
             try {
-                ProtoLog.d(WM_DEBUG_TASKS, "moveStackToDisplay: moving stackId=%d to "
-                        + "displayId=%d", stackId, displayId);
-                mRootWindowContainer.moveStackToDisplay(stackId, displayId, ON_TOP);
+                ProtoLog.d(WM_DEBUG_TASKS, "moveRootTaskToDisplay: moving taskId=%d to "
+                        + "displayId=%d", taskId, displayId);
+                mRootWindowContainer.moveStackToDisplay(taskId, displayId, ON_TOP);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -3509,7 +3493,7 @@
     @Override
     public void toggleFreeformWindowingMode(IBinder token) {
         synchronized (mGlobalLock) {
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
                 final ActivityRecord r = ActivityRecord.forTokenLocked(token);
                 if (r == null) {
@@ -3829,7 +3813,7 @@
 
     @Override
     public boolean showAssistFromActivity(IBinder token, Bundle args) {
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
                 ActivityRecord caller = ActivityRecord.forTokenLocked(token);
@@ -3873,7 +3857,7 @@
         try {
             activityToCallback.app.getThread().scheduleLocalVoiceInteractionStarted(activity,
                     voiceInteractor);
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 startRunningVoiceLocked(voiceSession, activityToCallback.info.applicationInfo.uid);
             } finally {
@@ -3993,26 +3977,26 @@
     }
 
     /**
-     * Moves the top activity in the input stackId to the pinned stack.
+     * Moves the top activity in the input rootTaskId to the pinned root task.
      *
-     * @param stackId Id of stack to move the top activity to pinned stack.
-     * @param bounds  Bounds to use for pinned stack.
-     * @return True if the top activity of the input stack was successfully moved to the pinned
-     * stack.
+     * @param rootTaskId Id of root task to move the top activity to pinned root task.
+     * @param bounds     Bounds to use for pinned root task.
+     * @return True if the top activity of the input stack was successfully moved to the pinned root
+     * task.
      */
     @Override
-    public boolean moveTopActivityToPinnedStack(int stackId, Rect bounds) {
+    public boolean moveTopActivityToPinnedRootTask(int rootTaskId, Rect bounds) {
         enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
-                "moveTopActivityToPinnedStack()");
+                "moveTopActivityToPinnedRootTask()");
         synchronized (mGlobalLock) {
             if (!mSupportsPictureInPicture) {
-                throw new IllegalStateException("moveTopActivityToPinnedStack:"
+                throw new IllegalStateException("moveTopActivityToPinnedRootTask:"
                         + "Device doesn't support picture-in-picture mode");
             }
 
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
-                return mRootWindowContainer.moveTopStackActivityToPinnedStack(stackId);
+                return mRootWindowContainer.moveTopStackActivityToPinnedRootTask(rootTaskId);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -4184,11 +4168,11 @@
 
     // TODO(b/149338177): remove when CTS no-longer requires it
     @Override
-    public void resizeDockedStack(Rect dockedBounds, Rect tempDockedTaskBounds,
+    public void resizePrimarySplitScreen(Rect dockedBounds, Rect tempDockedTaskBounds,
             Rect tempDockedTaskInsetBounds,
             Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizeDockedStack()");
-        long ident = Binder.clearCallingIdentity();
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizePrimarySplitScreen()");
+        final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
                 final TaskDisplayArea tc = mRootWindowContainer.getDefaultTaskDisplayArea();
@@ -4710,7 +4694,7 @@
                 if (disableNonVrUi) {
                     // If we are in a VR mode where Picture-in-Picture mode is unsupported,
                     // then remove the pinned stack.
-                    mRootWindowContainer.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
+                    mRootWindowContainer.removeRootTasksInWindowingModes(WINDOWING_MODE_PINNED);
                 }
             }
         });
@@ -4791,26 +4775,6 @@
     }
 
     /**
-     * Makes the display with the given id a single task instance display. I.e the display can only
-     * contain one task.
-     */
-    @Override
-    public void setDisplayToSingleTaskInstance(int displayId) {
-        mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS,
-                "setDisplayToSingleTaskInstance");
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            final DisplayContent display =
-                    mRootWindowContainer.getDisplayContentOrCreate(displayId);
-            if (display != null) {
-                display.setDisplayToSingleTaskInstance();
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    /**
      * Requests that an activity should enter picture-in-picture mode if possible.
      */
     @Override
@@ -5145,7 +5109,9 @@
                         deferResume);
             }
 
-            kept = ensureConfigAndVisibilityAfterUpdate(starting, changes);
+            if (!deferResume) {
+                kept = ensureConfigAndVisibilityAfterUpdate(starting, changes);
+            }
         } finally {
             continueWindowLayout();
         }
@@ -5580,6 +5546,13 @@
         mH.sendMessage(m);
     }
 
+    void updateTopApp(ActivityRecord topResumedActivity) {
+        final ActivityRecord top = topResumedActivity != null ? topResumedActivity
+                // If there is no resumed activity, it will choose the pausing or focused activity.
+                : mRootWindowContainer.getTopResumedActivity();
+        mTopApp = top != null ? top.app : null;
+    }
+
     void updateActivityUsageStats(ActivityRecord activity, int event) {
         ComponentName taskRoot = null;
         final Task task = activity.getTask();
@@ -6032,10 +6005,6 @@
         return allUids.contains(uid);
     }
 
-    void notifySingleTaskDisplayEmpty(int displayId) {
-        mTaskChangeNotificationController.notifySingleTaskDisplayEmpty(displayId);
-    }
-
     final class H extends Handler {
         static final int REPORT_TIME_TRACKER_MSG = 1;
 
@@ -6103,11 +6072,6 @@
         }
 
         @Override
-        public void notifySingleTaskDisplayDrawn(int displayId) {
-            mTaskChangeNotificationController.notifySingleTaskDisplayDrawn(displayId);
-        }
-
-        @Override
         public List<IBinder> getTopVisibleActivities() {
             synchronized (mGlobalLock) {
                 return mRootWindowContainer.getTopVisibleActivities();
@@ -6286,8 +6250,8 @@
         }
 
         @Override
-        public void cancelRecentsAnimation(boolean restoreHomeStackPosition) {
-            ActivityTaskManagerService.this.cancelRecentsAnimation(restoreHomeStackPosition);
+        public void cancelRecentsAnimation(boolean restoreHomeRootTaskPosition) {
+            ActivityTaskManagerService.this.cancelRecentsAnimation(restoreHomeRootTaskPosition);
         }
 
         @Override
@@ -6379,17 +6343,7 @@
         @HotPath(caller = HotPath.OOM_ADJUSTMENT)
         @Override
         public int getTopProcessState() {
-            synchronized (mGlobalLockWithoutBoost) {
-                return mTopProcessState;
-            }
-        }
-
-        @HotPath(caller = HotPath.OOM_ADJUSTMENT)
-        @Override
-        public boolean isHeavyWeightProcess(WindowProcessController proc) {
-            synchronized (mGlobalLockWithoutBoost) {
-                return proc == mHeavyWeightProcess;
-            }
+            return mTopProcessState;
         }
 
         @HotPath(caller = HotPath.PROCESS_CHANGE)
@@ -6421,9 +6375,7 @@
         @HotPath(caller = HotPath.OOM_ADJUSTMENT)
         @Override
         public boolean isSleeping() {
-            synchronized (mGlobalLockWithoutBoost) {
-                return isSleepingLocked();
-            }
+            return mSleeping;
         }
 
         @Override
@@ -7217,15 +7169,7 @@
         @HotPath(caller = HotPath.OOM_ADJUSTMENT)
         @Override
         public WindowProcessController getTopApp() {
-            synchronized (mGlobalLockWithoutBoost) {
-                if (mRootWindowContainer == null) {
-                    // Return null if mRootWindowContainer not yet initialize, while update
-                    // oomadj after AMS created.
-                    return null;
-                }
-                final ActivityRecord top = mRootWindowContainer.getTopResumedActivity();
-                return top != null ? top.app : null;
-            }
+            return mTopApp;
         }
 
         @Override
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index dd1d55b..3e79879 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -58,7 +58,7 @@
         checkCaller();
 
         synchronized (mService.mGlobalLock) {
-            long origId = Binder.clearCallingIdentity();
+            final long origId = Binder.clearCallingIdentity();
             try {
                 // We remove the task from recents to preserve backwards
                 if (!mService.mStackSupervisor.removeTaskById(mTaskId, false,
@@ -76,7 +76,7 @@
         checkCaller();
 
         synchronized (mService.mGlobalLock) {
-            long origId = Binder.clearCallingIdentity();
+            final long origId = Binder.clearCallingIdentity();
             try {
                 Task task = mService.mRootWindowContainer.anyTaskForId(mTaskId,
                         MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
@@ -162,7 +162,7 @@
         checkCaller();
 
         synchronized (mService.mGlobalLock) {
-            long origId = Binder.clearCallingIdentity();
+            final long origId = Binder.clearCallingIdentity();
             try {
                 Task task = mService.mRootWindowContainer.anyTaskForId(mTaskId,
                         MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 0e47ea8..26c701b 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -21,7 +21,6 @@
 import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
 import static android.view.WindowManager.TRANSIT_ACTIVITY_RELAUNCH;
 import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
-import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
@@ -30,7 +29,6 @@
 import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
 import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
 import static android.view.WindowManager.TRANSIT_TASK_CHANGE_WINDOWING_MODE;
 import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
 import static android.view.WindowManager.TRANSIT_TASK_OPEN;
@@ -92,7 +90,6 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
-import android.graphics.Path;
 import android.graphics.Picture;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
@@ -134,7 +131,6 @@
 import com.android.server.AttributeCache;
 import com.android.server.wm.animation.ClipRectLRAnimation;
 import com.android.server.wm.animation.ClipRectTBAnimation;
-import com.android.server.wm.animation.CurvedTranslateAnimation;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -161,9 +157,6 @@
     static final Interpolator TOUCH_RESPONSE_INTERPOLATOR =
             new PathInterpolator(0.3f, 0f, 0.1f, 1f);
 
-    private static final Interpolator THUMBNAIL_DOCK_INTERPOLATOR =
-            new PathInterpolator(0.85f, 0f, 1f, 1f);
-
     /**
      * Maximum duration for the clip reveal animation. This is used when there is a lot of movement
      * involved, to make it more understandable.
@@ -1127,11 +1120,8 @@
             scale.setInterpolator(interpolator);
             scale.setDuration(duration);
             Animation alpha = new AlphaAnimation(1f, 0f);
-            alpha.setInterpolator(mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS
-                    ? THUMBNAIL_DOCK_INTERPOLATOR : mThumbnailFadeOutInterpolator);
-            alpha.setDuration(mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS
-                    ? duration / 2
-                    : duration);
+            alpha.setInterpolator(mThumbnailFadeOutInterpolator);
+            alpha.setDuration(duration);
             Animation translate = createCurvedMotion(fromX, toX, fromY, toY);
             translate.setInterpolator(interpolator);
             translate.setDuration(duration);
@@ -1194,44 +1184,15 @@
     }
 
     private Animation createCurvedMotion(float fromX, float toX, float fromY, float toY) {
-
-        // Almost no x-change - use linear animation
-        if (Math.abs(toX - fromX) < 1f || mNextAppTransition != TRANSIT_DOCK_TASK_FROM_RECENTS) {
-            return new TranslateAnimation(fromX, toX, fromY, toY);
-        } else {
-            final Path path = createCurvedPath(fromX, toX, fromY, toY);
-            return new CurvedTranslateAnimation(path);
-        }
-    }
-
-    private Path createCurvedPath(float fromX, float toX, float fromY, float toY) {
-        final Path path = new Path();
-        path.moveTo(fromX, fromY);
-
-        if (fromY > toY) {
-            // If the object needs to go up, move it in horizontal direction first, then vertical.
-            path.cubicTo(fromX, fromY, toX, 0.9f * fromY + 0.1f * toY, toX, toY);
-        } else {
-            // If the object needs to go down, move it in vertical direction first, then horizontal.
-            path.cubicTo(fromX, fromY, fromX, 0.1f * fromY + 0.9f * toY, toX, toY);
-        }
-        return path;
+        return new TranslateAnimation(fromX, toX, fromY, toY);
     }
 
     private long getAspectScaleDuration() {
-        if (mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS) {
-            return (long) (THUMBNAIL_APP_TRANSITION_DURATION * 1.35f);
-        } else {
-            return THUMBNAIL_APP_TRANSITION_DURATION;
-        }
+        return THUMBNAIL_APP_TRANSITION_DURATION;
     }
 
     private Interpolator getAspectScaleInterpolator() {
-        if (mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS) {
-            return mFastOutSlowInInterpolator;
-        } else {
-            return TOUCH_RESPONSE_INTERPOLATOR;
-        }
+        return TOUCH_RESPONSE_INTERPOLATOR;
     }
 
     /**
@@ -1734,7 +1695,6 @@
                             ? WindowAnimation_activityCloseEnterAnimation
                             : WindowAnimation_activityCloseExitAnimation;
                     break;
-                case TRANSIT_DOCK_TASK_FROM_RECENTS:
                 case TRANSIT_TASK_OPEN:
                     animAttr = enter
                             ? WindowAnimation_taskOpenEnterAnimation
@@ -1805,7 +1765,6 @@
 
     int getAppStackClipMode() {
         return mNextAppTransition == TRANSIT_ACTIVITY_RELAUNCH
-                || mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS
                 || mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL
                 || mNextAppTransition == TRANSIT_KEYGUARD_GOING_AWAY
                 || mNextAppTransition == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER
@@ -2059,9 +2018,6 @@
             case TRANSIT_ACTIVITY_RELAUNCH: {
                 return "TRANSIT_ACTIVITY_RELAUNCH";
             }
-            case TRANSIT_DOCK_TASK_FROM_RECENTS: {
-                return "TRANSIT_DOCK_TASK_FROM_RECENTS";
-            }
             case TRANSIT_KEYGUARD_GOING_AWAY: {
                 return "TRANSIT_KEYGUARD_GOING_AWAY";
             }
@@ -2083,9 +2039,6 @@
             case TRANSIT_CRASHING_ACTIVITY_CLOSE: {
                 return "TRANSIT_CRASHING_ACTIVITY_CLOSE";
             }
-            case TRANSIT_SHOW_SINGLE_TASK_DISPLAY: {
-                return "TRANSIT_SHOW_SINGLE_TASK_DISPLAY";
-            }
             default: {
                 return "<UNKNOWN: " + transition + ">";
             }
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 57d51c5..14eedf7 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -21,7 +21,6 @@
 import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
 import static android.view.WindowManager.TRANSIT_ACTIVITY_RELAUNCH;
 import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
-import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
@@ -29,7 +28,6 @@
 import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
 import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
 import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
 import static android.view.WindowManager.TRANSIT_TASK_OPEN;
 import static android.view.WindowManager.TRANSIT_TASK_TO_BACK;
@@ -216,12 +214,6 @@
         mService.mAtmService.mStackSupervisor.getActivityMetricsLogger().notifyTransitionStarting(
                 mTempTransitionReasons);
 
-        if (transit == TRANSIT_SHOW_SINGLE_TASK_DISPLAY) {
-            mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
-                mService.mAtmInternal.notifySingleTaskDisplayDrawn(mDisplayContent.getDisplayId());
-            });
-        }
-
         Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
 
         mDisplayContent.pendingLayoutChanges |=
@@ -701,11 +693,9 @@
             boolean openingAppHasWallpaper, boolean closingAppHasWallpaper) {
         // Given no app transition pass it through instead of a wallpaper transition.
         // Never convert the crashing transition.
-        // Never update the transition for the wallpaper if we are just docking from recents
         // Never convert a change transition since the top activity isn't changing and will likely
         // still be above an opening wallpaper.
         if (transit == TRANSIT_NONE || transit == TRANSIT_CRASHING_ACTIVITY_CLOSE
-                || transit == TRANSIT_DOCK_TASK_FROM_RECENTS
                 || AppTransition.isChangeTransit(transit)) {
             return transit;
         }
diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
index 958a7a8..301783c 100644
--- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java
+++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
@@ -18,13 +18,13 @@
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SYNC_ENGINE;
 
-import android.util.ArrayMap;
+import android.annotation.NonNull;
 import android.util.ArraySet;
+import android.util.SparseArray;
+import android.view.SurfaceControl;
 
 import com.android.internal.protolog.common.ProtoLog;
 
-import java.util.Set;
-
 /**
  * Utility class for collecting WindowContainers that will merge transactions.
  * For example to use to synchronously resize all the children of a window container
@@ -45,94 +45,122 @@
  *   5. If there were no sub windows anywhere in the hierarchy to wait on, then
  *      transactionReady is immediately invoked, otherwise all the windows are poked
  *      to redraw and to deliver a buffer to {@link WindowState#finishDrawing}.
- *      Once all this drawing is complete the WindowContainer that's ready will be added to the
- *      set of ready WindowContainers. When the final onTransactionReady is called, it will merge
- *      the transactions of the all the WindowContainers and will be delivered to the
- *      TransactionReadyListener
+ *      Once all this drawing is complete, all the transactions will be merged and delivered
+ *      to TransactionReadyListener.
+ *
+ * This works primarily by setting-up state and then watching/waiting for the registered subtrees
+ * to enter into a "finished" state (either by receiving drawn content or by disappearing). This
+ * checks the subtrees during surface-placement.
  */
 class BLASTSyncEngine {
     private static final String TAG = "BLASTSyncEngine";
 
     interface TransactionReadyListener {
-        void onTransactionReady(int mSyncId, Set<WindowContainer> windowContainersReady);
-    };
+        void onTransactionReady(int mSyncId, SurfaceControl.Transaction transaction);
+    }
 
-    // Holds state associated with a single synchronous set of operations.
-    class SyncState implements TransactionReadyListener {
-        int mSyncId;
-        int mRemainingTransactions;
-        TransactionReadyListener mListener;
+    /**
+     * Holds state associated with a single synchronous set of operations.
+     */
+    class SyncGroup {
+        final int mSyncId;
+        final TransactionReadyListener mListener;
         boolean mReady = false;
-        Set<WindowContainer> mWindowContainersReady = new ArraySet<>();
+        final ArraySet<WindowContainer> mRootMembers = new ArraySet<>();
+        private SurfaceControl.Transaction mOrphanTransaction = null;
 
-        private void tryFinish() {
-            if (mRemainingTransactions == 0 && mReady) {
-                ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncSet{%x:%d} Finished. Reporting %d "
-                        + "containers to %s", BLASTSyncEngine.this.hashCode(), mSyncId,
-                        mWindowContainersReady.size(), mListener);
-                mListener.onTransactionReady(mSyncId, mWindowContainersReady);
-                mPendingSyncs.remove(mSyncId);
-            }
-        }
-
-        public void onTransactionReady(int syncId, Set<WindowContainer> windowContainersReady) {
-            mRemainingTransactions--;
-            ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncSet{%x:%d} Child ready, now ready=%b"
-                    + " and waiting on %d transactions", BLASTSyncEngine.this.hashCode(), mSyncId,
-                    mReady, mRemainingTransactions);
-            mWindowContainersReady.addAll(windowContainersReady);
-            tryFinish();
-        }
-
-        void setReady() {
-            ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncSet{%x:%d} Set ready",
-                    BLASTSyncEngine.this.hashCode(), mSyncId);
-            mReady = true;
-            tryFinish();
-        }
-
-        boolean addToSync(WindowContainer wc) {
-            ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncSet{%x:%d} Trying to add %s",
-                    BLASTSyncEngine.this.hashCode(), mSyncId, wc);
-            if (wc.prepareForSync(this, mSyncId)) {
-                mRemainingTransactions++;
-                ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncSet{%x:%d} Added %s. now waiting "
-                        + "on %d transactions", BLASTSyncEngine.this.hashCode(), mSyncId, wc,
-                        mRemainingTransactions);
-                return true;
-            }
-            return false;
-        }
-
-        SyncState(TransactionReadyListener l, int id) {
-            mListener = l;
+        private SyncGroup(TransactionReadyListener listener, int id) {
             mSyncId = id;
-            mRemainingTransactions = 0;
+            mListener = listener;
         }
-    };
 
+        /**
+         * Gets a transaction to dump orphaned operations into. Orphaned operations are operations
+         * that were on the mSyncTransactions of "root" subtrees which have been removed during the
+         * sync period.
+         */
+        @NonNull
+        SurfaceControl.Transaction getOrphanTransaction() {
+            if (mOrphanTransaction == null) {
+                // Lazy since this isn't common
+                mOrphanTransaction = mWm.mTransactionFactory.get();
+            }
+            return mOrphanTransaction;
+        }
+
+        private void onSurfacePlacement() {
+            if (!mReady) return;
+            ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: onSurfacePlacement checking %s",
+                    mSyncId, mRootMembers);
+            for (int i = mRootMembers.size() - 1; i >= 0; --i) {
+                final WindowContainer wc = mRootMembers.valueAt(i);
+                if (!wc.isSyncFinished()) {
+                    ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d:  Unfinished container: %s",
+                            mSyncId, wc);
+                    return;
+                }
+            }
+            ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: Finished!", mSyncId);
+            SurfaceControl.Transaction merged = mWm.mTransactionFactory.get();
+            if (mOrphanTransaction != null) {
+                merged.merge(mOrphanTransaction);
+            }
+            for (WindowContainer wc : mRootMembers) {
+                wc.finishSync(merged, false /* cancel */);
+            }
+            mListener.onTransactionReady(mSyncId, merged);
+            mActiveSyncs.remove(mSyncId);
+        }
+
+        private void setReady() {
+            ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: Set ready", mSyncId);
+            mReady = true;
+            mWm.mWindowPlacerLocked.requestTraversal();
+        }
+
+        private void addToSync(WindowContainer wc) {
+            if (!mRootMembers.add(wc)) {
+                return;
+            }
+            ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: Adding to group: %s", mSyncId, wc);
+            wc.setSyncGroup(this);
+            wc.prepareSync();
+            mWm.mWindowPlacerLocked.requestTraversal();
+        }
+
+        void onCancelSync(WindowContainer wc) {
+            mRootMembers.remove(wc);
+        }
+    }
+
+    private final WindowManagerService mWm;
     private int mNextSyncId = 0;
+    private final SparseArray<SyncGroup> mActiveSyncs = new SparseArray<>();
 
-    private final ArrayMap<Integer, SyncState> mPendingSyncs = new ArrayMap<>();
-
-    BLASTSyncEngine() {
+    BLASTSyncEngine(WindowManagerService wms) {
+        mWm = wms;
     }
 
     int startSyncSet(TransactionReadyListener listener) {
         final int id = mNextSyncId++;
-        final SyncState s = new SyncState(listener, id);
-        mPendingSyncs.put(id, s);
-        ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncSet{%x:%d} Start for %s", hashCode(), id, listener);
+        final SyncGroup s = new SyncGroup(listener, id);
+        mActiveSyncs.put(id, s);
+        ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: Started for listener: %s", id, listener);
         return id;
     }
 
-    boolean addToSyncSet(int id, WindowContainer wc) {
-        final SyncState st = mPendingSyncs.get(id);
-        return st.addToSync(wc);
+    void addToSyncSet(int id, WindowContainer wc) {
+        mActiveSyncs.get(id).addToSync(wc);
     }
 
     void setReady(int id) {
-        final SyncState st = mPendingSyncs.get(id);
-        st.setReady();
+        mActiveSyncs.get(id).setReady();
+    }
+
+    void onSurfacePlacement() {
+        // backwards since each state can remove itself if finished
+        for (int i = mActiveSyncs.size() - 1; i >= 0; --i) {
+            mActiveSyncs.valueAt(i).onSurfacePlacement();
+        }
     }
 }
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartCallback.java b/services/core/java/com/android/server/wm/BackgroundActivityStartCallback.java
index 4e742b9..af6c255 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartCallback.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartCallback.java
@@ -24,8 +24,8 @@
  */
 public interface BackgroundActivityStartCallback {
     /**
-     * The token that allowed the activity start that triggered {@link
-     * #onExclusiveTokenActivityStart()}.
+     * The token for which this callback is responsible for deciding whether the app can start
+     * background activities or not.
      *
      * Ideally this should just return a final variable, don't do anything costly here (don't hold
      * any locks).
@@ -33,7 +33,10 @@
     IBinder getToken();
 
     /**
-     * Called when the background activity start happens.
+     * Returns true if the background activity start due to originating token in {@link #getToken()}
+     * should be allowed or not.
+     *
+     * This will be called holding the WM lock, don't do anything costly here.
      */
-    void onExclusiveTokenActivityStart(String packageName);
+    boolean isActivityStartAllowed(int uid, String packageName);
 }
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 13033a6..60a62dc 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -157,6 +157,11 @@
         mResolvedOverrideConfiguration.setTo(mRequestedOverrideConfiguration);
     }
 
+    /** Returns {@code true} if requested override override configuration is not empty. */
+    boolean hasRequestedOverrideConfiguration() {
+        return mHasOverrideConfiguration;
+    }
+
     /** Returns requested override configuration applied to this configuration container. */
     public Configuration getRequestedOverrideConfiguration() {
         return mRequestedOverrideConfiguration;
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 8bd42f0..bb838ba 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -41,6 +41,7 @@
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.server.policy.WindowManagerPolicy;
 
+import java.io.PrintWriter;
 import java.util.Comparator;
 import java.util.function.BiFunction;
 import java.util.function.Consumer;
@@ -71,6 +72,13 @@
     IDisplayAreaOrganizer mOrganizer;
     private final Configuration mTmpConfiguration = new Configuration();
 
+    /**
+     * Whether this {@link DisplayArea} should ignore fixed-orientation request. If {@code true}, it
+     * can never specify orientation, but shows the fixed-orientation apps below it in the
+     * letterbox; otherwise, it rotates based on the fixed-orientation request.
+     */
+    protected boolean mIgnoreOrientationRequest;
+
     DisplayArea(WindowManagerService wms, Type type, String name) {
         this(wms, type, name, FEATURE_UNDEFINED);
     }
@@ -127,6 +135,61 @@
         }
     }
 
+    @Override
+    int getOrientation(int candidate) {
+        mLastOrientationSource = null;
+        if (mIgnoreOrientationRequest) {
+            return SCREEN_ORIENTATION_UNSET;
+        }
+
+        return super.getOrientation(candidate);
+    }
+
+    @Override
+    boolean handlesOrientationChangeFromDescendant() {
+        return !mIgnoreOrientationRequest && super.handlesOrientationChangeFromDescendant();
+    }
+
+    /**
+     * Sets whether this {@link DisplayArea} should ignore fixed-orientation request from apps and
+     * windows below it.
+     *
+     * @return Whether the display orientation changed after calling this method.
+     */
+    boolean setIgnoreOrientationRequest(boolean ignoreOrientationRequest) {
+        if (mIgnoreOrientationRequest == ignoreOrientationRequest) {
+            return false;
+        }
+        mIgnoreOrientationRequest = ignoreOrientationRequest;
+
+        // Check whether we should notify Display to update orientation.
+        if (mDisplayContent == null) {
+            return false;
+        }
+
+        // The orientation request from this DA may now be respected.
+        if (!ignoreOrientationRequest) {
+            return mDisplayContent.updateOrientation();
+        }
+
+        final int lastOrientation = mDisplayContent.getLastOrientation();
+        final WindowContainer lastOrientationSource = mDisplayContent.getLastOrientationSource();
+        if (lastOrientation == SCREEN_ORIENTATION_UNSET
+                || lastOrientation == SCREEN_ORIENTATION_UNSPECIFIED) {
+            // Orientation won't be changed.
+            return false;
+        }
+        if (lastOrientationSource == null || lastOrientationSource.isDescendantOf(this)) {
+            // Try update if the orientation may be affected.
+            return mDisplayContent.updateOrientation();
+        }
+        return false;
+    }
+
+    boolean getIgnoreOrientationRequest() {
+        return mIgnoreOrientationRequest;
+    }
+
     /**
      * When a {@link DisplayArea} is repositioned, it should only be moved among its siblings of the
      * same {@link Type}.
@@ -200,6 +263,34 @@
     }
 
     @Override
+    void dump(PrintWriter pw, String prefix, boolean dumpAll) {
+        super.dump(pw, prefix, dumpAll);
+        if (mIgnoreOrientationRequest) {
+            pw.println(prefix + "mIgnoreOrientationRequest=true");
+        }
+        if (hasRequestedOverrideConfiguration()) {
+            pw.println(prefix + "overrideConfig=" + getRequestedOverrideConfiguration());
+        }
+    }
+
+    void dumpChildDisplayArea(PrintWriter pw, String prefix, boolean dumpAll) {
+        final String doublePrefix = prefix + "  ";
+        for (int i = getChildCount() - 1; i >= 0; i--) {
+            final DisplayArea<?> childArea = getChildAt(i).asDisplayArea();
+            if (childArea == null) {
+                continue;
+            }
+            pw.println(prefix + "* " + childArea.getName());
+            if (childArea.isTaskDisplayArea()) {
+                // TaskDisplayArea can only contain task. And it is already printed by display.
+                continue;
+            }
+            childArea.dump(pw, doublePrefix, dumpAll);
+            childArea.dumpChildDisplayArea(pw, doublePrefix, dumpAll);
+        }
+    }
+
+    @Override
     long getProtoFieldId() {
         return DISPLAY_AREA;
     }
@@ -370,6 +461,9 @@
                 Comparator.comparingInt(WindowToken::getWindowLayerFromType);
 
         private final Predicate<WindowState> mGetOrientingWindow = w -> {
+            if (!w.isVisible() || !w.mLegacyPolicyVisibilityAfterAnim) {
+                return false;
+            }
             final WindowManagerPolicy policy = mWmService.mPolicy;
             if (policy.isKeyguardHostWindow(w.mAttrs)) {
                 if (mWmService.mKeyguardGoingAway) {
@@ -405,6 +499,11 @@
 
         @Override
         int getOrientation(int candidate) {
+            mLastOrientationSource = null;
+            if (mIgnoreOrientationRequest) {
+                return SCREEN_ORIENTATION_UNSET;
+            }
+
             // Find a window requesting orientation.
             final WindowState win = getWindow(mGetOrientingWindow);
 
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 22b446d..98a1bac 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -28,8 +28,8 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
@@ -90,20 +90,25 @@
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
 import static com.android.server.wm.DisplayContentProto.APP_TRANSITION;
+import static com.android.server.wm.DisplayContentProto.CAN_SHOW_IME;
 import static com.android.server.wm.DisplayContentProto.CLOSING_APPS;
+import static com.android.server.wm.DisplayContentProto.CURRENT_FOCUS;
 import static com.android.server.wm.DisplayContentProto.DISPLAY_FRAMES;
 import static com.android.server.wm.DisplayContentProto.DISPLAY_INFO;
 import static com.android.server.wm.DisplayContentProto.DISPLAY_READY;
+import static com.android.server.wm.DisplayContentProto.DISPLAY_ROTATION;
 import static com.android.server.wm.DisplayContentProto.DPI;
 import static com.android.server.wm.DisplayContentProto.FOCUSED_APP;
 import static com.android.server.wm.DisplayContentProto.FOCUSED_ROOT_TASK_ID;
 import static com.android.server.wm.DisplayContentProto.ID;
+import static com.android.server.wm.DisplayContentProto.IME_INSETS_SOURCE_PROVIDER;
+import static com.android.server.wm.DisplayContentProto.INPUT_METHOD_CONTROL_TARGET;
+import static com.android.server.wm.DisplayContentProto.INPUT_METHOD_INPUT_TARGET;
+import static com.android.server.wm.DisplayContentProto.INPUT_METHOD_TARGET;
 import static com.android.server.wm.DisplayContentProto.OPENING_APPS;
 import static com.android.server.wm.DisplayContentProto.RESUMED_ACTIVITY;
 import static com.android.server.wm.DisplayContentProto.ROOT_DISPLAY_AREA;
-import static com.android.server.wm.DisplayContentProto.ROTATION;
 import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION;
-import static com.android.server.wm.DisplayContentProto.SINGLE_TASK_INSTANCE;
 import static com.android.server.wm.Task.ActivityState.RESUMED;
 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
@@ -186,6 +191,8 @@
 import android.view.InputChannel;
 import android.view.InputDevice;
 import android.view.InputWindowHandle;
+import android.view.InsetsSource;
+import android.view.InsetsState;
 import android.view.InsetsState.InternalInsetsType;
 import android.view.MagnificationSpec;
 import android.view.RemoteAnimationDefinition;
@@ -247,10 +254,23 @@
 
     ActivityTaskManagerService mAtmService;
 
-    /** Unique identifier of this display. */
+    /**
+     * Unique logical identifier of this display.
+     *
+     * @see DisplayInfo#displayId
+     */
     final int mDisplayId;
 
     /**
+     * Unique physical identifier of this display. Unlike {@link #mDisplayId} this value can change
+     * at runtime if the underlying physical display changes.
+     *
+     * @see DisplayInfo#uniqueId
+     */
+    @Nullable
+    String mCurrentUniqueDisplayId;
+
+    /**
      * We organize all top-level Surfaces into the following layer.
      * It contains a few Surfaces which are always on top of others, and omitted from
      * Screen-Magnification, for example the strict mode flash or the fullscreen magnification
@@ -584,9 +604,6 @@
      */
     private boolean mRemoved;
 
-    /** The display can only contain one task. */
-    boolean mSingleTaskInstance;
-
     /**
      * Non-null if the last size compatibility mode activity is using non-native screen
      * configuration. The activity is not able to put in multi-window mode, so it exists only one
@@ -910,6 +927,7 @@
         mAtmService = mWmService.mAtmService;
         mDisplay = display;
         mDisplayId = display.getDisplayId();
+        mCurrentUniqueDisplayId = display.getUniqueId();
         mOffTokenAcquirer = mRootWindowContainer.mDisplayOffTokenAcquirer;
         mWallpaperController = new WallpaperController(mWmService, this);
         display.getDisplayInfo(mDisplayInfo);
@@ -1291,7 +1309,7 @@
         // If display rotation class tells us that it doesn't consider app requested orientation,
         // this display won't rotate just because of an app changes its requested orientation. Thus
         // it indicates that this display chooses not to handle this request.
-        final boolean handled = getDisplayRotation().respectAppRequestedOrientation();
+        final boolean handled = handlesOrientationChangeFromDescendant();
         if (config == null) {
             return handled;
         }
@@ -1315,7 +1333,7 @@
 
     @Override
     boolean handlesOrientationChangeFromDescendant() {
-        return getDisplayRotation().respectAppRequestedOrientation();
+        return !mIgnoreOrientationRequest && !getDisplayRotation().isFixedToUserRotation();
     }
 
     /**
@@ -2327,6 +2345,13 @@
     @Override
     int getOrientation() {
         mLastOrientationSource = null;
+        if (!handlesOrientationChangeFromDescendant()) {
+            // Return SCREEN_ORIENTATION_UNSPECIFIED so that Display respect sensor rotation
+            ProtoLog.v(WM_DEBUG_ORIENTATION,
+                    "Display id=%d is ignoring all orientation requests, return %d",
+                    mDisplayId, SCREEN_ORIENTATION_UNSPECIFIED);
+            return SCREEN_ORIENTATION_UNSPECIFIED;
+        }
 
         if (mWmService.mDisplayFrozen) {
             if (mWmService.mPolicy.isKeyguardLocked()) {
@@ -2343,19 +2368,15 @@
         }
 
         final int orientation = super.getOrientation();
-        if (orientation != SCREEN_ORIENTATION_UNSET && orientation != SCREEN_ORIENTATION_BEHIND) {
+        if (orientation == SCREEN_ORIENTATION_UNSET) {
+            // Return SCREEN_ORIENTATION_UNSPECIFIED so that Display respect sensor rotation
             ProtoLog.v(WM_DEBUG_ORIENTATION,
-                    "App is requesting an orientation, return %d for display id=%d",
-                    orientation, mDisplayId);
-            return orientation;
+                    "No app or window is requesting an orientation, return %d for display id=%d",
+                    SCREEN_ORIENTATION_UNSPECIFIED, mDisplayId);
+            return SCREEN_ORIENTATION_UNSPECIFIED;
         }
 
-        ProtoLog.v(WM_DEBUG_ORIENTATION,
-                "No app is requesting an orientation, return %d for display id=%d",
-                getLastOrientation(), mDisplayId);
-        // The next app has not been requested to be visible, so we keep the current orientation
-        // to prevent freezing/unfreezing the display too early.
-        return getLastOrientation();
+        return orientation;
     }
 
     void updateDisplayInfo() {
@@ -2411,13 +2432,20 @@
         final int newHeight = rotated ? mDisplayInfo.logicalWidth : mDisplayInfo.logicalHeight;
         final int newDensity = mDisplayInfo.logicalDensityDpi;
         final DisplayCutout newCutout = mDisplayInfo.displayCutout;
+        final String newUniqueId = mDisplayInfo.uniqueId;
 
         final boolean displayMetricsChanged = mInitialDisplayWidth != newWidth
                 || mInitialDisplayHeight != newHeight
                 || mInitialDisplayDensity != mDisplayInfo.logicalDensityDpi
                 || !Objects.equals(mInitialDisplayCutout, newCutout);
+        final boolean physicalDisplayChanged = !newUniqueId.equals(mCurrentUniqueDisplayId);
 
-        if (displayMetricsChanged) {
+        if (displayMetricsChanged || physicalDisplayChanged) {
+            if (physicalDisplayChanged) {
+                // Reapply the window settings as the underlying physical display has changed.
+                mWmService.mDisplayWindowSettings.applySettingsToDisplayLocked(this);
+            }
+
             // If there is an override set for base values - use it, otherwise use new values.
             updateBaseDisplayMetrics(mIsSizeForced ? mBaseDisplayWidth : newWidth,
                     mIsSizeForced ? mBaseDisplayHeight : newHeight,
@@ -2428,6 +2456,7 @@
             mInitialDisplayHeight = newHeight;
             mInitialDisplayDensity = newDensity;
             mInitialDisplayCutout = newCutout;
+            mCurrentUniqueDisplayId = newUniqueId;
             reconfigureDisplayLocked();
         }
     }
@@ -2446,6 +2475,10 @@
 
     /** Update base (override) display metrics. */
     void updateBaseDisplayMetrics(int baseWidth, int baseHeight, int baseDensity) {
+        final int originalWidth = mBaseDisplayWidth;
+        final int originalHeight = mBaseDisplayHeight;
+        final int originalDensity = mBaseDisplayDensity;
+
         mBaseDisplayWidth = baseWidth;
         mBaseDisplayHeight = baseHeight;
         mBaseDisplayDensity = baseDensity;
@@ -2460,9 +2493,11 @@
             }
         }
 
-        mBaseDisplayRect.set(0, 0, mBaseDisplayWidth, mBaseDisplayHeight);
-
-        updateBounds();
+        if (mBaseDisplayWidth != originalWidth || mBaseDisplayHeight != originalHeight
+                || mBaseDisplayDensity != originalDensity) {
+            mBaseDisplayRect.set(0, 0, mBaseDisplayWidth, mBaseDisplayHeight);
+            updateBounds();
+        }
     }
 
     /**
@@ -2834,7 +2869,7 @@
         proto.write(ID, mDisplayId);
         proto.write(DPI, mBaseDisplayDensity);
         mDisplayInfo.dumpDebug(proto, DISPLAY_INFO);
-        proto.write(ROTATION, getRotation());
+        mDisplayRotation.dumpDebug(proto, DISPLAY_ROTATION);
         final ScreenRotationAnimation screenRotationAnimation = getRotationAnimation();
         if (screenRotationAnimation != null) {
             screenRotationAnimation.dumpDebug(proto, SCREEN_ROTATION_ANIMATION);
@@ -2851,7 +2886,6 @@
             mClosingApps.valueAt(i).writeIdentifierToProto(proto, CLOSING_APPS);
         }
 
-        proto.write(SINGLE_TASK_INSTANCE, mSingleTaskInstance);
         final Task focusedStack = getFocusedStack();
         if (focusedStack != null) {
             proto.write(FOCUSED_ROOT_TASK_ID, focusedStack.getRootTaskId());
@@ -2864,7 +2898,26 @@
             proto.write(FOCUSED_ROOT_TASK_ID, INVALID_TASK_ID);
         }
         proto.write(DISPLAY_READY, isReady());
-
+        if (mInputMethodTarget != null) {
+            mInputMethodTarget.dumpDebug(proto, INPUT_METHOD_TARGET, logLevel);
+        }
+        if (mInputMethodInputTarget != null) {
+            mInputMethodInputTarget.dumpDebug(proto, INPUT_METHOD_INPUT_TARGET, logLevel);
+        }
+        if (mInputMethodControlTarget != null
+                && mInputMethodControlTarget.getWindow() != null) {
+            mInputMethodControlTarget.getWindow().dumpDebug(proto, INPUT_METHOD_CONTROL_TARGET,
+                    logLevel);
+        }
+        if (mCurrentFocus != null) {
+            mCurrentFocus.dumpDebug(proto, CURRENT_FOCUS, logLevel);
+        }
+        if (mInsetsStateController != null
+                && mInsetsStateController.getImeSourceProvider() != null) {
+            mInsetsStateController.getImeSourceProvider().dumpDebug(proto,
+                    IME_INSETS_SOURCE_PROVIDER, logLevel);
+        }
+        proto.write(CAN_SHOW_IME, canShowIme());
         proto.end(token);
     }
 
@@ -2877,8 +2930,7 @@
     public void dump(PrintWriter pw, String prefix, boolean dumpAll) {
         super.dump(pw, prefix, dumpAll);
         pw.print(prefix);
-        pw.println("Display: mDisplayId=" + mDisplayId + " stacks=" + getStackCount() + (
-                mSingleTaskInstance ? " mSingleTaskInstance" : ""));
+        pw.println("Display: mDisplayId=" + mDisplayId + " stacks=" + getStackCount());
         final String subPrefix = "  " + prefix;
         pw.print(subPrefix); pw.print("init="); pw.print(mInitialDisplayWidth); pw.print("x");
         pw.print(mInitialDisplayHeight); pw.print(" "); pw.print(mInitialDisplayDensity);
@@ -2929,6 +2981,10 @@
         }
 
         pw.println();
+        pw.println(prefix + "Display areas in top down Z order:");
+        dumpChildDisplayArea(pw, subPrefix, dumpAll);
+
+        pw.println();
         pw.println(prefix + "Task display areas in top down Z order:");
         forAllTaskDisplayAreas(taskDisplayArea -> {
             taskDisplayArea.dump(pw, prefix + "  ", dumpAll);
@@ -3374,6 +3430,14 @@
         mUpdateImeTarget = updateImeTarget;
         WindowState target = getWindow(mComputeImeTargetPredicate);
 
+        // Keeps the IME target with the last window while swiping up to recents to prevent
+        // flickering due to IME hide animation on top of recents.
+        // TODO(b/166736352): This logic should go away once we switch over target immediately
+        //  and do the screenshot to preserve IME on disappearing target
+        if (target != null && curTarget != null && target.isActivityTypeHome()
+                && curTarget.getInsetsState().getSource(ITYPE_IME).isVisible()) {
+            return curTarget;
+        }
 
         // Yet more tricksyness!  If this window is a "starting" window, we do actually want
         // to be on top of it, but it is not -really- where input will go. So look down below
@@ -3546,7 +3610,7 @@
         );
     }
 
-    private void updateImeParent() {
+    void updateImeParent() {
         final SurfaceControl newParent = computeImeParent();
         if (newParent != null) {
             getPendingTransaction().reparent(mImeWindowsContainers.mSurfaceControl, newParent);
@@ -4182,6 +4246,10 @@
 
         @Override
         int getOrientation(int candidate) {
+            if (mIgnoreOrientationRequest) {
+                return SCREEN_ORIENTATION_UNSET;
+            }
+
             // IME does not participate in orientation.
             return candidate;
         }
@@ -4758,7 +4826,7 @@
             boolean ignoreRequest) {
         final int type = win.mAttrs.type;
         final boolean stickyHideNav =
-                !win.getRequestedInsetsState().getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR)
+                !win.getRequestedVisibility(ITYPE_NAVIGATION_BAR)
                         && win.mAttrs.insetsFlags.behavior == BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
         return (!stickyHideNav || ignoreRequest) && type != TYPE_INPUT_METHOD
                 && type != TYPE_NOTIFICATION_SHADE && win.getActivityType() != ACTIVITY_TYPE_HOME;
@@ -4929,18 +4997,18 @@
     }
 
     /**
-     * Removes stacks in the input windowing modes from the system if they are of activity type
+     * Removes root tasks in the input windowing modes from the system if they are of activity type
      * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
      */
-    void removeStacksInWindowingModes(int... windowingModes) {
+    void removeRootTasksInWindowingModes(int... windowingModes) {
         forAllTaskDisplayAreas(taskDisplayArea -> {
-            taskDisplayArea.removeStacksInWindowingModes(windowingModes);
+            taskDisplayArea.removeRootTasksInWindowingModes(windowingModes);
         });
     }
 
-    void removeStacksWithActivityTypes(int... activityTypes) {
+    void removeRootTasksWithActivityTypes(int... activityTypes) {
         forAllTaskDisplayAreas(taskDisplayArea -> {
-            taskDisplayArea.removeStacksWithActivityTypes(activityTypes);
+            taskDisplayArea.removeRootTasksWithActivityTypes(activityTypes);
         });
     }
 
@@ -5003,7 +5071,9 @@
                 }
             }
 
-            kept = mAtmService.ensureConfigAndVisibilityAfterUpdate(starting, changes);
+            if (!deferResume) {
+                kept = mAtmService.ensureConfigAndVisibilityAfterUpdate(starting, changes);
+            }
         } finally {
             mAtmService.continueWindowLayout();
         }
@@ -5229,29 +5299,6 @@
         mSleeping = asleep;
     }
 
-    void setDisplayToSingleTaskInstance() {
-        int tdaCount = reduceOnAllTaskDisplayAreas((taskDisplayArea, count) -> ++count,
-                0 /* initValue */);
-        if (tdaCount > 1) {
-            throw new IllegalArgumentException(
-                    "Display already has multiple task display areas. display=" + this);
-        }
-        final int stackCount = getDefaultTaskDisplayArea().getStackCount();
-        if (stackCount > 1) {
-            throw new IllegalArgumentException("Display already has multiple stacks. display="
-                    + this);
-        }
-        if (stackCount > 0) {
-            final Task stack = getDefaultTaskDisplayArea().getStackAt(0);
-            if (stack.getChildCount() > 1) {
-                throw new IllegalArgumentException("Display stack already has multiple tasks."
-                        + " display=" + this + " stack=" + stack);
-            }
-        }
-
-        mSingleTaskInstance = true;
-    }
-
     /**
      * Check if the display has {@link Display#FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD} applied.
      */
@@ -5260,11 +5307,6 @@
         return (flags & FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD) != 0;
     }
 
-    /** Returns true if the display can only contain one task */
-    boolean isSingleTaskInstance() {
-        return mSingleTaskInstance;
-    }
-
     @VisibleForTesting
     void removeAllTasks() {
         forAllTasks((t) -> { t.getRootTask().removeChild(t, "removeAllTasks"); });
@@ -5288,8 +5330,13 @@
         return mDisplayPolicy.getSystemUiContext();
     }
 
-    Point getDisplayPosition() {
-        return mWmService.mDisplayManagerInternal.getDisplayPosition(getDisplayId());
+    @Override
+    boolean setIgnoreOrientationRequest(boolean ignoreOrientationRequest) {
+        if (mIgnoreOrientationRequest == ignoreOrientationRequest) return false;
+        final boolean rotationChanged = super.setIgnoreOrientationRequest(ignoreOrientationRequest);
+        mWmService.mDisplayWindowSettings.setIgnoreOrientationRequest(
+                this, mIgnoreOrientationRequest);
+        return rotationChanged;
     }
 
     /**
@@ -5480,6 +5527,7 @@
 
     class RemoteInsetsControlTarget implements InsetsControlTarget {
         private final IDisplayWindowInsetsController mRemoteInsetsController;
+        private final InsetsState mRequestedInsetsState = new InsetsState();
 
         RemoteInsetsControlTarget(IDisplayWindowInsetsController controller) {
             mRemoteInsetsController = controller;
@@ -5535,6 +5583,19 @@
                 Slog.w(TAG, "Failed to deliver showInsets", e);
             }
         }
+
+        @Override
+        public boolean getRequestedVisibility(@InternalInsetsType int type) {
+            return mRequestedInsetsState.getSourceOrDefaultVisibility(type);
+        }
+
+        void updateRequestedVisibility(InsetsState state) {
+            for (int i = 0; i < InsetsState.SIZE; i++) {
+                final InsetsSource source = state.peekSource(i);
+                if (source == null) continue;
+                mRequestedInsetsState.addSource(source);
+            }
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/DisplayFrames.java b/services/core/java/com/android/server/wm/DisplayFrames.java
index f8495b5..d67f3f9 100644
--- a/services/core/java/com/android/server/wm/DisplayFrames.java
+++ b/services/core/java/com/android/server/wm/DisplayFrames.java
@@ -16,9 +16,8 @@
 
 package com.android.server.wm;
 
-import static android.view.Surface.ROTATION_180;
-import static android.view.Surface.ROTATION_270;
-import static android.view.Surface.ROTATION_90;
+import static com.android.server.wm.DisplayFramesProto.CURRENT;
+import static com.android.server.wm.DisplayFramesProto.DOCK;
 import static com.android.server.wm.DisplayFramesProto.STABLE_BOUNDS;
 
 import android.annotation.NonNull;
@@ -155,6 +154,8 @@
     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
         mStable.dumpDebug(proto, STABLE_BOUNDS);
+        mDock.dumpDebug(proto, DOCK);
+        mCurrent.dumpDebug(proto, CURRENT);
         proto.end(token);
     }
 
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index dce798e..b0ddb61 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -188,7 +188,6 @@
 import com.android.server.statusbar.StatusBarManagerInternal;
 import com.android.server.wallpaper.WallpaperManagerInternal;
 import com.android.server.wm.InputMonitor.EventReceiverInputConsumer;
-import com.android.server.wm.utils.InsetUtils;
 
 import java.io.PrintWriter;
 import java.util.function.Consumer;
@@ -929,7 +928,6 @@
                 attrs.hideTimeoutMilliseconds = mAccessibilityManager.getRecommendedTimeoutMillis(
                         (int) attrs.hideTimeoutMilliseconds,
                         AccessibilityManager.FLAG_CONTENT_TEXT);
-                attrs.windowAnimations = com.android.internal.R.style.Animation_Toast;
                 // Toasts can't be clickable
                 attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
                 break;
@@ -1041,9 +1039,12 @@
         }
 
         if (attrs.providesInsetsTypes != null) {
-            mContext.enforcePermission(
-                    android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
-                    "DisplayPolicy");
+            // Recents component is allowed to add inset types.
+            if (!mService.mAtmInternal.isCallerRecents(callingUid)) {
+                mContext.enforcePermission(
+                        android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
+                        "DisplayPolicy");
+            }
             enforceSingleInsetsTypeCorrespondingToWindowType(attrs.providesInsetsTypes);
 
             for (@InternalInsetsType int insetType : attrs.providesInsetsTypes) {
@@ -1437,25 +1438,15 @@
      * @param attrs The LayoutParams of the window.
      * @param windowToken The token of the window.
      * @param outFrame The frame of the window.
-     * @param outContentInsets The areas covered by system windows, expressed as positive insets.
-     * @param outStableInsets The areas covered by stable system windows irrespective of their
-     *                        current visibility. Expressed as positive insets.
      * @param outDisplayCutout The area that has been cut away from the display.
+     * @param outInsetsState The insets state of this display from the client's perspective.
+     * @param localClient Whether the client is from the our process.
      * @return Whether to always consume the system bars.
      *         See {@link #areSystemBarsForcedShownLw(WindowState)}.
      */
     boolean getLayoutHint(LayoutParams attrs, WindowToken windowToken, Rect outFrame,
-            Rect outContentInsets, Rect outStableInsets,
-            DisplayCutout.ParcelableWrapper outDisplayCutout) {
-        final int fl = attrs.flags;
-        final int pfl = attrs.privateFlags;
-        final int sysUiVis = attrs.systemUiVisibility | attrs.subtreeSystemUiVisibility;
-
-        final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) != 0;
-        final boolean layoutInScreenAndInsetDecor = layoutInScreen
-                && (fl & FLAG_LAYOUT_INSET_DECOR) != 0;
-        final boolean screenDecor = (pfl & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0;
-
+            DisplayCutout.ParcelableWrapper outDisplayCutout, InsetsState outInsetsState,
+            boolean localClient) {
         final boolean isFixedRotationTransforming =
                 windowToken != null && windowToken.isFixedRotationTransforming();
         final ActivityRecord activity = windowToken != null ? windowToken.asActivityRecord() : null;
@@ -1464,56 +1455,36 @@
                 // Use token (activity) bounds if it is rotated because its task is not rotated.
                 ? windowToken.getBounds()
                 : (task != null ? task.getBounds() : null);
+        final InsetsState state =
+                mDisplayContent.getInsetsPolicy().getInsetsForWindowMetrics(attrs);
+        computeWindowBounds(attrs, state, outFrame);
+        if (taskBounds != null) {
+            outFrame.intersect(taskBounds);
+        }
+
+        final int fl = attrs.flags;
+        final int pfl = attrs.privateFlags;
+        final boolean layoutInScreenAndInsetDecor = (fl & FLAG_LAYOUT_IN_SCREEN) != 0
+                && (fl & FLAG_LAYOUT_INSET_DECOR) != 0;
+        final boolean screenDecor = (pfl & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0;
         final DisplayFrames displayFrames = isFixedRotationTransforming
                 ? windowToken.getFixedRotationTransformDisplayFrames()
                 : mDisplayContent.mDisplayFrames;
-
         if (layoutInScreenAndInsetDecor && !screenDecor) {
-            if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
-                    || (attrs.getFitInsetsTypes() & Type.navigationBars()) == 0) {
-                outFrame.set(displayFrames.mUnrestricted);
-            } else {
-                outFrame.set(displayFrames.mRestricted);
-            }
-
-            final boolean isFloatingTask = task != null && task.isFloating();
-            final Rect sf = isFloatingTask ? null : displayFrames.mStable;
-            final Rect cf;
-            if (isFloatingTask) {
-                cf = null;
-            } else if ((sysUiVis & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) {
-                if ((fl & FLAG_FULLSCREEN) != 0) {
-                    cf = displayFrames.mStableFullscreen;
-                } else {
-                    cf = displayFrames.mStable;
-                }
-            } else if ((fl & FLAG_FULLSCREEN) != 0) {
-                cf = displayFrames.mUnrestricted;
-            } else {
-                cf = displayFrames.mCurrent;
-            }
-
-            if (taskBounds != null) {
-                outFrame.intersect(taskBounds);
-            }
-            InsetUtils.insetsBetweenFrames(outFrame, cf, outContentInsets);
-            InsetUtils.insetsBetweenFrames(outFrame, sf, outStableInsets);
-            outDisplayCutout.set(displayFrames.mDisplayCutout.calculateRelativeTo(outFrame)
-                    .getDisplayCutout());
+            outDisplayCutout.set(
+                    displayFrames.mDisplayCutout.calculateRelativeTo(outFrame).getDisplayCutout());
         } else {
-            if (layoutInScreen) {
-                outFrame.set(displayFrames.mUnrestricted);
-            } else {
-                outFrame.set(displayFrames.mStable);
-            }
-            if (taskBounds != null) {
-                outFrame.intersect(taskBounds);
-            }
-
-            outContentInsets.setEmpty();
-            outStableInsets.setEmpty();
             outDisplayCutout.set(DisplayCutout.NO_CUTOUT);
         }
+
+        final boolean inSizeCompatMode = WindowState.inSizeCompatMode(attrs, windowToken);
+        outInsetsState.set(state, inSizeCompatMode || localClient);
+        if (inSizeCompatMode) {
+            final float compatScale = windowToken != null
+                    ? windowToken.getSizeCompatScale()
+                    : mDisplayContent.mCompatibleScreenScale;
+            outInsetsState.scale(1f / compatScale);
+        }
         return mForceShowSystemBars;
     }
 
@@ -1614,8 +1585,9 @@
      */
     public void beginLayoutLw(DisplayFrames displayFrames, int uiMode) {
         displayFrames.onBeginLayout();
-        updateInsetsStateForDisplayCutout(displayFrames,
-                mDisplayContent.getInsetsStateController().getRawInsetsState());
+        final InsetsState state = mDisplayContent.getInsetsStateController().getRawInsetsState();
+        updateInsetsStateForDisplayCutout(displayFrames, state);
+        state.setDisplayFrame(displayFrames.mUnrestricted);
         mSystemGestures.screenWidth = displayFrames.mUnrestricted.width();
         mSystemGestures.screenHeight = displayFrames.mUnrestricted.height();
 
@@ -1657,10 +1629,8 @@
                 provider != null ? provider.getControlTarget() : null;
         final WindowState navControllingWin =
                 navControlTarget instanceof WindowState ? (WindowState) navControlTarget : null;
-        final InsetsState requestedState = navControllingWin != null
-                ? navControllingWin.getRequestedInsetsState() : null;
-        final boolean navVisible = requestedState != null
-                ? requestedState.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR)
+        final boolean navVisible = navControllingWin != null
+                ? navControllingWin.getRequestedVisibility(ITYPE_NAVIGATION_BAR)
                 : InsetsState.getDefaultVisibility(ITYPE_NAVIGATION_BAR);
         final boolean showBarsByTouch = navControllingWin != null
                 && navControllingWin.mAttrs.insetsFlags.behavior == BEHAVIOR_SHOW_BARS_BY_TOUCH;
@@ -1981,6 +1951,28 @@
         return !notFocusableForIm;
     }
 
+    private void computeWindowBounds(WindowManager.LayoutParams attrs, InsetsState state,
+            Rect outBounds) {
+        final @InsetsType int typesToFit = attrs.getFitInsetsTypes();
+        final @InsetsSide int sidesToFit = attrs.getFitInsetsSides();
+        final ArraySet<Integer> types = InsetsState.toInternalType(typesToFit);
+        final Rect dfu = state.getDisplayFrame();
+        Insets insets = Insets.of(0, 0, 0, 0);
+        for (int i = types.size() - 1; i >= 0; i--) {
+            final InsetsSource source = state.peekSource(types.valueAt(i));
+            if (source == null) {
+                continue;
+            }
+            insets = Insets.max(insets, source.calculateInsets(
+                    dfu, attrs.isFitInsetsIgnoringVisibility()));
+        }
+        final int left = (sidesToFit & Side.LEFT) != 0 ? insets.left : 0;
+        final int top = (sidesToFit & Side.TOP) != 0 ? insets.top : 0;
+        final int right = (sidesToFit & Side.RIGHT) != 0 ? insets.right : 0;
+        final int bottom = (sidesToFit & Side.BOTTOM) != 0 ? insets.bottom : 0;
+        outBounds.set(dfu.left + left, dfu.top + top, dfu.right - right, dfu.bottom - bottom);
+    }
+
     /**
      * Called for each window attached to the window manager as layout is proceeding. The
      * implementation of this function must take care of setting the window's frame, either here or
@@ -2027,31 +2019,12 @@
 
         sf.set(displayFrames.mStable);
 
-        final @InsetsType int typesToFit = attrs.getFitInsetsTypes();
-        final @InsetsSide int sidesToFit = attrs.getFitInsetsSides();
-        final ArraySet<Integer> types = InsetsState.toInternalType(typesToFit);
-        getRotatedWindowBounds(displayFrames, win, sTmpRect);
-        final Rect dfu = sTmpRect;
-        Insets insets = Insets.of(0, 0, 0, 0);
-        for (int i = types.size() - 1; i >= 0; i--) {
-            final InsetsSource source = mDisplayContent.getInsetsPolicy()
-                    .getInsetsForDispatch(win).peekSource(types.valueAt(i));
-            if (source == null) {
-                continue;
-            }
-            insets = Insets.max(insets, source.calculateInsets(
-                    dfu, attrs.isFitInsetsIgnoringVisibility()));
-        }
-        final int left = (sidesToFit & Side.LEFT) != 0 ? insets.left : 0;
-        final int top = (sidesToFit & Side.TOP) != 0 ? insets.top : 0;
-        final int right = (sidesToFit & Side.RIGHT) != 0 ? insets.right : 0;
-        final int bottom = (sidesToFit & Side.BOTTOM) != 0 ? insets.bottom : 0;
-        df.set(dfu.left + left, dfu.top + top, dfu.right - right, dfu.bottom - bottom);
+        final InsetsState state = mDisplayContent.getInsetsPolicy().getInsetsForWindow(win);
+        computeWindowBounds(attrs, state, df);
         if (attached == null) {
             pf.set(df);
             if ((pfl & PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME) != 0) {
-                final InsetsSource source = mDisplayContent.getInsetsPolicy()
-                        .getInsetsForDispatch(win).peekSource(ITYPE_IME);
+                final InsetsSource source = state.peekSource(ITYPE_IME);
                 if (source != null) {
                     pf.inset(source.calculateInsets(pf, false /* ignoreVisibility */));
                 }
@@ -2071,11 +2044,9 @@
         // the cutout safe zone.
         if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) {
             final boolean attachedInParent = attached != null && !layoutInScreen;
-            final InsetsState requestedInsetsState = win.getRequestedInsetsState();
-            final boolean requestedFullscreen = (fl & FLAG_FULLSCREEN) != 0
-                    || !requestedInsetsState.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR);
+            final boolean requestedFullscreen = !win.getRequestedVisibility(ITYPE_STATUS_BAR);
             final boolean requestedHideNavigation =
-                    !requestedInsetsState.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR);
+                    !win.getRequestedVisibility(ITYPE_NAVIGATION_BAR);
 
             // TYPE_BASE_APPLICATION windows are never considered floating here because they don't
             // get cropped / shifted to the displayFrame in WindowState.
@@ -2128,6 +2099,7 @@
             // They will later be cropped or shifted using the displayFrame in WindowState,
             // which prevents overlap with the DisplayCutout.
             if (!attachedInParent && !floatingInScreenWindow) {
+                getRotatedWindowBounds(displayFrames, win, sTmpRect);
                 sTmpRect.set(pf);
                 pf.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
                 windowFrames.setParentFrameWasClippedByDisplayCutout(!sTmpRect.equals(pf));
@@ -2375,7 +2347,7 @@
                 topIsFullscreen = topAppHidesStatusBar;
                 // The subtle difference between the window for mTopFullscreenOpaqueWindowState
                 // and mTopIsFullscreen is that mTopIsFullscreen is set only if the window
-                // has the FLAG_FULLSCREEN set.  Not sure if there is another way that to be the
+                // requests to hide the status bar.  Not sure if there is another way that to be the
                 // case though.
                 if (!topIsFullscreen || mDisplayContent.getDefaultTaskDisplayArea()
                         .isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) {
@@ -2419,16 +2391,7 @@
         if (mTopFullscreenOpaqueWindowState == null || mForceShowSystemBars) {
             return false;
         }
-        final LayoutParams attrs = mTopFullscreenOpaqueWindowState.getAttrs();
-        final int fl = attrs.flags;
-        final InsetsSource request = mTopFullscreenOpaqueWindowState.getRequestedInsetsState()
-                .peekSource(ITYPE_STATUS_BAR);
-        if (WindowManagerDebugConfig.DEBUG) {
-            Slog.d(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrame());
-            Slog.d(TAG, "attr: " + attrs + " request: " + request);
-        }
-        return (fl & LayoutParams.FLAG_FULLSCREEN) != 0
-                || (request != null && !request.isVisible());
+        return !mTopFullscreenOpaqueWindowState.getRequestedVisibility(ITYPE_STATUS_BAR);
     }
 
     /**
@@ -2862,18 +2825,15 @@
             return;
         }
 
-        final InsetsState requestedState = controlTarget.getRequestedInsetsState();
         final @InsetsType int restorePositionTypes =
-                (requestedState.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR)
+                (controlTarget.getRequestedVisibility(ITYPE_NAVIGATION_BAR)
                         ? Type.navigationBars() : 0)
-                | (requestedState.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR)
+                | (controlTarget.getRequestedVisibility(ITYPE_STATUS_BAR)
                         ? Type.statusBars() : 0)
-                | (mExtraNavBarAlt != null
-                        && requestedState.getSourceOrDefaultVisibility(
+                | (mExtraNavBarAlt != null && controlTarget.getRequestedVisibility(
                                 ITYPE_EXTRA_NAVIGATION_BAR)
                         ? Type.navigationBars() : 0)
-                | (mClimateBarAlt != null
-                        && requestedState.getSourceOrDefaultVisibility(
+                | (mClimateBarAlt != null && controlTarget.getRequestedVisibility(
                                 ITYPE_CLIMATE_BAR)
                         ? Type.statusBars() : 0);
 
@@ -2979,12 +2939,11 @@
                 win.mAttrs.insetsFlags.appearance, mTopFullscreenOpaqueWindowState,
                 mTopFullscreenOpaqueOrDimmingWindowState,
                 mDisplayContent.mInputMethodWindow, navColorWin) | opaqueAppearance;
-        final InsetsState requestedInsets = win.getRequestedInsetsState();
         final int behavior = win.mAttrs.insetsFlags.behavior;
         final boolean isImmersive = behavior == BEHAVIOR_SHOW_BARS_BY_SWIPE
                 || behavior == BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
-        final boolean isFullscreen = !requestedInsets.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR)
-                || !requestedInsets.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR);
+        final boolean isFullscreen = !win.getRequestedVisibility(ITYPE_STATUS_BAR)
+                || !win.getRequestedVisibility(ITYPE_NAVIGATION_BAR);
         if (mLastDisableFlags == disableFlags
                 && mLastAppearance == appearance
                 && mLastFullscreenAppearance == fullscreenAppearance
@@ -3150,10 +3109,7 @@
                 freeformStackVisible, resizing, fullscreenDrawsNavBarBackground,
                 dockedDrawsNavigationBarBackground);
 
-        final InsetsState requestedInsetsState = win.getRequestedInsetsState();
-        final boolean requestHideNavBar = !requestedInsetsState.getSourceOrDefaultVisibility(
-                        ITYPE_NAVIGATION_BAR);
-
+        final boolean requestHideNavBar = !win.getRequestedVisibility(ITYPE_NAVIGATION_BAR);
         final long now = SystemClock.uptimeMillis();
         final boolean pendingPanic = mPendingPanicGestureUptime != 0
                 && now - mPendingPanicGestureUptime <= PANIC_GESTURE_EXPIRATION;
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 0f43e49..c4aaf7c 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -24,6 +24,11 @@
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
+import static com.android.server.wm.DisplayRotationProto.FIXED_TO_USER_ROTATION_MODE;
+import static com.android.server.wm.DisplayRotationProto.FROZEN_TO_USER_ROTATION;
+import static com.android.server.wm.DisplayRotationProto.LAST_ORIENTATION;
+import static com.android.server.wm.DisplayRotationProto.ROTATION;
+import static com.android.server.wm.DisplayRotationProto.USER_ROTATION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -51,6 +56,7 @@
 import android.provider.Settings;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
 import android.view.IDisplayWindowRotationCallback;
 import android.view.IWindowManager;
 import android.view.Surface;
@@ -847,13 +853,8 @@
         }
     }
 
-    /**
-     * Returns {@code true} if this display rotation takes app requested orientation into
-     * consideration; {@code false} otherwise. For the time being the only case where this is {@code
-     * false} is when {@link #isFixedToUserRotation()} is {@code true}.
-     */
-    boolean respectAppRequestedOrientation() {
-        return !isFixedToUserRotation();
+    int getFixedToUserRotationMode() {
+        return mFixedToUserRotation;
     }
 
     public int getLandscapeRotation() {
@@ -1453,6 +1454,16 @@
         pw.println(prefix + "  mFixedToUserRotation=" + isFixedToUserRotation());
     }
 
+    void dumpDebug(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        proto.write(ROTATION, getRotation());
+        proto.write(FROZEN_TO_USER_ROTATION, isRotationFrozen());
+        proto.write(USER_ROTATION, getUserRotation());
+        proto.write(FIXED_TO_USER_ROTATION_MODE, mFixedToUserRotation);
+        proto.write(LAST_ORIENTATION, mLastOrientation);
+        proto.end(token);
+    }
+
     private class OrientationListener extends WindowOrientationListener {
         final SparseArray<Runnable> mRunnableCache = new SparseArray<>(5);
         boolean mEnabled;
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index c8c83a6..f647bea 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -112,6 +112,7 @@
         private boolean mShouldShowSystemDecors = false;
         private boolean mShouldShowIme = false;
         private int mFixedToUserRotation = IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT;
+        private boolean mIgnoreOrientationRequest = false;
 
         private Entry(String name) {
             mName = name;
@@ -131,6 +132,7 @@
             mShouldShowSystemDecors = copyFrom.mShouldShowSystemDecors;
             mShouldShowIme = copyFrom.mShouldShowIme;
             mFixedToUserRotation = copyFrom.mFixedToUserRotation;
+            mIgnoreOrientationRequest = copyFrom.mIgnoreOrientationRequest;
         }
 
         /** @return {@code true} if all values are default. */
@@ -144,7 +146,8 @@
                     && !mShouldShowWithInsecureKeyguard
                     && !mShouldShowSystemDecors
                     && !mShouldShowIme
-                    && mFixedToUserRotation == IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT;
+                    && mFixedToUserRotation == IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT
+                    && !mIgnoreOrientationRequest;
         }
     }
 
@@ -248,6 +251,15 @@
         writeSettingsIfNeeded(entry, displayInfo);
     }
 
+    void setIgnoreOrientationRequest(
+            DisplayContent displayContent, boolean ignoreOrientationRequest) {
+        final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+        final Entry entry = getOrCreateEntry(displayInfo);
+        if (entry.mIgnoreOrientationRequest == ignoreOrientationRequest) return;
+        entry.mIgnoreOrientationRequest = ignoreOrientationRequest;
+        writeSettingsIfNeeded(entry, displayInfo);
+    }
+
     private int getWindowingModeLocked(Entry entry, DisplayContent dc) {
         int windowingMode = entry != null ? entry.mWindowingMode
                 : WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -385,13 +397,17 @@
         dc.getDisplayRotation().restoreSettings(entry.mUserRotationMode,
                 entry.mUserRotation, entry.mFixedToUserRotation);
 
-        if (entry.mForcedDensity != 0) {
-            dc.mBaseDisplayDensity = entry.mForcedDensity;
-        }
-        if (entry.mForcedWidth != 0 && entry.mForcedHeight != 0) {
-            dc.updateBaseDisplayMetrics(entry.mForcedWidth, entry.mForcedHeight,
-                    dc.mBaseDisplayDensity);
-        }
+        final boolean hasDensityOverride = entry.mForcedDensity != 0;
+        final boolean hasSizeOverride = entry.mForcedWidth != 0 && entry.mForcedHeight != 0;
+        dc.mIsDensityForced = hasDensityOverride;
+        dc.mIsSizeForced = hasSizeOverride;
+        dc.setIgnoreOrientationRequest(entry.mIgnoreOrientationRequest);
+
+        final int width = hasSizeOverride ? entry.mForcedWidth : dc.mBaseDisplayWidth;
+        final int height = hasSizeOverride ? entry.mForcedHeight : dc.mBaseDisplayHeight;
+        final int density = hasDensityOverride ? entry.mForcedDensity : dc.mBaseDisplayDensity;
+        dc.updateBaseDisplayMetrics(width, height, density);
+
         dc.mDisplayScalingDisabled = entry.mForcedScalingMode == FORCE_SCALING_MODE_DISABLED;
     }
 
@@ -526,6 +542,8 @@
             entry.mShouldShowSystemDecors = getBooleanAttribute(parser, "shouldShowSystemDecors");
             entry.mShouldShowIme = getBooleanAttribute(parser, "shouldShowIme");
             entry.mFixedToUserRotation = getIntAttribute(parser, "fixedToUserRotation");
+            entry.mIgnoreOrientationRequest
+                    = getBooleanAttribute(parser, "ignoreOrientationRequest");
             mEntries.put(name, entry);
         }
         XmlUtils.skipCurrentTag(parser);
@@ -610,6 +628,10 @@
                     out.attribute(null, "fixedToUserRotation",
                             Integer.toString(entry.mFixedToUserRotation));
                 }
+                if (entry.mIgnoreOrientationRequest) {
+                    out.attribute(null, "ignoreOrientationRequest",
+                            Boolean.toString(entry.mIgnoreOrientationRequest));
+                }
                 out.endTag(null, "display");
             }
 
diff --git a/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java b/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java
index c5c2364..14880ed 100644
--- a/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java
+++ b/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java
@@ -71,7 +71,7 @@
     }
 
     private void doTake(IBinder permissionOwner) throws RemoteException {
-        long origId = Binder.clearCallingIdentity();
+        final long origId = Binder.clearCallingIdentity();
         try {
             for (int i = 0; i < mUris.size(); i++) {
                 UriGrantsManager.getService().grantUriPermissionFromOwner(
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 1d8cdf7..0813b4f 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -17,7 +17,11 @@
 package com.android.server.wm;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IME;
+import static com.android.server.wm.ImeInsetsSourceProviderProto.IME_TARGET_FROM_IME;
+import static com.android.server.wm.ImeInsetsSourceProviderProto.INSETS_SOURCE_PROVIDER;
+import static com.android.server.wm.ImeInsetsSourceProviderProto.IS_IME_LAYOUT_DRAWN;
 
+import android.util.proto.ProtoOutputStream;
 import android.view.InsetsSource;
 import android.view.WindowInsets;
 
@@ -154,4 +158,15 @@
             pw.println();
         }
     }
+
+    @Override
+    void dumpDebug(ProtoOutputStream proto, long fieldId, @WindowTraceLogLevel int logLevel) {
+        final long token = proto.start(fieldId);
+        super.dumpDebug(proto, INSETS_SOURCE_PROVIDER, logLevel);
+        if (mImeTargetFromIme != null) {
+            mImeTargetFromIme.getWindow().dumpDebug(proto, IME_TARGET_FROM_IME, logLevel);
+        }
+        proto.write(IS_IME_LAYOUT_DRAWN, mIsImeLayoutDrawn);
+        proto.end(token);
+    }
 }
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index acf5f75..470c2b1 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -265,6 +265,7 @@
                 consumer.mWindowHandle.layoutParamsFlags |= FLAG_NOT_TOUCH_MODAL;
                 break;
             case INPUT_CONSUMER_RECENTS_ANIMATION:
+                consumer.mWindowHandle.focusable = true;
                 break;
             default:
                 throw new IllegalArgumentException("Illegal input consumer : " + name
@@ -285,10 +286,12 @@
         inputWindowHandle.dispatchingTimeoutMillis = child.getInputDispatchingTimeoutMillis();
         inputWindowHandle.visible = isVisible;
         inputWindowHandle.focusable = focusable;
+        inputWindowHandle.touchOcclusionMode = child.getTouchOcclusionMode();
         inputWindowHandle.hasWallpaper = hasWallpaper;
         inputWindowHandle.paused = child.mActivityRecord != null ? child.mActivityRecord.paused : false;
         inputWindowHandle.ownerPid = child.mSession.mPid;
         inputWindowHandle.ownerUid = child.mSession.mUid;
+        inputWindowHandle.packageName = child.getOwningPackage();
         inputWindowHandle.inputFeatures = child.mAttrs.inputFeatures;
         inputWindowHandle.displayId = child.getDisplayId();
 
@@ -528,7 +531,7 @@
                     // event. This is used to omit Surfaces from occlusion detection.
                     populateOverlayInputInfo(mInvalidInputWindow, w.getName(), type, isVisible);
                     mInputTransaction.setInputWindowInfo(
-                            w.mWinAnimator.mSurfaceController.getClientViewRootSurface(),
+                            w.mWinAnimator.mSurfaceController.mSurfaceControl,
                             mInvalidInputWindow);
                     return;
                 }
@@ -543,7 +546,7 @@
 
             if (mAddRecentsAnimationInputConsumerHandle && shouldApplyRecentsInputConsumer) {
                 if (recentsAnimationController.updateInputConsumerForApp(
-                        mRecentsAnimationInputConsumer.mWindowHandle, focusable)) {
+                        mRecentsAnimationInputConsumer.mWindowHandle)) {
                     mRecentsAnimationInputConsumer.show(mInputTransaction, w);
                     mAddRecentsAnimationInputConsumerHandle = false;
                 }
@@ -597,8 +600,7 @@
 
             if (w.mWinAnimator.hasSurface()) {
                 mInputTransaction.setInputWindowInfo(
-                    w.mWinAnimator.mSurfaceController.getClientViewRootSurface(),
-                    inputWindowHandle);
+                        w.mWinAnimator.mSurfaceController.mSurfaceControl, inputWindowHandle);
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/InsetsControlTarget.java b/services/core/java/com/android/server/wm/InsetsControlTarget.java
index 5e7ed3f..287dd74 100644
--- a/services/core/java/com/android/server/wm/InsetsControlTarget.java
+++ b/services/core/java/com/android/server/wm/InsetsControlTarget.java
@@ -18,6 +18,7 @@
 
 import android.inputmethodservice.InputMethodService;
 import android.view.InsetsState;
+import android.view.InsetsState.InternalInsetsType;
 import android.view.WindowInsets.Type.InsetsType;
 
 /**
@@ -39,10 +40,10 @@
     }
 
     /**
-     * @return The requested {@link InsetsState} of this target.
+     * @return The requested visibility of this target.
      */
-    default InsetsState getRequestedInsetsState() {
-        return InsetsState.EMPTY;
+    default boolean getRequestedVisibility(@InternalInsetsType int type) {
+        return InsetsState.getDefaultVisibility(type);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 3617884..a2b9cf9 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -28,6 +28,7 @@
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.StatusBarManager;
 import android.util.IntArray;
@@ -173,13 +174,7 @@
             // animation frame which will be triggered if a new leash is created.
             mDisplayContent.mWmService.mAnimator.getChoreographer().postFrameCallback(time -> {
                 synchronized (mDisplayContent.mWmService.mGlobalLock) {
-                    final InsetsState state = new InsetsState(mStateController.getRawInsetsState());
-                    startAnimation(true /* show */, () -> {
-                        synchronized (mDisplayContent.mWmService.mGlobalLock) {
-                            mStateController.notifyInsetsChanged();
-                        }
-                    }, state);
-                    mStateController.onInsetsModified(mDummyControlTarget, state);
+                    startAnimation(true /* show */, null /* callback */);
                 }
             });
         }
@@ -189,15 +184,18 @@
         if (mShowingTransientTypes.size() == 0) {
             return;
         }
-        InsetsState state = new InsetsState(mStateController.getRawInsetsState());
         startAnimation(false /* show */, () -> {
             synchronized (mDisplayContent.mWmService.mGlobalLock) {
+                for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) {
+                    // We are about to clear mShowingTransientTypes, we don't want the transient bar
+                    // can cause insets on the client. Restore the client visibility.
+                    final @InternalInsetsType int type = mShowingTransientTypes.get(i);
+                    mStateController.getSourceProvider(type).setClientVisible(false);
+                }
                 mShowingTransientTypes.clear();
-                mStateController.notifyInsetsChanged();
                 updateBarControlTarget(mFocusedWin);
             }
-        }, state);
-        mStateController.onInsetsModified(mDummyControlTarget, state);
+        });
     }
 
     boolean isTransient(@InternalInsetsType int type) {
@@ -205,13 +203,25 @@
     }
 
     /**
-     * @see InsetsStateController#getInsetsForDispatch
+     * @see InsetsStateController#getInsetsForWindow
      */
-    InsetsState getInsetsForDispatch(WindowState target) {
-        final InsetsState originalState = mStateController.getInsetsForDispatch(target);
+    InsetsState getInsetsForWindow(WindowState target) {
+        final InsetsState originalState = mStateController.getInsetsForWindow(target);
+        return adjustVisibilityForTransientTypes(originalState);
+    }
+
+    /**
+     * @see InsetsStateController#getInsetsForWindowMetrics
+     */
+    InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) {
+        final InsetsState originalState = mStateController.getInsetsForWindowMetrics(attrs);
+        return adjustVisibilityForTransientTypes(originalState);
+    }
+
+    private InsetsState adjustVisibilityForTransientTypes(InsetsState originalState) {
         InsetsState state = originalState;
         for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) {
-            final int type = mShowingTransientTypes.get(i);
+            final @InternalInsetsType int type = mShowingTransientTypes.get(i);
             final InsetsSource originalSource = state.peekSource(type);
             if (originalSource != null && originalSource.isVisible()) {
                 if (state == originalState) {
@@ -227,26 +237,25 @@
         return state;
     }
 
-    void onInsetsModified(WindowState windowState, InsetsState state) {
-        mStateController.onInsetsModified(windowState, state);
-        checkAbortTransient(windowState, state);
+    void onInsetsModified(InsetsControlTarget caller) {
+        mStateController.onInsetsModified(caller);
+        checkAbortTransient(caller);
         updateBarControlTarget(mFocusedWin);
     }
 
     /**
-     * Called when a window modified the insets state. If the window set a insets source to visible
-     * while it is shown transiently, we need to abort the transient state.
+     * Called when a control target modified the insets state. If the target set a insets source to
+     * visible while it is shown transiently, we need to abort the transient state.
      *
-     * @param windowState who changed the insets state.
-     * @param state the modified insets state.
+     * @param caller who changed the insets state.
      */
-    private void checkAbortTransient(WindowState windowState, InsetsState state) {
+    private void checkAbortTransient(InsetsControlTarget caller) {
         if (mShowingTransientTypes.size() != 0) {
             IntArray abortTypes = new IntArray();
             for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) {
-                final int type = mShowingTransientTypes.get(i);
-                if (mStateController.isFakeTarget(type, windowState)
-                        && state.getSource(type).isVisible()) {
+                final @InternalInsetsType int type = mShowingTransientTypes.get(i);
+                if (mStateController.isFakeTarget(type, caller)
+                        && caller.getRequestedVisibility(type)) {
                     mShowingTransientTypes.remove(i);
                     abortTypes.add(type);
                 }
@@ -393,12 +402,12 @@
     }
 
     @VisibleForTesting
-    void startAnimation(boolean show, Runnable callback, InsetsState state) {
+    void startAnimation(boolean show, Runnable callback) {
         int typesReady = 0;
         final SparseArray<InsetsSourceControl> controls = new SparseArray<>();
         final IntArray showingTransientTypes = mShowingTransientTypes;
         for (int i = showingTransientTypes.size() - 1; i >= 0; i--) {
-            int type = showingTransientTypes.get(i);
+            final @InternalInsetsType int type = showingTransientTypes.get(i);
             InsetsSourceProvider provider = mStateController.getSourceProvider(type);
             InsetsSourceControl control = provider.getControl(mDummyControlTarget);
             if (control == null || control.getLeash() == null) {
@@ -406,7 +415,6 @@
             }
             typesReady |= InsetsState.toPublicType(type);
             controls.put(control.getType(), new InsetsSourceControl(control));
-            state.setSourceVisible(type, show);
         }
         controlAnimationUnchecked(typesReady, controls, show, callback);
     }
@@ -428,12 +436,9 @@
             mId = id;
         }
 
-        private void updateVisibility(InsetsControlTarget controlTarget,
+        private void updateVisibility(@Nullable InsetsControlTarget controlTarget,
                 @InternalInsetsType int type) {
-            final WindowState controllingWin =
-                    controlTarget instanceof WindowState ? (WindowState) controlTarget : null;
-            setVisible(controllingWin == null
-                    || controllingWin.getRequestedInsetsState().getSourceOrDefaultVisibility(type));
+            setVisible(controlTarget == null || controlTarget.getRequestedVisibility(type));
         }
 
         private void setVisible(boolean visible) {
@@ -463,7 +468,9 @@
         @Override
         protected void onAnimationFinish() {
             super.onAnimationFinish();
-            DisplayThread.getHandler().post(mFinishCallback);
+            if (mFinishCallback != null) {
+                DisplayThread.getHandler().post(mFinishCallback);
+            }
         }
 
         private class InsetsPolicyAnimationControlCallbacks implements
@@ -484,10 +491,10 @@
                 mAnimatingShown = show;
 
                 mAnimationControl = new InsetsAnimationControlImpl(controls,
-                        mFocusedWin.getDisplayContent().getBounds(), getState(),
+                        mFocusedWin.getDisplayContent().getBounds(), mFocusedWin.getInsetsState(),
                         mListener, typesReady, this, mListener.getDurationMs(),
                         InsetsController.SYSTEM_BARS_INTERPOLATOR,
-                        show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE);
+                        show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE, null /* translator */);
                 SurfaceAnimationThread.getHandler().post(
                         () -> mListener.onReady(mAnimationControl, typesReady));
             }
@@ -495,8 +502,7 @@
             /** Called on SurfaceAnimationThread without global WM lock held. */
             @Override
             public void scheduleApplyChangeInsets(InsetsAnimationControlRunner runner) {
-                InsetsState state = getState();
-                if (mAnimationControl.applyChangeInsets(state)) {
+                if (mAnimationControl.applyChangeInsets(null /* outState */)) {
                     mAnimationControl.finish(mAnimatingShown);
                 }
             }
@@ -507,20 +513,6 @@
                 // onAnimationFinished callback.
             }
 
-            /**
-             * This method will return a state with fullscreen frame override. No need to make copy
-             * after getting state from this method.
-             * @return The client insets state with full display frame override.
-             */
-            private InsetsState getState() {
-                // To animate the transient animation correctly, we need to let the state hold
-                // the full display frame.
-                InsetsState overrideState = new InsetsState(mFocusedWin.getRequestedInsetsState(),
-                        true);
-                overrideState.setDisplayFrame(mFocusedWin.getDisplayContent().getBounds());
-                return overrideState;
-            }
-
             /** Called on SurfaceAnimationThread without global WM lock held. */
             @Override
             public void applySurfaceParams(
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 5bed186..e83151d 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -23,6 +23,21 @@
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IME;
+import static com.android.server.wm.InsetsSourceProviderProto.CAPTURED_LEASH;
+import static com.android.server.wm.InsetsSourceProviderProto.CLIENT_VISIBLE;
+import static com.android.server.wm.InsetsSourceProviderProto.CONTROL;
+import static com.android.server.wm.InsetsSourceProviderProto.CONTROLLABLE;
+import static com.android.server.wm.InsetsSourceProviderProto.CONTROL_TARGET;
+import static com.android.server.wm.InsetsSourceProviderProto.FAKE_CONTROL;
+import static com.android.server.wm.InsetsSourceProviderProto.FAKE_CONTROL_TARGET;
+import static com.android.server.wm.InsetsSourceProviderProto.FINISH_SEAMLESS_ROTATE_FRAME_NUMBER;
+import static com.android.server.wm.InsetsSourceProviderProto.FRAME;
+import static com.android.server.wm.InsetsSourceProviderProto.IME_OVERRIDDEN_FRAME;
+import static com.android.server.wm.InsetsSourceProviderProto.IS_LEASH_READY_FOR_DISPATCHING;
+import static com.android.server.wm.InsetsSourceProviderProto.PENDING_CONTROL_TARGET;
+import static com.android.server.wm.InsetsSourceProviderProto.SEAMLESS_ROTATING;
+import static com.android.server.wm.InsetsSourceProviderProto.SERVER_VISIBLE;
+import static com.android.server.wm.InsetsSourceProviderProto.SOURCE;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_INSETS_CONTROL;
 import static com.android.server.wm.WindowManagerService.H.LAYOUT_AND_ASSIGN_WINDOW_LAYERS_IF_NEEDED;
 
@@ -323,11 +338,12 @@
         }
     }
 
-    boolean onInsetsModified(InsetsControlTarget caller, InsetsSource modifiedSource) {
-        if (mControlTarget != caller || modifiedSource.isVisible() == mClientVisible) {
+    boolean updateClientVisibility(InsetsControlTarget caller) {
+        final boolean requestedVisible = caller.getRequestedVisibility(mSource.getType());
+        if (caller != mControlTarget || requestedVisible == mClientVisible) {
             return false;
         }
-        setClientVisible(modifiedSource.isVisible());
+        setClientVisible(requestedVisible);
         return true;
     }
 
@@ -335,7 +351,7 @@
         mIsLeashReadyForDispatching = true;
     }
 
-    private void setClientVisible(boolean clientVisible) {
+    void setClientVisible(boolean clientVisible) {
         if (mClientVisible == clientVisible) {
             return;
         }
@@ -452,6 +468,36 @@
         }
     }
 
+    void dumpDebug(ProtoOutputStream proto, long fieldId, @WindowTraceLogLevel int logLevel) {
+        final long token = proto.start(fieldId);
+        mSource.dumpDebug(proto, SOURCE);
+        mTmpRect.dumpDebug(proto, FRAME);
+        mFakeControl.dumpDebug(proto, FAKE_CONTROL);
+        if (mControl != null) {
+            mControl.dumpDebug(proto, CONTROL);
+        }
+        if (mControlTarget != null && mControlTarget.getWindow() != null) {
+            mControlTarget.getWindow().dumpDebug(proto, CONTROL_TARGET, logLevel);
+        }
+        if (mPendingControlTarget != null && mPendingControlTarget.getWindow() != null) {
+            mPendingControlTarget.getWindow().dumpDebug(proto, PENDING_CONTROL_TARGET, logLevel);
+        }
+        if (mFakeControlTarget != null && mFakeControlTarget.getWindow() != null) {
+            mFakeControlTarget.getWindow().dumpDebug(proto, FAKE_CONTROL_TARGET, logLevel);
+        }
+        if (mAdapter != null && mAdapter.mCapturedLeash != null) {
+            mAdapter.mCapturedLeash.dumpDebug(proto, CAPTURED_LEASH);
+        }
+        mImeOverrideFrame.dumpDebug(proto, IME_OVERRIDDEN_FRAME);
+        proto.write(IS_LEASH_READY_FOR_DISPATCHING, mIsLeashReadyForDispatching);
+        proto.write(CLIENT_VISIBLE, mClientVisible);
+        proto.write(SERVER_VISIBLE, mServerVisible);
+        proto.write(SEAMLESS_ROTATING, mSeamlessRotating);
+        proto.write(FINISH_SEAMLESS_ROTATE_FRAME_NUMBER, mFinishSeamlessRotateFrameNumber);
+        proto.write(CONTROLLABLE, mControllable);
+        proto.end(token);
+    }
+
     private class ControlAdapter implements AnimationAdapter {
 
         private SurfaceControl mCapturedLeash;
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index b59452f..e7f140f 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -97,14 +97,16 @@
     }
 
     /**
-     * When dispatching window state to the client, we'll need to exclude the source that represents
-     * the window that is being dispatched. We also need to exclude certain types of insets source
-     * for client within specific windowing modes.
+     * Gets the insets state from the perspective of the target. When performing layout of the
+     * target or dispatching insets to the target, we need to exclude sources which should not be
+     * visible to the target. e.g., the source which represents the target window itself, and the
+     * IME source when the target is above IME. We also need to exclude certain types of insets
+     * source for client within specific windowing modes.
      *
-     * @param target The client we dispatch the state to.
+     * @param target The window associate with the perspective.
      * @return The state stripped of the necessary information.
      */
-    InsetsState getInsetsForDispatch(@NonNull WindowState target) {
+    InsetsState getInsetsForWindow(@NonNull WindowState target) {
         final InsetsState rotatedState = target.mToken.getFixedRotationTransformInsetsState();
         if (rotatedState != null) {
             return rotatedState;
@@ -112,17 +114,23 @@
         final InsetsSourceProvider provider = target.getControllableInsetProvider();
         final @InternalInsetsType int type = provider != null
                 ? provider.getSource().getType() : ITYPE_INVALID;
-        return getInsetsForDispatchInner(type, target.getWindowingMode(), target.isAlwaysOnTop(),
+        return getInsetsForTarget(type, target.getWindowingMode(), target.isAlwaysOnTop(),
                 isAboveIme(target));
     }
 
     InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) {
         final @InternalInsetsType int type = getInsetsTypeForLayoutParams(attrs);
         final WindowToken token = mDisplayContent.getWindowToken(attrs.token);
+        if (token != null) {
+            final InsetsState rotatedState = token.getFixedRotationTransformInsetsState();
+            if (rotatedState != null) {
+                return rotatedState;
+            }
+        }
         final @WindowingMode int windowingMode = token != null
                 ? token.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
         final boolean alwaysOnTop = token != null && token.isAlwaysOnTop();
-        return getInsetsForDispatchInner(type, windowingMode, alwaysOnTop, isAboveIme(token));
+        return getInsetsForTarget(type, windowingMode, alwaysOnTop, isAboveIme(token));
     }
 
     private boolean isAboveIme(WindowContainer target) {
@@ -165,8 +173,11 @@
         return ITYPE_INVALID;
     }
 
-    /** @see #getInsetsForDispatch */
-    private InsetsState getInsetsForDispatchInner(@InternalInsetsType int type,
+    /**
+     * @see #getInsetsForWindow
+     * @see #getInsetsForWindowMetrics
+     */
+    private InsetsState getInsetsForTarget(@InternalInsetsType int type,
             @WindowingMode int windowingMode, boolean isAlwaysOnTop, boolean aboveIme) {
         InsetsState state = mState;
 
@@ -270,7 +281,6 @@
      * Called when a layout pass has occurred.
      */
     void onPostLayout() {
-        mState.setDisplayFrame(mDisplayContent.getBounds());
         for (int i = mProviders.size() - 1; i >= 0; i--) {
             mProviders.valueAt(i).onPostLayout();
         }
@@ -289,16 +299,10 @@
         winInsetsChanged.clear();
     }
 
-    void onInsetsModified(InsetsControlTarget windowState, InsetsState state) {
+    void onInsetsModified(InsetsControlTarget caller) {
         boolean changed = false;
-        for (int i = 0; i < InsetsState.SIZE; i++) {
-            final InsetsSource source = state.peekSource(i);
-            if (source == null) continue;
-            final InsetsSourceProvider provider = mProviders.get(source.getType());
-            if (provider == null) {
-                continue;
-            }
-            changed |= provider.onInsetsModified(windowState, source);
+        for (int i = mProviders.size() - 1; i >= 0; i--) {
+            changed |= mProviders.valueAt(i).updateClientVisibility(caller);
         }
         if (changed) {
             notifyInsetsChanged();
@@ -464,11 +468,24 @@
                 final InsetsSourceProvider provider = mProviders.valueAt(i);
                 provider.onSurfaceTransactionApplied();
             }
+            final ArraySet<InsetsControlTarget> newControlTargets = new ArraySet<>();
             for (int i = mPendingControlChanged.size() - 1; i >= 0; i--) {
                 final InsetsControlTarget controlTarget = mPendingControlChanged.valueAt(i);
                 controlTarget.notifyInsetsControlChanged();
+                if (mControlTargetTypeMap.containsKey(controlTarget)) {
+                    // We only collect targets who get controls, not lose controls.
+                    newControlTargets.add(controlTarget);
+                }
             }
             mPendingControlChanged.clear();
+
+            // This updates the insets visibilities AFTER sending current insets state and controls
+            // to the clients, so that the clients can change the current visibilities to the
+            // requested visibilities with animations.
+            for (int i = newControlTargets.size() - 1; i >= 0; i--) {
+                onInsetsModified(newControlTargets.valueAt(i));
+            }
+            newControlTargets.clear();
         });
     }
 
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 80c7366..71eb18c 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -555,7 +555,7 @@
                     && !mService.mWindowManager.mPowerManager.isInteractive()
                     && (mRequestDismissKeyguard || occludedByActivity)) {
                 controller.mStackSupervisor.wakeUp("handleTurnScreenOn");
-                mTopOccludesActivity.setCurrentLaunchCanTurnScreenOn(false);
+                mTopTurnScreenOnActivity.setCurrentLaunchCanTurnScreenOn(false);
             }
 
             if (lastOccluded != mOccluded) {
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index d292580..0503c0d 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -1378,14 +1378,6 @@
                 break;
         }
 
-        // Tasks managed by/associated with an ActivityView should be excluded from recents.
-        // singleTaskInstance is set on the VirtualDisplay managed by ActivityView
-        // TODO(b/126185105): Find a different signal to use besides isSingleTaskInstance
-        final Task rootTask = task.getRootTask();
-        if (rootTask != null && rootTask.isSingleTaskInstance()) {
-            return false;
-        }
-
         // If we're in lock task mode, ignore the root task
         if (task == mService.getLockTaskController().getRootTask()) {
             return false;
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 38d20a8..4ad2575 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -283,6 +283,20 @@
         public void hideCurrentInputMethod() {
             final long token = Binder.clearCallingIdentity();
             try {
+                synchronized (mService.getWindowManagerLock()) {
+                    // Make sure to update the correct IME parent in case that the IME parent may
+                    // be computed as display layer when re-layout window happens during rotation
+                    // but there is intermediate state that the bounds of task and the IME
+                    // target's activity is not the same during rotating.
+                    mDisplayContent.updateImeParent();
+
+                    // Ignore hiding IME if IME window is attached to app.
+                    // Since we would like to snapshot Task with IME window while transitioning
+                    // to recents.
+                    if (mDisplayContent.isImeAttachedToApp()) {
+                        return;
+                    }
+                }
                 final InputMethodManagerInternal inputMethodManagerInternal =
                         LocalServices.getService(InputMethodManagerInternal.class);
                 if (inputMethodManagerInternal != null) {
@@ -375,9 +389,7 @@
         final int taskCount = visibleTasks.size();
         for (int i = 0; i < taskCount; i++) {
             final Task task = visibleTasks.get(i);
-            final WindowConfiguration config = task.getWindowConfiguration();
-            if (config.tasksAreFloating()
-                    || config.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+            if (skipAnimation(task)) {
                 continue;
             }
             addAnimation(task, !recentTaskIds.get(task.mTaskId));
@@ -420,6 +432,19 @@
         }
     }
 
+
+    /**
+     * Whether a task should be filtered from the recents animation. This can be true for tasks
+     * being displayed outside of recents.
+     */
+    private boolean skipAnimation(Task task) {
+        final WindowConfiguration config = task.getWindowConfiguration();
+        return task.isAlwaysOnTop()
+                || config.tasksAreFloating()
+                || config.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+    }
+
+
     @VisibleForTesting
     AnimationAdapter addAnimation(Task task, boolean isRecentTaskInvisible) {
         return addAnimation(task, isRecentTaskInvisible, false /* hidden */,
@@ -515,8 +540,9 @@
 
     void addTaskToTargets(Task task, OnAnimationFinishedCallback finishedCallback) {
         if (mRunner != null) {
-            // No need to send task appeared when the task target already exists.
-            if (isAnimatingTask(task)) {
+            // No need to send task appeared when the task target already exists, or when the
+            // task is being managed as a multi-window mode outside of recents (e.g. bubbles).
+            if (isAnimatingTask(task) || skipAnimation(task)) {
                 return;
             }
             final RemoteAnimationTarget target = createTaskRemoteAnimation(task, finishedCallback);
@@ -813,15 +839,13 @@
                 && !isTargetApp(activity) && isAnimatingApp(activity);
     }
 
-    boolean updateInputConsumerForApp(InputWindowHandle inputWindowHandle,
-            boolean focusable) {
+    boolean updateInputConsumerForApp(InputWindowHandle inputWindowHandle) {
         // Update the input consumer touchable region to match the target app main window
         final WindowState targetAppMainWindow = mTargetActivityRecord != null
                 ? mTargetActivityRecord.findMainWindow()
                 : null;
         if (targetAppMainWindow != null) {
             targetAppMainWindow.getBounds(mTmpRect);
-            inputWindowHandle.focusable = focusable;
             inputWindowHandle.touchableRegion.set(mTmpRect);
             return true;
         }
diff --git a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
index 9181a0f..e89e59c 100644
--- a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
+++ b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
@@ -231,11 +231,6 @@
 
         final ActivityTaskManagerService atmService = mTargetStack.mAtmService;
         TaskDisplayArea taskDisplayArea = mTargetStack.getDisplayArea();
-        final boolean singleTaskInstanceDisplay =
-                taskDisplayArea.mDisplayContent.isSingleTaskInstance();
-        if (singleTaskInstanceDisplay) {
-            taskDisplayArea = atmService.mRootWindowContainer.getDefaultTaskDisplayArea();
-        }
 
         final int windowingMode = mTargetStack.getWindowingMode();
         final int activityType = mTargetStack.getActivityType();
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index cabf1bf..d5ad791 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -38,7 +38,6 @@
 import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
 import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
 import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
 import static android.view.WindowManager.TRANSIT_TASK_TO_BACK;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
@@ -512,6 +511,7 @@
     void onChildPositionChanged(WindowContainer child) {
         mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
                 !mWmService.mPerDisplayFocusEnabled /* updateInputWindows */);
+        mStackSupervisor.updateTopResumedActivityIfNeeded();
     }
 
     /**
@@ -864,6 +864,7 @@
         mWmService.openSurfaceTransaction();
         try {
             applySurfaceChangesTransaction();
+            mWmService.mSyncEngine.onSurfacePlacement();
         } catch (RuntimeException e) {
             Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
         } finally {
@@ -2038,7 +2039,7 @@
         // Also dismiss the pinned stack whenever we switch users. Removing the pinned stack will
         // also cause all tasks to be moved to the fullscreen stack at a position that is
         // appropriate.
-        removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
+        removeRootTasksInWindowingModes(WINDOWING_MODE_PINNED);
 
         mUserStackInFront.put(mCurrentUser, focusStackId);
         mCurrentUser = userId;
@@ -2131,37 +2132,30 @@
                     + displayId);
         }
 
-        if (displayContent.isSingleTaskInstance() && displayContent.getStackCount() > 0) {
-            // We don't allow moving stacks to single instance display that already has a child.
-            Slog.e(TAG, "Can not move stackId=" + stackId
-                    + " to single task instance display=" + displayContent);
-            return;
-        }
-
         moveStackToTaskDisplayArea(stackId, displayContent.getDefaultTaskDisplayArea(), onTop);
     }
 
-    boolean moveTopStackActivityToPinnedStack(int stackId) {
-        final Task stack = getStack(stackId);
-        if (stack == null) {
+    boolean moveTopStackActivityToPinnedRootTask(int rootTaskId) {
+        final Task rootTask = getStack(rootTaskId);
+        if (rootTask == null) {
             throw new IllegalArgumentException(
-                    "moveTopStackActivityToPinnedStack: Unknown stackId=" + stackId);
+                    "moveTopStackActivityToPinnedRootTask: Unknown rootTaskId=" + rootTaskId);
         }
 
-        final ActivityRecord r = stack.topRunningActivity();
+        final ActivityRecord r = rootTask.topRunningActivity();
         if (r == null) {
-            Slog.w(TAG, "moveTopStackActivityToPinnedStack: No top running activity"
-                    + " in stack=" + stack);
+            Slog.w(TAG, "moveTopStackActivityToPinnedRootTask: No top running activity"
+                    + " in rootTask=" + rootTask);
             return false;
         }
 
         if (!mService.mForceResizableActivities && !r.supportsPictureInPicture()) {
-            Slog.w(TAG, "moveTopStackActivityToPinnedStack: Picture-In-Picture not supported for "
-                    + " r=" + r);
+            Slog.w(TAG, "moveTopStackActivityToPinnedRootTask: Picture-In-Picture not supported "
+                    + "for r=" + r);
             return false;
         }
 
-        moveActivityToPinnedStack(r, "moveTopActivityToPinnedStack");
+        moveActivityToPinnedStack(r, "moveTopStackActivityToPinnedRootTask");
         return true;
     }
 
@@ -2418,21 +2412,7 @@
                     if (displayShouldSleep) {
                         stack.goToSleepIfPossible(false /* shuttingDown */);
                     } else {
-                        // When the display which can only contain one task turns on, start a
-                        // special transition.
-                        // {@link AppTransitionController#handleAppTransitionReady} later picks up
-                        // the transition, and schedules
-                        // {@link ITaskStackListener#onSingleTaskDisplayDrawn} callback which is
-                        // triggered after contents are drawn on the display.
-                        if (display.isSingleTaskInstance()) {
-                            display.mDisplayContent.prepareAppTransition(
-                                    TRANSIT_SHOW_SINGLE_TASK_DISPLAY, false,
-                                    0 /* flags */, true /* forceOverride*/);
-                        }
                         stack.awakeFromSleepingLocked();
-                        if (display.isSingleTaskInstance()) {
-                            display.executeAppTransition();
-                        }
                         if (stack.isFocusedStackOnDisplay()
                                 && !mStackSupervisor.getKeyguardController()
                                 .isKeyguardOrAodShowing(display.mDisplayId)) {
@@ -2647,12 +2627,6 @@
                 + " in=" + taskDisplayArea);
     }
 
-    @Override
-    void positionChildAt(int position, DisplayContent child, boolean includingParents) {
-        super.positionChildAt(position, child, includingParents);
-        mStackSupervisor.updateTopResumedActivityIfNeeded();
-    }
-
     Configuration getDisplayOverrideConfiguration(int displayId) {
         final DisplayContent displayContent = getDisplayContentOrCreate(displayId);
         if (displayContent == null) {
@@ -3310,18 +3284,18 @@
     }
 
     /**
-     * Removes stacks in the input windowing modes from the system if they are of activity type
+     * Removes root tasks in the input windowing modes from the system if they are of activity type
      * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
      */
-    void removeStacksInWindowingModes(int... windowingModes) {
+    void removeRootTasksInWindowingModes(int... windowingModes) {
         for (int i = getChildCount() - 1; i >= 0; --i) {
-            getChildAt(i).removeStacksInWindowingModes(windowingModes);
+            getChildAt(i).removeRootTasksInWindowingModes(windowingModes);
         }
     }
 
-    void removeStacksWithActivityTypes(int... activityTypes) {
+    void removeRootTasksWithActivityTypes(int... activityTypes) {
         for (int i = getChildCount() - 1; i >= 0; --i) {
-            getChildAt(i).removeStacksWithActivityTypes(activityTypes);
+            getChildAt(i).removeRootTasksWithActivityTypes(activityTypes);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index 25732e7..7ed22a1 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -203,16 +203,14 @@
                     .setCallsite("ScreenRotationAnimation")
                     .build();
 
-            // In case display bounds change, screenshot buffer and surface may mismatch so set a
-            // scaling mode.
-            SurfaceControl.Transaction t2 = mService.mTransactionFactory.get();
-            t2.setOverrideScalingMode(mScreenshotLayer, Surface.SCALING_MODE_SCALE_TO_WINDOW);
-            t2.apply(true /* sync */);
-
             // Capture a screenshot into the surface we just created.
             final int displayId = displayContent.getDisplayId();
             final Surface surface = mService.mSurfaceFactory.get();
+            // In case display bounds change, screenshot buffer and surface may mismatch so set a
+            // scaling mode.
             surface.copyFrom(mScreenshotLayer);
+            surface.setScalingMode(Surface.SCALING_MODE_SCALE_TO_WINDOW);
+
             SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
                     mService.mDisplayManagerInternal.systemScreenshot(displayId);
             if (screenshotBuffer != null) {
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 9ff99f5..70dbf68 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -84,6 +84,9 @@
     private final ArraySet<WindowSurfaceController> mAlertWindowSurfaces = new ArraySet<>();
     private final DragDropController mDragDropController;
     final boolean mCanAddInternalSystemWindow;
+    // If non-system overlays from this process can be hidden by the user or app using
+    // HIDE_NON_SYSTEM_OVERLAY_WINDOWS.
+    final boolean mOverlaysCanBeHidden;
     final boolean mCanHideNonSystemOverlayWindows;
     final boolean mCanAcquireSleepToken;
     private AlertWindowNotification mAlertWindowNotification;
@@ -92,6 +95,7 @@
     private float mLastReportedAnimatorScale;
     private String mPackageName;
     private String mRelayoutTag;
+    private final InsetsState mDummyRequestedVisibility = new InsetsState();
     private final InsetsSourceControl[] mDummyControls =  new InsetsSourceControl[0];
 
     public Session(WindowManagerService service, IWindowSessionCallback callback) {
@@ -104,6 +108,8 @@
                 INTERNAL_SYSTEM_WINDOW) == PERMISSION_GRANTED;
         mCanHideNonSystemOverlayWindows = service.mContext.checkCallingOrSelfPermission(
                 HIDE_NON_SYSTEM_OVERLAY_WINDOWS) == PERMISSION_GRANTED;
+        mOverlaysCanBeHidden = !mCanAddInternalSystemWindow
+                && !mService.mAtmInternal.isCallerRecents(mUid);
         mCanAcquireSleepToken = service.mContext.checkCallingOrSelfPermission(DEVICE_POWER)
                 == PERMISSION_GRANTED;
         mShowingAlertWindowNotificationAllowed = mService.mShowAlertWindowNotifications;
@@ -158,35 +164,33 @@
 
     @Override
     public int addToDisplay(IWindow window, WindowManager.LayoutParams attrs,
-            int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
-            Rect outStableInsets,
+            int viewVisibility, int displayId, InsetsState requestedVisibility, Rect outFrame,
             DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
             InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {
-        return mService.addWindow(this, window, attrs, viewVisibility, displayId, outFrame,
-                outContentInsets, outStableInsets, outDisplayCutout, outInputChannel,
-                outInsetsState, outActiveControls, UserHandle.getUserId(mUid));
+        return mService.addWindow(this, window, attrs, viewVisibility, displayId,
+                UserHandle.getUserId(mUid), requestedVisibility, outFrame, outDisplayCutout,
+                outInputChannel, outInsetsState, outActiveControls);
     }
 
 
     @Override
     public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
-            int viewVisibility, int displayId, int userId, Rect outFrame,
-            Rect outContentInsets, Rect outStableInsets,
-            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
-            InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {
-        return mService.addWindow(this, window, attrs, viewVisibility, displayId, outFrame,
-                outContentInsets, outStableInsets, outDisplayCutout, outInputChannel,
-                outInsetsState, outActiveControls, userId);
+            int viewVisibility, int displayId, int userId, InsetsState requestedVisibility,
+            Rect outFrame, DisplayCutout.ParcelableWrapper outDisplayCutout,
+            InputChannel outInputChannel, InsetsState outInsetsState,
+            InsetsSourceControl[] outActiveControls) {
+        return mService.addWindow(this, window, attrs, viewVisibility, displayId, userId,
+                requestedVisibility, outFrame, outDisplayCutout, outInputChannel, outInsetsState,
+                outActiveControls);
     }
 
     @Override
     public int addToDisplayWithoutInputChannel(IWindow window, WindowManager.LayoutParams attrs,
-            int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
-            InsetsState outInsetsState) {
+            int viewVisibility, int displayId, InsetsState outInsetsState) {
         return mService.addWindow(this, window, attrs, viewVisibility, displayId,
-                new Rect() /* outFrame */, outContentInsets, outStableInsets,
-                new DisplayCutout.ParcelableWrapper() /* cutout */, null /* outInputChannel */,
-                outInsetsState, mDummyControls, UserHandle.getUserId(mUid));
+                UserHandle.getUserId(mUid), mDummyRequestedVisibility,
+                new Rect() /* outFrame */, new DisplayCutout.ParcelableWrapper() /* cutout */,
+                null /* outInputChannel */, outInsetsState, mDummyControls);
     }
 
     @Override
@@ -204,15 +208,14 @@
             int requestedWidth, int requestedHeight, int viewFlags, int flags, long frameNumber,
             ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
             SurfaceControl outSurfaceControl, InsetsState outInsetsState,
-            InsetsSourceControl[] outActiveControls, Point outSurfaceSize,
-            SurfaceControl outBLASTSurfaceControl) {
+            InsetsSourceControl[] outActiveControls, Point outSurfaceSize) {
         if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from "
                 + Binder.getCallingPid());
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mRelayoutTag);
         int res = mService.relayoutWindow(this, window, attrs,
                 requestedWidth, requestedHeight, viewFlags, flags, frameNumber,
                 outFrames, mergedConfiguration, outSurfaceControl, outInsetsState,
-                outActiveControls, outSurfaceSize, outBLASTSurfaceControl);
+                outActiveControls, outSurfaceSize);
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         if (false) Slog.d(TAG_WM, "<<<<<< EXITING relayout to "
                 + Binder.getCallingPid());
@@ -255,7 +258,7 @@
 
     @Override
     public boolean performHapticFeedback(int effectId, boolean always) {
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             return mService.mPolicy.performHapticFeedback(mUid, mPackageName,
                         effectId, always, null);
@@ -313,7 +316,7 @@
         if (DEBUG_TASK_POSITIONING) Slog.d(
                 TAG_WM, "startMovingTask: {" + startX + "," + startY + "}");
 
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             return mService.mTaskPositioningController.startMovingTask(window, startX, startY);
         } finally {
@@ -325,7 +328,7 @@
     public void finishMovingTask(IWindow window) {
         if (DEBUG_TASK_POSITIONING) Slog.d(TAG_WM, "finishMovingTask");
 
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             mService.mTaskPositioningController.finishTaskPositioning(window);
         } finally {
@@ -335,7 +338,7 @@
 
     @Override
     public void reportSystemGestureExclusionChanged(IWindow window, List<Rect> exclusionRects) {
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             mService.reportSystemGestureExclusionChanged(this, window, exclusionRects);
         } finally {
@@ -352,7 +355,7 @@
     @Override
     public void setWallpaperPosition(IBinder window, float x, float y, float xStep, float yStep) {
         synchronized (mService.mGlobalLock) {
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
                 actionOnWallpaper(window, (wpController, windowState) ->
                         wpController.setWindowWallpaperPosition(windowState, x, y, xStep, yStep));
@@ -369,7 +372,7 @@
                     + zoom);
         }
         synchronized (mService.mGlobalLock) {
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
                 actionOnWallpaper(window, (wpController, windowState) ->
                         wpController.setWallpaperZoomOut(windowState, zoom));
@@ -398,7 +401,7 @@
     @Override
     public void setWallpaperDisplayOffset(IBinder window, int x, int y) {
         synchronized (mService.mGlobalLock) {
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
                 actionOnWallpaper(window, (wpController, windowState) ->
                         wpController.setWindowWallpaperDisplayOffset(windowState, x, y));
@@ -412,7 +415,7 @@
     public Bundle sendWallpaperCommand(IBinder window, String action, int x, int y,
             int z, Bundle extras, boolean sync) {
         synchronized (mService.mGlobalLock) {
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
                 final WindowState windowState = mService.windowForClientLocked(this, window, true);
                 return windowState.getDisplayContent().mWallpaperController
@@ -494,9 +497,8 @@
             final WindowState windowState = mService.windowForClientLocked(this, window,
                     false /* throwOnError */);
             if (windowState != null) {
-                windowState.updateRequestedInsetsState(state);
-                windowState.getDisplayContent().getInsetsPolicy().onInsetsModified(
-                        windowState, state);
+                windowState.updateRequestedVisibility(state);
+                windowState.getDisplayContent().getInsetsPolicy().onInsetsModified(windowState);
             }
         }
     }
@@ -533,7 +535,7 @@
 
         boolean changed;
 
-        if (!mCanAddInternalSystemWindow) {
+        if (mOverlaysCanBeHidden) {
             // We want to track non-system signature apps adding alert windows so we can post an
             // on-going notification for the user to control their visibility.
             if (visible) {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index f984576..b9d1e9b 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -67,7 +67,6 @@
 import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
 import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
 import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
 import static android.view.WindowManager.TRANSIT_TASK_CHANGE_WINDOWING_MODE;
 import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
 import static android.view.WindowManager.TRANSIT_TASK_OPEN;
@@ -114,6 +113,7 @@
 import static com.android.server.wm.IdentifierProto.HASH_CODE;
 import static com.android.server.wm.IdentifierProto.TITLE;
 import static com.android.server.wm.IdentifierProto.USER_ID;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
 import static com.android.server.wm.Task.ActivityState.PAUSED;
 import static com.android.server.wm.Task.ActivityState.PAUSING;
 import static com.android.server.wm.Task.ActivityState.RESUMED;
@@ -1474,14 +1474,6 @@
         // Update task bounds if needed.
         adjustBoundsForDisplayChangeIfNeeded(getDisplayContent());
 
-        if (getWindowConfiguration().windowsAreScaleable()) {
-            // We force windows out of SCALING_MODE_FREEZE so that we can continue to animate them
-            // while a resize is pending.
-            forceWindowsScaleable(true /* force */);
-        } else {
-            forceWindowsScaleable(false /* force */);
-        }
-
         mRootWindowContainer.updateUIDsPresentOnDisplay();
 
         // Resume next focusable stack after reparenting to another display if we aren't removing
@@ -2914,6 +2906,11 @@
             return;
         }
 
+        if (refActivity != null && refActivity.hasCompatDisplayInsets()) {
+            // App prefers to keep its original size.
+            return;
+        }
+
         final int parentWidth = parentBounds.width();
         final int parentHeight = parentBounds.height();
         final float aspect = ((float) parentHeight) / parentWidth;
@@ -3212,10 +3209,6 @@
         if (DEBUG_STACK) Slog.i(TAG, "removeTask: removing taskId=" + mTaskId);
         EventLogTags.writeWmTaskRemoved(mTaskId, "removeTask");
 
-        if (mDisplayContent != null && mDisplayContent.isSingleTaskInstance()) {
-            mAtmService.notifySingleTaskDisplayEmpty(mDisplayContent.mDisplayId);
-        }
-
         // If applicable let the TaskOrganizer know the Task is vanishing.
         setTaskOrganizer(null);
 
@@ -3669,14 +3662,9 @@
         super.setInitialSurfaceControlProperties(b);
     }
 
-    boolean isTaskAnimating() {
-        final RecentsAnimationController recentsAnim = mWmService.getRecentsAnimationController();
-        if (recentsAnim != null) {
-            if (recentsAnim.isAnimatingTask(this)) {
-                return true;
-            }
-        }
-        return forAllTasks((t) -> { return t != this && t.isTaskAnimating(); });
+    /** Checking if self or its child tasks are animated by recents animation. */
+    boolean isAnimatingByRecents() {
+        return isAnimating(CHILDREN, ANIMATION_TYPE_RECENTS);
     }
 
     @Override
@@ -3780,17 +3768,6 @@
         positionChildAt(position, child, false /* includeParents */);
     }
 
-    void forceWindowsScaleable(boolean force) {
-        mWmService.openSurfaceTransaction();
-        try {
-            for (int i = mChildren.size() - 1; i >= 0; i--) {
-                mChildren.get(i).forceWindowsScaleableInTransaction(force);
-            }
-        } finally {
-            mWmService.closeSurfaceTransaction("forceWindowsScaleable");
-        }
-    }
-
     void setTaskDescription(TaskDescription taskDescription) {
         mTaskDescription = taskDescription;
     }
@@ -3818,6 +3795,10 @@
                 || activityType == ACTIVITY_TYPE_ASSISTANT;
     }
 
+    boolean isTaskLetterboxed() {
+        return getWindowingMode() == WINDOWING_MODE_FULLSCREEN && !matchParentBounds();
+    }
+
     @Override
     boolean fillsParent() {
         // From the perspective of policy, we still want to report that this task fills parent
@@ -3964,7 +3945,7 @@
         if (control != null) {
             // We let the transition to be controlled by RecentsAnimation, and callback task's
             // RemoteAnimationTarget for remote runner to animate.
-            if (enter) {
+            if (enter && !isHomeOrRecentsStack()) {
                 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
                         "applyAnimationUnchecked, control: %s, task: %s, transit: %s",
                         control, asTask(), AppTransition.appTransitionToString(transit));
@@ -4062,6 +4043,9 @@
         info.topActivityInfo = mReuseActivitiesReport.top != null
                 ? mReuseActivitiesReport.top.info
                 : null;
+        forAllActivities(r -> {
+            info.addLaunchCookie(r.mLaunchCookie);
+        });
     }
 
     @Nullable PictureInPictureParams getPictureInPictureParams() {
@@ -4792,9 +4776,11 @@
             // If the task is not yet visible when it is added to the task organizer, then we should
             // hide it to allow the task organizer to show it when it is properly reparented. We
             // skip this for tasks created by the organizer because they can synchronously update
-            // the leash before new children are added to the task.
+            // the leash before new children are added to the task.  Also skip this if the task
+            // has already been sent to the organizer which can happen before the first draw if
+            // an existing task is reported to the organizer when it first registers.
             if (!mAtmService.getTransitionController().isShellTransitionsEnabled()
-                    && !mCreatedByOrganizer
+                    && !mCreatedByOrganizer && !mTaskAppearedSent
                     && mTaskOrganizer != null && !prevHasBeenVisible) {
                 getSyncTransaction().hide(getSurfaceControl());
                 commitPendingTransaction();
@@ -4846,6 +4832,11 @@
 
     @VisibleForTesting
     boolean setTaskOrganizer(ITaskOrganizer organizer) {
+        return setTaskOrganizer(organizer, false /* skipTaskAppeared */);
+    }
+
+    @VisibleForTesting
+    boolean setTaskOrganizer(ITaskOrganizer organizer, boolean skipTaskAppeared) {
         if (mTaskOrganizer == organizer) {
             return false;
         }
@@ -4858,7 +4849,9 @@
         sendTaskVanished(prevOrganizer);
 
         if (mTaskOrganizer != null) {
-            sendTaskAppeared();
+            if (!skipTaskAppeared) {
+                sendTaskAppeared();
+            }
         } else {
             // No longer managed by any organizer.
             mTaskAppearedSent = false;
@@ -4871,6 +4864,10 @@
         return true;
     }
 
+    boolean updateTaskOrganizerState(boolean forceUpdate) {
+        return updateTaskOrganizerState(forceUpdate, false /* skipTaskAppeared */);
+    }
+
     /**
      * Called when the task state changes (ie. from windowing mode change) an the task organizer
      * state should also be updated.
@@ -4878,9 +4875,10 @@
      * @param forceUpdate Updates the task organizer to the one currently specified in the task
      *                    org controller for the task's windowing mode, ignoring the cached
      *                    windowing mode checks.
+     * @param skipTaskAppeared Skips calling taskAppeared for the new organizer if it has changed
      * @return {@code true} if task organizer changed.
      */
-    boolean updateTaskOrganizerState(boolean forceUpdate) {
+    boolean updateTaskOrganizerState(boolean forceUpdate, boolean skipTaskAppeared) {
         if (getSurfaceControl() == null) {
             // Can't call onTaskAppeared without a surfacecontrol, so defer this until after one
             // is created.
@@ -4896,7 +4894,7 @@
         if (!forceUpdate && mTaskOrganizer == organizer) {
             return false;
         }
-        return setTaskOrganizer(organizer);
+        return setTaskOrganizer(organizer, skipTaskAppeared);
     }
 
     @Override
@@ -5198,11 +5196,6 @@
                 !PRESERVE_WINDOWS);
     }
 
-    /** @return true if the stack can only contain one task */
-    boolean isSingleTaskInstance() {
-        return mDisplayContent != null && mDisplayContent.isSingleTaskInstance();
-    }
-
     final boolean isHomeOrRecentsStack() {
         return isActivityTypeHome() || isActivityTypeRecents();
     }
@@ -5883,7 +5876,11 @@
         final TaskDisplayArea taskDisplayArea = getDisplayArea();
 
         // If the top activity is the resumed one, nothing to do.
+        // For devices that are not in fullscreen mode (e.g. freeform windows), it's possible
+        // we still want to proceed if the visibility of other windows have changed (e.g. bringing
+        // a fullscreen window forward to cover another freeform activity.)
         if (mResumedActivity == next && next.isState(RESUMED)
+                && taskDisplayArea.getWindowingMode() != WINDOWING_MODE_FREEFORM
                 && taskDisplayArea.allResumedActivitiesComplete()) {
             // Make sure we have executed any pending transitions, since there
             // should be nothing left to do at this point.
@@ -6352,13 +6349,6 @@
                 if (newTask) {
                     if (r.mLaunchTaskBehind) {
                         transit = TRANSIT_TASK_OPEN_BEHIND;
-                    } else if (dc.isSingleTaskInstance()) {
-                        // If a new task is being launched in a single task display, we don't need
-                        // to play normal animation, but need to trigger a callback when an app
-                        // transition is actually handled. So ignore already prepared activity, and
-                        // override it.
-                        transit = TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
-                        keepCurTransition = false;
                     } else {
                         // If a new task is being launched, then mark the existing top activity as
                         // supporting picture-in-picture while pausing only if the starting activity
@@ -6779,15 +6769,6 @@
             // nothing to do!
             if (noAnimation) {
                 ActivityOptions.abort(options);
-            } else if (isSingleTaskInstance()) {
-                // When a task is moved front on the display which can only contain one task, start
-                // a special transition.
-                // {@link AppTransitionController#handleAppTransitionReady} later picks up the
-                // transition, and schedules
-                // {@link ITaskStackListener#onSingleTaskDisplayDrawn} callback which is triggered
-                // after contents are drawn on the display.
-                updateTransitLocked(TRANSIT_SHOW_SINGLE_TASK_DISPLAY, options,
-                        true /* forceOverride */);
             } else {
                 updateTransitLocked(TRANSIT_TASK_TO_FRONT, options, false /* forceOverride */);
             }
@@ -6835,9 +6816,6 @@
                     mStackSupervisor.mNoAnimActivities.add(r);
                 }
                 ActivityOptions.abort(options);
-            } else if (isSingleTaskInstance()) {
-                updateTransitLocked(TRANSIT_SHOW_SINGLE_TASK_DISPLAY, options,
-                        true /* forceOverride */);
             } else {
                 updateTransitLocked(TRANSIT_TASK_TO_FRONT, options, false /* forceOverride */);
             }
@@ -6911,7 +6889,7 @@
         moveToBack("moveTaskToBackLocked", tr);
 
         if (inPinnedWindowingMode()) {
-            mStackSupervisor.removeStack(this);
+            mStackSupervisor.removeRootTask(this);
             return true;
         }
 
@@ -7203,10 +7181,6 @@
     }
 
     void addChild(WindowContainer child, final boolean toTop, boolean showForAllUsers) {
-        if (isSingleTaskInstance() && hasChild()) {
-            throw new IllegalStateException("Can only have one child on stack=" + this);
-        }
-
         Task task = child.asTask();
         try {
 
diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index 2a24118..5364f9c 100644
--- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -54,14 +54,12 @@
     private static final int NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED_MSG = 19;
     private static final int NOTIFY_SIZE_COMPAT_MODE_ACTIVITY_CHANGED_MSG = 20;
     private static final int NOTIFY_BACK_PRESSED_ON_TASK_ROOT = 21;
-    private static final int NOTIFY_SINGLE_TASK_DISPLAY_DRAWN = 22;
-    private static final int NOTIFY_TASK_DISPLAY_CHANGED_LISTENERS_MSG = 23;
-    private static final int NOTIFY_TASK_LIST_UPDATED_LISTENERS_MSG = 24;
-    private static final int NOTIFY_SINGLE_TASK_DISPLAY_EMPTY = 25;
-    private static final int NOTIFY_TASK_LIST_FROZEN_UNFROZEN_MSG = 26;
-    private static final int NOTIFY_TASK_FOCUS_CHANGED_MSG = 27;
-    private static final int NOTIFY_TASK_REQUESTED_ORIENTATION_CHANGED_MSG = 28;
-    private static final int NOTIFY_ACTIVITY_ROTATED_MSG = 29;
+    private static final int NOTIFY_TASK_DISPLAY_CHANGED_LISTENERS_MSG = 22;
+    private static final int NOTIFY_TASK_LIST_UPDATED_LISTENERS_MSG = 23;
+    private static final int NOTIFY_TASK_LIST_FROZEN_UNFROZEN_MSG = 24;
+    private static final int NOTIFY_TASK_FOCUS_CHANGED_MSG = 25;
+    private static final int NOTIFY_TASK_REQUESTED_ORIENTATION_CHANGED_MSG = 26;
+    private static final int NOTIFY_ACTIVITY_ROTATED_MSG = 27;
 
     // Delay in notifying task stack change listeners (in millis)
     private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100;
@@ -156,14 +154,6 @@
         l.onSizeCompatModeActivityChanged(m.arg1, (IBinder) m.obj);
     };
 
-    private final TaskStackConsumer mNotifySingleTaskDisplayDrawn = (l, m) -> {
-        l.onSingleTaskDisplayDrawn(m.arg1);
-    };
-
-    private final TaskStackConsumer mNotifySingleTaskDisplayEmpty = (l, m) -> {
-        l.onSingleTaskDisplayEmpty(m.arg1);
-    };
-
     private final TaskStackConsumer mNotifyTaskDisplayChanged = (l, m) -> {
         l.onTaskDisplayChanged(m.arg1, m.arg2);
     };
@@ -261,12 +251,6 @@
                 case NOTIFY_BACK_PRESSED_ON_TASK_ROOT:
                     forAllRemoteListeners(mNotifyBackPressedOnTaskRoot, msg);
                     break;
-                case NOTIFY_SINGLE_TASK_DISPLAY_DRAWN:
-                    forAllRemoteListeners(mNotifySingleTaskDisplayDrawn, msg);
-                    break;
-                case NOTIFY_SINGLE_TASK_DISPLAY_EMPTY:
-                    forAllRemoteListeners(mNotifySingleTaskDisplayEmpty, msg);
-                    break;
                 case NOTIFY_TASK_DISPLAY_CHANGED_LISTENERS_MSG:
                     forAllRemoteListeners(mNotifyTaskDisplayChanged, msg);
                     break;
@@ -520,27 +504,6 @@
     }
 
     /**
-     * Notify listeners that contents are drawn for the first time on a single task display.
-     */
-    void notifySingleTaskDisplayDrawn(int displayId) {
-        final Message msg = mHandler.obtainMessage(NOTIFY_SINGLE_TASK_DISPLAY_DRAWN,
-                displayId, 0 /* unused */);
-        forAllLocalListeners(mNotifySingleTaskDisplayDrawn, msg);
-        msg.sendToTarget();
-    }
-
-    /**
-     * Notify listeners that the last task is removed from a single task display.
-     */
-    void notifySingleTaskDisplayEmpty(int displayId) {
-        final Message msg = mHandler.obtainMessage(
-                NOTIFY_SINGLE_TASK_DISPLAY_EMPTY,
-                displayId, 0 /* unused */);
-        forAllLocalListeners(mNotifySingleTaskDisplayEmpty, msg);
-        msg.sendToTarget();
-    }
-
-    /**
      * Notify listeners that a task is reparented to another display.
      */
     void notifyTaskDisplayChanged(int taskId, int newDisplayId) {
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index ce99102..9392666 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -29,10 +29,12 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.app.WindowConfiguration.isSplitScreenWindowingMode;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS;
 import static com.android.server.wm.ActivityTaskManagerService.TAG_STACK;
@@ -150,13 +152,6 @@
     private boolean mRemoved;
 
     /**
-     * Whether the task display area should ignore fixed-orientation request. If {@code true}, it
-     * can never specify orientation, but show the fixed-orientation apps in the letterbox;
-     * otherwise, it rotates based on the fixed-orientation request when it has the focus.
-     */
-    private boolean mIgnoreOrientationRequest;
-
-    /**
      * The id of a leaf task that most recently being moved to front.
      */
     private int mLastLeafTaskToFrontId;
@@ -316,9 +311,6 @@
     @Override
     void addChild(Task task, int position) {
         if (DEBUG_STACK) Slog.d(TAG_WM, "Set task=" + task + " on taskDisplayArea=" + this);
-        if (mDisplayContent.mSingleTaskInstance && getStackCount() == 1) {
-            throw new IllegalStateException("addChild: Can only have one task on display=" + this);
-        }
 
         addStackReferenceIfNeeded(task);
         position = findPositionForStack(position, task, true /* adding */);
@@ -654,28 +646,9 @@
         }
     }
 
-    /**
-     * Sets whether the task display area should ignore fixed-orientation request from apps.
-     *
-     * @return Whether the display orientation changed
-     */
-    boolean setIgnoreOrientationRequest(boolean ignoreOrientationRequest) {
-        if (mIgnoreOrientationRequest == ignoreOrientationRequest) {
-            return false;
-        }
-
-        mIgnoreOrientationRequest = ignoreOrientationRequest;
-        if (isLastFocused()) {
-            // Update orientation if this TDA is the last focused, otherwise it shouldn't affect
-            // the display.
-            return mDisplayContent.updateOrientation();
-        }
-
-        return false;
-    }
-
     @Override
     int getOrientation(int candidate) {
+        mLastOrientationSource = null;
         // Only allow to specify orientation if this TDA is not set to ignore orientation request,
         // and it has the focus.
         if (mIgnoreOrientationRequest || !isLastFocused()) {
@@ -708,7 +681,21 @@
             return SCREEN_ORIENTATION_UNSPECIFIED;
         }
 
-        return super.getOrientation(candidate);
+        final int orientation = super.getOrientation(candidate);
+        if (orientation != SCREEN_ORIENTATION_UNSET
+                && orientation != SCREEN_ORIENTATION_BEHIND) {
+            ProtoLog.v(WM_DEBUG_ORIENTATION,
+                    "App is requesting an orientation, return %d for display id=%d",
+                    orientation, mDisplayContent.mDisplayId);
+            return orientation;
+        }
+
+        ProtoLog.v(WM_DEBUG_ORIENTATION,
+                "No app is requesting an orientation, return %d for display id=%d",
+                mDisplayContent.getLastOrientation(), mDisplayContent.mDisplayId);
+        // The next app has not been requested to be visible, so we keep the current orientation
+        // to prevent freezing/unfreezing the display too early.
+        return mDisplayContent.getLastOrientation();
     }
 
     @Override
@@ -764,7 +751,7 @@
             // The split screen divider anchor is located above the split screen window.
             mTmpLayerForSplitScreenDividerAnchor = layer++;
         }
-        if (s.isTaskAnimating() || s.isAppTransitioning()) {
+        if (s.isAnimatingByRecents() || s.isAppTransitioning()) {
             // The animation layer is located above the highest animating stack and no
             // higher.
             mTmpLayerForAnimationLayer = layer++;
@@ -1030,14 +1017,6 @@
      */
     Task createStack(int windowingMode, int activityType, boolean onTop, ActivityInfo info,
             Intent intent, boolean createdByOrganizer) {
-        if (mDisplayContent.mSingleTaskInstance && getStackCount() > 0) {
-            // Create stack on default display instead since this display can only contain 1 stack.
-            // TODO: Kinda a hack, but better that having the decision at each call point. Hoping
-            // this goes away once ActivityView is no longer using virtual displays.
-            return mRootWindowContainer.getDefaultTaskDisplayArea().createStack(
-                    windowingMode, activityType, onTop, info, intent, createdByOrganizer);
-        }
-
         if (activityType == ACTIVITY_TYPE_UNDEFINED && !createdByOrganizer) {
             // Can't have an undefined stack type yet...so re-map to standard. Anyone that wants
             // anything else should be passing it in anyways...except for the task organizer.
@@ -1289,65 +1268,65 @@
     }
 
     /**
-     * Removes stacks in the input windowing modes from the system if they are of activity type
+     * Removes root tasks in the input windowing modes from the system if they are of activity type
      * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
      */
-    void removeStacksInWindowingModes(int... windowingModes) {
+    void removeRootTasksInWindowingModes(int... windowingModes) {
         if (windowingModes == null || windowingModes.length == 0) {
             return;
         }
 
-        // Collect the stacks that are necessary to be removed instead of performing the removal
-        // by looping mStacks, so that we don't miss any stacks after the stack size changed or
-        // stacks reordered.
-        final ArrayList<Task> stacks = new ArrayList<>();
+        // Collect the root tasks that are necessary to be removed instead of performing the removal
+        // by looping the children, so that we don't miss any root tasks after the children size
+        // changed or reordered.
+        final ArrayList<Task> rootTasks = new ArrayList<>();
         for (int j = windowingModes.length - 1; j >= 0; --j) {
             final int windowingMode = windowingModes[j];
-            for (int i = getStackCount() - 1; i >= 0; --i) {
-                final Task stack = getStackAt(i);
-                if (stack.mCreatedByOrganizer
-                        || !stack.isActivityTypeStandardOrUndefined()
-                        || stack.getWindowingMode() != windowingMode) {
+            for (int i = mChildren.size() - 1; i >= 0; --i) {
+                final Task rootTask = mChildren.get(i);
+                if (rootTask.mCreatedByOrganizer
+                        || !rootTask.isActivityTypeStandardOrUndefined()
+                        || rootTask.getWindowingMode() != windowingMode) {
                     continue;
                 }
-                stacks.add(stack);
+                rootTasks.add(rootTask);
             }
         }
 
-        for (int i = stacks.size() - 1; i >= 0; --i) {
-            mRootWindowContainer.mStackSupervisor.removeStack(stacks.get(i));
+        for (int i = rootTasks.size() - 1; i >= 0; --i) {
+            mRootWindowContainer.mStackSupervisor.removeRootTask(rootTasks.get(i));
         }
     }
 
-    void removeStacksWithActivityTypes(int... activityTypes) {
+    void removeRootTasksWithActivityTypes(int... activityTypes) {
         if (activityTypes == null || activityTypes.length == 0) {
             return;
         }
 
-        // Collect the stacks that are necessary to be removed instead of performing the removal
-        // by looping mStacks, so that we don't miss any stacks after the stack size changed or
-        // stacks reordered.
-        final ArrayList<Task> stacks = new ArrayList<>();
+        // Collect the root tasks that are necessary to be removed instead of performing the removal
+        // by looping the children, so that we don't miss any root tasks after the children size
+        // changed or reordered.
+        final ArrayList<Task> rootTasks = new ArrayList<>();
         for (int j = activityTypes.length - 1; j >= 0; --j) {
             final int activityType = activityTypes[j];
-            for (int i = getStackCount() - 1; i >= 0; --i) {
-                final Task stack = getStackAt(i);
+            for (int i = mChildren.size() - 1; i >= 0; --i) {
+                final Task rootTask = mChildren.get(i);
                 // Collect the root tasks that are currently being organized.
-                if (stack.mCreatedByOrganizer) {
-                    for (int k = stack.getChildCount() - 1; k >= 0; --k) {
-                        final Task childStack = (Task) stack.getChildAt(k);
-                        if (childStack.getActivityType() == activityType) {
-                            stacks.add(childStack);
+                if (rootTask.mCreatedByOrganizer) {
+                    for (int k = rootTask.getChildCount() - 1; k >= 0; --k) {
+                        final Task task = (Task) rootTask.getChildAt(k);
+                        if (task.getActivityType() == activityType) {
+                            rootTasks.add(task);
                         }
                     }
-                } else if (stack.getActivityType() == activityType) {
-                    stacks.add(stack);
+                } else if (rootTask.getActivityType() == activityType) {
+                    rootTasks.add(rootTask);
                 }
             }
         }
 
-        for (int i = stacks.size() - 1; i >= 0; --i) {
-            mRootWindowContainer.mStackSupervisor.removeStack(stacks.get(i));
+        for (int i = rootTasks.size() - 1; i >= 0; --i) {
+            mRootWindowContainer.mStackSupervisor.removeRootTask(rootTasks.get(i));
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 8201d10..6504f00 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -32,6 +32,7 @@
 import android.app.WindowConfiguration;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
+import android.content.pm.ParceledListSlice;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -39,6 +40,7 @@
 import android.view.SurfaceControl;
 import android.window.ITaskOrganizer;
 import android.window.ITaskOrganizerController;
+import android.window.TaskAppearedInfo;
 import android.window.WindowContainerToken;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -76,8 +78,6 @@
             WINDOWING_MODE_FREEFORM
     };
 
-    private final WindowManagerGlobalLock mGlobalLock;
-
     private class DeathRecipient implements IBinder.DeathRecipient {
         ITaskOrganizer mTaskOrganizer;
 
@@ -103,39 +103,38 @@
      * transaction before they are presented to the task org.
      */
     private class TaskOrganizerCallbacks {
-        final WindowManagerService mService;
         final ITaskOrganizer mTaskOrganizer;
         final Consumer<Runnable> mDeferTaskOrgCallbacksConsumer;
 
-        private final SurfaceControl.Transaction mTransaction;
-
-        TaskOrganizerCallbacks(WindowManagerService wm, ITaskOrganizer taskOrg,
+        TaskOrganizerCallbacks(ITaskOrganizer taskOrg,
                 Consumer<Runnable> deferTaskOrgCallbacksConsumer) {
-            mService = wm;
             mDeferTaskOrgCallbacksConsumer = deferTaskOrgCallbacksConsumer;
             mTaskOrganizer = taskOrg;
-            mTransaction = wm.mTransactionFactory.get();
         }
 
         IBinder getBinder() {
             return mTaskOrganizer.asBinder();
         }
 
+        SurfaceControl prepareLeash(Task task, boolean visible, String reason) {
+            SurfaceControl outSurfaceControl = new SurfaceControl(task.getSurfaceControl(), reason);
+            if (!task.mCreatedByOrganizer && !visible) {
+                // To prevent flashes, we hide the task prior to sending the leash to the
+                // task org if the task has previously hidden (ie. when entering PIP)
+                mTransaction.hide(outSurfaceControl);
+                mTransaction.apply();
+            }
+            return outSurfaceControl;
+        }
+
         void onTaskAppeared(Task task) {
             ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task appeared taskId=%d", task.mTaskId);
             final boolean visible = task.isVisible();
             final RunningTaskInfo taskInfo = task.getTaskInfo();
             mDeferTaskOrgCallbacksConsumer.accept(() -> {
                 try {
-                    SurfaceControl outSurfaceControl = new SurfaceControl(task.getSurfaceControl(),
-                            "TaskOrganizerController.onTaskAppeared");
-                    if (!task.mCreatedByOrganizer && !visible) {
-                        // To prevent flashes, we hide the task prior to sending the leash to the
-                        // task org if the task has previously hidden (ie. when entering PIP)
-                        mTransaction.hide(outSurfaceControl);
-                        mTransaction.apply();
-                    }
-                    mTaskOrganizer.onTaskAppeared(taskInfo, outSurfaceControl);
+                    mTaskOrganizer.onTaskAppeared(taskInfo, prepareLeash(task, visible,
+                            "TaskOrganizerController.onTaskAppeared"));
                 } catch (RemoteException e) {
                     Slog.e(TAG, "Exception sending onTaskAppeared callback", e);
                 }
@@ -162,17 +161,18 @@
                 return;
             }
             ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task info changed taskId=%d", task.mTaskId);
-            mDeferTaskOrgCallbacksConsumer.accept(() -> {
-                if (!task.isOrganized()) {
-                    // This is safe to ignore if the task is no longer organized
-                    return;
-                }
-                try {
-                    mTaskOrganizer.onTaskInfoChanged(taskInfo);
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Exception sending onTaskInfoChanged callback", e);
-                }
-            });
+            if (!task.isOrganized()) {
+                // This is safe to ignore if the task is no longer organized
+                return;
+            }
+            try {
+                // Purposely notify of task info change immediately instead of deferring (like
+                // appear and vanish) to allow info changes (such as new PIP params) to flow
+                // without waiting.
+                mTaskOrganizer.onTaskInfoChanged(taskInfo);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Exception sending onTaskInfoChanged callback", e);
+            }
         }
 
         void onBackPressedOnTaskRoot(Task task) {
@@ -183,17 +183,15 @@
                 // by the organizer that don't receive that signal
                 return;
             }
-            mDeferTaskOrgCallbacksConsumer.accept(() -> {
-                if (!task.isOrganized()) {
-                    // This is safe to ignore if the task is no longer organized
-                    return;
-                }
-                try {
-                   mTaskOrganizer.onBackPressedOnTaskRoot(task.getTaskInfo());
-                } catch (Exception e) {
-                    Slog.e(TAG, "Exception sending onBackPressedOnTaskRoot callback", e);
-                }
-            });
+            if (!task.isOrganized()) {
+                // This is safe to ignore if the task is no longer organized
+                return;
+            }
+            try {
+                mTaskOrganizer.onBackPressedOnTaskRoot(task.getTaskInfo());
+            } catch (Exception e) {
+                Slog.e(TAG, "Exception sending onBackPressedOnTaskRoot callback", e);
+            }
         }
     }
 
@@ -208,8 +206,7 @@
                     mDeferTaskOrgCallbacksConsumer != null
                             ? mDeferTaskOrgCallbacksConsumer
                             : mService.mWindowManager.mAnimator::addAfterPrepareSurfacesRunnable;
-            mOrganizer = new TaskOrganizerCallbacks(mService.mWindowManager, organizer,
-                    deferTaskOrgCallbacksConsumer);
+            mOrganizer = new TaskOrganizerCallbacks(organizer, deferTaskOrgCallbacksConsumer);
             mDeathRecipient = new DeathRecipient(organizer);
             try {
                 organizer.asBinder().linkToDeath(mDeathRecipient, 0);
@@ -219,6 +216,18 @@
             mUid = uid;
         }
 
+        /**
+         * Register this task with this state, but doesn't trigger the task appeared callback to
+         * the organizer.
+         */
+        SurfaceControl addTaskWithoutCallback(Task t, String reason) {
+            t.mTaskAppearedSent = true;
+            if (!mOrganizedTasks.contains(t)) {
+                mOrganizedTasks.add(t);
+            }
+            return mOrganizer.prepareLeash(t, t.isVisible(), reason);
+        }
+
         void addTask(Task t) {
             if (t.mTaskAppearedSent) return;
 
@@ -265,6 +274,9 @@
         }
     }
 
+    private final ActivityTaskManagerService mService;
+    private final WindowManagerGlobalLock mGlobalLock;
+
     // List of task organizers by priority
     private final LinkedList<ITaskOrganizer> mTaskOrganizers = new LinkedList<>();
     private final HashMap<IBinder, TaskOrganizerState> mTaskOrganizerStates = new HashMap<>();
@@ -273,8 +285,7 @@
     // Set of organized tasks (by taskId) that dispatch back pressed to their organizers
     private final HashSet<Integer> mInterceptBackPressedOnRootTasks = new HashSet();
 
-    private final ActivityTaskManagerService mService;
-
+    private SurfaceControl.Transaction mTransaction;
     private RunningTaskInfo mTmpTaskInfo;
     private Consumer<Runnable> mDeferTaskOrgCallbacksConsumer;
 
@@ -299,7 +310,7 @@
      * Register a TaskOrganizer to manage tasks as they enter the a supported windowing mode.
      */
     @Override
-    public void registerTaskOrganizer(ITaskOrganizer organizer) {
+    public ParceledListSlice<TaskAppearedInfo> registerTaskOrganizer(ITaskOrganizer organizer) {
         enforceStackPermission("registerTaskOrganizer()");
         final int uid = Binder.getCallingUid();
         final long origId = Binder.clearCallingIdentity();
@@ -307,17 +318,36 @@
             synchronized (mGlobalLock) {
                 ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Register task organizer=%s uid=%d",
                         organizer.asBinder(), uid);
+
+                // Defer initializing the transaction since the transaction factory can be set up
+                // by the tests after construction of the controller
+                if (mTransaction == null) {
+                    mTransaction = mService.mWindowManager.mTransactionFactory.get();
+                }
+
                 if (!mTaskOrganizerStates.containsKey(organizer.asBinder())) {
                     mTaskOrganizers.add(organizer);
                     mTaskOrganizerStates.put(organizer.asBinder(),
                             new TaskOrganizerState(organizer, uid));
                 }
+
+                final ArrayList<TaskAppearedInfo> taskInfos = new ArrayList<>();
+                final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder());
                 mService.mRootWindowContainer.forAllTasks((task) -> {
                     if (ArrayUtils.contains(UNSUPPORTED_WINDOWING_MODES, task.getWindowingMode())) {
                         return;
                     }
-                    task.updateTaskOrganizerState(true /* forceUpdate */);
+
+                    boolean returnTask = !task.mCreatedByOrganizer;
+                    task.updateTaskOrganizerState(true /* forceUpdate */,
+                            returnTask /* skipTaskAppeared */);
+                    if (returnTask) {
+                        SurfaceControl outSurfaceControl = state.addTaskWithoutCallback(task,
+                                "TaskOrganizerController.registerTaskOrganizer");
+                        taskInfos.add(new TaskAppearedInfo(task.getTaskInfo(), outSurfaceControl));
+                    }
                 });
+                return new ParceledListSlice<>(taskInfos);
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index bc4f752..3ce04af 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -157,7 +157,6 @@
         if (shouldDisableSnapshots()) {
             return;
         }
-
         // We need to take a snapshot of the task if and only if all activities of the task are
         // either closing or hidden.
         getClosingTasks(closingApps, mTmpTasks);
@@ -368,7 +367,8 @@
 
         SurfaceControl[] excludeLayers;
         final WindowState imeWindow = task.getDisplayContent().mInputMethodWindow;
-        if (imeWindow != null) {
+        // Exclude IME window snapshot when IME isn't proper to attach to app.
+        if (imeWindow != null && !task.getDisplayContent().isImeAttachedToApp()) {
             excludeLayers = new SurfaceControl[1];
             excludeLayers[0] = imeWindow.getSurfaceControl();
         } else {
@@ -444,10 +444,17 @@
         for (int i = closingApps.size() - 1; i >= 0; i--) {
             final ActivityRecord activity = closingApps.valueAt(i);
             final Task task = activity.getTask();
+            if (task == null) continue;
 
+            // Since RecentsAnimation will handle task snapshot while switching apps with the
+            // best capture timing (e.g. IME window capture),
+            // No need additional task capture while task is controlled by RecentsAnimation.
+            if (task.isAnimatingByRecents()) {
+                mSkipClosingAppSnapshotTasks.add(task);
+            }
             // If the task of the app is not visible anymore, it means no other app in that task
             // is opening. Thus, the task is closing.
-            if (task != null && !task.isVisible() && !mSkipClosingAppSnapshotTasks.contains(task)) {
+            if (!task.isVisible() && !mSkipClosingAppSnapshotTasks.contains(task)) {
                 outClosingTasks.add(task);
             }
         }
@@ -481,8 +488,7 @@
         final int color = ColorUtils.setAlphaComponent(
                 task.getTaskDescription().getBackgroundColor(), 255);
         final LayoutParams attrs = mainWindow.getAttrs();
-        final InsetsState insetsState = new InsetsState(mainWindow.getInsetsState());
-        mergeInsetsSources(insetsState, mainWindow.getRequestedInsetsState());
+        final InsetsState insetsState = getInsetsStateWithVisibilityOverride(mainWindow);
         final Rect systemBarInsets = getSystemBarInsets(mainWindow.getFrame(), insetsState);
         final SystemBarBackgroundPainter decorPainter = new SystemBarBackgroundPainter(attrs.flags,
                 attrs.privateFlags, attrs.insetsFlags.appearance, task.getTaskDescription(),
@@ -571,7 +577,10 @@
                 synchronized (mService.mGlobalLock) {
                     mTmpTasks.clear();
                     mService.mRoot.forAllTasks(task -> {
-                        if (task.isVisible()) {
+                        // Since RecentsAnimation will handle task snapshot while switching apps
+                        // with the best capture timing (e.g. IME window capture), No need
+                        // additional task capture while task is controlled by RecentsAnimation.
+                        if (task.isVisible() && !task.isAnimatingByRecents()) {
                             mTmpTasks.add(task);
                         }
                     });
@@ -602,13 +611,18 @@
         return 0;
     }
 
-    static void mergeInsetsSources(InsetsState base, InsetsState other) {
+    static InsetsState getInsetsStateWithVisibilityOverride(WindowState win) {
+        final InsetsState state = new InsetsState(win.getInsetsState());
         for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) {
-            final InsetsSource source = other.peekSource(type);
-            if (source != null) {
-                base.addSource(source);
+            final boolean requestedVisible = win.getRequestedVisibility(type);
+            InsetsSource source = state.peekSource(type);
+            if (source != null && source.isVisible() != requestedVisible) {
+                source = new InsetsSource(source);
+                source.setVisible(requestedVisible);
+                state.addSource(source);
             }
         }
+        return state;
     }
 
     static Rect getSystemBarInsets(Rect frame, InsetsState state) {
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 6904740..aab5da6 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -42,8 +42,8 @@
 import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES;
 import static com.android.internal.policy.DecorView.getNavigationBarRect;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW;
+import static com.android.server.wm.TaskSnapshotController.getInsetsStateWithVisibilityOverride;
 import static com.android.server.wm.TaskSnapshotController.getSystemBarInsets;
-import static com.android.server.wm.TaskSnapshotController.mergeInsetsSources;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
@@ -122,7 +122,6 @@
 
     //tmp vars for unused relayout params
     private static final Point sTmpSurfaceSize = new Point();
-    private static final SurfaceControl sTmpSurfaceControl = new SurfaceControl();
 
     private final Window mWindow;
     private final Surface mSurface;
@@ -237,14 +236,14 @@
             task.getBounds(taskBounds);
             currentOrientation = topFullscreenOpaqueWindow.getConfiguration().orientation;
             activityType = activity.getActivityType();
-            insetsState = new InsetsState(topFullscreenOpaqueWindow.getInsetsState());
-            mergeInsetsSources(insetsState, topFullscreenOpaqueWindow.getRequestedInsetsState());
+            insetsState = getInsetsStateWithVisibilityOverride(topFullscreenOpaqueWindow);
+
         }
         try {
             final int res = session.addToDisplay(window, layoutParams,
-                    View.GONE, activity.getDisplayContent().getDisplayId(), tmpFrames.frame,
-                    tmpFrames.contentInsets, tmpFrames.stableInsets, tmpFrames.displayCutout,
-                    null /* outInputChannel */, mTmpInsetsState, mTempControls);
+                    View.GONE, activity.getDisplayContent().getDisplayId(), mTmpInsetsState,
+                    tmpFrames.frame, tmpFrames.displayCutout, null /* outInputChannel */,
+                    mTmpInsetsState, mTempControls);
             if (res < 0) {
                 Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
                 return null;
@@ -260,7 +259,7 @@
         try {
             session.relayout(window, layoutParams, -1, -1, View.VISIBLE, 0, -1,
                     tmpFrames, tmpMergedConfiguration, surfaceControl, mTmpInsetsState,
-                    mTempControls, sTmpSurfaceSize, sTmpSurfaceControl);
+                    mTempControls, sTmpSurfaceSize);
         } catch (RemoteException e) {
             // Local call.
         }
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index fc67cd2..ac86698 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 
+import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
@@ -31,7 +32,6 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Slog;
-import android.view.Display;
 import android.view.SurfaceControl;
 import android.view.WindowManager;
 import android.view.animation.Animation;
@@ -43,7 +43,6 @@
 
 import java.util.ArrayList;
 import java.util.Map;
-import java.util.Set;
 
 /**
  * Represents a logical transition.
@@ -70,6 +69,7 @@
     private int mSyncId;
     private @WindowManager.TransitionFlags int mFlags;
     private final TransitionController mController;
+    private final BLASTSyncEngine mSyncEngine;
     final ArrayMap<WindowContainer, ChangeInfo> mParticipants = new ArrayMap<>();
     private int mState = STATE_COLLECTING;
     private boolean mReadyCalled = false;
@@ -79,7 +79,8 @@
         mType = type;
         mFlags = flags;
         mController = controller;
-        mSyncId = mController.mSyncEngine.startSyncSet(this);
+        mSyncEngine = mController.mAtm.mWindowManager.mSyncEngine;
+        mSyncId = mSyncEngine.startSyncSet(this);
     }
 
     /**
@@ -104,10 +105,8 @@
         if (mSyncId < 0) return;
         ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Collecting in transition %d: %s",
                 mSyncId, wc);
-        // Add to sync set before checking contains because it may not have added it at other
-        // times (eg. if wc was previously invisible).
-        mController.mSyncEngine.addToSyncSet(mSyncId, wc);
         if (mParticipants.containsKey(wc)) return;
+        mSyncEngine.addToSyncSet(mSyncId, wc);
         mParticipants.put(wc, new ChangeInfo());
     }
 
@@ -125,8 +124,7 @@
         }
         ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
                 "Finish collecting in transition %d", mSyncId);
-        mController.mSyncEngine.setReady(mSyncId);
-        mController.mAtm.mWindowManager.mWindowPlacerLocked.requestTraversal();
+        mSyncEngine.setReady(mSyncId);
     }
 
     /** The transition has finished animating and is ready to finalize WM state */
@@ -146,7 +144,7 @@
     }
 
     @Override
-    public void onTransactionReady(int syncId, Set<WindowContainer> windowContainersReady) {
+    public void onTransactionReady(int syncId, SurfaceControl.Transaction transaction) {
         if (syncId != mSyncId) {
             Slog.e(TAG, "Unexpected Sync ID " + syncId + ". Expected " + mSyncId);
             return;
@@ -155,10 +153,8 @@
         mController.moveToPlaying(this);
         final TransitionInfo info = calculateTransitionInfo(mType, mParticipants);
 
-        SurfaceControl.Transaction mergedTransaction = new SurfaceControl.Transaction();
-        int displayId = Display.DEFAULT_DISPLAY;
-        for (WindowContainer container : windowContainersReady) {
-            container.mergeBlastSyncTransaction(mergedTransaction);
+        int displayId = DEFAULT_DISPLAY;
+        for (WindowContainer container : mParticipants.keySet()) {
             displayId = container.mDisplayContent.getDisplayId();
         }
 
@@ -168,14 +164,14 @@
             try {
                 ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
                         "Calling onTransitionReady: %s", info);
-                mController.getTransitionPlayer().onTransitionReady(this, info, mergedTransaction);
+                mController.getTransitionPlayer().onTransitionReady(this, info, transaction);
             } catch (RemoteException e) {
                 // If there's an exception when trying to send the mergedTransaction to the
                 // client, we should immediately apply it here so the transactions aren't lost.
-                mergedTransaction.apply();
+                transaction.apply();
             }
         } else {
-            mergedTransaction.apply();
+            transaction.apply();
         }
         mSyncId = -1;
     }
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index d102c19..7cdc177 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -51,7 +51,6 @@
         Arrays.sort(SUPPORTED_LEGACY_TRANSIT_TYPES);
     }
 
-    final BLASTSyncEngine mSyncEngine = new BLASTSyncEngine();
     private ITransitionPlayer mTransitionPlayer;
     private final IBinder.DeathRecipient mTransitionPlayerDeath = () -> mTransitionPlayer = null;
     final ActivityTaskManagerService mAtm;
diff --git a/services/core/java/com/android/server/wm/VrController.java b/services/core/java/com/android/server/wm/VrController.java
index 54cac38..9e159ab 100644
--- a/services/core/java/com/android/server/wm/VrController.java
+++ b/services/core/java/com/android/server/wm/VrController.java
@@ -101,7 +101,7 @@
     //      - Calls to setPersistentVrThread will fail.
     //      - No threads will have elevated scheduling priority for VR.
     //
-    private int mVrState = FLAG_NON_VR_MODE;
+    private volatile int mVrState = FLAG_NON_VR_MODE;
 
     // The single VR render thread on the device that is given elevated scheduling priority.
     private int mVrRenderThreadTid = 0;
@@ -146,6 +146,14 @@
     }
 
     /**
+     * Called without lock to determine whether to call {@link #onTopProcChangedLocked} in lock. It
+     * is used to optimize performance for the path that may have lock contention frequently.
+     */
+    boolean isInterestingToSchedGroup() {
+        return (mVrState & (FLAG_VR_MODE | FLAG_PERSISTENT_VR_MODE)) != 0;
+    }
+
+    /**
      * Called when ActivityManagerService's TOP_APP process has changed.
      *
      * <p>Note: This must be called with the global ActivityManagerService lock held.
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 38ec924..da3a928 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -58,6 +58,7 @@
 
 import android.annotation.CallSuper;
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.WindowConfiguration;
 import android.content.pm.ActivityInfo;
@@ -96,7 +97,6 @@
 import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.LinkedList;
-import java.util.Set;
 import java.util.function.BiFunction;
 import java.util.function.Consumer;
 import java.util.function.Function;
@@ -109,8 +109,7 @@
  * changes are made to this class.
  */
 class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E>
-        implements Comparable<WindowContainer>, Animatable, SurfaceFreezer.Freezable,
-                   BLASTSyncEngine.TransactionReadyListener {
+        implements Comparable<WindowContainer>, Animatable, SurfaceFreezer.Freezable {
 
     private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowContainer" : TAG_WM;
 
@@ -290,16 +289,35 @@
      */
     RemoteToken mRemoteToken = null;
 
-    BLASTSyncEngine mBLASTSyncEngine = new BLASTSyncEngine();
-    SurfaceControl.Transaction mBLASTSyncTransaction;
-    boolean mUsingBLASTSyncTransaction = false;
-    BLASTSyncEngine.TransactionReadyListener mWaitingListener;
-    int mWaitingSyncId;
+    /** This isn't participating in a sync. */
+    public static final int SYNC_STATE_NONE = 0;
+
+    /** This is currently waiting for itself to finish drawing. */
+    public static final int SYNC_STATE_WAITING_FOR_DRAW = 1;
+
+    /** This container is ready, but it might still have unfinished children. */
+    public static final int SYNC_STATE_READY = 2;
+
+    @IntDef(prefix = { "SYNC_STATE_" }, value = {
+            SYNC_STATE_NONE,
+            SYNC_STATE_WAITING_FOR_DRAW,
+            SYNC_STATE_READY,
+    })
+    @interface SyncState {}
+
+    /**
+     * If non-null, references the sync-group directly waiting on this container. Otherwise, this
+     * container is only being waited-on by its parents (if in a sync-group). This has implications
+     * on how this container is handled during parent changes.
+     */
+    BLASTSyncEngine.SyncGroup mSyncGroup = null;
+    final SurfaceControl.Transaction mSyncTransaction;
+    @SyncState int mSyncState = SYNC_STATE_NONE;
 
     WindowContainer(WindowManagerService wms) {
         mWmService = wms;
         mPendingTransaction = wms.mTransactionFactory.get();
-        mBLASTSyncTransaction = wms.mTransactionFactory.get();
+        mSyncTransaction = wms.mTransactionFactory.get();
         mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, wms);
         mSurfaceFreezer = new SurfaceFreezer(this, wms);
     }
@@ -357,6 +375,7 @@
         // Send onParentChanged notification here is we disabled sending it in setParent for
         // reparenting case.
         onParentChanged(newParent, oldParent);
+        onSyncReparent(oldParent, newParent);
     }
 
     final protected void setParent(WindowContainer<WindowContainer> parent) {
@@ -372,6 +391,7 @@
                 onDisplayChanged(mParent.mDisplayContent);
             }
             onParentChanged(mParent, oldParent);
+            onSyncReparent(oldParent, mParent);
         }
     }
 
@@ -612,6 +632,8 @@
             scheduleAnimation();
         }
 
+        // This must happen after updating the surface so that sync transactions can be handled
+        // properly.
         if (mParent != null) {
             mParent.removeChild(this);
         }
@@ -861,13 +883,6 @@
         }
     }
 
-    void forceWindowsScaleableInTransaction(boolean force) {
-        for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowContainer wc = mChildren.get(i);
-            wc.forceWindowsScaleableInTransaction(force);
-        }
-    }
-
     /**
      * @return {@code true} when an application can override an app transition animation on this
      * container.
@@ -2254,8 +2269,8 @@
      * {@link #getPendingTransaction()}
      */
     public Transaction getSyncTransaction() {
-        if (mUsingBLASTSyncTransaction) {
-            return mBLASTSyncTransaction;
+        if (mSyncState != SYNC_STATE_NONE) {
+            return mSyncTransaction;
         }
 
         return getPendingTransaction();
@@ -2744,8 +2759,9 @@
             pw.print(prefix); pw.println("ContainerAnimator:");
             mSurfaceAnimator.dump(pw, prefix + "  ");
         }
-        if (mLastOrientationSource != null) {
+        if (mLastOrientationSource != null && this == mDisplayContent) {
             pw.println(prefix + "mLastOrientationSource=" + mLastOrientationSource);
+            pw.println(prefix + "deepestLastOrientationSource=" + getLastOrientationSource());
         }
     }
 
@@ -2901,69 +2917,140 @@
         }
     }
 
-    @Override
-    public void onTransactionReady(int mSyncId, Set<WindowContainer> windowContainersReady) {
-        if (mWaitingListener == null) {
-            return;
-        }
-
-        windowContainersReady.add(this);
-        mWaitingListener.onTransactionReady(mWaitingSyncId, windowContainersReady);
-
-        mWaitingListener = null;
-        mWaitingSyncId = -1;
-    }
-
     /**
-     * Returns true if any of the children elected to participate in the Sync
+     * Call this when this container finishes drawing content.
+     *
+     * @return {@code true} if consumed (this container is part of a sync group).
      */
-    boolean addChildrenToSyncSet(int localId) {
-        boolean willSync = false;
-
-        for (int i = 0; i < mChildren.size(); i++) {
-            final WindowContainer child = mChildren.get(i);
-            willSync |= mBLASTSyncEngine.addToSyncSet(localId, child);
-        }
-        return willSync;
-    }
-
-    boolean setPendingListener(BLASTSyncEngine.TransactionReadyListener waitingListener,
-        int waitingId) {
-        // If we are invisible, no need to sync, likewise if we are already engaged in a sync,
-        // we can't support overlapping syncs on a single container yet.
-        if (!isVisible() || mWaitingListener != null) {
-            ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "- NOT adding to sync: visible=%b "
-                            + "hasListener=%b", isVisible(), mWaitingListener != null);
-            return false;
-        }
-        mUsingBLASTSyncTransaction = true;
-
-        // Make sure to set these before we call setReady in case the sync was a no-op
-        mWaitingSyncId = waitingId;
-        mWaitingListener = waitingListener;
+    boolean onSyncFinishedDrawing() {
+        if (mSyncState == SYNC_STATE_NONE) return false;
+        mSyncState = SYNC_STATE_READY;
+        mWmService.mWindowPlacerLocked.requestTraversal();
+        ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "onSyncFinishedDrawing %s", this);
         return true;
     }
 
-    boolean prepareForSync(BLASTSyncEngine.TransactionReadyListener waitingListener,
-            int waitingId) {
-        boolean willSync = setPendingListener(waitingListener, waitingId);
-        if (!willSync) {
+    void setSyncGroup(@NonNull BLASTSyncEngine.SyncGroup group) {
+        ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "setSyncGroup #%d on %s", group.mSyncId, this);
+        if (group != null) {
+            if (mSyncGroup != null && mSyncGroup != group) {
+                throw new IllegalStateException("Can't sync on 2 engines simultaneously");
+            }
+        }
+        mSyncGroup = group;
+    }
+
+    /**
+     * Prepares this container for participation in a sync-group. This includes preparing all its
+     * children.
+     *
+     * @return {@code true} if something changed (eg. this wasn't already in the sync group).
+     */
+    boolean prepareSync() {
+        if (mSyncState != SYNC_STATE_NONE) {
+            // Already part of sync
             return false;
         }
-
-        int localId = mBLASTSyncEngine.startSyncSet(this);
-        willSync |= addChildrenToSyncSet(localId);
-        mBLASTSyncEngine.setReady(localId);
-
-        return willSync;
+        for (int i = getChildCount() - 1; i >= 0; --i) {
+            final WindowContainer child = getChildAt(i);
+            child.prepareSync();
+        }
+        mSyncState = SYNC_STATE_READY;
+        return true;
     }
 
     boolean useBLASTSync() {
-        return mUsingBLASTSyncTransaction;
+        return mSyncState != SYNC_STATE_NONE;
     }
 
-    void mergeBlastSyncTransaction(Transaction t) {
-        t.merge(mBLASTSyncTransaction);
-        mUsingBLASTSyncTransaction = false;
+    /**
+     * Recursively finishes/cleans-up sync state of this subtree and collects all the sync
+     * transactions into `outMergedTransaction`.
+     * @param outMergedTransaction A transaction to merge all the recorded sync operations into.
+     * @param cancel If true, this is being finished because it is leaving the sync group rather
+     *               than due to the sync group completing.
+     */
+    void finishSync(Transaction outMergedTransaction, boolean cancel) {
+        if (mSyncState == SYNC_STATE_NONE) return;
+        ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "finishSync cancel=%b for %s", cancel, this);
+        outMergedTransaction.merge(mSyncTransaction);
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            mChildren.get(i).finishSync(outMergedTransaction, cancel);
+        }
+        mSyncState = SYNC_STATE_NONE;
+        if (cancel && mSyncGroup != null) mSyncGroup.onCancelSync(this);
+        mSyncGroup = null;
+    }
+
+    /**
+     * Checks if the subtree rooted at this container is finished syncing (everything is ready or
+     * not visible). NOTE, this is not const: it will cancel/prepare itself depending on its state
+     * in the hierarchy.
+     *
+     * @return {@code true} if this subtree is finished waiting for sync participants.
+     */
+    boolean isSyncFinished() {
+        if (!isVisibleRequested()) {
+            return true;
+        }
+        if (mSyncState == SYNC_STATE_NONE) {
+            prepareSync();
+        }
+        if (mSyncState == SYNC_STATE_WAITING_FOR_DRAW) {
+            return false;
+        }
+        // READY
+        // Loop from top-down.
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer child = mChildren.get(i);
+            final boolean childFinished = child.isSyncFinished();
+            if (childFinished && child.isVisibleRequested() && child.fillsParent()) {
+                // Any lower children will be covered-up, so we can consider this finished.
+                return true;
+            }
+            if (!childFinished) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Called during reparent to handle sync state when the hierarchy changes.
+     * If this is in a sync group and gets reparented out, it will cancel syncing.
+     * If this is not in a sync group and gets parented into one, it will prepare itself.
+     * If its moving around within a sync-group, it needs to restart its syncing since a
+     * hierarchy change implies a configuration change.
+     */
+    private void onSyncReparent(WindowContainer oldParent, WindowContainer newParent) {
+        if (newParent == null || newParent.mSyncState == SYNC_STATE_NONE) {
+            if (mSyncState == SYNC_STATE_NONE) {
+                return;
+            }
+            if (newParent == null) {
+                // This is getting removed.
+                if (oldParent.mSyncState != SYNC_STATE_NONE) {
+                    // In order to keep the transaction in sync, merge it into the parent.
+                    finishSync(oldParent.mSyncTransaction, true /* cancel */);
+                } else if (mSyncGroup != null) {
+                    // This is watched directly by the sync-group, so merge this transaction into
+                    // into the sync-group so it isn't lost
+                    finishSync(mSyncGroup.getOrphanTransaction(), true /* cancel */);
+                } else {
+                    throw new IllegalStateException("This container is in sync mode without a sync"
+                            + " group: " + this);
+                }
+                return;
+            } else if (mSyncGroup == null) {
+                // This is being reparented out of the sync-group. To prevent ordering issues on
+                // this container, immediately apply/cancel sync on it.
+                finishSync(getPendingTransaction(), true /* cancel */);
+                return;
+            }
+            // Otherwise this is the "root" of a synced subtree, so continue on to preparation.
+        }
+        // This container's situation has changed so we need to restart its sync.
+        mSyncState = SYNC_STATE_NONE;
+        prepareSync();
     }
 }
diff --git a/services/core/java/com/android/server/wm/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java
index d96b645..259dee4 100644
--- a/services/core/java/com/android/server/wm/WindowFrames.java
+++ b/services/core/java/com/android/server/wm/WindowFrames.java
@@ -207,28 +207,6 @@
         return (mLastFrame.width() != mFrame.width()) || (mLastFrame.height() != mFrame.height());
     }
 
-    // TODO(b/118118435): Remove after migration.
-    /**
-     * Calculate the insets for the type
-     * {@link android.view.WindowManager.LayoutParams#TYPE_DOCK_DIVIDER}
-     *
-     * @param cutoutInsets The insets for the cutout.
-     */
-    void calculateDockedDividerInsets(Rect cutoutInsets) {
-        // For the docked divider, we calculate the stable insets like a full-screen window
-        // so it can use it to calculate the snap positions.
-        mTmpRect.set(mDisplayFrame);
-        mTmpRect.inset(cutoutInsets);
-        mTmpRect.intersectUnchecked(mStableFrame);
-        InsetUtils.insetsBetweenFrames(mDisplayFrame, mTmpRect, mStableInsets);
-
-        // The divider doesn't care about insets in any case, so set it to empty so we don't
-        // trigger a relayout when moving it.
-        mContentInsets.setEmpty();
-        mVisibleInsets.setEmpty();
-        mDisplayCutout = WmDisplayCutout.NO_CUTOUT;
-    }
-
     /**
      * Calculate the insets for a window.
      *
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 315014c..66cb674 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -597,4 +597,9 @@
      * @return The corresponding {@link WindowState#getName()}
      */
     public abstract @Nullable String getImeTargetNameForLogging(int displayId);
+
+    /**
+     * Moves the {@link WindowToken} {@code binder} to the display specified by {@code displayId}.
+     */
+    public abstract void moveWindowTokenToDisplay(IBinder binder, int displayId);
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 19cfcb2..66f2e4d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -120,11 +120,10 @@
 import static com.android.server.wm.WindowManagerServiceDumpProto.FOCUSED_APP;
 import static com.android.server.wm.WindowManagerServiceDumpProto.FOCUSED_DISPLAY_ID;
 import static com.android.server.wm.WindowManagerServiceDumpProto.FOCUSED_WINDOW;
+import static com.android.server.wm.WindowManagerServiceDumpProto.HARD_KEYBOARD_AVAILABLE;
 import static com.android.server.wm.WindowManagerServiceDumpProto.INPUT_METHOD_WINDOW;
-import static com.android.server.wm.WindowManagerServiceDumpProto.LAST_ORIENTATION;
 import static com.android.server.wm.WindowManagerServiceDumpProto.POLICY;
 import static com.android.server.wm.WindowManagerServiceDumpProto.ROOT_WINDOW_CONTAINER;
-import static com.android.server.wm.WindowManagerServiceDumpProto.ROTATION;
 
 import android.Manifest;
 import android.Manifest.permission;
@@ -226,7 +225,7 @@
 import android.view.IPinnedStackListener;
 import android.view.IRecentsAnimationRunner;
 import android.view.IRotationWatcher;
-import android.view.IScrollCaptureController;
+import android.view.IScrollCaptureCallbacks;
 import android.view.ISystemGestureExclusionListener;
 import android.view.IWallpaperVisibilityListener;
 import android.view.IWindow;
@@ -668,6 +667,8 @@
     // Whether to enable BLASTSyncEngine Transaction passing.
     final boolean mUseBLASTSync = false;
 
+    final BLASTSyncEngine mSyncEngine;
+
     int mDockedStackCreateMode = SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
     Rect mDockedStackCreateBounds;
 
@@ -1192,6 +1193,8 @@
                     DeviceConfig.NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT,
                     WM_USE_BLAST_ADAPTER_FLAG, false);
 
+        mSyncEngine = new BLASTSyncEngine(this);
+
         mWindowPlacerLocked = new WindowSurfacePlacer(this);
         mTaskSnapshotController = new TaskSnapshotController(this);
 
@@ -1376,10 +1379,9 @@
     }
 
     public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
-            int displayId, Rect outFrame, Rect outContentInsets, Rect outStableInsets,
+            int displayId, int requestUserId, InsetsState requestedVisibility, Rect outFrame,
             DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
-            InsetsState outInsetsState, InsetsSourceControl[] outActiveControls,
-            int requestUserId) {
+            InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {
         Arrays.fill(outActiveControls, null);
         int[] appOp = new int[1];
         final boolean isRoundedCornerOverlay = (attrs.privateFlags
@@ -1574,6 +1576,7 @@
 
             final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
             displayPolicy.adjustWindowParamsLw(win, win.mAttrs, callingPid, callingUid);
+            win.updateRequestedVisibility(requestedVisibility);
 
             res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid);
             if (res != WindowManagerGlobal.ADD_OKAY) {
@@ -1691,11 +1694,10 @@
                 prepareNoneTransitionForRelaunching(activity);
             }
 
-            if (displayPolicy.getLayoutHint(win.mAttrs, token, outFrame, outContentInsets,
-                    outStableInsets, outDisplayCutout)) {
+            if (displayPolicy.getLayoutHint(win.mAttrs, token, outFrame, outDisplayCutout,
+                    outInsetsState, win.isClientLocal())) {
                 res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS;
             }
-            outInsetsState.set(win.getInsetsState(), win.isClientLocal());
 
             if (mInTouchMode) {
                 res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;
@@ -2013,7 +2015,7 @@
     }
 
     void setTransparentRegionWindow(Session session, IWindow client, Region region) {
-        long origId = Binder.clearCallingIdentity();
+        final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
                 WindowState w = windowForClientLocked(session, client, false);
@@ -2031,7 +2033,7 @@
 
     void setInsetsWindow(Session session, IWindow client, int touchableInsets, Rect contentInsets,
             Rect visibleInsets, Region touchableRegion) {
-        long origId = Binder.clearCallingIdentity();
+        final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
                 WindowState w = windowForClientLocked(session, client, false);
@@ -2103,14 +2105,13 @@
             int requestedWidth, int requestedHeight, int viewVisibility, int flags,
             long frameNumber, ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
             SurfaceControl outSurfaceControl, InsetsState outInsetsState,
-            InsetsSourceControl[] outActiveControls, Point outSurfaceSize,
-            SurfaceControl outBLASTSurfaceControl) {
+            InsetsSourceControl[] outActiveControls, Point outSurfaceSize) {
         Arrays.fill(outActiveControls, null);
         int result = 0;
         boolean configChanged;
         final int pid = Binder.getCallingPid();
         final int uid = Binder.getCallingUid();
-        long origId = Binder.clearCallingIdentity();
+        final long origId = Binder.clearCallingIdentity();
         synchronized (mGlobalLock) {
             final WindowState win = windowForClientLocked(session, client, false);
             if (win == null) {
@@ -2279,8 +2280,7 @@
                 result = win.relayoutVisibleWindow(result, attrChanges);
 
                 try {
-                    result = createSurfaceControl(outSurfaceControl, outBLASTSurfaceControl,
-                            result, win, winAnimator);
+                    result = createSurfaceControl(outSurfaceControl, result, win, winAnimator);
                 } catch (Exception e) {
                     displayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);
 
@@ -2312,7 +2312,6 @@
                     // surface, let the client use that, but don't create new surface at this point.
                     Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: getSurface");
                     winAnimator.mSurfaceController.getSurfaceControl(outSurfaceControl);
-                    winAnimator.mSurfaceController.getBLASTSurfaceControl(outBLASTSurfaceControl);
                     Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
                 } else {
                     if (DEBUG_VISIBILITY) Slog.i(TAG_WM, "Releasing surface in: " + win);
@@ -2500,8 +2499,7 @@
         return focusMayChange;
     }
 
-    private int createSurfaceControl(SurfaceControl outSurfaceControl,
-            SurfaceControl outBLASTSurfaceControl, int result,
+    private int createSurfaceControl(SurfaceControl outSurfaceControl, int result,
             WindowState win, WindowStateAnimator winAnimator) {
         if (!win.mHasSurface) {
             result |= RELAYOUT_RES_SURFACE_CHANGED;
@@ -2516,7 +2514,6 @@
         }
         if (surfaceController != null) {
             surfaceController.getSurfaceControl(outSurfaceControl);
-            surfaceController.getBLASTSurfaceControl(outBLASTSurfaceControl);
             ProtoLog.i(WM_SHOW_TRANSACTIONS, "OUT SURFACE %s: copied", outSurfaceControl);
 
         } else {
@@ -2723,6 +2720,32 @@
         }
     }
 
+    /** @see WindowManagerInternal#moveWindowTokenToDisplay(IBinder, int)  */
+    public void moveWindowTokenToDisplay(IBinder binder, int displayId) {
+        synchronized (mGlobalLock) {
+            final DisplayContent dc = mRoot.getDisplayContentOrCreate(displayId);
+            if (dc == null) {
+                ProtoLog.w(WM_ERROR, "moveWindowTokenToDisplay: Attempted to move token: %s"
+                        + " to non-exiting displayId=%d", binder, displayId);
+                return;
+            }
+            final WindowToken token = mRoot.getWindowToken(binder);
+            if (token == null) {
+                ProtoLog.w(WM_ERROR,
+                        "moveWindowTokenToDisplay: Attempted to move non-existing token: %s",
+                        binder);
+                return;
+            }
+            if (token.getDisplayContent() == dc) {
+                ProtoLog.w(WM_ERROR,
+                        "moveWindowTokenToDisplay: Cannot move to the original display "
+                                + "for token: %s", binder);
+                return;
+            }
+            dc.reParentWindowToken(token);
+        }
+    }
+
     void setNewDisplayOverrideConfiguration(Configuration overrideConfig,
             @NonNull DisplayContent dc) {
         if (dc.mWaitingForConfig) {
@@ -3057,7 +3080,7 @@
             throw new SecurityException("Requires INTERACT_ACROSS_USERS permission");
         }
 
-        long origId = Binder.clearCallingIdentity();
+        final long origId = Binder.clearCallingIdentity();
         try {
             return mPolicy.isKeyguardSecure(userId);
         } finally {
@@ -3687,16 +3710,63 @@
     @Override
     public void setFixedToUserRotation(int displayId, int fixedToUserRotation) {
         if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION,
-                "freezeRotation()")) {
+                "setFixedToUserRotation()")) {
             throw new SecurityException("Requires SET_ORIENTATION permission");
         }
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final DisplayContent display = mRoot.getDisplayContent(displayId);
+                if (display == null) {
+                    Slog.w(TAG, "Trying to set fixed to user rotation for a missing display.");
+                    return;
+                }
+                display.getDisplayRotation().setFixedToUserRotation(fixedToUserRotation);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    int getFixedToUserRotation(int displayId) {
         synchronized (mGlobalLock) {
             final DisplayContent display = mRoot.getDisplayContent(displayId);
             if (display == null) {
-                Slog.w(TAG, "Trying to set rotate for app for a missing display.");
-                return;
+                Slog.w(TAG, "Trying to get fixed to user rotation for a missing display.");
+                return -1;
             }
-            display.getDisplayRotation().setFixedToUserRotation(fixedToUserRotation);
+            return display.getDisplayRotation().getFixedToUserRotationMode();
+        }
+    }
+
+    @Override
+    public void setIgnoreOrientationRequest(int displayId, boolean ignoreOrientationRequest) {
+        mAtmInternal.enforceCallerIsRecentsOrHasPermission(
+                android.Manifest.permission.SET_ORIENTATION, "setIgnoreOrientationRequest()");
+
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final DisplayContent display = mRoot.getDisplayContent(displayId);
+                if (display == null) {
+                    Slog.w(TAG, "Trying to setIgnoreOrientationRequest() for a missing display.");
+                    return;
+                }
+                display.setIgnoreOrientationRequest(ignoreOrientationRequest);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    boolean getIgnoreOrientationRequest(int displayId) {
+        synchronized (mGlobalLock) {
+            final DisplayContent display = mRoot.getDisplayContent(displayId);
+            if (display == null) {
+                Slog.w(TAG, "Trying to getIgnoreOrientationRequest() for a missing display.");
+                return false;
+            }
+            return display.getIgnoreOrientationRequest();
         }
     }
 
@@ -3723,7 +3793,7 @@
                     + "rotation constant.");
         }
 
-        long origId = Binder.clearCallingIdentity();
+        final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
                 final DisplayContent display = mRoot.getDisplayContent(displayId);
@@ -3758,7 +3828,7 @@
 
         ProtoLog.v(WM_DEBUG_ORIENTATION, "thawRotation: mRotation=%d", getDefaultDisplayRotation());
 
-        long origId = Binder.clearCallingIdentity();
+        final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
                 final DisplayContent display = mRoot.getDisplayContent(displayId);
@@ -3785,13 +3855,24 @@
         synchronized (mGlobalLock) {
             final DisplayContent display = mRoot.getDisplayContent(displayId);
             if (display == null) {
-                Slog.w(TAG, "Trying to thaw rotation for a missing display.");
+                Slog.w(TAG, "Trying to check if rotation is frozen on a missing display.");
                 return false;
             }
             return display.getDisplayRotation().isRotationFrozen();
         }
     }
 
+    int getDisplayUserRotation(int displayId) {
+        synchronized (mGlobalLock) {
+            final DisplayContent display = mRoot.getDisplayContent(displayId);
+            if (display == null) {
+                Slog.w(TAG, "Trying to get user rotation of a missing display.");
+                return -1;
+            }
+            return display.getDisplayRotation().getUserRotation();
+        }
+    }
+
     /**
      * Recalculate the current rotation.
      *
@@ -3811,7 +3892,7 @@
 
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation");
 
-        long origId = Binder.clearCallingIdentity();
+        final long origId = Binder.clearCallingIdentity();
 
         try {
             synchronized (mGlobalLock) {
@@ -3956,8 +4037,8 @@
                 if (dc == null || dc.mRemoteInsetsControlTarget == null) {
                     return;
                 }
-                dc.getInsetsStateController().onInsetsModified(
-                        dc.mRemoteInsetsControlTarget, state);
+                dc.mRemoteInsetsControlTarget.updateRequestedVisibility(state);
+                dc.getInsetsStateController().onInsetsModified(dc.mRemoteInsetsControlTarget);
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -4104,7 +4185,7 @@
             throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS);
         }
 
-        long origId = Binder.clearCallingIdentity();
+        final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
                 mPolicy.setOverrideFoldedArea(area);
@@ -4118,7 +4199,7 @@
      * Get the display folded area.
      */
     @NonNull Rect getFoldedArea() {
-        long origId = Binder.clearCallingIdentity();
+        final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
                 return mPolicy.getFoldedArea();
@@ -4135,7 +4216,7 @@
                 != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Must hold permission " + MANAGE_ACTIVITY_STACKS);
         }
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             mDisplayNotificationController.registerListener(listener);
         } finally {
@@ -5971,9 +6052,8 @@
         }
         proto.write(DISPLAY_FROZEN, mDisplayFrozen);
         final DisplayContent defaultDisplayContent = getDefaultDisplayContentLocked();
-        proto.write(ROTATION, defaultDisplayContent.getRotation());
-        proto.write(LAST_ORIENTATION, defaultDisplayContent.getLastOrientation());
         proto.write(FOCUSED_DISPLAY_ID, topFocusedDisplayContent.getDisplayId());
+        proto.write(HARD_KEYBOARD_AVAILABLE, mHardKeyboardAvailable);
     }
 
     private void dumpWindowsLocked(PrintWriter pw, boolean dumpAll,
@@ -6824,16 +6904,17 @@
      *
      * @param displayId the display for the request
      * @param behindClient token for a window, used to filter the search to windows behind it
-     * @param taskId specifies the id of a task the result must belong to or -1 to ignore task ids
-     * @param controller the controller to receive results; a call to either
-     *      {@link IScrollCaptureController#onClientConnected} or
-     *      {@link IScrollCaptureController#onClientUnavailable}.
+     * @param taskId specifies the id of a task the result must belong to or -1 to match any task
+     * @param callbacks to receive responses
      */
     public void requestScrollCapture(int displayId, @Nullable IBinder behindClient, int taskId,
-            IScrollCaptureController controller) {
+            IScrollCaptureCallbacks callbacks) {
         if (!checkCallingPermission(READ_FRAME_BUFFER, "requestScrollCapture()")) {
             throw new SecurityException("Requires READ_FRAME_BUFFER permission");
         }
+        if (behindClient != null && !isWindowToken(behindClient)) {
+            throw new IllegalArgumentException("behindClient must be a window token");
+        }
         final long token = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
@@ -6841,26 +6922,26 @@
                 if (dc == null) {
                     ProtoLog.e(WM_ERROR,
                             "Invalid displayId for requestScrollCapture: %d", displayId);
-                    controller.onClientUnavailable();
+                    callbacks.onUnavailable();
                     return;
                 }
                 WindowState topWindow = null;
                 if (behindClient != null) {
-                    topWindow = windowForClientLocked(null, behindClient, /* throwOnError*/ true);
+                    topWindow = windowForClientLocked(null, behindClient, /* throwOnError*/ false);
                 }
                 WindowState targetWindow = dc.findScrollCaptureTargetWindow(topWindow, taskId);
                 if (targetWindow == null) {
-                    controller.onClientUnavailable();
+                    callbacks.onUnavailable();
                     return;
                 }
                 // Forward to the window for handling.
                 try {
-                    targetWindow.mClient.requestScrollCapture(controller);
+                    targetWindow.mClient.requestScrollCapture(callbacks);
                 } catch (RemoteException e) {
                     ProtoLog.w(WM_ERROR,
                             "requestScrollCapture: caught exception dispatching to window."
                                     + "token=%s", targetWindow.mClient.asBinder());
-                    controller.onClientUnavailable();
+                    callbacks.onUnavailable();
                 }
             }
         } catch (RemoteException e) {
@@ -7355,6 +7436,11 @@
             }
         }
 
+        @Override
+        public void moveWindowTokenToDisplay(IBinder binder, int displayId) {
+            WindowManagerService.this.moveWindowTokenToDisplay(binder, displayId);
+        }
+
         // TODO(multi-display): currently only used by PWM to notify keyguard transitions as well
         // forwarding it to SystemUI for synchronizing status and navigation bar animations.
         @Override
@@ -7856,7 +7942,7 @@
 
     @Override
     public void syncInputTransactions() {
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             waitForAnimationsToComplete();
 
@@ -8054,7 +8140,7 @@
     public boolean isLayerTracing() {
         mAtmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.DUMP,
                 "isLayerTracing");
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             Parcel data = null;
             Parcel reply = null;
@@ -8087,7 +8173,7 @@
     public void setLayerTracing(boolean enabled) {
         mAtmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.DUMP,
                 "setLayerTracing");
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             Parcel data = null;
             try {
@@ -8114,7 +8200,7 @@
     public void setLayerTracingFlags(int flags) {
         mAtmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.DUMP,
                 "setLayerTracingFlags");
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             Parcel data = null;
             try {
@@ -8161,9 +8247,9 @@
     }
 
     @Override
-    public boolean getWindowInsets(WindowManager.LayoutParams attrs,
-            int displayId, Rect outContentInsets, Rect outStableInsets,
+    public boolean getWindowInsets(WindowManager.LayoutParams attrs, int displayId,
             DisplayCutout.ParcelableWrapper outDisplayCutout, InsetsState outInsetsState) {
+        final boolean fromLocal = Binder.getCallingPid() == myPid();
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
@@ -8173,13 +8259,8 @@
                             + "could not be found!");
                 }
                 final WindowToken windowToken = dc.getWindowToken(attrs.token);
-                final InsetsStateController insetsStateController =
-                        dc.getInsetsStateController();
-                outInsetsState.set(insetsStateController.getInsetsForWindowMetrics(attrs));
-
                 return dc.getDisplayPolicy().getLayoutHint(attrs, windowToken,
-                        mTmpRect /* outFrame */, outContentInsets, outStableInsets,
-                        outDisplayCutout);
+                        mTmpRect /* outFrame */, outDisplayCutout, outInsetsState, fromLocal);
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 506e0dd..fe312e6 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -28,7 +28,6 @@
 import android.util.Pair;
 import android.view.Display;
 import android.view.IWindowManager;
-import android.view.Surface;
 import android.view.ViewDebug;
 
 import com.android.internal.os.ByteTransferPipe;
@@ -102,12 +101,18 @@
                         }
                     }
                     return result;
-                case "set-user-rotation":
-                    return runSetDisplayUserRotation(pw);
-                case "set-fix-to-user-rotation":
-                    return runSetFixToUserRotation(pw);
+                case "user-rotation":
+                    return runDisplayUserRotation(pw);
+                case "fixed-to-user-rotation":
+                    return runFixedToUserRotation(pw);
+                case "set-ignore-orientation-request":
+                    return runSetIgnoreOrientationRequest(pw);
+                case "get-ignore-orientation-request":
+                    return runGetIgnoreOrientationRequest(pw);
                 case "dump-visible-window-views":
                     return runDumpVisibleWindowViews(pw);
+                case "reset":
+                    return runReset(pw);
                 default:
                     return handleDefaultCommands(cmd);
             }
@@ -309,28 +314,37 @@
         return Integer.parseInt(s);
     }
 
-    private int runSetDisplayUserRotation(PrintWriter pw) {
-        final String lockMode = getNextArgRequired();
-
+    private int runDisplayUserRotation(PrintWriter pw) {
         int displayId = Display.DEFAULT_DISPLAY;
         String arg = getNextArg();
+        if (arg == null) {
+            return printDisplayUserRotation(pw, displayId);
+        }
+
         if ("-d".equals(arg)) {
             displayId = Integer.parseInt(getNextArgRequired());
             arg = getNextArg();
         }
 
+        final String lockMode = arg;
+        if (lockMode == null) {
+            return printDisplayUserRotation(pw, displayId);
+        }
+
         if ("free".equals(lockMode)) {
             mInternal.thawDisplayRotation(displayId);
             return 0;
         }
 
-        if (!lockMode.equals("lock")) {
-            getErrPrintWriter().println("Error: lock mode needs to be either free or lock.");
+        if (!"lock".equals(lockMode)) {
+            getErrPrintWriter().println("Error: argument needs to be either -d, free or lock.");
             return -1;
         }
 
+        arg = getNextArg();
         try {
-            final int rotation = arg != null ? Integer.parseInt(arg) : Surface.ROTATION_0;
+            final int rotation =
+                    arg != null ? Integer.parseInt(arg) : -1 /* lock to current rotation */;
             mInternal.freezeDisplayRotation(displayId, rotation);
             return 0;
         } catch (IllegalArgumentException e) {
@@ -339,12 +353,36 @@
         }
     }
 
-    private int runSetFixToUserRotation(PrintWriter pw) throws RemoteException {
+    private int printDisplayUserRotation(PrintWriter pw, int displayId) {
+        final int displayUserRotation = mInternal.getDisplayUserRotation(displayId);
+        if (displayUserRotation < 0) {
+            getErrPrintWriter().println("Error: check logcat for more details.");
+            return -1;
+        }
+        if (!mInternal.isDisplayRotationFrozen(displayId)) {
+            pw.println("free");
+            return 0;
+        }
+        pw.print("lock ");
+        pw.println(displayUserRotation);
+        return 0;
+    }
+
+    private int runFixedToUserRotation(PrintWriter pw) throws RemoteException {
         int displayId = Display.DEFAULT_DISPLAY;
-        String arg = getNextArgRequired();
+        String arg = getNextArg();
+        if (arg == null) {
+            printFixedToUserRotation(pw, displayId);
+            return 0;
+        }
+
         if ("-d".equals(arg)) {
             displayId = Integer.parseInt(getNextArgRequired());
-            arg = getNextArgRequired();
+            arg = getNextArg();
+        }
+
+        if (arg == null) {
+            return printFixedToUserRotation(pw, displayId);
         }
 
         final int fixedToUserRotation;
@@ -368,6 +406,65 @@
         return 0;
     }
 
+    private int printFixedToUserRotation(PrintWriter pw, int displayId) {
+        int fixedToUserRotationMode = mInternal.getFixedToUserRotation(displayId);
+        switch (fixedToUserRotationMode) {
+            case IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT:
+                pw.println("default");
+                return 0;
+            case IWindowManager.FIXED_TO_USER_ROTATION_DISABLED:
+                pw.println("disabled");
+                return 0;
+            case IWindowManager.FIXED_TO_USER_ROTATION_ENABLED:
+                pw.println("enabled");
+                return 0;
+            default:
+                getErrPrintWriter().println("Error: check logcat for more details.");
+                return -1;
+        }
+    }
+
+    private int runSetIgnoreOrientationRequest(PrintWriter pw) throws RemoteException {
+        int displayId = Display.DEFAULT_DISPLAY;
+        String arg = getNextArgRequired();
+        if ("-d".equals(arg)) {
+            displayId = Integer.parseInt(getNextArgRequired());
+            arg = getNextArgRequired();
+        }
+
+        final boolean ignoreOrientationRequest;
+        switch (arg) {
+            case "true":
+            case "1":
+                ignoreOrientationRequest = true;
+                break;
+            case "false":
+            case "0":
+                ignoreOrientationRequest = false;
+                break;
+            default:
+                getErrPrintWriter().println("Error: expecting true, 1, false, 0, but we "
+                        + "get " + arg);
+                return -1;
+        }
+
+        mInterface.setIgnoreOrientationRequest(displayId, ignoreOrientationRequest);
+        return 0;
+    }
+
+    private int runGetIgnoreOrientationRequest(PrintWriter pw) throws RemoteException {
+        int displayId = Display.DEFAULT_DISPLAY;
+        String arg = getNextArg();
+        if ("-d".equals(arg)) {
+            displayId = Integer.parseInt(getNextArgRequired());
+        }
+
+        final boolean ignoreOrientationRequest = mInternal.getIgnoreOrientationRequest(displayId);
+        pw.println("ignoreOrientationRequest " + ignoreOrientationRequest
+                + " for displayId=" + displayId);
+        return 0;
+    }
+
     private int runDumpVisibleWindowViews(PrintWriter pw) {
         if (!mInternal.checkCallingPermission(android.Manifest.permission.DUMP,
                 "runDumpVisibleWindowViews()")) {
@@ -412,6 +509,34 @@
         return 0;
     }
 
+    private int runReset(PrintWriter pw) throws RemoteException {
+        int displayId = getDisplayId(getNextArg());
+
+        // size
+        mInterface.clearForcedDisplaySize(displayId);
+
+        // density
+        mInterface.clearForcedDisplayDensityForUser(displayId, UserHandle.USER_CURRENT);
+
+        // folded-area
+        mInternal.setOverrideFoldedArea(new Rect());
+
+        // scaling
+        mInterface.setForcedDisplayScalingMode(displayId, DisplayContent.FORCE_SCALING_MODE_AUTO);
+
+        // user-rotation
+        mInternal.thawDisplayRotation(displayId);
+
+        // fixed-to-user-rotation
+        mInterface.setFixedToUserRotation(displayId, IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT);
+
+        // set-ignore-orientation-request
+        mInterface.setIgnoreOrientationRequest(displayId, false /* ignoreOrientationRequest */);
+
+        pw.println("Reset all settings for displayId=" + displayId);
+        return 0;
+    }
+
     @Override
     public void onHelp() {
         PrintWriter pw = getOutPrintWriter();
@@ -429,12 +554,17 @@
         pw.println("    Set display scaling mode.");
         pw.println("  dismiss-keyguard");
         pw.println("    Dismiss the keyguard, prompting user for auth if necessary.");
-        pw.println("  set-user-rotation [free|lock] [-d DISPLAY_ID] [rotation]");
-        pw.println("    Set user rotation mode and user rotation.");
+        pw.println("  user-rotation [-d DISPLAY_ID] [free|lock] [rotation]");
+        pw.println("    Print or set user rotation mode and user rotation.");
         pw.println("  dump-visible-window-views");
         pw.println("    Dumps the encoded view hierarchies of visible windows");
-        pw.println("  set-fix-to-user-rotation [-d DISPLAY_ID] [enabled|disabled]");
-        pw.println("    Enable or disable rotating display for app requested orientation.");
+        pw.println("  fixed-to-user-rotation [-d DISPLAY_ID] [enabled|disabled|default]");
+        pw.println("    Print or set rotating display for app requested orientation.");
+        pw.println("  set-ignore-orientation-request [-d DISPLAY_ID] [true|1|false|0]");
+        pw.println("  get-ignore-orientation-request [-d DISPLAY_ID] ");
+        pw.println("    If app requested orientation should be ignored.");
+        pw.println("  reset [-d DISPLAY_ID]");
+        pw.println("    Reset all override settings.");
         if (!IS_USER) {
             pw.println("  tracing (start | stop)");
             pw.println("    Start or stop window tracing.");
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index f1641cd..ae152d2 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -57,7 +57,6 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 /**
  * Server side implementation for the interface for organizing windows
@@ -84,7 +83,6 @@
     private final ActivityTaskManagerService mService;
     private final WindowManagerGlobalLock mGlobalLock;
 
-    private final BLASTSyncEngine mBLASTSyncEngine = new BLASTSyncEngine();
     private final HashMap<Integer, IWindowContainerTransactionCallback>
             mTransactionCallbacksByPendingSyncId = new HashMap();
 
@@ -120,7 +118,7 @@
     public IBinder startTransition(int type, @Nullable IBinder transitionToken,
             @Nullable WindowContainerTransaction t) {
         enforceStackPermission("startTransition()");
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
                 Transition transition = Transition.fromBinder(transitionToken);
@@ -147,7 +145,7 @@
             @Nullable WindowContainerTransaction t,
             @Nullable IWindowContainerTransactionCallback callback) {
         enforceStackPermission("finishTransition()");
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
                 int syncId = -1;
@@ -176,7 +174,7 @@
             throw new IllegalArgumentException(
                     "Null transaction passed to applySyncTransaction");
         }
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
                 int effects = 0;
@@ -207,7 +205,7 @@
                         final Map.Entry<IBinder, WindowContainerTransaction.Change> entry =
                                 entries.next();
                         final WindowContainer wc = WindowContainer.fromBinder(entry.getKey());
-                        if (!wc.isAttached()) {
+                        if (wc == null || !wc.isAttached()) {
                             Slog.e(TAG, "Attempt to operate on detached container: " + wc);
                             continue;
                         }
@@ -380,24 +378,18 @@
         return effects;
     }
 
-    private int applyTaskDisplayAreaChanges(TaskDisplayArea taskDisplayArea,
-            WindowContainerTransaction.Change c) {
-        int effects = applyDisplayAreaChanges(taskDisplayArea, c);
-        if ((c.getChangeMask()
-                & WindowContainerTransaction.Change.CHANGE_IGNORE_ORIENTATION_REQUEST) != 0) {
-            if (taskDisplayArea.setIgnoreOrientationRequest(c.getIgnoreOrientationRequest())) {
-                effects |= TRANSACT_EFFECTS_LIFECYCLE;
-            }
-        }
-
-        return effects;
-    }
-
-    private int applyDisplayAreaChanges(WindowContainer container,
+    private int applyDisplayAreaChanges(DisplayArea displayArea,
             WindowContainerTransaction.Change c) {
         final int[] effects = new int[1];
 
-        container.forAllTasks(task -> {
+        if ((c.getChangeMask()
+                & WindowContainerTransaction.Change.CHANGE_IGNORE_ORIENTATION_REQUEST) != 0) {
+            if (displayArea.setIgnoreOrientationRequest(c.getIgnoreOrientationRequest())) {
+                effects[0] |= TRANSACT_EFFECTS_LIFECYCLE;
+            }
+        }
+
+        displayArea.forAllTasks(task -> {
             Task tr = (Task) task;
             if ((c.getChangeMask() & WindowContainerTransaction.Change.CHANGE_HIDDEN) != 0) {
                 if (tr.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, c.getHidden())) {
@@ -475,10 +467,8 @@
 
         int effects = applyChanges(wc, c);
 
-        if (wc instanceof TaskDisplayArea) {
-            effects |= applyTaskDisplayAreaChanges((TaskDisplayArea) wc, c);
-        } else if (wc instanceof DisplayArea) {
-            effects |= applyDisplayAreaChanges(wc, c);
+        if (wc instanceof DisplayArea) {
+            effects |= applyDisplayAreaChanges(wc.asDisplayArea(), c);
         } else if (wc instanceof Task) {
             effects |= applyTaskChanges(wc.asTask(), c);
         }
@@ -513,7 +503,7 @@
 
     @VisibleForTesting
     int startSyncWithOrganizer(IWindowContainerTransactionCallback callback) {
-        int id = mBLASTSyncEngine.startSyncSet(this);
+        int id = mService.mWindowManager.mSyncEngine.startSyncSet(this);
         mTransactionCallbacksByPendingSyncId.put(id, callback);
         return id;
     }
@@ -521,31 +511,26 @@
     @VisibleForTesting
     void setSyncReady(int id) {
         ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Set sync ready, syncId=%d", id);
-        mBLASTSyncEngine.setReady(id);
+        mService.mWindowManager.mSyncEngine.setReady(id);
     }
 
     @VisibleForTesting
     void addToSyncSet(int syncId, WindowContainer wc) {
-        mBLASTSyncEngine.addToSyncSet(syncId, wc);
+        mService.mWindowManager.mSyncEngine.addToSyncSet(syncId, wc);
     }
 
     @Override
-    public void onTransactionReady(int syncId, Set<WindowContainer> windowContainersReady) {
+    public void onTransactionReady(int syncId, SurfaceControl.Transaction t) {
         ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Transaction ready, syncId=%d", syncId);
         final IWindowContainerTransactionCallback callback =
                 mTransactionCallbacksByPendingSyncId.get(syncId);
 
-        SurfaceControl.Transaction mergedTransaction = new SurfaceControl.Transaction();
-        for (WindowContainer container : windowContainersReady) {
-            container.mergeBlastSyncTransaction(mergedTransaction);
-        }
-
         try {
-            callback.onTransactionReady(syncId, mergedTransaction);
+            callback.onTransactionReady(syncId, t);
         } catch (RemoteException e) {
             // If there's an exception when trying to send the mergedTransaction to the client, we
             // should immediately apply it here so the transactions aren't lost.
-            mergedTransaction.apply();
+            t.apply();
         }
 
         mTransactionCallbacksByPendingSyncId.remove(syncId);
@@ -589,7 +574,7 @@
     @Override
     public void registerTransitionPlayer(ITransitionPlayer player) {
         enforceStackPermission("registerTransitionPlayer()");
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
                 mTransitionController.registerTransitionPlayer(player);
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 4b8a398..2e7905c 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -178,10 +178,14 @@
     // Whether this process has ever started a service with the BIND_INPUT_METHOD permission.
     private volatile boolean mHasImeService;
 
+    /** Whether {@link #mActivities} is not empty. */
+    private volatile boolean mHasActivities;
     /** All activities running in the process (exclude destroying). */
     private final ArrayList<ActivityRecord> mActivities = new ArrayList<>();
     /** The activities will be removed but still belong to this process. */
     private ArrayList<ActivityRecord> mInactiveActivities;
+    /** Whether {@link #mRecentTasks} is not empty. */
+    private volatile boolean mHasRecentTasks;
     // any tasks this process had run root activities in
     private final ArrayList<Task> mRecentTasks = new ArrayList<>();
     // The most recent top-most activity that was resumed in the process for pre-Q app.
@@ -219,13 +223,14 @@
     private boolean mRunningRemoteAnimation;
 
     @Nullable
-    private BackgroundActivityStartCallback mBackgroundActivityStartCallback;
+    private final BackgroundActivityStartCallback mBackgroundActivityStartCallback;
 
     /** The state for oom-adjustment calculation. */
     private final OomScoreReferenceState mOomRefState;
 
     public WindowProcessController(@NonNull ActivityTaskManagerService atm, ApplicationInfo info,
-            String name, int uid, int userId, Object owner, WindowProcessListener listener) {
+            String name, int uid, int userId, Object owner,
+            @NonNull WindowProcessListener listener) {
         mInfo = info;
         mName = name;
         mUid = uid;
@@ -386,7 +391,6 @@
     }
 
     void postPendingUiCleanMsg(boolean pendingUiClean) {
-        if (mListener == null) return;
         // Posting on handler so WM lock isn't held when we call into AM.
         final Message m = PooledLambda.obtainMessage(
                 WindowProcessListener::setPendingUiClean, mListener, pendingUiClean);
@@ -551,8 +555,7 @@
             return true;
         }
         // allow if the flag was explicitly set
-        if (!mBackgroundActivityStartTokens.isEmpty()) {
-            onBackgroundStartAllowedByToken();
+        if (isBackgroundStartAllowedByToken()) {
             if (DEBUG_ACTIVITY_STARTS) {
                 Slog.d(TAG, "[WindowProcessController(" + mPid
                         + ")] Activity start allowed: process allowed by token");
@@ -562,18 +565,29 @@
         return false;
     }
 
-    private void onBackgroundStartAllowedByToken() {
+    /**
+     * If there are no tokens, we don't allow *by token*. If there are tokens, we need to check if
+     * the callback handles all the tokens, if so we ask the callback if the activity should be
+     * started, otherwise we allow.
+     */
+    private boolean isBackgroundStartAllowedByToken() {
+        if (mBackgroundActivityStartTokens.isEmpty()) {
+            return false;
+        }
         if (mBackgroundActivityStartCallback == null) {
-            return;
+            // We have tokens but no callback to decide => allow
+            return true;
         }
         IBinder callbackToken = mBackgroundActivityStartCallback.getToken();
         for (IBinder token : mBackgroundActivityStartTokens.values()) {
             if (token != callbackToken) {
-                return;
+                // The callback doesn't handle all the tokens => allow
+                return true;
             }
         }
-        mAtm.mH.post(() ->
-                mBackgroundActivityStartCallback.onExclusiveTokenActivityStart(mInfo.packageName));
+        // The callback handles all the tokens => callback decides
+        return mBackgroundActivityStartCallback.isActivityStartAllowed(mInfo.uid,
+                mInfo.packageName);
     }
 
     private boolean isBoundByForegroundUid() {
@@ -646,6 +660,7 @@
             return;
         }
         mActivities.add(r);
+        mHasActivities = true;
         if (mInactiveActivities != null) {
             mInactiveActivities.remove(r);
         }
@@ -674,20 +689,20 @@
             mInactiveActivities.remove(r);
         }
         mActivities.remove(r);
+        mHasActivities = !mActivities.isEmpty();
         updateActivityConfigurationListener();
     }
 
     void clearActivities() {
         mInactiveActivities = null;
         mActivities.clear();
+        mHasActivities = false;
         updateActivityConfigurationListener();
     }
 
     @HotPath(caller = HotPath.OOM_ADJUSTMENT)
     public boolean hasActivities() {
-        synchronized (mAtm.mGlobalLockWithoutBoost) {
-            return !mActivities.isEmpty();
-        }
+        return mHasActivities;
     }
 
     @HotPath(caller = HotPath.OOM_ADJUSTMENT)
@@ -697,9 +712,7 @@
 
     @HotPath(caller = HotPath.LRU_UPDATE)
     public boolean hasActivitiesOrRecentTasks() {
-        synchronized (mAtm.mGlobalLockWithoutBoost) {
-            return !mActivities.isEmpty() || !mRecentTasks.isEmpty();
-        }
+        return mHasActivities || mHasRecentTasks;
     }
 
     private boolean hasActivityInVisibleTask() {
@@ -1147,7 +1160,6 @@
     }
 
     void clearProfilerIfNeeded() {
-        if (mListener == null) return;
         // Posting on handler so WM lock isn't held when we call into AM.
         mAtm.mH.sendMessage(PooledLambda.obtainMessage(
                 WindowProcessListener::clearProfilerIfNeeded, mListener));
@@ -1155,7 +1167,6 @@
 
     void updateProcessInfo(boolean updateServiceConnectionActivities, boolean activityChange,
             boolean updateOomAdj, boolean addPendingTopUid) {
-        if (mListener == null) return;
         if (addPendingTopUid) {
             mAtm.mAmInternal.addPendingTopUid(mUid, mPid);
         }
@@ -1169,14 +1180,12 @@
     }
 
     void updateServiceConnectionActivities() {
-        if (mListener == null) return;
         // Posting on handler so WM lock isn't held when we call into AM.
         mAtm.mH.sendMessage(PooledLambda.obtainMessage(
                 WindowProcessListener::updateServiceConnectionActivities, mListener));
     }
 
     void setPendingUiCleanAndForceProcessStateUpTo(int newState) {
-        if (mListener == null) return;
         // Posting on handler so WM lock isn't held when we call into AM.
         final Message m = PooledLambda.obtainMessage(
                 WindowProcessListener::setPendingUiCleanAndForceProcessStateUpTo,
@@ -1185,7 +1194,7 @@
     }
 
     boolean isRemoved() {
-        return mListener == null ? false : mListener.isRemoved();
+        return mListener.isRemoved();
     }
 
     private boolean shouldSetProfileProc() {
@@ -1210,7 +1219,6 @@
     }
 
     void onStartActivity(int topProcessState, ActivityInfo info) {
-        if (mListener == null) return;
         String packageName = null;
         if ((info.flags & ActivityInfo.FLAG_MULTIPROCESS) == 0
                 || !"android".equals(info.packageName)) {
@@ -1234,7 +1242,6 @@
     }
 
     void appDied(String reason) {
-        if (mListener == null) return;
         // Posting on handler so WM lock isn't held when we call into AM.
         final Message m = PooledLambda.obtainMessage(
                 WindowProcessListener::appDied, mListener, reason);
@@ -1250,20 +1257,27 @@
         mAtm.mStackSupervisor.removeHistoryRecords(this);
 
         boolean hasVisibleActivities = false;
-        if (mInactiveActivities != null && !mInactiveActivities.isEmpty()) {
+        final boolean hasInactiveActivities =
+                mInactiveActivities != null && !mInactiveActivities.isEmpty();
+        final ArrayList<ActivityRecord> activities =
+                (mHasActivities || hasInactiveActivities) ? new ArrayList<>() : mActivities;
+        if (mHasActivities) {
+            activities.addAll(mActivities);
+        }
+        if (hasInactiveActivities) {
             // Make sure that all activities in this process are handled.
-            mActivities.addAll(mInactiveActivities);
+            activities.addAll(mInactiveActivities);
         }
         if (isRemoved()) {
             // The package of the died process should be force-stopped, so make its activities as
             // finishing to prevent the process from being started again if the next top (or being
             // visible) activity also resides in the same process. This must be done before removal.
-            for (int i = mActivities.size() - 1; i >= 0; i--) {
-                mActivities.get(i).makeFinishingLocked();
+            for (int i = activities.size() - 1; i >= 0; i--) {
+                activities.get(i).makeFinishingLocked();
             }
         }
-        for (int i = mActivities.size() - 1; i >= 0; i--) {
-            final ActivityRecord r = mActivities.get(i);
+        for (int i = activities.size() - 1; i >= 0; i--) {
+            final ActivityRecord r = activities.get(i);
             if (r.mVisibleRequested || r.isVisible()) {
                 // While an activity launches a new activity, it's possible that the old activity
                 // is already requested to be hidden (mVisibleRequested=false), but this visibility
@@ -1400,17 +1414,6 @@
             return;
         }
 
-        if (mListener.isCached()) {
-            // This process is in a cached state. We will delay delivering the config change to the
-            // process until the process is no longer cached.
-            mHasPendingConfigurationChange = true;
-            return;
-        }
-
-        dispatchConfigurationChange(config);
-    }
-
-    private void dispatchConfigurationChange(Configuration config) {
         if (mPauseConfigurationDispatchCount > 0) {
             mHasPendingConfigurationChange = true;
             return;
@@ -1475,22 +1478,22 @@
 
     /** Returns the total time (in milliseconds) spent executing in both user and system code. */
     public long getCpuTime() {
-        return (mListener != null) ? mListener.getCpuTime() : 0;
+        return mListener.getCpuTime();
     }
 
     void addRecentTask(Task task) {
         mRecentTasks.add(task);
+        mHasRecentTasks = true;
     }
 
     void removeRecentTask(Task task) {
         mRecentTasks.remove(task);
+        mHasRecentTasks = !mRecentTasks.isEmpty();
     }
 
     @HotPath(caller = HotPath.OOM_ADJUSTMENT)
     public boolean hasRecentTasks() {
-        synchronized (mAtm.mGlobalLockWithoutBoost) {
-            return !mRecentTasks.isEmpty();
-        }
+        return mHasRecentTasks;
     }
 
     void clearRecentTasks() {
@@ -1498,6 +1501,7 @@
             mRecentTasks.get(i).clearRootProcess();
         }
         mRecentTasks.clear();
+        mHasRecentTasks = false;
     }
 
     public void appEarlyNotResponding(String annotation, Runnable killAppCallback) {
@@ -1556,22 +1560,6 @@
     }
 
     /**
-     * Called to notify WindowProcessController of a change in the process's cached state.
-     *
-     * @param isCached whether or not the process is cached.
-     */
-    @HotPath(caller = HotPath.OOM_ADJUSTMENT)
-    public void onProcCachedStateChanged(boolean isCached) {
-        if (!isCached) {
-            synchronized (mAtm.mGlobalLockWithoutBoost) {
-                if (mHasPendingConfigurationChange) {
-                    dispatchConfigurationChange(getConfiguration());
-                }
-            }
-        }
-    }
-
-    /**
      * Called to notify {@link WindowProcessController} of a started service.
      *
      * @param serviceInfo information describing the started service.
@@ -1602,23 +1590,28 @@
 
     @HotPath(caller = HotPath.OOM_ADJUSTMENT)
     public void onTopProcChanged() {
-        synchronized (mAtm.mGlobalLockWithoutBoost) {
-            mAtm.mVrController.onTopProcChangedLocked(this);
+        if (mAtm.mVrController.isInterestingToSchedGroup()) {
+            mAtm.mH.post(() -> {
+                synchronized (mAtm.mGlobalLock) {
+                    mAtm.mVrController.onTopProcChangedLocked(this);
+                }
+            });
         }
     }
 
     @HotPath(caller = HotPath.OOM_ADJUSTMENT)
     public boolean isHomeProcess() {
-        synchronized (mAtm.mGlobalLockWithoutBoost) {
-            return this == mAtm.mHomeProcess;
-        }
+        return this == mAtm.mHomeProcess;
     }
 
     @HotPath(caller = HotPath.OOM_ADJUSTMENT)
     public boolean isPreviousProcess() {
-        synchronized (mAtm.mGlobalLockWithoutBoost) {
-            return this == mAtm.mPreviousProcess;
-        }
+        return this == mAtm.mPreviousProcess;
+    }
+
+    @HotPath(caller = HotPath.OOM_ADJUSTMENT)
+    public boolean isHeavyWeightProcess() {
+        return this == mAtm.mHeavyWeightProcess;
     }
 
     void setRunningRecentsAnimation(boolean running) {
@@ -1687,8 +1680,6 @@
     }
 
     void dumpDebug(ProtoOutputStream proto, long fieldId) {
-        if (mListener != null) {
-            mListener.dumpDebug(proto, fieldId);
-        }
+        mListener.dumpDebug(proto, fieldId);
     }
 }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 8f42b3f..a4b4fcb 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -25,6 +25,7 @@
 import static android.app.WindowConfiguration.isSplitScreenWindowingMode;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.graphics.GraphicsProtos.dumpPointProto;
+import static android.hardware.input.InputManager.BLOCK_UNTRUSTED_TOUCHES;
 import static android.os.IInputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
 import static android.os.PowerManager.DRAW_WAKE_LOCK;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
@@ -182,6 +183,7 @@
 import android.annotation.Nullable;
 import android.app.AppOpsManager;
 import android.app.admin.DevicePolicyCache;
+import android.app.compat.CompatChanges;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Matrix;
@@ -198,6 +200,7 @@
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.TouchOcclusionMode;
 import android.os.Trace;
 import android.os.WorkSource;
 import android.provider.Settings;
@@ -221,6 +224,7 @@
 import android.view.InputWindowHandle;
 import android.view.InsetsSource;
 import android.view.InsetsState;
+import android.view.InsetsState.InternalInsetsType;
 import android.view.Surface.Rotation;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
@@ -247,10 +251,8 @@
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
-import java.util.Set;
 import java.util.function.Predicate;
 
 /** A window in the window manager. */
@@ -434,7 +436,7 @@
 
     /**
      * Usually empty. Set to the task's tempInsetFrame. See
-     *{@link android.app.IActivityTaskManager#resizeDockedStack}.
+     *{@link android.app.IActivityTaskManager#resizePrimarySplitScreen}.
      */
     private final Rect mInsetFrame = new Rect();
 
@@ -654,15 +656,6 @@
     private static final StringBuilder sTmpSB = new StringBuilder();
 
     /**
-     * Whether the next surfacePlacement call should notify that the blast sync is ready.
-     * This is set to true when {@link #finishDrawing(Transaction)} is called so
-     * {@link #onTransactionReady(int, Set)} is called after the next surfacePlacement. This allows
-     * Transactions to get flushed into the syncTransaction before notifying {@link BLASTSyncEngine}
-     * that this WindowState is ready.
-     */
-    private boolean mNotifyBlastOnSurfacePlacement;
-
-    /**
      * Compares two window sub-layers and returns -1 if the first is lesser than the second in terms
      * of z-order and 1 otherwise.
      */
@@ -702,34 +695,25 @@
      */
     int mFrameRateSelectionPriority = RefreshRatePolicy.LAYER_PRIORITY_UNSET;
 
-    /**
-     * BLASTSyncEngine ID corresponding to a sync-set for all
-     * our children. We add our children to this set in Sync,
-     * but we save it and don't mark it as ready until finishDrawing
-     * this way we have a two way latch between all our children finishing
-     * and drawing ourselves.
-     */
-    private int mLocalSyncId = -1;
-
     static final int BLAST_TIMEOUT_DURATION = 5000; /* milliseconds */
 
     private final WindowProcessController mWpcForDisplayConfigChanges;
 
     /**
-     * @return The insets state as requested by the client, i.e. the dispatched insets state
-     *         for which the visibilities are overridden with what the client requested.
+     * Returns the visibility of the given {@link InternalInsetsType type} requested by the client.
+     *
+     * @param type the given {@link InternalInsetsType type}.
+     * @return {@code true} if the type is requested visible.
      */
     @Override
-    public InsetsState getRequestedInsetsState() {
-        return mRequestedInsetsState;
+    public boolean getRequestedVisibility(@InternalInsetsType int type) {
+        return mRequestedInsetsState.getSourceOrDefaultVisibility(type);
     }
 
     /**
-     * @see #getRequestedInsetsState()
+     * @see #getRequestedVisibility(int)
      */
-    void updateRequestedInsetsState(InsetsState state) {
-
-        // Only update the sources the client is actually controlling.
+    void updateRequestedVisibility(InsetsState state) {
         for (int i = 0; i < InsetsState.SIZE; i++) {
             final InsetsSource source = state.peekSource(i);
             if (source == null) continue;
@@ -958,21 +942,35 @@
                 : service.mAtmService.getProcessController(s.mPid, s.mUid);
     }
 
+    int getTouchOcclusionMode() {
+        if (!CompatChanges.isChangeEnabled(BLOCK_UNTRUSTED_TOUCHES, mOwnerUid)) {
+            return TouchOcclusionMode.ALLOW;
+        }
+        if (WindowManager.LayoutParams.isSystemAlertWindowType(mAttrs.type)) {
+            return TouchOcclusionMode.USE_OPACITY;
+        }
+        return TouchOcclusionMode.BLOCK_UNTRUSTED;
+    }
+
     void attach() {
         if (DEBUG) Slog.v(TAG, "Attaching " + this + " token=" + mToken);
         mSession.windowAddedLocked(mAttrs.packageName);
     }
 
+    boolean inSizeCompatMode() {
+        return inSizeCompatMode(mAttrs, mActivityRecord);
+    }
+
     /**
      * @return {@code true} if the application runs in size compatibility mode.
      * @see android.content.res.CompatibilityInfo#supportsScreen
-     * @see ActivityRecord#inSizeCompatMode
+     * @see ActivityRecord#inSizeCompatMode()
      */
-    boolean inSizeCompatMode() {
-        return (mAttrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0
-                || (mActivityRecord != null && mActivityRecord.hasSizeCompatBounds()
-                        // Exclude starting window because it is not displayed by the application.
-                        && mAttrs.type != TYPE_APPLICATION_STARTING);
+    static boolean inSizeCompatMode(WindowManager.LayoutParams attrs, WindowToken windowToken) {
+        return (attrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0
+                || (windowToken != null && windowToken.hasSizeCompatBounds()
+                // Exclude starting window because it is not displayed by the application.
+                && attrs.type != TYPE_APPLICATION_STARTING);
     }
 
     /**
@@ -1210,14 +1208,8 @@
                     Math.min(windowFrames.mStableFrame.bottom, windowFrames.mFrame.bottom));
         }
 
-        if (mAttrs.type == TYPE_DOCK_DIVIDER) {
-            final WmDisplayCutout c = windowFrames.mDisplayCutout.calculateRelativeTo(
-                    windowFrames.mDisplayFrame);
-            windowFrames.calculateDockedDividerInsets(c.getDisplayCutout().getSafeInsets());
-        } else {
-            windowFrames.calculateInsets(windowsAreFloating, isFullscreenAndFillsDisplay,
-                    getDisplayFrames(dc.mDisplayFrames).mUnrestricted);
-        }
+        windowFrames.calculateInsets(windowsAreFloating, isFullscreenAndFillsDisplay,
+                getDisplayFrames(dc.mDisplayFrames).mUnrestricted);
 
         windowFrames.setDisplayCutout(
                 windowFrames.mDisplayCutout.calculateRelativeTo(windowFrames.mFrame));
@@ -1531,7 +1523,12 @@
      * modification according to the state of transient bars.
      */
     InsetsState getInsetsState() {
-        return getDisplayContent().getInsetsPolicy().getInsetsForDispatch(this);
+        InsetsState state = getDisplayContent().getInsetsPolicy().getInsetsForWindow(this);
+        if (inSizeCompatMode()) {
+            state = new InsetsState(state, true);
+            state.scale(mInvGlobalScale);
+        }
+        return state;
     }
 
     @Override
@@ -2159,16 +2156,7 @@
         }
     }
 
-    @Override
-    void forceWindowsScaleableInTransaction(boolean force) {
-        if (mWinAnimator != null && mWinAnimator.hasSurface()) {
-            mWinAnimator.mSurfaceController.forceScaleableInTransaction(force);
-        }
-
-        super.forceWindowsScaleableInTransaction(force);
-    }
-
-    @Override
+  @Override
     void removeImmediately() {
         super.removeImmediately();
 
@@ -2223,7 +2211,6 @@
     void removeIfPossible() {
         super.removeIfPossible();
         removeIfPossible(false /*keepVisibleDeadWindow*/);
-        immediatelyNotifyBlastSync();
     }
 
     private void removeIfPossible(boolean keepVisibleDeadWindow) {
@@ -3052,7 +3039,7 @@
     }
 
     void setForceHideNonSystemOverlayWindowIfNeeded(boolean forceHide) {
-        if (mOwnerCanAddInternalSystemWindow
+        if (!mSession.mOverlaysCanBeHidden
                 || (!isSystemAlertWindowType(mAttrs.type) && mAttrs.type != TYPE_TOAST)) {
             return;
         }
@@ -3602,11 +3589,6 @@
             backdropFrame.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
         }
         outFrames.displayCutout.set(mWindowFrames.mDisplayCutout.getDisplayCutout());
-
-        // TODO(b/149813814): Remove legacy insets.
-        outFrames.contentInsets.set(mWindowFrames.mLastContentInsets);
-        outFrames.visibleInsets.set(mWindowFrames.mLastVisibleInsets);
-        outFrames.stableInsets.set(mWindowFrames.mLastStableInsets);
     }
 
     void reportResized() {
@@ -5324,8 +5306,7 @@
         // Send information to SufaceFlinger about the priority of the current window.
         updateFrameRateSelectionPriorityIfNeeded();
 
-        mWinAnimator.prepareSurfaceLocked(true);
-        notifyBlastSyncTransaction();
+        mWinAnimator.prepareSurfaceLocked(SurfaceControl.getGlobalTransaction(), true);
         super.prepareSurfaces();
     }
 
@@ -5639,7 +5620,7 @@
      * into the state of the control target.
      *
      * @param insetProvider the provider which should not be visible to the client.
-     * @see InsetsStateController#getInsetsForDispatch(WindowState)
+     * @see InsetsStateController#getInsetsForWindow(WindowState)
      */
     void setControllableInsetProvider(InsetsSourceProvider insetProvider) {
         mControllableInsetProvider = insetProvider;
@@ -5756,100 +5737,64 @@
 
     void setViewVisibility(int viewVisibility) {
         mViewVisibility = viewVisibility;
-        // The viewVisibility is set to GONE with a client request to relayout. If this occurs and
-        // there's a blast sync transaction waiting, finishDrawing will never be called since the
-        // client will not render when visibility is GONE. Therefore, call finishDrawing here to
-        // prevent system server from blocking on a window that will not draw.
-        if (viewVisibility == View.GONE && mUsingBLASTSyncTransaction) {
-            immediatelyNotifyBlastSync();
-        }
     }
 
     SurfaceControl getClientViewRootSurface() {
-        return mWinAnimator.getClientViewRootSurface();
+        return mWinAnimator.getSurfaceControl();
     }
 
     @Override
-    boolean prepareForSync(BLASTSyncEngine.TransactionReadyListener waitingListener,
-            int waitingId) {
-        // If the window is goneForLayout, relayout won't be called so we'd just wait forever.
-        if (isGoneForLayout()) {
+    boolean prepareSync() {
+        if (!super.prepareSync()) {
             return false;
         }
-        boolean willSync = setPendingListener(waitingListener, waitingId);
-        if (!willSync) {
-            return false;
-        }
-        requestRedrawForSync();
-
-        mLocalSyncId = mBLASTSyncEngine.startSyncSet(this);
-        addChildrenToSyncSet(mLocalSyncId);
-
         // In the WindowContainer implementation we immediately mark ready
         // since a generic WindowContainer only needs to wait for its
         // children to finish and is immediately ready from its own
         // perspective but at the WindowState level we need to wait for ourselves
-        // to draw even if the children draw first our don't need to sync, so we omit
-        // the set ready call until later in finishDrawing()
+        // to draw even if the children draw first our don't need to sync, so we start
+        // in WAITING state rather than READY.
+        mSyncState = SYNC_STATE_WAITING_FOR_DRAW;
+        requestRedrawForSync();
+
         mWmService.mH.removeMessages(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this);
         mWmService.mH.sendNewMessageDelayed(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this,
             BLAST_TIMEOUT_DURATION);
-
         return true;
     }
 
     boolean finishDrawing(SurfaceControl.Transaction postDrawTransaction) {
-        if (!mUsingBLASTSyncTransaction) {
+        if (!onSyncFinishedDrawing()) {
             return mWinAnimator.finishDrawingLocked(postDrawTransaction);
         }
 
         if (postDrawTransaction != null) {
-            mBLASTSyncTransaction.merge(postDrawTransaction);
+            mSyncTransaction.merge(postDrawTransaction);
         }
 
-        mNotifyBlastOnSurfacePlacement = true;
         mWinAnimator.finishDrawingLocked(null);
         // We always want to force a traversal after a finish draw for blast sync.
         return true;
     }
 
-    private void notifyBlastSyncTransaction() {
+    void immediatelyNotifyBlastSync() {
+        finishDrawing(null);
         mWmService.mH.removeMessages(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this);
-
-        if (!mNotifyBlastOnSurfacePlacement || mWaitingListener == null) {
-            mNotifyBlastOnSurfacePlacement = false;
-            return;
-        }
+        if (!useBLASTSync()) return;
 
         final Task task = getTask();
         if (task != null) {
             final SurfaceControl.Transaction t = task.getMainWindowSizeChangeTransaction();
             if (t != null) {
-                mBLASTSyncTransaction.merge(t);
+                mSyncTransaction.merge(t);
             }
             task.setMainWindowSizeChangeTransaction(null);
         }
-
-        // If localSyncId is >0 then we are syncing with children and will
-        // invoke transaction ready from our own #transactionReady callback
-        // we just need to signal our side of the sync (setReady). But if we
-        // have no sync operation at this level transactionReady will never
-        // be invoked and we need to invoke it ourself.
-        if (mLocalSyncId >= 0) {
-            mBLASTSyncEngine.setReady(mLocalSyncId);
-            return;
-        }
-
-        mWaitingListener.onTransactionReady(mWaitingSyncId,  Collections.singleton(this));
-
-        mWaitingSyncId = 0;
-        mWaitingListener = null;
-        mNotifyBlastOnSurfacePlacement = false;
     }
 
-    void immediatelyNotifyBlastSync() {
-        finishDrawing(null);
-        notifyBlastSyncTransaction();
+    @Override
+    boolean fillsParent() {
+        return mAttrs.type == TYPE_APPLICATION_STARTING;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 13d8dc4..0111d48 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -359,8 +359,8 @@
             // surface before destroying it.
             if (mSurfaceController != null && mPendingDestroySurface != null) {
                 mPostDrawTransaction.reparentChildren(
-                    mSurfaceController.getClientViewRootSurface(),
-                    mPendingDestroySurface.getClientViewRootSurface()).apply();
+                    mSurfaceController.mSurfaceControl,
+                    mPendingDestroySurface.mSurfaceControl).apply();
             }
             destroySurfaceLocked();
             mSurfaceDestroyDeferred = true;
@@ -371,7 +371,7 @@
             // Our SurfaceControl is always at layer 0 within the parent Surface managed by
             // window-state. We want this old Surface to stay on top of the new one
             // until we do the swap, so we place it at a positive layer.
-            t.setLayer(mSurfaceController.getClientViewRootSurface(), PRESERVED_SURFACE_LAYER);
+            t.setLayer(mSurfaceController.mSurfaceControl, PRESERVED_SURFACE_LAYER);
         }
         mDestroyPreservedSurfaceUponRedraw = true;
         mSurfaceDestroyDeferred = true;
@@ -393,8 +393,8 @@
                 && !mPendingDestroySurface.mChildrenDetached
                 && (mWin.mActivityRecord == null || !mWin.mActivityRecord.isRelaunching())) {
             mPostDrawTransaction.reparentChildren(
-                    mPendingDestroySurface.getClientViewRootSurface(),
-                    mSurfaceController.getClientViewRootSurface()).apply();
+                    mPendingDestroySurface.mSurfaceControl,
+                    mSurfaceController.mSurfaceControl).apply();
         }
 
         destroyDeferredSurfaceLocked();
@@ -652,7 +652,7 @@
       return true;
     }
 
-    void setSurfaceBoundariesLocked(final boolean recoveringMemory) {
+    void setSurfaceBoundariesLocked(SurfaceControl.Transaction t, final boolean recoveringMemory) {
         if (mSurfaceController == null) {
             return;
         }
@@ -662,11 +662,11 @@
         final Task task = w.getTask();
 
         if (shouldConsumeMainWindowSizeTransaction()) {
-            task.getMainWindowSizeChangeTask().getSurfaceControl().deferTransactionUntil(
-                    mWin.getClientViewRootSurface(), mWin.getFrameNumber());
-            mSurfaceController.deferTransactionUntil(mWin.getClientViewRootSurface(),
-                    mWin.getFrameNumber());
-            SurfaceControl.mergeToGlobalTransaction(task.getMainWindowSizeChangeTransaction());
+            t.deferTransactionUntil(task.getMainWindowSizeChangeTask().getSurfaceControl(),
+                mWin.getClientViewRootSurface(), mWin.getFrameNumber());
+            t.deferTransactionUntil(mSurfaceController.mSurfaceControl,
+                mWin.getClientViewRootSurface(), mWin.getFrameNumber());
+            t.merge(task.getMainWindowSizeChangeTransaction());
             task.setMainWindowSizeChangeTransaction(null);
         }
 
@@ -691,8 +691,8 @@
                     // the WS position is reset (so the stack position is shown) at the same
                     // time that the buffer size changes.
                     setOffsetPositionForStackResize(false);
-                    mSurfaceController.deferTransactionUntil(mWin.getClientViewRootSurface(),
-                            mWin.getFrameNumber());
+                    t.deferTransactionUntil(mSurfaceController.mSurfaceControl,
+                        mWin.getClientViewRootSurface(), mWin.getFrameNumber());
                 } else {
                     final Task stack = mWin.getRootTask();
                     mTmpPos.x = 0;
@@ -705,9 +705,9 @@
                 }
             }
             if (!mIsWallpaper) {
-                mSurfaceController.setPositionInTransaction(xOffset, yOffset, recoveringMemory);
+                mSurfaceController.setPosition(t, xOffset, yOffset, recoveringMemory);
             } else {
-                setWallpaperPositionAndScale(
+                setWallpaperPositionAndScale(t,
                         xOffset, yOffset, mWallpaperScale, recoveringMemory);
             }
         }
@@ -716,7 +716,7 @@
             // Wallpaper is already updated above when calling setWallpaperPositionAndScale so
             // we only need to consider the non-wallpaper case here.
             if (!mIsWallpaper) {
-                mSurfaceController.setMatrixInTransaction(
+                mSurfaceController.setMatrix(t,
                     mDsDx * w.mHScale,
                     mDtDx * w.mVScale,
                     mDtDy * w.mHScale,
@@ -738,7 +738,7 @@
         }
     }
 
-    void prepareSurfaceLocked(final boolean recoveringMemory) {
+    void prepareSurfaceLocked(SurfaceControl.Transaction t, final boolean recoveringMemory) {
         final WindowState w = mWin;
         if (!hasSurface()) {
 
@@ -755,7 +755,7 @@
 
         computeShownFrameLocked();
 
-        setSurfaceBoundariesLocked(recoveringMemory);
+        setSurfaceBoundariesLocked(t, recoveringMemory);
 
         if (mIsWallpaper && !w.mWallpaperVisible) {
             // Wallpaper is no longer visible and there is no wp target => hide it.
@@ -797,7 +797,7 @@
             boolean prepared = true;
 
             if (mIsWallpaper) {
-                setWallpaperPositionAndScale(
+                setWallpaperPositionAndScale(t,
                         mXOffset, mYOffset, mWallpaperScale, recoveringMemory);
             } else {
                 prepared =
@@ -859,6 +859,7 @@
 
         if (displayed) {
             w.mToken.hasVisible = true;
+            mSurfaceController.setBackgroundBlurRadius(w.mAttrs.backgroundBlurRadius);
         }
     }
 
@@ -884,7 +885,8 @@
                     Slog.i(TAG, ">>> OPEN TRANSACTION setWallpaperOffset");
                 }
                 mService.openSurfaceTransaction();
-                setWallpaperPositionAndScale(dx, dy, scale, false);
+                setWallpaperPositionAndScale(SurfaceControl.getGlobalTransaction(),
+                    dx, dy, scale, false);
             } catch (RuntimeException e) {
                 Slog.w(TAG, "Error positioning surface of " + mWin
                         + " pos=(" + dx + "," + dy + ")", e);
@@ -899,8 +901,8 @@
         return true;
     }
 
-    private void setWallpaperPositionAndScale(int dx, int dy, float scale,
-            boolean recoveringMemory) {
+    private void setWallpaperPositionAndScale(SurfaceControl.Transaction t,
+        int dx, int dy, float scale, boolean recoveringMemory) {
         DisplayInfo displayInfo = mWin.getDisplayInfo();
         Matrix matrix = mWin.mTmpMatrix;
         matrix.setTranslate(dx, dy);
@@ -909,9 +911,9 @@
         matrix.getValues(mWin.mTmpMatrixArray);
         matrix.reset();
 
-        mSurfaceController.setPositionInTransaction(mWin.mTmpMatrixArray[MTRANS_X],
+        mSurfaceController.setPosition(t,mWin.mTmpMatrixArray[MTRANS_X],
                 mWin.mTmpMatrixArray[MTRANS_Y], recoveringMemory);
-        mSurfaceController.setMatrixInTransaction(
+        mSurfaceController.setMatrix(t,
                 mDsDx * mWin.mTmpMatrixArray[MSCALE_X] * mWin.mHScale,
                 mDtDx * mWin.mTmpMatrixArray[MSKEW_Y] * mWin.mVScale,
                 mDtDy * mWin.mTmpMatrixArray[MSKEW_X] * mWin.mHScale,
@@ -970,10 +972,6 @@
      * @return Returns true if the surface was successfully shown.
      */
     private boolean showSurfaceRobustlyLocked() {
-        if (mWin.getWindowConfiguration().windowsAreScaleable()) {
-            mSurfaceController.forceScaleableInTransaction(true);
-        }
-
         boolean shown = mSurfaceController.showRobustlyInTransaction();
         if (!shown)
             return false;
@@ -987,8 +985,8 @@
             // Instead let the children get removed when the old surface is deleted.
             if (!mPendingDestroySurface.mChildrenDetached) {
                 mPostDrawTransaction.reparentChildren(
-                        mPendingDestroySurface.getClientViewRootSurface(),
-                        mSurfaceController.getClientViewRootSurface());
+                        mPendingDestroySurface.mSurfaceControl,
+                        mSurfaceController.mSurfaceControl);
             }
         }
 
@@ -1204,10 +1202,10 @@
         mOffsetPositionForStackResize = offsetPositionForStackResize;
     }
 
-    SurfaceControl getClientViewRootSurface() {
+    SurfaceControl getSurfaceControl() {
         if (!hasSurface()) {
             return null;
         }
-        return mSurfaceController.getClientViewRootSurface();
+        return mSurfaceController.mSurfaceControl;
     }
 }
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 9b40822..21cb9a7 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -30,7 +30,6 @@
 import static com.android.server.wm.WindowSurfaceControllerProto.LAYER;
 import static com.android.server.wm.WindowSurfaceControllerProto.SHOWN;
 
-import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.Debug;
 import android.os.Trace;
@@ -51,18 +50,12 @@
 
     SurfaceControl mSurfaceControl;
 
-    /**
-     * WM only uses for deferred transactions.
-     */
-    SurfaceControl mBLASTSurfaceControl;
-
     // Should only be set from within setShown().
     private boolean mSurfaceShown = false;
     private float mSurfaceX = 0;
     private float mSurfaceY = 0;
     private int mSurfaceW = 0;
     private int mSurfaceH = 0;
-    private Rect mSurfaceCrop = new Rect(0, 0, -1, -1);
 
     // Initialize to the identity matrix.
     private float mLastDsdx = 1;
@@ -70,17 +63,12 @@
     private float mLastDsdy = 0;
     private float mLastDtdy = 1;
 
+    private int mLastBackgroundBlurRadius = 0;
+
     private float mSurfaceAlpha = 0;
 
     private int mSurfaceLayer = 0;
 
-    // Surface flinger doesn't support crop rectangles where width or height is non-positive.
-    // However, we need to somehow handle the situation where the cropping would completely hide
-    // the window. We achieve this by explicitly hiding the surface and not letting it be shown.
-    private boolean mHiddenForCrop = false;
-
-    // Initially a surface is hidden after just being created.
-    private boolean mHiddenForOtherReasons = true;
     private final String title;
 
     private final WindowManagerService mService;
@@ -121,32 +109,16 @@
 
         final boolean useBLAST = mService.mUseBLAST && ((win.getAttrs().privateFlags &
                 WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST) != 0);
+
         if (useBLAST) {
-            b.setContainerLayer();
+            b.setBLASTLayer();
         }
 
         mSurfaceControl = b.build();
 
-        if (useBLAST) {
-            mBLASTSurfaceControl = win.makeSurface()
-                .setParent(mSurfaceControl)
-                .setName(name + "(BLAST)")
-                .setHidden(false)
-                .setBLASTLayer()
-                .setCallsite("WindowSurfaceController")
-                .build();
-        }
-
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
     }
 
-    void reparentChildrenInTransaction(WindowSurfaceController other) {
-        ProtoLog.i(WM_SHOW_TRANSACTIONS, "REPARENT from: %s to: %s", this, other);
-        if ((mSurfaceControl != null) && (other.mSurfaceControl != null)) {
-            mSurfaceControl.reparentChildren(other.mSurfaceControl);
-        }
-    }
-
     void detachChildren() {
         ProtoLog.i(WM_SHOW_TRANSACTIONS, "SEVER CHILDREN");
         mChildrenDetached = true;
@@ -157,7 +129,6 @@
 
     void hide(SurfaceControl.Transaction transaction, String reason) {
         ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE HIDE ( %s ): %s", reason, title);
-        mHiddenForOtherReasons = true;
 
         mAnimator.destroyPreservedSurfaceLocked();
         if (mSurfaceShown) {
@@ -189,57 +160,9 @@
         } finally {
             setShown(false);
             mSurfaceControl = null;
-            if (mBLASTSurfaceControl != null) {
-                mBLASTSurfaceControl.release();
-            }
         }
     }
 
-    void setCropInTransaction(Rect clipRect, boolean recoveringMemory) {
-        ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE CROP %s: %s", clipRect.toShortString(), title);
-        try {
-            if (clipRect.width() > 0 && clipRect.height() > 0) {
-                if (!clipRect.equals(mSurfaceCrop)) {
-                    mSurfaceControl.setWindowCrop(clipRect);
-                    mSurfaceCrop.set(clipRect);
-                }
-                mHiddenForCrop = false;
-                updateVisibility();
-            } else {
-                mHiddenForCrop = true;
-                mAnimator.destroyPreservedSurfaceLocked();
-                updateVisibility();
-            }
-        } catch (RuntimeException e) {
-            Slog.w(TAG, "Error setting crop surface of " + this
-                    + " crop=" + clipRect.toShortString(), e);
-            if (!recoveringMemory) {
-                mAnimator.reclaimSomeSurfaceMemory("crop", true);
-            }
-        }
-    }
-
-    void clearCropInTransaction(boolean recoveringMemory) {
-        ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE CLEAR CROP: %s", title);
-        try {
-            Rect clipRect = new Rect(0, 0, -1, -1);
-            if (mSurfaceCrop.equals(clipRect)) {
-                return;
-            }
-            mSurfaceControl.setWindowCrop(clipRect);
-            mSurfaceCrop.set(clipRect);
-        } catch (RuntimeException e) {
-            Slog.w(TAG, "Error setting clearing crop of " + this, e);
-            if (!recoveringMemory) {
-                mAnimator.reclaimSomeSurfaceMemory("crop", true);
-            }
-        }
-    }
-
-    void setPositionInTransaction(float left, float top, boolean recoveringMemory) {
-        setPosition(null, left, top, recoveringMemory);
-    }
-
     void setPosition(SurfaceControl.Transaction t, float left, float top,
             boolean recoveringMemory) {
         final boolean surfaceMoved = mSurfaceX != left || mSurfaceY != top;
@@ -251,11 +174,7 @@
                 ProtoLog.i(WM_SHOW_TRANSACTIONS,
                         "SURFACE POS (setPositionInTransaction) @ (%f,%f): %s", left, top, title);
 
-                if (t == null) {
-                    mSurfaceControl.setPosition(left, top);
-                } else {
-                    t.setPosition(mSurfaceControl, left, top);
-                }
+                t.setPosition(mSurfaceControl, left, top);
             } catch (RuntimeException e) {
                 Slog.w(TAG, "Error positioning surface of " + this
                         + " pos=(" + left + "," + top + ")", e);
@@ -266,11 +185,6 @@
         }
     }
 
-    void setMatrixInTransaction(float dsdx, float dtdx, float dtdy, float dsdy,
-            boolean recoveringMemory) {
-        setMatrix(null, dsdx, dtdx, dtdy, dsdy, false);
-    }
-
     void setMatrix(SurfaceControl.Transaction t, float dsdx, float dtdx,
             float dtdy, float dsdy, boolean recoveringMemory) {
         final boolean matrixChanged = mLastDsdx != dsdx || mLastDtdx != dtdx ||
@@ -287,11 +201,7 @@
         try {
             ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE MATRIX [%f,%f,%f,%f]: %s",
                     dsdx, dtdx, dtdy, dsdy, title);
-            if (t == null) {
-                mSurfaceControl.setMatrix(dsdx, dtdx, dtdy, dsdy);
-            } else {
-                t.setMatrix(mSurfaceControl, dsdx, dtdx, dtdy, dsdy);
-            }
+            t.setMatrix(mSurfaceControl, dsdx, dtdx, dtdy, dsdy);
         } catch (RuntimeException e) {
             // If something goes wrong with the surface (such
             // as running out of memory), don't take down the
@@ -304,31 +214,6 @@
         }
     }
 
-    boolean setBufferSizeInTransaction(int width, int height, boolean recoveringMemory) {
-        final boolean surfaceResized = mSurfaceW != width || mSurfaceH != height;
-        if (surfaceResized) {
-            mSurfaceW = width;
-            mSurfaceH = height;
-
-            try {
-                ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE SIZE %dx%d: %s", width, height, title);
-                mSurfaceControl.setBufferSize(width, height);
-            } catch (RuntimeException e) {
-                // If something goes wrong with the surface (such
-                // as running out of memory), don't take down the
-                // entire system.
-                Slog.e(TAG, "Error resizing surface of " + title
-                        + " size=(" + width + "x" + height + ")", e);
-                if (!recoveringMemory) {
-                    mAnimator.reclaimSomeSurfaceMemory("size", true);
-                }
-                return false;
-            }
-            return true;
-        }
-        return false;
-    }
-
     boolean prepareToShowInTransaction(float alpha,
             float dsdx, float dtdx, float dsdy,
             float dtdy, boolean recoveringMemory) {
@@ -385,6 +270,26 @@
         }
     }
 
+    void setBackgroundBlurRadius(int radius) {
+        ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE backgroundBlur=%o: %s", radius, title);
+
+        if (mSurfaceControl == null || radius == mLastBackgroundBlurRadius) {
+            return;
+        }
+        mLastBackgroundBlurRadius = radius;
+
+        if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setBackgroundBlurRadius");
+        mService.openSurfaceTransaction();
+        try {
+            mSurfaceControl.setBackgroundBlurRadius(radius);
+        } finally {
+            mService.closeSurfaceTransaction("setBackgroundBlurRadius");
+            if (SHOW_LIGHT_TRANSACTIONS) {
+                Slog.i(TAG, "<<< CLOSE TRANSACTION setBackgroundBlurRadius");
+            }
+        }
+    }
+
     void setSecure(boolean isSecure) {
         ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE isSecure=%b: %s", isSecure, title);
 
@@ -421,35 +326,15 @@
         }
     }
 
-    void getContainerRect(Rect rect) {
-        mAnimator.getContainerRect(rect);
-    }
-
     boolean showRobustlyInTransaction() {
         ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE SHOW (performLayout): %s", title);
         if (DEBUG_VISIBILITY) Slog.v(TAG, "Showing " + this
                 + " during relayout");
-        mHiddenForOtherReasons = false;
-        return updateVisibility();
-    }
 
-    private boolean updateVisibility() {
-        if (mHiddenForCrop || mHiddenForOtherReasons) {
-            if (mSurfaceShown) {
-                hideSurface(mTmpTransaction);
-                SurfaceControl.mergeToGlobalTransaction(mTmpTransaction);
-            }
-            return false;
-        } else {
-            if (!mSurfaceShown) {
-                return showSurface();
-            } else {
-                return true;
-            }
+        if (mSurfaceShown) {
+            return true;
         }
-    }
 
-    private boolean showSurface() {
         try {
             setShown(true);
             mSurfaceControl.show();
@@ -459,7 +344,6 @@
         }
 
         mAnimator.reclaimSomeSurfaceMemory("show", true);
-
         return false;
     }
 
@@ -468,13 +352,6 @@
         mSurfaceControl.deferTransactionUntil(barrier, frame);
     }
 
-    void forceScaleableInTransaction(boolean force) {
-        // -1 means we don't override the default or client specified
-        // scaling mode.
-        int scalingMode = force ? SCALING_MODE_SCALE_TO_WINDOW : -1;
-        mSurfaceControl.setOverrideScalingMode(scalingMode);
-    }
-
     boolean clearWindowContentFrameStats() {
         if (mSurfaceControl == null) {
             return false;
@@ -489,7 +366,6 @@
         return mSurfaceControl.getContentFrameStats(outStats);
     }
 
-
     boolean hasSurface() {
         return mSurfaceControl != null;
     }
@@ -498,16 +374,6 @@
         outSurfaceControl.copyFrom(mSurfaceControl, "WindowSurfaceController.getSurfaceControl");
     }
 
-    void getBLASTSurfaceControl(SurfaceControl outSurfaceControl) {
-        if (mBLASTSurfaceControl != null) {
-            outSurfaceControl.copyFrom(mBLASTSurfaceControl, "WindowSurfaceController.getBLASTSurfaceControl");
-        }
-    }
-
-    int getLayer() {
-        return mSurfaceLayer;
-    }
-
     boolean getShown() {
         return mSurfaceShown;
     }
@@ -524,14 +390,6 @@
         }
     }
 
-    float getX() {
-        return mSurfaceX;
-    }
-
-    float getY() {
-        return mSurfaceY;
-    }
-
     int getWidth() {
         return mSurfaceW;
     }
@@ -540,21 +398,6 @@
         return mSurfaceH;
     }
 
-    /**
-     * Returns the Surface which the client-framework ViewRootImpl will be using.
-     * This is either the WSA SurfaceControl or it's BLAST child surface.
-     * This has too main uses:
-     * 1. This is the Surface the client will add children to, we use this to make
-     *    sure we don't reparent the BLAST surface itself when calling reparentChildren
-     * 2. We use this as the barrier Surface for some deferTransaction operations.
-     */
-    SurfaceControl getClientViewRootSurface() {
-        if (mBLASTSurfaceControl != null) {
-            return mBLASTSurfaceControl;
-        }
-        return mSurfaceControl;
-    }
-
     void dumpDebug(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
         proto.write(SHOWN, mSurfaceShown);
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index fa0b8cc..5ced6a5 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -335,6 +335,13 @@
     }
 
     /**
+     * @return {@code true} if this window token has bounds for size compatibility mode.
+     */
+    boolean hasSizeCompatBounds() {
+        return false;
+    }
+
+    /**
      * Returns true if the new window is considered greater than the existing window in terms of
      * z-order.
      */
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 30f6fa6..9f83baf 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -21,6 +21,7 @@
         "BroadcastRadio/TunerCallback.cpp",
         "BroadcastRadio/convert.cpp",
         "BroadcastRadio/regions.cpp",
+        "gnss/GnssConfiguration.cpp",
         "stats/PowerStatsPuller.cpp",
         "stats/SubsystemSleepStatePuller.cpp",
         "com_android_server_adb_AdbDebuggingManager.cpp",
@@ -37,7 +38,6 @@
         "com_android_server_locksettings_SyntheticPasswordManager.cpp",
         "com_android_server_net_NetworkStatsService.cpp",
         "com_android_server_power_PowerManagerService.cpp",
-        "com_android_server_powerstats_PowerStatsService.cpp",
         "com_android_server_security_VerityUtils.cpp",
         "com_android_server_SerialService.cpp",
         "com_android_server_soundtrigger_middleware_AudioSessionProviderImpl.cpp",
diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp
index 76b1713..43f50bf 100644
--- a/services/core/jni/com_android_server_SystemServer.cpp
+++ b/services/core/jni/com_android_server_SystemServer.cpp
@@ -23,6 +23,7 @@
 #include <jni.h>
 #include <nativehelper/JNIHelp.h>
 
+#include <android/hidl/manager/1.2/IServiceManager.h>
 #include <binder/IServiceManager.h>
 #include <hidl/HidlTransportSupport.h>
 #include <incremental_service.h>
@@ -64,6 +65,7 @@
     using ::android::frameworks::stats::V1_0::IStats;
     using ::android::frameworks::stats::V1_0::implementation::StatsHal;
     using ::android::hardware::configureRpcThreadpool;
+    using ::android::hidl::manager::V1_0::IServiceManager;
 
     status_t err;
 
@@ -74,15 +76,22 @@
 
     sp<ISensorManager> sensorService = new SensorManager(vm);
     err = sensorService->registerAsService();
-    ALOGE_IF(err != OK, "Cannot register %s: %d", ISensorManager::descriptor, err);
+    LOG_ALWAYS_FATAL_IF(err != OK, "Cannot register %s: %d", ISensorManager::descriptor, err);
 
     sp<ISchedulingPolicyService> schedulingService = new SchedulingPolicyService();
-    err = schedulingService->registerAsService();
-    ALOGE_IF(err != OK, "Cannot register %s: %d", ISchedulingPolicyService::descriptor, err);
+    if (IServiceManager::Transport::HWBINDER ==
+        hardware::defaultServiceManager1_2()->getTransport(ISchedulingPolicyService::descriptor,
+                                                           "default")) {
+        err = schedulingService->registerAsService("default");
+        LOG_ALWAYS_FATAL_IF(err != OK, "Cannot register %s: %d",
+                            ISchedulingPolicyService::descriptor, err);
+    } else {
+        ALOGW("%s is deprecated. Skipping registration.", ISchedulingPolicyService::descriptor);
+    }
 
     sp<IStats> statsHal = new StatsHal();
     err = statsHal->registerAsService();
-    ALOGE_IF(err != OK, "Cannot register %s: %d", IStats::descriptor, err);
+    LOG_ALWAYS_FATAL_IF(err != OK, "Cannot register %s: %d", IStats::descriptor, err);
 }
 
 static void android_server_SystemServer_initZygoteChildHeapProfiling(JNIEnv* /* env */,
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index e39a3d1..c1d5f19 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -74,6 +74,9 @@
 
 using android::base::ParseUint;
 using android::base::StringPrintf;
+using android::os::BlockUntrustedTouchesMode;
+using android::os::InputEventInjectionResult;
+using android::os::InputEventInjectionSync;
 
 // Maximum allowable delay value in a vibration pattern before
 // which the delay will be truncated.
@@ -96,6 +99,7 @@
     jmethodID notifyInputChannelBroken;
     jmethodID notifyANR;
     jmethodID notifyFocusChanged;
+    jmethodID notifyUntrustedTouch;
     jmethodID filterInputEvent;
     jmethodID interceptKeyBeforeQueueing;
     jmethodID interceptMotionBeforeQueueingNonInteractive;
@@ -253,6 +257,7 @@
             const sp<IBinder>& token, const std::string& reason) override;
     void notifyInputChannelBroken(const sp<IBinder>& token) override;
     void notifyFocusChanged(const sp<IBinder>& oldToken, const sp<IBinder>& newToken) override;
+    void notifyUntrustedTouch(const std::string& obscuringPackage) override;
     bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override;
     void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override;
     void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags) override;
@@ -752,6 +757,17 @@
     }
 }
 
+void NativeInputManager::notifyUntrustedTouch(const std::string& obscuringPackage) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+    ALOGD("notifyUntrustedTouch - obscuringPackage=%s", obscuringPackage.c_str());
+#endif
+    ATRACE_CALL();
+    JNIEnv* env = jniEnv();
+    jstring jPackage = env->NewStringUTF(obscuringPackage.c_str());
+    env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyUntrustedTouch, jPackage);
+    checkAndClearExceptionFromCallback(env, "notifyUntrustedTouch");
+}
+
 void NativeInputManager::notifyFocusChanged(const sp<IBinder>& oldToken,
         const sp<IBinder>& newToken) {
 #if DEBUG_INPUT_DISPATCHER_POLICY
@@ -1453,22 +1469,40 @@
     im->getInputManager()->getDispatcher()->setInTouchMode(inTouchMode);
 }
 
+static void nativeSetMaximumObscuringOpacityForTouch(JNIEnv* /* env */, jclass /* clazz */,
+                                                     jlong ptr, jfloat opacity) {
+    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+    im->getInputManager()->getDispatcher()->setMaximumObscuringOpacityForTouch(opacity);
+}
+
+static void nativeSetBlockUntrustedTouchesMode(JNIEnv* env, jclass /* clazz */, jlong ptr,
+                                               jint mode) {
+    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+    im->getInputManager()->getDispatcher()->setBlockUntrustedTouchesMode(
+            static_cast<BlockUntrustedTouchesMode>(mode));
+}
+
 static jint nativeInjectInputEvent(JNIEnv* env, jclass /* clazz */,
         jlong ptr, jobject inputEventObj, jint injectorPid, jint injectorUid,
         jint syncMode, jint timeoutMillis, jint policyFlags) {
     NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
 
+    // static_cast is safe because the value was already checked at the Java layer
+    InputEventInjectionSync mode = static_cast<InputEventInjectionSync>(syncMode);
+
     if (env->IsInstanceOf(inputEventObj, gKeyEventClassInfo.clazz)) {
         KeyEvent keyEvent;
         status_t status = android_view_KeyEvent_toNative(env, inputEventObj, & keyEvent);
         if (status) {
             jniThrowRuntimeException(env, "Could not read contents of KeyEvent object.");
-            return INPUT_EVENT_INJECTION_FAILED;
+            return static_cast<jint>(InputEventInjectionResult::FAILED);
         }
 
-        const int32_t result =
+        const InputEventInjectionResult result =
                 im->getInputManager()->getDispatcher()->injectInputEvent(&keyEvent, injectorPid,
-                                                                         injectorUid, syncMode,
+                                                                         injectorUid, mode,
                                                                          std::chrono::milliseconds(
                                                                                  timeoutMillis),
                                                                          uint32_t(policyFlags));
@@ -1477,19 +1511,19 @@
         const MotionEvent* motionEvent = android_view_MotionEvent_getNativePtr(env, inputEventObj);
         if (!motionEvent) {
             jniThrowRuntimeException(env, "Could not read contents of MotionEvent object.");
-            return INPUT_EVENT_INJECTION_FAILED;
+            return static_cast<jint>(InputEventInjectionResult::FAILED);
         }
 
-        const int32_t result =
-                (jint)im->getInputManager()
-                        ->getDispatcher()
-                        ->injectInputEvent(motionEvent, injectorPid, injectorUid, syncMode,
-                                           std::chrono::milliseconds(timeoutMillis),
-                                           uint32_t(policyFlags));
+        const InputEventInjectionResult result =
+                im->getInputManager()->getDispatcher()->injectInputEvent(motionEvent, injectorPid,
+                                                                         injectorUid, mode,
+                                                                         std::chrono::milliseconds(
+                                                                                 timeoutMillis),
+                                                                         uint32_t(policyFlags));
         return static_cast<jint>(result);
     } else {
         jniThrowRuntimeException(env, "Invalid input event type.");
-        return INPUT_EVENT_INJECTION_FAILED;
+        return static_cast<jint>(InputEventInjectionResult::FAILED);
     }
 }
 
@@ -1790,6 +1824,9 @@
         {"nativePilferPointers", "(JLandroid/os/IBinder;)V", (void*)nativePilferPointers},
         {"nativeSetInputFilterEnabled", "(JZ)V", (void*)nativeSetInputFilterEnabled},
         {"nativeSetInTouchMode", "(JZ)V", (void*)nativeSetInTouchMode},
+        {"nativeSetMaximumObscuringOpacityForTouch", "(JF)V",
+         (void*)nativeSetMaximumObscuringOpacityForTouch},
+        {"nativeSetBlockUntrustedTouchesMode", "(JI)V", (void*)nativeSetBlockUntrustedTouchesMode},
         {"nativeInjectInputEvent", "(JLandroid/view/InputEvent;IIIII)I",
          (void*)nativeInjectInputEvent},
         {"nativeVerifyInputEvent", "(JLandroid/view/InputEvent;)Landroid/view/VerifiedInputEvent;",
@@ -1869,6 +1906,9 @@
     GET_METHOD_ID(gServiceClassInfo.notifyFocusChanged, clazz,
             "notifyFocusChanged", "(Landroid/os/IBinder;Landroid/os/IBinder;)V");
 
+    GET_METHOD_ID(gServiceClassInfo.notifyUntrustedTouch, clazz, "notifyUntrustedTouch",
+                  "(Ljava/lang/String;)V");
+
     GET_METHOD_ID(gServiceClassInfo.notifyANR, clazz,
             "notifyANR",
             "(Landroid/view/InputApplicationHandle;Landroid/os/IBinder;Ljava/lang/String;)J");
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 91645ba..e9d048a 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "GnssLocationProvider"
-
+// Define LOG_TAG and LOG_NDEBUG before <log/log.h> to overwrite the default values.
+#define LOG_TAG "GnssLocationProviderJni"
 #define LOG_NDEBUG 0
 
 #include <android/hardware/gnss/1.0/IGnss.h>
@@ -39,6 +39,7 @@
 #include <nativehelper/JNIHelp.h>
 #include "android_runtime/AndroidRuntime.h"
 #include "android_runtime/Log.h"
+#include "gnss/GnssConfiguration.h"
 #include "hardware_legacy/power.h"
 #include "jni.h"
 #include "utils/Log.h"
@@ -59,7 +60,6 @@
 static jclass class_location;
 static jclass class_gnssNavigationMessage;
 static jclass class_gnssClock;
-static jclass class_gnssConfiguration_halInterfaceVersion;
 static jclass class_gnssAntennaInfoBuilder;
 static jclass class_phaseCenterOffset;
 static jclass class_sphericalCorrections;
@@ -125,7 +125,6 @@
 static jmethodID method_gnssNavigationMessageCtor;
 static jmethodID method_gnssClockCtor;
 static jmethodID method_gnssMeasurementCtor;
-static jmethodID method_halInterfaceVersionCtor;
 static jmethodID method_gnssAntennaInfoBuilderCtor;
 static jmethodID method_phaseCenterOffsetCtor;
 static jmethodID method_sphericalCorrectionsCtor;
@@ -149,6 +148,7 @@
 using android::String16;
 using android::wp;
 using android::binder::Status;
+using android::gnss::GnssConfigurationInterface;
 
 using android::hardware::Return;
 using android::hardware::Void;
@@ -156,7 +156,6 @@
 using android::hardware::hidl_string;
 using android::hardware::hidl_death_recipient;
 
-using android::hardware::gnss::PsdsType;
 using android::hardware::gnss::V1_0::GnssLocationFlags;
 using android::hardware::gnss::V1_0::IAGnssRilCallback;
 using android::hardware::gnss::V1_0::IGnssGeofenceCallback;
@@ -193,10 +192,6 @@
 using IGnssCallback_V1_0 = android::hardware::gnss::V1_0::IGnssCallback;
 using IGnssCallback_V2_0 = android::hardware::gnss::V2_0::IGnssCallback;
 using IGnssCallback_V2_1 = android::hardware::gnss::V2_1::IGnssCallback;
-using IGnssConfiguration_V1_0 = android::hardware::gnss::V1_0::IGnssConfiguration;
-using IGnssConfiguration_V1_1 = android::hardware::gnss::V1_1::IGnssConfiguration;
-using IGnssConfiguration_V2_0 = android::hardware::gnss::V2_0::IGnssConfiguration;
-using IGnssConfiguration_V2_1 = android::hardware::gnss::V2_1::IGnssConfiguration;
 using IGnssDebug_V1_0 = android::hardware::gnss::V1_0::IGnssDebug;
 using IGnssDebug_V2_0 = android::hardware::gnss::V2_0::IGnssDebug;
 using IGnssAntennaInfo = android::hardware::gnss::V2_1::IGnssAntennaInfo;
@@ -228,9 +223,13 @@
 using android::hardware::gnss::visibility_control::V1_0::IGnssVisibilityControl;
 using android::hardware::gnss::visibility_control::V1_0::IGnssVisibilityControlCallback;
 
+using android::hardware::gnss::BlocklistedSource;
+using android::hardware::gnss::GnssConstellationType;
+using android::hardware::gnss::PsdsType;
 using IGnssAidl = android::hardware::gnss::IGnss;
 using IGnssPsdsAidl = android::hardware::gnss::IGnssPsds;
 using IGnssPsdsCallbackAidl = android::hardware::gnss::IGnssPsdsCallback;
+using IGnssConfigurationAidl = android::hardware::gnss::IGnssConfiguration;
 
 struct GnssDeathRecipient : virtual public hidl_death_recipient
 {
@@ -266,10 +265,6 @@
 sp<IGnssBatching_V2_0> gnssBatchingIface_V2_0 = nullptr;
 sp<IGnssDebug_V1_0> gnssDebugIface = nullptr;
 sp<IGnssDebug_V2_0> gnssDebugIface_V2_0 = nullptr;
-sp<IGnssConfiguration_V1_0> gnssConfigurationIface = nullptr;
-sp<IGnssConfiguration_V1_1> gnssConfigurationIface_V1_1 = nullptr;
-sp<IGnssConfiguration_V2_0> gnssConfigurationIface_V2_0 = nullptr;
-sp<IGnssConfiguration_V2_1> gnssConfigurationIface_V2_1 = nullptr;
 sp<IGnssNi> gnssNiIface = nullptr;
 sp<IGnssMeasurement_V1_0> gnssMeasurementIface = nullptr;
 sp<IGnssMeasurement_V1_1> gnssMeasurementIface_V1_1 = nullptr;
@@ -281,6 +276,8 @@
 sp<IGnssVisibilityControl> gnssVisibilityControlIface = nullptr;
 sp<IGnssAntennaInfo> gnssAntennaInfoIface = nullptr;
 
+std::unique_ptr<GnssConfigurationInterface> gnssConfigurationIface = nullptr;
+
 #define WAKE_LOCK_NAME  "GPS"
 
 namespace android {
@@ -455,25 +452,14 @@
     }
 }
 
-static jboolean checkAidlStatus(const Status& status, const char* errorMessage,
-                                const bool success) {
+static jboolean checkAidlStatus(const Status& status, const char* errorMessage) {
     if (!status.isOk()) {
         ALOGE("%s AIDL transport error: %s", errorMessage, status.toString8().c_str());
         return JNI_FALSE;
     }
-    if (!success) {
-        ALOGE("AIDL return failure: %s", errorMessage);
-        return JNI_FALSE;
-    }
     return JNI_TRUE;
 }
 
-static jobject createHalInterfaceVersionJavaObject(JNIEnv* env, jint major, jint minor) {
-    jobject version = env->NewObject(class_gnssConfiguration_halInterfaceVersion,
-            method_halInterfaceVersionCtor, major, minor);
-    return version;
-}
-
 struct ScopedJniString {
     ScopedJniString(JNIEnv* env, jstring javaString) : mEnv(env), mJavaString(javaString) {
         mNativeString = mEnv->GetStringUTFChars(mJavaString, nullptr);
@@ -2160,13 +2146,6 @@
     class_gnssClock = (jclass) env->NewGlobalRef(gnssClockClass);
     method_gnssClockCtor = env->GetMethodID(class_gnssClock, "<init>", "()V");
 
-    jclass gnssConfiguration_halInterfaceVersionClass = env->FindClass(
-            "com/android/server/location/gnss/GnssConfiguration$HalInterfaceVersion");
-    class_gnssConfiguration_halInterfaceVersion =
-            (jclass) env->NewGlobalRef(gnssConfiguration_halInterfaceVersionClass);
-    method_halInterfaceVersionCtor =
-            env->GetMethodID(class_gnssConfiguration_halInterfaceVersion, "<init>", "(II)V");
-
     jclass arrayListClass = env->FindClass("java/util/ArrayList");
     class_arrayList = (jclass)env->NewGlobalRef(arrayListClass);
     method_arrayListCtor = env->GetMethodID(class_arrayList, "<init>", "()V");
@@ -2174,6 +2153,8 @@
 
     jclass doubleArrayClass = env->FindClass("[D");
     class_doubleArray = (jclass)env->NewGlobalRef(doubleArrayClass);
+
+    gnss::GnssConfiguration_class_init_once(env);
 }
 
 /* Initialization needed at system boot and whenever GNSS service dies. */
@@ -2371,39 +2352,41 @@
         gnssNiIface = gnssNi;
     }
 
-    if (gnssHal_V2_1 != nullptr) {
+    if (gnssHalAidl != nullptr) {
+        sp<IGnssConfigurationAidl> gnssConfigurationAidl;
+        auto status = gnssHalAidl->getExtensionGnssConfiguration(&gnssConfigurationAidl);
+        if (checkAidlStatus(status,
+                            "Unable to get a handle to GnssConfiguration AIDL interface.")) {
+            gnssConfigurationIface =
+                    std::make_unique<android::gnss::GnssConfiguration>(gnssConfigurationAidl);
+        }
+    } else if (gnssHal_V2_1 != nullptr) {
         auto gnssConfiguration = gnssHal_V2_1->getExtensionGnssConfiguration_2_1();
-        if (!gnssConfiguration.isOk()) {
-            ALOGD("Unable to get a handle to GnssConfiguration_V2_1");
-        } else {
-            gnssConfigurationIface_V2_1 = gnssConfiguration;
-            gnssConfigurationIface_V2_0 = gnssConfigurationIface_V2_1;
-            gnssConfigurationIface_V1_1 = gnssConfigurationIface_V2_1;
-            gnssConfigurationIface = gnssConfigurationIface_V2_1;
+        if (checkHidlReturn(gnssConfiguration,
+                            "Unable to get a handle to GnssConfiguration_V2_1")) {
+            gnssConfigurationIface =
+                    std::make_unique<android::gnss::GnssConfiguration_V2_1>(gnssConfiguration);
         }
     } else if (gnssHal_V2_0 != nullptr) {
         auto gnssConfiguration = gnssHal_V2_0->getExtensionGnssConfiguration_2_0();
-        if (!gnssConfiguration.isOk()) {
-            ALOGD("Unable to get a handle to GnssConfiguration_V2_0");
-        } else {
-            gnssConfigurationIface_V2_0 = gnssConfiguration;
-            gnssConfigurationIface_V1_1 = gnssConfigurationIface_V2_0;
-            gnssConfigurationIface = gnssConfigurationIface_V2_0;
+        if (checkHidlReturn(gnssConfiguration,
+                            "Unable to get a handle to GnssConfiguration_V2_0")) {
+            gnssConfigurationIface =
+                    std::make_unique<android::gnss::GnssConfiguration_V2_0>(gnssConfiguration);
         }
     } else if (gnssHal_V1_1 != nullptr) {
         auto gnssConfiguration = gnssHal_V1_1->getExtensionGnssConfiguration_1_1();
-        if (!gnssConfiguration.isOk()) {
-            ALOGD("Unable to get a handle to GnssConfiguration_V1_1");
-        } else {
-            gnssConfigurationIface_V1_1 = gnssConfiguration;
-            gnssConfigurationIface = gnssConfigurationIface_V1_1;
+        if (checkHidlReturn(gnssConfiguration,
+                            "Unable to get a handle to GnssConfiguration_V1_1")) {
+            gnssConfigurationIface =
+                    std::make_unique<android::gnss::GnssConfiguration_V1_1>(gnssConfiguration);
         }
     } else {
-        auto gnssConfiguration_V1_0 = gnssHal->getExtensionGnssConfiguration();
-        if (!gnssConfiguration_V1_0.isOk()) {
-            ALOGD("Unable to get a handle to GnssConfiguration");
-        } else {
-            gnssConfigurationIface = gnssConfiguration_V1_0;
+        auto gnssConfiguration = gnssHal->getExtensionGnssConfiguration();
+        if (checkHidlReturn(gnssConfiguration,
+                            "Unable to get a handle to GnssConfiguration_V1_0")) {
+            gnssConfigurationIface =
+                    std::make_unique<android::gnss::GnssConfiguration_V1_0>(gnssConfiguration);
         }
     }
 
@@ -2459,25 +2442,10 @@
 
 static jobject android_location_GnssConfiguration_get_gnss_configuration_version(
         JNIEnv* env, jclass /* jclazz */) {
-    jint major, minor;
-    if (gnssConfigurationIface_V2_1 != nullptr) {
-        major = 2;
-        minor = 1;
-    }
-    else if (gnssConfigurationIface_V2_0 != nullptr) {
-        major = 2;
-        minor = 0;
-    } else if (gnssConfigurationIface_V1_1 != nullptr) {
-        major = 1;
-        minor = 1;
-    } else if (gnssConfigurationIface != nullptr) {
-        major = 1;
-        minor = 0;
-    } else {
+    if (gnssConfigurationIface == nullptr) {
         return nullptr;
     }
-
-    return createHalInterfaceVersionJavaObject(env, major, minor);
+    return gnssConfigurationIface->getVersion(env);
 }
 
 /* Initialization needed each time the GPS service is shutdown. */
@@ -2519,9 +2487,8 @@
     // Set IGnssPsds or IGnssXtra callback.
     if (gnssPsdsAidlIface != nullptr) {
         sp<IGnssPsdsCallbackAidl> gnssPsdsCallbackAidl = new GnssPsdsCallbackAidl();
-        bool success;
-        auto status = gnssPsdsAidlIface->setCallback(gnssPsdsCallbackAidl, &success);
-        if (!checkAidlStatus(status, "IGnssPsdsAidl setCallback() failed.", success)) {
+        auto status = gnssPsdsAidlIface->setCallback(gnssPsdsCallbackAidl);
+        if (!checkAidlStatus(status, "IGnssPsdsAidl setCallback() failed.")) {
             gnssPsdsAidlIface = nullptr;
         }
     } else {
@@ -2814,13 +2781,11 @@
 
     jbyte* bytes = reinterpret_cast<jbyte *>(env->GetPrimitiveArrayCritical(data, 0));
     if (gnssPsdsAidlIface != nullptr) {
-        bool success;
         auto status = gnssPsdsAidlIface->injectPsdsData(static_cast<PsdsType>(psdsType),
                                                         std::vector<uint8_t>((const uint8_t*)bytes,
                                                                              (const uint8_t*)bytes +
-                                                                                     length),
-                                                        &success);
-        checkAidlStatus(status, "IGnssPsdsAidl injectPsdsData() failed.", success);
+                                                                                     length));
+        checkAidlStatus(status, "IGnssPsdsAidl injectPsdsData() failed.");
     } else if (gnssPsdsIface != nullptr) {
         auto result = gnssPsdsIface->injectPsdsData_3_0(psdsType,
                                                         std::string((const char*)bytes, length));
@@ -3486,9 +3451,7 @@
         ALOGE("%s: IGnssConfiguration interface not available.", __func__);
         return JNI_FALSE;
     }
-
-    auto result = gnssConfigurationIface->setEmergencySuplPdn(emergencySuplPdn);
-    return checkHidlReturn(result, "IGnssConfiguration setEmergencySuplPdn() failed.");
+    return gnssConfigurationIface->setEmergencySuplPdn(emergencySuplPdn);
 }
 
 static jboolean android_location_GnssConfiguration_set_supl_version(JNIEnv*,
@@ -3498,25 +3461,17 @@
         ALOGE("%s: IGnssConfiguration interface not available.", __func__);
         return JNI_FALSE;
     }
-    auto result = gnssConfigurationIface->setSuplVersion(version);
-    return checkHidlReturn(result, "IGnssConfiguration setSuplVersion() failed.");
+    return gnssConfigurationIface->setSuplVersion(version);
 }
 
 static jboolean android_location_GnssConfiguration_set_supl_es(JNIEnv*,
                                                                jobject,
                                                                jint suplEs) {
-    if (gnssConfigurationIface_V2_0 != nullptr || gnssConfigurationIface_V2_1 != nullptr) {
-        ALOGI("Config parameter SUPL_ES is deprecated in IGnssConfiguration.hal version 2.0 and higher.");
-        return JNI_FALSE;
-    }
-
     if (gnssConfigurationIface == nullptr) {
         ALOGE("%s: IGnssConfiguration interface not available.", __func__);
         return JNI_FALSE;
     }
-
-    auto result = gnssConfigurationIface->setSuplEs(suplEs);
-    return checkHidlReturn(result, "IGnssConfiguration setSuplEs() failed.");
+    return gnssConfigurationIface->setSuplEs(suplEs);
 }
 
 static jboolean android_location_GnssConfiguration_set_supl_mode(JNIEnv*,
@@ -3526,26 +3481,17 @@
         ALOGE("%s: IGnssConfiguration interface not available.", __func__);
         return JNI_FALSE;
     }
-
-    auto result = gnssConfigurationIface->setSuplMode(mode);
-    return checkHidlReturn(result, "IGnssConfiguration setSuplMode() failed.");
+    return gnssConfigurationIface->setSuplMode(mode);
 }
 
 static jboolean android_location_GnssConfiguration_set_gps_lock(JNIEnv*,
                                                                 jobject,
                                                                 jint gpsLock) {
-    if (gnssConfigurationIface_V2_0 != nullptr || gnssConfigurationIface_V2_1 != nullptr) {
-        ALOGI("Config parameter GPS_LOCK is deprecated in IGnssConfiguration.hal version 2.0.");
-        return JNI_FALSE;
-    }
-
     if (gnssConfigurationIface == nullptr) {
         ALOGE("%s: IGnssConfiguration interface not available.", __func__);
         return JNI_FALSE;
     }
-
-    auto result = gnssConfigurationIface->setGpsLock(gpsLock);
-    return checkHidlReturn(result, "IGnssConfiguration setGpsLock() failed.");
+    return gnssConfigurationIface->setGpsLock(gpsLock);
 }
 
 static jboolean android_location_GnssConfiguration_set_lpp_profile(JNIEnv*,
@@ -3555,9 +3501,7 @@
         ALOGE("%s: IGnssConfiguration interface not available.", __func__);
         return JNI_FALSE;
     }
-
-    auto result = gnssConfigurationIface->setLppProfile(lppProfile);
-    return checkHidlReturn(result, "IGnssConfiguration setLppProfile() failed.");
+    return gnssConfigurationIface->setLppProfile(lppProfile);
 }
 
 static jboolean android_location_GnssConfiguration_set_gnss_pos_protocol_select(JNIEnv*,
@@ -3567,59 +3511,16 @@
         ALOGE("%s: IGnssConfiguration interface not available.", __func__);
         return JNI_FALSE;
     }
-
-    auto result = gnssConfigurationIface->setGlonassPositioningProtocol(gnssPosProtocol);
-    return checkHidlReturn(result, "IGnssConfiguration setGlonassPositioningProtocol() failed.");
+    return gnssConfigurationIface->setGlonassPositioningProtocol(gnssPosProtocol);
 }
 
 static jboolean android_location_GnssConfiguration_set_satellite_blacklist(
         JNIEnv* env, jobject, jintArray constellations, jintArray sv_ids) {
-    if (gnssConfigurationIface_V1_1 == nullptr && gnssConfigurationIface_V2_1 == nullptr) {
+    if (gnssConfigurationIface == nullptr) {
         ALOGI("IGnssConfiguration interface does not support satellite blacklist.");
         return JNI_FALSE;
     }
-
-    jint *constellation_array = env->GetIntArrayElements(constellations, 0);
-    if (nullptr == constellation_array) {
-        ALOGI("GetIntArrayElements returns nullptr.");
-        return JNI_FALSE;
-    }
-    jsize length = env->GetArrayLength(constellations);
-
-    jint *sv_id_array = env->GetIntArrayElements(sv_ids, 0);
-    if (nullptr == sv_id_array) {
-        ALOGI("GetIntArrayElements returns nullptr.");
-        return JNI_FALSE;
-    }
-
-    if (length != env->GetArrayLength(sv_ids)) {
-        ALOGI("Lengths of constellations and sv_ids are inconsistent.");
-        return JNI_FALSE;
-    }
-
-    if (gnssConfigurationIface_V2_1 != nullptr) {
-        hidl_vec<IGnssConfiguration_V2_1::BlacklistedSource> sources;
-        sources.resize(length);
-
-        for (int i = 0; i < length; i++) {
-            sources[i].constellation = static_cast<GnssConstellationType_V2_0>(constellation_array[i]);
-            sources[i].svid = sv_id_array[i];
-        }
-
-        auto result = gnssConfigurationIface_V2_1->setBlacklist_2_1(sources);
-        return checkHidlReturn(result, "IGnssConfiguration_V2_1 setBlacklist_2_1() failed.");
-    }
-
-    hidl_vec<IGnssConfiguration_V1_1::BlacklistedSource> sources;
-    sources.resize(length);
-
-    for (int i = 0; i < length; i++) {
-        sources[i].constellation = static_cast<GnssConstellationType_V1_0>(constellation_array[i]);
-        sources[i].svid = sv_id_array[i];
-    }
-
-    auto result = gnssConfigurationIface_V1_1->setBlacklist(sources);
-    return checkHidlReturn(result, "IGnssConfiguration setBlacklist() failed.");
+    return gnssConfigurationIface->setBlocklist(env, constellations, sv_ids);
 }
 
 static jboolean android_location_GnssConfiguration_set_es_extension_sec(
@@ -3628,15 +3529,7 @@
         ALOGE("%s: IGnssConfiguration interface not available.", __func__);
         return JNI_FALSE;
     }
-
-    if (gnssConfigurationIface_V2_0 == nullptr) {
-        ALOGI("Config parameter ES_EXTENSION_SEC is not supported in IGnssConfiguration.hal"
-                " versions earlier than 2.0.");
-        return JNI_FALSE;
-    }
-
-    auto result = gnssConfigurationIface_V2_0->setEsExtensionSec(emergencyExtensionSeconds);
-    return checkHidlReturn(result, "IGnssConfiguration setEsExtensionSec() failed.");
+    return gnssConfigurationIface->setEsExtensionSec(emergencyExtensionSeconds);
 }
 
 static jint android_location_GnssBatchingProvider_get_batch_size(JNIEnv*, jclass) {
diff --git a/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp b/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp
deleted file mode 100644
index 5eb6b73..0000000
--- a/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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_TAG "PowerStatsService"
-
-#include <android/hardware/power/stats/1.0/IPowerStats.h>
-#include <jni.h>
-#include <nativehelper/JNIHelp.h>
-
-#include <log/log.h>
-
-using android::hardware::hidl_vec;
-using android::hardware::Return;
-using android::hardware::power::stats::V1_0::EnergyData;
-using android::hardware::power::stats::V1_0::RailInfo;
-using android::hardware::power::stats::V1_0::Status;
-
-static jclass class_railInfo;
-static jmethodID method_railInfoInit;
-static jclass class_energyData;
-static jmethodID method_energyDataInit;
-
-namespace android {
-
-static std::mutex gPowerStatsHalMutex;
-static sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHalV1_0_ptr = nullptr;
-
-static void deinitPowerStats() {
-    gPowerStatsHalV1_0_ptr = nullptr;
-}
-
-struct PowerStatsHalDeathRecipient : virtual public hardware::hidl_death_recipient {
-    virtual void serviceDied(uint64_t cookie,
-                             const wp<android::hidl::base::V1_0::IBase> &who) override {
-        // The HAL just died. Reset all handles to HAL services.
-        std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
-        deinitPowerStats();
-    }
-};
-
-sp<PowerStatsHalDeathRecipient> gPowerStatsHalDeathRecipient = new PowerStatsHalDeathRecipient();
-
-static bool connectToPowerStatsHal() {
-    if (gPowerStatsHalV1_0_ptr == nullptr) {
-        gPowerStatsHalV1_0_ptr = android::hardware::power::stats::V1_0::IPowerStats::getService();
-
-        if (gPowerStatsHalV1_0_ptr == nullptr) {
-            ALOGE("Unable to get power.stats HAL service.");
-            return false;
-        }
-
-        // Link death recipient to power.stats service handle
-        hardware::Return<bool> linked =
-                gPowerStatsHalV1_0_ptr->linkToDeath(gPowerStatsHalDeathRecipient, 0);
-        if (!linked.isOk()) {
-            ALOGE("Transaction error in linking to power.stats HAL death: %s",
-                  linked.description().c_str());
-            deinitPowerStats();
-            return false;
-        } else if (!linked) {
-            ALOGW("Unable to link to power.stats HAL death notifications");
-            return false;
-        }
-    }
-    return true;
-}
-
-static bool checkResult(const Return<void> &ret, const char *function) {
-    if (!ret.isOk()) {
-        ALOGE("%s failed: requested HAL service not available. Description: %s", function,
-              ret.description().c_str());
-        if (ret.isDeadObject()) {
-            deinitPowerStats();
-        }
-        return false;
-    }
-    return true;
-}
-
-static jobjectArray nativeGetRailInfo(JNIEnv *env, jclass clazz) {
-    std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
-
-    if (!connectToPowerStatsHal()) {
-        ALOGE("nativeGetRailInfo failed to connect to power.stats HAL");
-        return nullptr;
-    }
-
-    hidl_vec<RailInfo> list;
-    Return<void> ret = gPowerStatsHalV1_0_ptr->getRailInfo([&list](auto rails, auto status) {
-        if (status != Status::SUCCESS) {
-            ALOGW("Rail information is not available");
-        } else {
-            list = std::move(rails);
-        }
-    });
-
-    if (!checkResult(ret, __func__)) {
-        ALOGE("getRailInfo failed");
-        return nullptr;
-    } else {
-        jobjectArray railInfoArray = env->NewObjectArray(list.size(), class_railInfo, nullptr);
-        for (int i = 0; i < list.size(); i++) {
-            jstring railName = env->NewStringUTF(list[i].railName.c_str());
-            jstring subsysName = env->NewStringUTF(list[i].subsysName.c_str());
-            jobject railInfo = env->NewObject(class_railInfo, method_railInfoInit, list[i].index,
-                                              railName, subsysName, list[i].samplingRate);
-            env->SetObjectArrayElement(railInfoArray, i, railInfo);
-            env->DeleteLocalRef(railName);
-            env->DeleteLocalRef(subsysName);
-            env->DeleteLocalRef(railInfo);
-        }
-        return railInfoArray;
-    }
-}
-
-static jobjectArray nativeGetEnergyData(JNIEnv *env, jclass clazz) {
-    std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
-
-    if (!connectToPowerStatsHal()) {
-        ALOGE("nativeGetEnergy failed to connect to power.stats HAL");
-    }
-
-    hidl_vec<EnergyData> list;
-    Return<void> ret =
-            gPowerStatsHalV1_0_ptr->getEnergyData({}, [&list](auto energyData, auto status) {
-                if (status != Status::SUCCESS) {
-                    ALOGW("getEnergyData is not supported");
-                } else {
-                    list = std::move(energyData);
-                }
-            });
-
-    if (!checkResult(ret, __func__)) {
-        ALOGE("getEnergyData failed");
-        return nullptr;
-    } else {
-        jobjectArray energyDataArray = env->NewObjectArray(list.size(), class_energyData, nullptr);
-        for (int i = 0; i < list.size(); i++) {
-            jobject energyData = env->NewObject(class_energyData, method_energyDataInit,
-                                                list[i].index, list[i].timestamp, list[i].energy);
-            env->SetObjectArrayElement(energyDataArray, i, energyData);
-            env->DeleteLocalRef(energyData);
-        }
-        return energyDataArray;
-    }
-}
-
-static jboolean nativeInit(JNIEnv *env, jclass clazz) {
-    std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
-
-    jclass temp = env->FindClass("com/android/server/powerstats/PowerStatsData$RailInfo");
-    if (temp == nullptr) return false;
-
-    class_railInfo = (jclass)env->NewGlobalRef(temp);
-    if (class_railInfo == nullptr) return false;
-
-    method_railInfoInit =
-            env->GetMethodID(class_railInfo, "<init>", "(JLjava/lang/String;Ljava/lang/String;J)V");
-    if (method_railInfoInit == nullptr) return false;
-
-    temp = env->FindClass("com/android/server/powerstats/PowerStatsData$EnergyData");
-    if (temp == nullptr) return false;
-
-    class_energyData = (jclass)env->NewGlobalRef(temp);
-    if (class_energyData == nullptr) return false;
-
-    method_energyDataInit = env->GetMethodID(class_energyData, "<init>", "(JJJ)V");
-    if (method_energyDataInit == nullptr) return false;
-
-    bool rv = true;
-
-    if (!connectToPowerStatsHal()) {
-        ALOGE("nativeInit failed to connect to power.stats HAL");
-        rv = false;
-    } else {
-        Return<void> ret = gPowerStatsHalV1_0_ptr->getRailInfo([&rv](auto rails, auto status) {
-            if (status != Status::SUCCESS) {
-                ALOGE("nativeInit RailInfo is unavailable");
-                rv = false;
-            }
-        });
-
-        ret = gPowerStatsHalV1_0_ptr->getEnergyData({}, [&rv](auto energyData, auto status) {
-            if (status != Status::SUCCESS) {
-                ALOGE("nativeInit EnergyData is unavailable");
-                rv = false;
-            }
-        });
-    }
-
-    return rv;
-}
-
-static const JNINativeMethod method_table[] = {
-        {"nativeInit", "()Z", (void *)nativeInit},
-        {"nativeGetRailInfo", "()[Lcom/android/server/powerstats/PowerStatsData$RailInfo;",
-         (void *)nativeGetRailInfo},
-        {"nativeGetEnergyData", "()[Lcom/android/server/powerstats/PowerStatsData$EnergyData;",
-         (void *)nativeGetEnergyData},
-};
-
-int register_android_server_PowerStatsService(JNIEnv *env) {
-    return jniRegisterNativeMethods(env,
-                                    "com/android/server/powerstats/"
-                                    "PowerStatsHALWrapper$PowerStatsHALWrapperImpl",
-                                    method_table, NELEM(method_table));
-}
-
-}; // namespace android
diff --git a/services/core/jni/gnss/GnssConfiguration.cpp b/services/core/jni/gnss/GnssConfiguration.cpp
new file mode 100644
index 0000000..8610c75
--- /dev/null
+++ b/services/core/jni/gnss/GnssConfiguration.cpp
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_TAG before <log/log.h> to overwrite the default value.
+#define LOG_TAG "GnssConfigurationJni"
+
+#include "GnssConfiguration.h"
+
+using android::hardware::gnss::GnssConstellationType;
+using GnssConstellationType_V1_0 = android::hardware::gnss::V1_0::GnssConstellationType;
+using GnssConstellationType_V2_0 = android::hardware::gnss::V2_0::GnssConstellationType;
+
+using android::binder::Status;
+using android::hardware::hidl_vec;
+using android::hardware::Return;
+
+using android::hardware::gnss::IGnssConfiguration;
+using IGnssConfiguration_V1_0 = android::hardware::gnss::V1_0::IGnssConfiguration;
+using IGnssConfiguration_V1_1 = android::hardware::gnss::V1_1::IGnssConfiguration;
+using IGnssConfiguration_V2_0 = android::hardware::gnss::V2_0::IGnssConfiguration;
+using IGnssConfiguration_V2_1 = android::hardware::gnss::V2_1::IGnssConfiguration;
+
+using android::hardware::gnss::BlocklistedSource;
+using BlocklistedSource_V1_1 = IGnssConfiguration_V1_1::BlacklistedSource;
+using BlocklistedSource_V2_1 = IGnssConfiguration_V2_1::BlacklistedSource;
+
+namespace {
+
+jclass class_gnssConfiguration_halInterfaceVersion;
+jmethodID method_halInterfaceVersionCtor;
+
+jboolean checkAidlStatus(const Status& status, const char* errorMessage) {
+    if (!status.isOk()) {
+        ALOGE("%s AIDL transport error: %s", errorMessage, status.toString8().c_str());
+        return JNI_FALSE;
+    }
+    return JNI_TRUE;
+}
+
+template <class T>
+inline void logHidlError(Return<T>& result, const char* errorMessage) {
+    ALOGE("%s HIDL transport error: %s", errorMessage, result.description().c_str());
+}
+
+template <class T>
+jboolean checkHidlReturn(Return<T>& result, const char* errorMessage) {
+    if (!result.isOk()) {
+        logHidlError(result, errorMessage);
+        return JNI_FALSE;
+    } else {
+        return JNI_TRUE;
+    }
+}
+
+jobject createHalInterfaceVersionJavaObject(JNIEnv* env, jint major, jint minor) {
+    return env->NewObject(class_gnssConfiguration_halInterfaceVersion,
+                          method_halInterfaceVersionCtor, major, minor);
+}
+
+} // anonymous namespace
+
+namespace android::gnss {
+
+void GnssConfiguration_class_init_once(JNIEnv* env) {
+    jclass gnssConfiguration_halInterfaceVersionClass = env->FindClass(
+            "com/android/server/location/gnss/GnssConfiguration$HalInterfaceVersion");
+    class_gnssConfiguration_halInterfaceVersion =
+            (jclass)env->NewGlobalRef(gnssConfiguration_halInterfaceVersionClass);
+    method_halInterfaceVersionCtor =
+            env->GetMethodID(class_gnssConfiguration_halInterfaceVersion, "<init>", "(II)V");
+}
+
+// Implementation of GnssConfiguration (AIDL HAL)
+
+GnssConfiguration::GnssConfiguration(const sp<IGnssConfiguration>& iGnssConfiguration)
+      : mIGnssConfiguration(iGnssConfiguration) {}
+
+jobject GnssConfiguration::getVersion(JNIEnv* env) {
+    return createHalInterfaceVersionJavaObject(env, 3, 0);
+}
+
+jboolean GnssConfiguration::setEmergencySuplPdn(jint enable) {
+    auto status = mIGnssConfiguration->setEmergencySuplPdn(enable);
+    return checkAidlStatus(status, "IGnssConfiguration setEmergencySuplPdn() failed.");
+}
+
+jboolean GnssConfiguration::setSuplVersion(jint version) {
+    auto status = mIGnssConfiguration->setSuplVersion(version);
+    return checkAidlStatus(status, "IGnssConfiguration setSuplVersion() failed.");
+}
+
+jboolean GnssConfiguration::setSuplEs(jint enable) {
+    ALOGI("Config parameter SUPL_ES is deprecated in IGnssConfiguration AIDL HAL.");
+    return JNI_FALSE;
+}
+
+jboolean GnssConfiguration::setSuplMode(jint mode) {
+    auto status = mIGnssConfiguration->setSuplMode(mode);
+    return checkAidlStatus(status, "IGnssConfiguration setSuplMode() failed.");
+}
+
+jboolean GnssConfiguration::setGpsLock(jint gpsLock) {
+    ALOGI("Config parameter GPS_LOCK is not supported in IGnssConfiguration AIDL HAL.");
+    return JNI_FALSE;
+}
+
+jboolean GnssConfiguration::setLppProfile(jint lppProfile) {
+    auto status = mIGnssConfiguration->setLppProfile(lppProfile);
+    return checkAidlStatus(status, "IGnssConfiguration setLppProfile() failed.");
+}
+
+jboolean GnssConfiguration::setGlonassPositioningProtocol(jint gnssPosProtocol) {
+    auto status = mIGnssConfiguration->setGlonassPositioningProtocol(gnssPosProtocol);
+    return checkAidlStatus(status, "IGnssConfiguration setGlonassPositioningProtocol() failed.");
+}
+
+jboolean GnssConfiguration::setEsExtensionSec(jint emergencyExtensionSeconds) {
+    auto status = mIGnssConfiguration->setEsExtensionSec(emergencyExtensionSeconds);
+    return checkAidlStatus(status, "IGnssConfiguration setEsExtensionSec() failed.");
+}
+
+jboolean GnssConfiguration::setBlocklist(JNIEnv* env, jintArray& constellations,
+                                         jintArray& sv_ids) {
+    auto sources =
+            getBlocklistedSources<BlocklistedSource, GnssConstellationType>(env, constellations,
+                                                                            sv_ids);
+    auto status = mIGnssConfiguration->setBlocklist(sources);
+    return checkAidlStatus(status, "IGnssConfiguration setBlocklist() failed.");
+}
+
+// Implementation of GnssConfiguration_V1_0
+
+GnssConfiguration_V1_0::GnssConfiguration_V1_0(
+        const sp<IGnssConfiguration_V1_0>& iGnssConfiguration)
+      : mIGnssConfiguration_V1_0(iGnssConfiguration) {}
+
+jobject GnssConfiguration_V1_0::getVersion(JNIEnv* env) {
+    return createHalInterfaceVersionJavaObject(env, 1, 0);
+}
+
+jboolean GnssConfiguration_V1_0::setEmergencySuplPdn(jint enable) {
+    auto result = mIGnssConfiguration_V1_0->setEmergencySuplPdn(enable);
+    return checkHidlReturn(result, "IGnssConfiguration setEmergencySuplPdn() failed.");
+}
+
+jboolean GnssConfiguration_V1_0::setSuplVersion(jint version) {
+    auto result = mIGnssConfiguration_V1_0->setSuplVersion(version);
+    return checkHidlReturn(result, "IGnssConfiguration setSuplVersion() failed.");
+}
+
+jboolean GnssConfiguration_V1_0::setSuplEs(jint enable) {
+    auto result = mIGnssConfiguration_V1_0->setSuplEs(enable);
+    return checkHidlReturn(result, "IGnssConfiguration setSuplEs() failed.");
+}
+
+jboolean GnssConfiguration_V1_0::setSuplMode(jint mode) {
+    auto result = mIGnssConfiguration_V1_0->setSuplMode(mode);
+    return checkHidlReturn(result, "IGnssConfiguration setSuplMode() failed.");
+}
+
+jboolean GnssConfiguration_V1_0::setGpsLock(jint gpsLock) {
+    auto result = mIGnssConfiguration_V1_0->setGpsLock(gpsLock);
+    return checkHidlReturn(result, "IGnssConfiguration setGpsLock() failed.");
+}
+
+jboolean GnssConfiguration_V1_0::setLppProfile(jint lppProfile) {
+    auto result = mIGnssConfiguration_V1_0->setLppProfile(lppProfile);
+    return checkHidlReturn(result, "IGnssConfiguration setLppProfile() failed.");
+}
+
+jboolean GnssConfiguration_V1_0::setGlonassPositioningProtocol(jint gnssPosProtocol) {
+    auto result = mIGnssConfiguration_V1_0->setGlonassPositioningProtocol(gnssPosProtocol);
+    return checkHidlReturn(result, "IGnssConfiguration setGlonassPositioningProtocol() failed.");
+}
+
+jboolean GnssConfiguration_V1_0::setEsExtensionSec(jint emergencyExtensionSeconds) {
+    ALOGI("Config parameter ES_EXTENSION_SEC is not supported in IGnssConfiguration.hal"
+          " versions earlier than 2.0.");
+    return JNI_FALSE;
+}
+
+jboolean GnssConfiguration_V1_0::setBlocklist(JNIEnv* env, jintArray& constellations,
+                                              jintArray& sv_ids) {
+    ALOGI("IGnssConfiguration interface does not support satellite blocklist.");
+    return JNI_FALSE;
+}
+
+// Implementation of GnssConfiguration_V1_1
+
+GnssConfiguration_V1_1::GnssConfiguration_V1_1(
+        const sp<IGnssConfiguration_V1_1>& iGnssConfiguration)
+      : GnssConfiguration_V1_0{iGnssConfiguration}, mIGnssConfiguration_V1_1(iGnssConfiguration) {}
+
+jobject GnssConfiguration_V1_1::getVersion(JNIEnv* env) {
+    return createHalInterfaceVersionJavaObject(env, 1, 1);
+}
+
+jboolean GnssConfiguration_V1_1::setBlocklist(JNIEnv* env, jintArray& constellations,
+                                              jintArray& sv_ids) {
+    auto sources = getBlocklistedSources<BlocklistedSource_V1_1,
+                                         GnssConstellationType_V1_0>(env, constellations, sv_ids);
+    auto result = mIGnssConfiguration_V1_1->setBlacklist(sources);
+    return checkHidlReturn(result, "IGnssConfiguration setBlocklist() failed.");
+}
+
+// Implementation of GnssConfiguration_V2_0
+
+GnssConfiguration_V2_0::GnssConfiguration_V2_0(
+        const sp<IGnssConfiguration_V2_0>& iGnssConfiguration)
+      : GnssConfiguration_V1_1{iGnssConfiguration}, mIGnssConfiguration_V2_0(iGnssConfiguration) {}
+
+jobject GnssConfiguration_V2_0::getVersion(JNIEnv* env) {
+    return createHalInterfaceVersionJavaObject(env, 2, 0);
+}
+
+jboolean GnssConfiguration_V2_0::setSuplEs(jint enable) {
+    ALOGI("Config parameter SUPL_ES is deprecated in IGnssConfiguration.hal version 2.0 and "
+          "higher.");
+    return JNI_FALSE;
+}
+
+jboolean GnssConfiguration_V2_0::setGpsLock(jint enable) {
+    ALOGI("Config parameter GPS_LOCK is deprecated in IGnssConfiguration.hal version 2.0 and "
+          "higher.");
+    return JNI_FALSE;
+}
+
+jboolean GnssConfiguration_V2_0::setEsExtensionSec(jint emergencyExtensionSeconds) {
+    auto result = mIGnssConfiguration_V2_0->setEsExtensionSec(emergencyExtensionSeconds);
+    return checkHidlReturn(result, "IGnssConfiguration setEsExtensionSec() failed.");
+}
+
+// Implementation of GnssConfiguration_V2_1
+
+GnssConfiguration_V2_1::GnssConfiguration_V2_1(
+        const sp<IGnssConfiguration_V2_1>& iGnssConfiguration)
+      : GnssConfiguration_V2_0{iGnssConfiguration}, mIGnssConfiguration_V2_1(iGnssConfiguration) {}
+
+jobject GnssConfiguration_V2_1::getVersion(JNIEnv* env) {
+    return createHalInterfaceVersionJavaObject(env, 2, 1);
+}
+
+jboolean GnssConfiguration_V2_1::setBlocklist(JNIEnv* env, jintArray& constellations,
+                                              jintArray& sv_ids) {
+    auto sources = getBlocklistedSources<BlocklistedSource_V2_1,
+                                         GnssConstellationType_V2_0>(env, constellations, sv_ids);
+    auto result = mIGnssConfiguration_V2_1->setBlacklist_2_1(sources);
+    return checkHidlReturn(result, "IGnssConfiguration setBlocklist() failed.");
+}
+
+} // namespace android::gnss
diff --git a/services/core/jni/gnss/GnssConfiguration.h b/services/core/jni/gnss/GnssConfiguration.h
new file mode 100644
index 0000000..ea0f178
--- /dev/null
+++ b/services/core/jni/gnss/GnssConfiguration.h
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_SERVER_GNSS_GNSSCONFIGURATION_H
+#define _ANDROID_SERVER_GNSS_GNSSCONFIGURATION_H
+
+#pragma once
+
+#ifndef LOG_TAG
+#error LOG_TAG must be defined before including this file.
+#endif
+
+#include <android/hardware/gnss/1.0/IGnssConfiguration.h>
+#include <android/hardware/gnss/1.1/IGnssConfiguration.h>
+#include <android/hardware/gnss/2.0/IGnssConfiguration.h>
+#include <android/hardware/gnss/2.1/IGnssConfiguration.h>
+#include <android/hardware/gnss/BnGnssConfiguration.h>
+#include <log/log.h>
+
+#include "jni.h"
+
+namespace android::gnss {
+
+void GnssConfiguration_class_init_once(JNIEnv* env);
+
+class GnssConfigurationInterface {
+public:
+    virtual ~GnssConfigurationInterface() {}
+    virtual jobject getVersion(JNIEnv* env) = 0;
+    virtual jboolean setEmergencySuplPdn(jint enable) = 0;
+    virtual jboolean setSuplVersion(jint version) = 0;
+    virtual jboolean setSuplEs(jint enable) = 0;
+    virtual jboolean setSuplMode(jint mode) = 0;
+    virtual jboolean setGpsLock(jint gpsLock) = 0;
+    virtual jboolean setLppProfile(jint lppProfile) = 0;
+    virtual jboolean setGlonassPositioningProtocol(jint gnssPosProtocol) = 0;
+    virtual jboolean setEsExtensionSec(jint emergencyExtensionSeconds) = 0;
+    virtual jboolean setBlocklist(JNIEnv* env, jintArray& constellations, jintArray& sv_ids) = 0;
+
+protected:
+    template <class T_BlocklistSource, class T_ConstellationType>
+    hardware::hidl_vec<T_BlocklistSource> getBlocklistedSources(JNIEnv* env,
+                                                                jintArray& constellations,
+                                                                jintArray& sv_ids) {
+        jint* constellation_array = env->GetIntArrayElements(constellations, 0);
+        if (nullptr == constellation_array) {
+            ALOGI("GetIntArrayElements returns nullptr.");
+            return JNI_FALSE;
+        }
+
+        jsize length = env->GetArrayLength(constellations);
+
+        jint* sv_id_array = env->GetIntArrayElements(sv_ids, 0);
+        if (nullptr == sv_id_array) {
+            ALOGI("GetIntArrayElements returns nullptr.");
+            return JNI_FALSE;
+        }
+
+        if (length != env->GetArrayLength(sv_ids)) {
+            ALOGI("Lengths of constellations and sv_ids are inconsistent.");
+            return JNI_FALSE;
+        }
+
+        hardware::hidl_vec<T_BlocklistSource> sources;
+        sources.resize(length);
+
+        for (int i = 0; i < length; i++) {
+            sources[i].constellation = static_cast<T_ConstellationType>(constellation_array[i]);
+            sources[i].svid = sv_id_array[i];
+        }
+
+        env->ReleaseIntArrayElements(constellations, constellation_array, 0);
+        env->ReleaseIntArrayElements(sv_ids, sv_id_array, 0);
+
+        return sources;
+    }
+};
+
+class GnssConfiguration : public GnssConfigurationInterface {
+public:
+    GnssConfiguration(const sp<android::hardware::gnss::IGnssConfiguration>& iGnssConfiguration);
+    jobject getVersion(JNIEnv* env) override;
+    jboolean setEmergencySuplPdn(jint enable) override;
+    jboolean setSuplVersion(jint version) override;
+    jboolean setSuplEs(jint enable) override;
+    jboolean setSuplMode(jint mode) override;
+    jboolean setGpsLock(jint gpsLock) override;
+    jboolean setLppProfile(jint lppProfile) override;
+    jboolean setGlonassPositioningProtocol(jint gnssPosProtocol) override;
+    jboolean setEsExtensionSec(jint emergencyExtensionSeconds) override;
+    jboolean setBlocklist(JNIEnv* env, jintArray& constellations, jintArray& sv_ids) override;
+
+private:
+    const sp<android::hardware::gnss::IGnssConfiguration> mIGnssConfiguration;
+};
+
+class GnssConfiguration_V1_0 : public GnssConfigurationInterface {
+public:
+    GnssConfiguration_V1_0(
+            const sp<android::hardware::gnss::V1_0::IGnssConfiguration>& iGnssConfiguration);
+    jobject getVersion(JNIEnv* env) override;
+    jboolean setEmergencySuplPdn(jint enable);
+    jboolean setSuplVersion(jint version) override;
+    jboolean setSuplEs(jint enable) override;
+    jboolean setSuplMode(jint mode) override;
+    jboolean setGpsLock(jint gpsLock) override;
+    jboolean setLppProfile(jint lppProfile);
+    jboolean setGlonassPositioningProtocol(jint gnssPosProtocol) override;
+    jboolean setEsExtensionSec(jint emergencyExtensionSeconds) override;
+    jboolean setBlocklist(JNIEnv* env, jintArray& constellations, jintArray& sv_ids) override;
+
+private:
+    const sp<android::hardware::gnss::V1_0::IGnssConfiguration> mIGnssConfiguration_V1_0;
+};
+
+class GnssConfiguration_V1_1 : public GnssConfiguration_V1_0 {
+public:
+    GnssConfiguration_V1_1(
+            const sp<android::hardware::gnss::V1_1::IGnssConfiguration>& iGnssConfiguration);
+
+    jobject getVersion(JNIEnv* env) override;
+
+    jboolean setBlocklist(JNIEnv* env, jintArray& constellations, jintArray& sv_ids) override;
+
+private:
+    const sp<android::hardware::gnss::V1_1::IGnssConfiguration> mIGnssConfiguration_V1_1;
+};
+
+class GnssConfiguration_V2_0 : public GnssConfiguration_V1_1 {
+public:
+    GnssConfiguration_V2_0(
+            const sp<android::hardware::gnss::V2_0::IGnssConfiguration>& iGnssConfiguration);
+    jobject getVersion(JNIEnv* env) override;
+    jboolean setSuplEs(jint enable) override;
+    jboolean setGpsLock(jint enable) override;
+    jboolean setEsExtensionSec(jint emergencyExtensionSeconds) override;
+
+private:
+    const sp<android::hardware::gnss::V2_0::IGnssConfiguration> mIGnssConfiguration_V2_0;
+};
+
+class GnssConfiguration_V2_1 : public GnssConfiguration_V2_0 {
+public:
+    GnssConfiguration_V2_1(
+            const sp<android::hardware::gnss::V2_1::IGnssConfiguration>& iGnssConfiguration);
+    jobject getVersion(JNIEnv* env) override;
+    jboolean setBlocklist(JNIEnv* env, jintArray& constellations, jintArray& sv_ids) override;
+
+private:
+    const sp<android::hardware::gnss::V2_1::IGnssConfiguration> mIGnssConfiguration_V2_1;
+};
+
+} // namespace android::gnss
+
+#endif // _ANDROID_SERVER_GNSS_GNSSCONFIGURATION_H
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 48d5244..0ffa5c3 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -29,7 +29,6 @@
 int register_android_server_InputManager(JNIEnv* env);
 int register_android_server_LightsService(JNIEnv* env);
 int register_android_server_PowerManagerService(JNIEnv* env);
-int register_android_server_PowerStatsService(JNIEnv* env);
 int register_android_server_storage_AppFuse(JNIEnv* env);
 int register_android_server_SerialService(JNIEnv* env);
 int register_android_server_SystemServer(JNIEnv* env);
@@ -84,7 +83,6 @@
     register_android_server_broadcastradio_BroadcastRadioService(env);
     register_android_server_broadcastradio_Tuner(vm, env);
     register_android_server_PowerManagerService(env);
-    register_android_server_PowerStatsService(env);
     register_android_server_SerialService(env);
     register_android_server_InputManager(env);
     register_android_server_LightsService(env);
diff --git a/services/core/xsd/Android.bp b/services/core/xsd/Android.bp
index b7d6424..fb55e75 100644
--- a/services/core/xsd/Android.bp
+++ b/services/core/xsd/Android.bp
@@ -20,3 +20,11 @@
     api_dir: "display-device-config/schema",
     package_name: "com.android.server.display.config",
 }
+
+
+xsd_config {
+    name: "cec-config",
+    srcs: ["cec-config/cec-config.xsd"],
+    api_dir: "cec-config/schema",
+    package_name: "com.android.server.hdmi.cec.config",
+}
diff --git a/services/core/xsd/cec-config/cec-config.xsd b/services/core/xsd/cec-config/cec-config.xsd
new file mode 100644
index 0000000..0801c88
--- /dev/null
+++ b/services/core/xsd/cec-config/cec-config.xsd
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema version="2.0"
+           elementFormDefault="qualified"
+           attributeFormDefault="unqualified"
+  xmlns:xs="http://www.w3.org/2001/XMLSchema">
+  <xs:element name="cec-settings">
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element name="setting" type="setting" minOccurs="0" maxOccurs="unbounded"/>
+      </xs:sequence>
+    </xs:complexType>
+  </xs:element>
+  <xs:complexType name="setting">
+    <xs:attribute name="name" type="xs:string"/>
+    <xs:attribute name="user-configurable" type="xs:boolean"/>
+    <xs:element name="allowed-values" type="value-list" minOccurs="1" maxOccurs="1"/>
+    <xs:element name="default-value" type="value" minOccurs="1" maxOccurs="1"/>
+  </xs:complexType>
+  <xs:complexType name="value-list">
+      <xs:sequence>
+        <xs:element name="value" type="value" minOccurs="1" maxOccurs="unbounded"/>
+      </xs:sequence>
+  </xs:complexType>
+  <xs:complexType name="value">
+    <xs:attribute name="string-value" type="xs:string"/>
+  </xs:complexType>
+</xs:schema>
diff --git a/services/core/xsd/cec-config/schema/current.txt b/services/core/xsd/cec-config/schema/current.txt
new file mode 100644
index 0000000..34faf45
--- /dev/null
+++ b/services/core/xsd/cec-config/schema/current.txt
@@ -0,0 +1,40 @@
+// Signature format: 2.0
+package com.android.server.hdmi.cec.config {
+
+  public class CecSettings {
+    ctor public CecSettings();
+    method public java.util.List<com.android.server.hdmi.cec.config.Setting> getSetting();
+  }
+
+  public class Setting {
+    ctor public Setting();
+    method public com.android.server.hdmi.cec.config.ValueList getAllowedValues();
+    method public com.android.server.hdmi.cec.config.Value getDefaultValue();
+    method public String getName();
+    method public boolean getUserConfigurable();
+    method public void setAllowedValues(com.android.server.hdmi.cec.config.ValueList);
+    method public void setDefaultValue(com.android.server.hdmi.cec.config.Value);
+    method public void setName(String);
+    method public void setUserConfigurable(boolean);
+  }
+
+  public class Value {
+    ctor public Value();
+    method public String getStringValue();
+    method public void setStringValue(String);
+  }
+
+  public class ValueList {
+    ctor public ValueList();
+    method public java.util.List<com.android.server.hdmi.cec.config.Value> getValue();
+  }
+
+  public class XmlParser {
+    ctor public XmlParser();
+    method public static com.android.server.hdmi.cec.config.CecSettings read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+  }
+
+}
+
diff --git a/services/core/xsd/cec-config/schema/last_current.txt b/services/core/xsd/cec-config/schema/last_current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/services/core/xsd/cec-config/schema/last_current.txt
diff --git a/services/core/xsd/cec-config/schema/last_removed.txt b/services/core/xsd/cec-config/schema/last_removed.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/services/core/xsd/cec-config/schema/last_removed.txt
diff --git a/services/core/xsd/cec-config/schema/removed.txt b/services/core/xsd/cec-config/schema/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/services/core/xsd/cec-config/schema/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/services/core/xsd/platform-compat-config.xsd b/services/core/xsd/platform-compat-config.xsd
index 5a4c682..9924708 100644
--- a/services/core/xsd/platform-compat-config.xsd
+++ b/services/core/xsd/platform-compat-config.xsd
@@ -29,6 +29,7 @@
                 <xs:attribute type="xs:boolean" name="disabled"/>
                 <xs:attribute type="xs:boolean" name="loggingOnly"/>
                 <xs:attribute type="xs:int" name="enableAfterTargetSdk"/>
+                <xs:attribute type="xs:int" name="enableSinceTargetSdk"/>
                 <xs:attribute type="xs:string" name="description"/>
             </xs:extension>
         </xs:simpleContent>
diff --git a/services/core/xsd/platform-compat-schema/current.txt b/services/core/xsd/platform-compat-schema/current.txt
index 7def58d..e3640ed 100644
--- a/services/core/xsd/platform-compat-schema/current.txt
+++ b/services/core/xsd/platform-compat-schema/current.txt
@@ -6,6 +6,7 @@
     method public String getDescription();
     method public boolean getDisabled();
     method public int getEnableAfterTargetSdk();
+    method public int getEnableSinceTargetSdk();
     method public long getId();
     method public boolean getLoggingOnly();
     method public String getName();
@@ -13,6 +14,7 @@
     method public void setDescription(String);
     method public void setDisabled(boolean);
     method public void setEnableAfterTargetSdk(int);
+    method public void setEnableSinceTargetSdk(int);
     method public void setId(long);
     method public void setLoggingOnly(boolean);
     method public void setName(String);
diff --git a/services/core/xsd/vts/Android.mk b/services/core/xsd/vts/Android.mk
deleted file mode 100644
index 6dc2c43..0000000
--- a/services/core/xsd/vts/Android.mk
+++ /dev/null
@@ -1,22 +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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := VtsValidateDefaultPermissions
-include test/vts/tools/build/Android.host_config.mk
diff --git a/services/core/xsd/vts/AndroidTest.xml b/services/core/xsd/vts/AndroidTest.xml
deleted file mode 100644
index 4f3b2ef..0000000
--- a/services/core/xsd/vts/AndroidTest.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?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="Config for VTS VtsValidateDefaultPermissions.">
-    <option name="config-descriptor:metadata" key="plan" value="vts-treble" />
-    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
-        <option name="abort-on-push-failure" value="false"/>
-        <option name="push-group" value="HostDrivenTest.push"/>
-        <option name="push" value="DATA/etc/default-permissions.xsd->/data/local/tmp/default-permissions.xsd"/>
-    </target_preparer>
-    <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
-        <option name="test-module-name" value="VtsValidateDefaultPermissions"/>
-        <option name="binary-test-source" value="_32bit::DATA/nativetest/vts_defaultPermissions_validate_test/vts_defaultPermissions_validate_test" />
-        <option name="binary-test-source" value="_64bit::DATA/nativetest64/vts_defaultPermissions_validate_test/vts_defaultPermissions_validate_test" />
-        <option name="binary-test-type" value="gtest"/>
-        <option name="test-timeout" value="30s"/>
-    </test>
-</configuration>
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java b/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java
index aa38880..fdde4ea 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java
@@ -205,9 +205,10 @@
             dialogIntent.setComponent(targetInfo.getComponentName());
         }
 
+        // Simple notification clicks are immutable
         PendingIntent notifyIntent = mInjector.pendingIntentGetActivityAsUser(userContext, 0,
-                dialogIntent, PendingIntent.FLAG_UPDATE_CURRENT, null,
-                UserHandle.of(parentUserId));
+                dialogIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE,
+                null, UserHandle.of(parentUserId));
 
         return new Notification.Builder(userContext, SystemNotificationChannels.SECURITY)
                 .setSmallIcon(smallIconId)
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 282cee0..e8f1276 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -411,37 +411,37 @@
     private static final int STATUS_BAR_DISABLE2_MASK =
             StatusBarManager.DISABLE2_QUICK_SETTINGS;
 
-    private static final Set<String> SECURE_SETTINGS_WHITELIST;
-    private static final Set<String> SECURE_SETTINGS_DEVICEOWNER_WHITELIST;
-    private static final Set<String> GLOBAL_SETTINGS_WHITELIST;
+    private static final Set<String> SECURE_SETTINGS_ALLOWLIST;
+    private static final Set<String> SECURE_SETTINGS_DEVICEOWNER_ALLOWLIST;
+    private static final Set<String> GLOBAL_SETTINGS_ALLOWLIST;
     private static final Set<String> GLOBAL_SETTINGS_DEPRECATED;
-    private static final Set<String> SYSTEM_SETTINGS_WHITELIST;
+    private static final Set<String> SYSTEM_SETTINGS_ALLOWLIST;
     private static final Set<Integer> DA_DISALLOWED_POLICIES;
     // A collection of user restrictions that are deprecated and should simply be ignored.
     private static final String AB_DEVICE_KEY = "ro.build.ab_update";
 
     static {
-        SECURE_SETTINGS_WHITELIST = new ArraySet<>();
-        SECURE_SETTINGS_WHITELIST.add(Settings.Secure.DEFAULT_INPUT_METHOD);
-        SECURE_SETTINGS_WHITELIST.add(Settings.Secure.SKIP_FIRST_USE_HINTS);
-        SECURE_SETTINGS_WHITELIST.add(Settings.Secure.INSTALL_NON_MARKET_APPS);
+        SECURE_SETTINGS_ALLOWLIST = new ArraySet<>();
+        SECURE_SETTINGS_ALLOWLIST.add(Settings.Secure.DEFAULT_INPUT_METHOD);
+        SECURE_SETTINGS_ALLOWLIST.add(Settings.Secure.SKIP_FIRST_USE_HINTS);
+        SECURE_SETTINGS_ALLOWLIST.add(Settings.Secure.INSTALL_NON_MARKET_APPS);
 
-        SECURE_SETTINGS_DEVICEOWNER_WHITELIST = new ArraySet<>();
-        SECURE_SETTINGS_DEVICEOWNER_WHITELIST.addAll(SECURE_SETTINGS_WHITELIST);
-        SECURE_SETTINGS_DEVICEOWNER_WHITELIST.add(Settings.Secure.LOCATION_MODE);
+        SECURE_SETTINGS_DEVICEOWNER_ALLOWLIST = new ArraySet<>();
+        SECURE_SETTINGS_DEVICEOWNER_ALLOWLIST.addAll(SECURE_SETTINGS_ALLOWLIST);
+        SECURE_SETTINGS_DEVICEOWNER_ALLOWLIST.add(Settings.Secure.LOCATION_MODE);
 
-        GLOBAL_SETTINGS_WHITELIST = new ArraySet<>();
-        GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.ADB_ENABLED);
-        GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.ADB_WIFI_ENABLED);
-        GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.AUTO_TIME);
-        GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.AUTO_TIME_ZONE);
-        GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.DATA_ROAMING);
-        GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.USB_MASS_STORAGE_ENABLED);
-        GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.WIFI_SLEEP_POLICY);
-        GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.STAY_ON_WHILE_PLUGGED_IN);
-        GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN);
-        GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.PRIVATE_DNS_MODE);
-        GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.PRIVATE_DNS_SPECIFIER);
+        GLOBAL_SETTINGS_ALLOWLIST = new ArraySet<>();
+        GLOBAL_SETTINGS_ALLOWLIST.add(Settings.Global.ADB_ENABLED);
+        GLOBAL_SETTINGS_ALLOWLIST.add(Settings.Global.ADB_WIFI_ENABLED);
+        GLOBAL_SETTINGS_ALLOWLIST.add(Settings.Global.AUTO_TIME);
+        GLOBAL_SETTINGS_ALLOWLIST.add(Settings.Global.AUTO_TIME_ZONE);
+        GLOBAL_SETTINGS_ALLOWLIST.add(Settings.Global.DATA_ROAMING);
+        GLOBAL_SETTINGS_ALLOWLIST.add(Settings.Global.USB_MASS_STORAGE_ENABLED);
+        GLOBAL_SETTINGS_ALLOWLIST.add(Settings.Global.WIFI_SLEEP_POLICY);
+        GLOBAL_SETTINGS_ALLOWLIST.add(Settings.Global.STAY_ON_WHILE_PLUGGED_IN);
+        GLOBAL_SETTINGS_ALLOWLIST.add(Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN);
+        GLOBAL_SETTINGS_ALLOWLIST.add(Settings.Global.PRIVATE_DNS_MODE);
+        GLOBAL_SETTINGS_ALLOWLIST.add(Settings.Global.PRIVATE_DNS_SPECIFIER);
 
         GLOBAL_SETTINGS_DEPRECATED = new ArraySet<>();
         GLOBAL_SETTINGS_DEPRECATED.add(Settings.Global.BLUETOOTH_ON);
@@ -450,11 +450,11 @@
         GLOBAL_SETTINGS_DEPRECATED.add(Settings.Global.NETWORK_PREFERENCE);
         GLOBAL_SETTINGS_DEPRECATED.add(Settings.Global.WIFI_ON);
 
-        SYSTEM_SETTINGS_WHITELIST = new ArraySet<>();
-        SYSTEM_SETTINGS_WHITELIST.add(Settings.System.SCREEN_BRIGHTNESS);
-        SYSTEM_SETTINGS_WHITELIST.add(Settings.System.SCREEN_BRIGHTNESS_FLOAT);
-        SYSTEM_SETTINGS_WHITELIST.add(Settings.System.SCREEN_BRIGHTNESS_MODE);
-        SYSTEM_SETTINGS_WHITELIST.add(Settings.System.SCREEN_OFF_TIMEOUT);
+        SYSTEM_SETTINGS_ALLOWLIST = new ArraySet<>();
+        SYSTEM_SETTINGS_ALLOWLIST.add(Settings.System.SCREEN_BRIGHTNESS);
+        SYSTEM_SETTINGS_ALLOWLIST.add(Settings.System.SCREEN_BRIGHTNESS_FLOAT);
+        SYSTEM_SETTINGS_ALLOWLIST.add(Settings.System.SCREEN_BRIGHTNESS_MODE);
+        SYSTEM_SETTINGS_ALLOWLIST.add(Settings.System.SCREEN_OFF_TIMEOUT);
 
         DA_DISALLOWED_POLICIES = new ArraySet<>();
         DA_DISALLOWED_POLICIES.add(DeviceAdminInfo.USES_POLICY_DISABLE_CAMERA);
@@ -1124,7 +1124,7 @@
         }
 
         boolean storageManagerIsNonDefaultBlockEncrypted() {
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
                 return StorageManager.isNonDefaultBlockEncrypted();
             } finally {
@@ -1148,10 +1148,12 @@
             return mContext.getSystemService(WifiManager.class);
         }
 
+        @SuppressWarnings("AndroidFrameworkBinderIdentity")
         long binderClearCallingIdentity() {
             return Binder.clearCallingIdentity();
         }
 
+        @SuppressWarnings("AndroidFrameworkBinderIdentity")
         void binderRestoreCallingIdentity(long token) {
             Binder.restoreCallingIdentity(token);
         }
@@ -1567,10 +1569,15 @@
     }
 
     /**
-     * Creates a new {@link CallerIdentity} object to represent the caller's identity. If no
-     * component name is provided, look up the component name and fill it in for the caller.
+     * Creates a new {@link CallerIdentity} object to represent the caller's identity, which should
+     * be an admin of a profile on the device. If no component name is provided, look up the
+     * component name and fill it in for the caller.
+     *
+     * Note: this method should only be called when the expected caller is an admin.
+     *
+     * @throws SecurityException if the caller is not an active admin.
      */
-    private CallerIdentity getCallerIdentityOptionalAdmin(@Nullable ComponentName adminComponent) {
+    private CallerIdentity getAdminCallerIdentity(@Nullable ComponentName adminComponent) {
         if (adminComponent == null) {
             ActiveAdmin admin = getActiveAdminOfCaller();
             if (admin != null) {
@@ -1584,24 +1591,66 @@
 
     /**
      * Creates a new {@link CallerIdentity} object to represent the caller's identity. If no
-     * package name is provided, look up the package name and fill it in for the caller.
+     * component name is provided, look up the component name and fill it in for the caller.
+     *
+     * Note: this method should only be called when the caller may not be an admin. If the caller
+     * is not an admin, the ComponentName in the returned identity will be null.
      */
-    private CallerIdentity getCallerIdentityOptionalPackage(@Nullable String callerPackage) {
+    private CallerIdentity getNonPrivilegedOrAdminCallerIdentity(
+            @Nullable ComponentName adminComponent) {
+        if (adminComponent == null) {
+            ActiveAdmin admin = getActiveAdminOfCaller();
+            if (admin != null) {
+                adminComponent = admin.info.getComponent();
+            } else {
+                return getCallerIdentity();
+            }
+        }
+        return getCallerIdentity(adminComponent);
+
+    }
+
+    /**
+     * Creates a new {@link CallerIdentity} object to represent the caller's identity. If no
+     * package name is provided, look up the package name and fill it in for the caller.
+     *
+     * Note: this method should only be called when the expected caller is an admin.
+     *
+     * @throws SecurityException if the caller is not an active admin.
+     */
+    private CallerIdentity getAdminCallerIdentityUsingPackage(@Nullable String callerPackage) {
         if (callerPackage == null) {
             ActiveAdmin admin = getActiveAdminOfCaller();
             if (admin != null) {
                 return getCallerIdentity(admin.info.getPackageName());
             }
             throw new SecurityException("Caller is not an active admin");
-        } else {
-            return getCallerIdentity(callerPackage);
         }
+        return getCallerIdentity(callerPackage);
+    }
+
+    /**
+     * Creates a new {@link CallerIdentity} object to represent the caller's identity. If no
+     * package name is provided, look up the package name and fill it in for the caller.
+     */
+    private CallerIdentity getNonPrivilegedOrAdminCallerIdentityUsingPackage(
+            @Nullable String callerPackage) {
+        if (callerPackage == null) {
+            ActiveAdmin admin = getActiveAdminOfCaller();
+            if (admin != null) {
+                callerPackage = admin.info.getPackageName();
+            } else {
+                return getCallerIdentity();
+            }
+        }
+        return getCallerIdentity(callerPackage);
     }
 
     /**
      * Retrieves the active admin of the caller. This method should not be called directly and
-     * should only be called by {@link #getCallerIdentityOptionalAdmin} or
-     * {@link #getCallerIdentityOptionalPackage}.
+     * should only be called by {@link #getAdminCallerIdentity},
+     * {@link #getNonPrivilegedOrAdminCallerIdentity}, {@link #getAdminCallerIdentityUsingPackage}
+     * or {@link #getNonPrivilegedOrAdminCallerIdentityUsingPackage}.
      */
     private ActiveAdmin getActiveAdminOfCaller() {
         final int callerUid = mInjector.binderGetCallingUid();
@@ -1731,7 +1780,7 @@
 
         final IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
             @Override
-            public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
+            public void send(int code, Intent intent, String resolvedType, IBinder allowlistToken,
                     IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
                 final int status = intent.getIntExtra(
                         PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE);
@@ -2104,9 +2153,11 @@
         mInjector.binderWithCleanCallingIdentity(() -> {
             int affectedUserHandle = parent ? getProfileParentId(userHandle) : userHandle;
             AlarmManager am = mInjector.getAlarmManager();
+            // Broadcast alarms sent by system are immutable
             PendingIntent pi = PendingIntent.getBroadcastAsUser(context, REQUEST_EXPIRE_PASSWORD,
                     new Intent(ACTION_EXPIRED_PASSWORD_NOTIFICATION),
-                    PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT,
+                    PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT
+                            | PendingIntent.FLAG_IMMUTABLE,
                     UserHandle.of(affectedUserHandle));
             am.cancel(pi);
             if (alarmTime != 0) {
@@ -2129,9 +2180,9 @@
     ActiveAdmin getActiveAdminUncheckedLocked(ComponentName who, int userHandle, boolean parent) {
         ensureLocked();
         if (parent) {
-            Preconditions.checkCallAuthorization(isManagedProfile(userHandle), String.format(
+            Preconditions.checkCallAuthorization(isManagedProfile(userHandle),
                     "You can not call APIs on the parent profile outside a managed profile, "
-                            + "userId = %d", userHandle));
+                            + "userId = %d", userHandle);
         }
         ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
         if (admin != null && parent) {
@@ -2146,61 +2197,63 @@
                 reqPolicy, /* permission= */ null);
     }
 
-    @NonNull ActiveAdmin getDeviceOwnerOfCallerLocked(final CallerIdentity caller) {
+    @NonNull ActiveAdmin getDeviceOwnerLocked(final CallerIdentity caller) {
         ensureLocked();
         ComponentName doComponent = mOwners.getDeviceOwnerComponent();
         Preconditions.checkState(doComponent != null,
-                String.format("No device owner for user %d", caller.getUid()));
+                "No device owner for user %d", caller.getUid());
 
         // Use the user ID of the caller instead of mOwners.getDeviceOwnerUserId() because
         // secondary, affiliated users will have their own admin.
         ActiveAdmin doAdmin = getUserData(caller.getUserId()).mAdminMap.get(doComponent);
         Preconditions.checkState(doAdmin != null,
-                String.format("Device owner %s for user %d not found", doComponent,
-                        caller.getUid()));
+                "Device owner %s for user %d not found", doComponent,
+                        caller.getUid());
 
         Preconditions.checkCallAuthorization(doAdmin.getUid() == caller.getUid(),
-                    String.format("Admin %s is not owned by uid %d, but uid %d", doComponent,
-                            caller.getUid(), doAdmin.getUid()));
+                    "Admin %s is not owned by uid %d, but uid %d", doComponent,
+                            caller.getUid(), doAdmin.getUid());
 
         Preconditions.checkCallAuthorization(
-                doAdmin.info.getComponent().equals(caller.getComponentName()),
-                String.format("Caller component %s is not device owner",
-                        caller.getComponentName()));
+                !caller.hasAdminComponent()
+                || doAdmin.info.getComponent().equals(caller.getComponentName()),
+                "Caller component %s is not device owner",
+                        caller.getComponentName());
 
         return doAdmin;
     }
 
-    @NonNull ActiveAdmin getProfileOwnerOfCallerLocked(final CallerIdentity caller) {
+    @NonNull ActiveAdmin getProfileOwnerLocked(final CallerIdentity caller) {
         ensureLocked();
         final ComponentName poAdminComponent = mOwners.getProfileOwnerComponent(caller.getUserId());
 
         Preconditions.checkState(poAdminComponent != null,
-                    String.format("No profile owner for user %d", caller.getUid()));
+                    "No profile owner for user %d", caller.getUid());
 
         ActiveAdmin poAdmin = getUserData(caller.getUserId()).mAdminMap.get(poAdminComponent);
         Preconditions.checkState(poAdmin != null,
-                    String.format("No device profile owner for caller %d", caller.getUid()));
+                    "No device profile owner for caller %d", caller.getUid());
 
         Preconditions.checkCallAuthorization(poAdmin.getUid() == caller.getUid(),
-                    String.format("Admin %s is not owned by uid %d", poAdminComponent,
-                            caller.getUid()));
+                    "Admin %s is not owned by uid %d", poAdminComponent,
+                            caller.getUid());
 
         Preconditions.checkCallAuthorization(
-                poAdmin.info.getComponent().equals(caller.getComponentName()),
-                String.format("Caller component %s is not profile owner",
-                        caller.getComponentName()));
+                !caller.hasAdminComponent()
+                || poAdmin.info.getComponent().equals(caller.getComponentName()),
+                "Caller component %s is not profile owner",
+                        caller.getComponentName());
 
         return poAdmin;
     }
 
     @NonNull ActiveAdmin getOrganizationOwnedProfileOwnerLocked(final CallerIdentity caller) {
-        final ActiveAdmin profileOwner = getProfileOwnerOfCallerLocked(caller);
+        final ActiveAdmin profileOwner = getProfileOwnerLocked(caller);
 
         Preconditions.checkCallAuthorization(
                 mOwners.isProfileOwnerOfOrganizationOwnedDevice(caller.getUserId()),
-                String.format("Admin %s is not of an org-owned device",
-                        profileOwner.info.getComponent()));
+                "Admin %s is not of an org-owned device",
+                        profileOwner.info.getComponent());
 
         return profileOwner;
     }
@@ -2217,10 +2270,10 @@
         }
 
         if (poAdminComponent != null) {
-            return getProfileOwnerOfCallerLocked(caller);
+            return getProfileOwnerLocked(caller);
         }
 
-        return getDeviceOwnerOfCallerLocked(caller);
+        return getDeviceOwnerLocked(caller);
     }
 
     @NonNull ActiveAdmin getParentOfAdminIfRequired(ActiveAdmin admin, boolean parent) {
@@ -3328,7 +3381,7 @@
                     updatePasswordQualityCacheForUserGroup(userId);
                     saveSettingsLocked(userId);
                 }
-                maybeLogPasswordComplexitySet(who, userId, parent, passwordPolicy);
+                logPasswordQualitySetIfSecurityLogEnabled(who, userId, parent, passwordPolicy);
             });
         }
         DevicePolicyEventLogger
@@ -3542,7 +3595,7 @@
                 updatePasswordValidityCheckpointLocked(userId, parent);
                 saveSettingsLocked(userId);
             }
-            maybeLogPasswordComplexitySet(who, userId, parent, passwordPolicy);
+            logPasswordQualitySetIfSecurityLogEnabled(who, userId, parent, passwordPolicy);
         }
         DevicePolicyEventLogger
                 .createEvent(DevicePolicyEnums.SET_PASSWORD_MINIMUM_LENGTH)
@@ -3820,7 +3873,7 @@
                 updatePasswordValidityCheckpointLocked(userId, parent);
                 saveSettingsLocked(userId);
             }
-            maybeLogPasswordComplexitySet(who, userId, parent, passwordPolicy);
+            logPasswordQualitySetIfSecurityLogEnabled(who, userId, parent, passwordPolicy);
         }
         DevicePolicyEventLogger
                 .createEvent(DevicePolicyEnums.SET_PASSWORD_MINIMUM_UPPER_CASE)
@@ -3850,7 +3903,7 @@
                 updatePasswordValidityCheckpointLocked(userId, parent);
                 saveSettingsLocked(userId);
             }
-            maybeLogPasswordComplexitySet(who, userId, parent, passwordPolicy);
+            logPasswordQualitySetIfSecurityLogEnabled(who, userId, parent, passwordPolicy);
         }
         DevicePolicyEventLogger
                 .createEvent(DevicePolicyEnums.SET_PASSWORD_MINIMUM_LOWER_CASE)
@@ -3882,7 +3935,7 @@
                 updatePasswordValidityCheckpointLocked(userId, parent);
                 saveSettingsLocked(userId);
             }
-            maybeLogPasswordComplexitySet(who, userId, parent, passwordPolicy);
+            logPasswordQualitySetIfSecurityLogEnabled(who, userId, parent, passwordPolicy);
         }
         DevicePolicyEventLogger
                 .createEvent(DevicePolicyEnums.SET_PASSWORD_MINIMUM_LETTERS)
@@ -3914,7 +3967,7 @@
                 updatePasswordValidityCheckpointLocked(userId, parent);
                 saveSettingsLocked(userId);
             }
-            maybeLogPasswordComplexitySet(who, userId, parent, passwordPolicy);
+            logPasswordQualitySetIfSecurityLogEnabled(who, userId, parent, passwordPolicy);
         }
         DevicePolicyEventLogger
                 .createEvent(DevicePolicyEnums.SET_PASSWORD_MINIMUM_NUMERIC)
@@ -3946,7 +3999,7 @@
                 updatePasswordValidityCheckpointLocked(userId, parent);
                 saveSettingsLocked(userId);
             }
-            maybeLogPasswordComplexitySet(who, userId, parent, passwordPolicy);
+            logPasswordQualitySetIfSecurityLogEnabled(who, userId, parent, passwordPolicy);
         }
         DevicePolicyEventLogger
                 .createEvent(DevicePolicyEnums.SET_PASSWORD_MINIMUM_SYMBOLS)
@@ -3979,7 +4032,7 @@
                 updatePasswordValidityCheckpointLocked(userId, parent);
                 saveSettingsLocked(userId);
             }
-            maybeLogPasswordComplexitySet(who, userId, parent, passwordPolicy);
+            logPasswordQualitySetIfSecurityLogEnabled(who, userId, parent, passwordPolicy);
         }
         DevicePolicyEventLogger
                 .createEvent(DevicePolicyEnums.SET_PASSWORD_MINIMUM_NON_LETTER)
@@ -4109,9 +4162,9 @@
 
         final CallerIdentity caller = getCallerIdentity();
         Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
-        Preconditions.checkCallAuthorization(isManagedProfile(userHandle), String.format(
+        Preconditions.checkCallAuthorization(isManagedProfile(userHandle),
                 "can not call APIs refering to the parent profile outside a managed profile, "
-                        + "userId = %d", userHandle));
+                        + "userId = %d", userHandle);
 
         synchronized (getLockObject()) {
             final int targetUser = getProfileParentId(userHandle);
@@ -4133,9 +4186,9 @@
 
         final CallerIdentity caller = getCallerIdentity();
         Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
-        Preconditions.checkCallAuthorization(!isManagedProfile(userHandle), String.format(
+        Preconditions.checkCallAuthorization(!isManagedProfile(userHandle),
                 "You can not check password sufficiency for a managed profile, userId = %d",
-                userHandle));
+                userHandle);
         enforceUserUnlocked(userHandle);
 
         synchronized (getLockObject()) {
@@ -4346,12 +4399,13 @@
         return mInjector.binderWithCleanCallingIdentity(() -> mUserManager.getUserInfo(userId));
     }
 
-    private boolean setPasswordPrivileged(@NonNull String password, int flags, int callingUid) {
+    private boolean setPasswordPrivileged(@NonNull String password, int flags,
+            CallerIdentity caller) {
         // Only allow setting password on an unsecured user
-        if (isLockScreenSecureUnchecked(UserHandle.getUserId(callingUid))) {
+        if (isLockScreenSecureUnchecked(caller.getUserId())) {
             throw new SecurityException("Cannot change current password");
         }
-        return resetPasswordInternal(password, 0, null, flags, callingUid);
+        return resetPasswordInternal(password, 0, null, flags, caller);
     }
 
     @Override
@@ -4361,30 +4415,34 @@
             return false;
         }
         if (password == null) password = "";
-        final int callingUid = mInjector.binderGetCallingUid();
-        final int userHandle = mInjector.userHandleGetCallingUserId();
+        final CallerIdentity caller = getCallerIdentity();
+        final int userHandle = caller.getUserId();
 
         // As of R, only privlleged caller holding RESET_PASSWORD can call resetPassword() to
         // set password to an unsecured user.
         if (hasCallingPermission(permission.RESET_PASSWORD)) {
-            return setPasswordPrivileged(password, flags, callingUid);
+            return setPasswordPrivileged(password, flags, caller);
         }
 
-        synchronized (getLockObject()) {
-            // If caller has PO (or DO) throw or fail silently depending on its target SDK level.
-            ActiveAdmin admin = getActiveAdminWithPolicyForUidLocked(
-                    null, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, callingUid);
-            if (admin != null) {
+        // If caller has PO (or DO) throw or fail silently depending on its target SDK level.
+        if (isDeviceOwner(caller) || isProfileOwner(caller)) {
+            synchronized (getLockObject()) {
+                ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
                 if (getTargetSdk(admin.info.getPackageName(), userHandle) < Build.VERSION_CODES.O) {
                     Slog.e(LOG_TAG, "DPC can no longer call resetPassword()");
                     return false;
                 }
                 throw new SecurityException("Device admin can no longer call resetPassword()");
             }
+        }
 
+        // Caller is not DO or PO, could either be unauthorized or Device Admin.
+        synchronized (getLockObject()) {
             // Legacy device admin cannot call resetPassword either
-            admin = getActiveAdminForCallerLocked(
+            ActiveAdmin admin = getActiveAdminForCallerLocked(
                     null, DeviceAdminInfo.USES_POLICY_RESET_PASSWORD, false);
+            Preconditions.checkCallAuthorization(admin != null,
+                    "Unauthorized caller cannot call resetPassword.");
             if (getTargetSdk(admin.info.getPackageName(),
                     userHandle) <= android.os.Build.VERSION_CODES.M) {
                 Slog.e(LOG_TAG, "Device admin can no longer call resetPassword()");
@@ -4395,7 +4453,8 @@
     }
 
     private boolean resetPasswordInternal(String password, long tokenHandle, byte[] token,
-            int flags, int callingUid) {
+            int flags, CallerIdentity caller) {
+        final int callingUid = caller.getUid();
         final int userHandle = UserHandle.getUserId(callingUid);
         synchronized (getLockObject()) {
             final PasswordMetrics minMetrics = getPasswordMinimumMetrics(userHandle);
@@ -4424,7 +4483,7 @@
             return false;
         }
 
-        boolean callerIsDeviceOwnerAdmin = isCallerDeviceOwner(callingUid);
+        boolean callerIsDeviceOwnerAdmin = isDeviceOwner(caller);
         boolean doNotAskCredentialsOnBoot =
                 (flags & DevicePolicyManager.RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT) != 0;
         if (callerIsDeviceOwnerAdmin && doNotAskCredentialsOnBoot) {
@@ -4961,7 +5020,7 @@
             ApplicationInfo ai = mInjector.getIPackageManager().getApplicationInfo(
                     packageName, 0, caller.getUserId());
             Preconditions.checkArgument(ai != null,
-                    String.format("Provided package %s is not installed", packageName));
+                    "Provided package %s is not installed", packageName);
             granteeUid = ai.uid;
         } catch (RemoteException e) {
             throw new IllegalStateException("Failure getting grantee uid", e);
@@ -5339,14 +5398,14 @@
         // Retrieve the user ID of the calling process.
         final int userId = caller.getUserId();
         final boolean hasDoDelegation = !Collections.disjoint(scopes, DEVICE_OWNER_DELEGATIONS);
+        // Ensure calling process is device/profile owner.
+        if (hasDoDelegation) {
+            Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+        } else {
+            Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+        }
+
         synchronized (getLockObject()) {
-            // Ensure calling process is device/profile owner.
-            if (hasDoDelegation) {
-                Preconditions.checkCallAuthorization(isDeviceOwner(caller));
-            } else {
-                // TODO move whole condition out of synchronized block
-                getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-            }
             // Ensure the delegate is installed (skip this for DELEGATION_CERT_INSTALL in pre-N).
             if (shouldCheckIfDelegatePackageIsInstalled(delegatePackage,
                         getTargetSdk(who.getPackageName(), userId), scopes)) {
@@ -5467,11 +5526,10 @@
         }
 
         // Retrieve the user ID of the calling process.
-        final int userId = mInjector.userHandleGetCallingUserId();
+        final CallerIdentity caller = getCallerIdentity(who);
+        Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
         synchronized (getLockObject()) {
-            // Ensure calling process is device/profile owner.
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-            return getDelegatePackagesInternalLocked(scope, userId);
+            return getDelegatePackagesInternalLocked(scope, caller.getUserId());
         }
     }
 
@@ -5580,7 +5638,7 @@
     private boolean isCallerDelegate(CallerIdentity caller, String scope) {
         Objects.requireNonNull(caller.getPackageName(), "callerPackage is null");
         Preconditions.checkArgument(Arrays.asList(DELEGATIONS).contains(scope),
-                String.format("Unexpected delegation scope: %s", scope));
+                "Unexpected delegation scope: %s", scope);
 
         synchronized (getLockObject()) {
             // Retrieve user policy data.
@@ -5600,11 +5658,12 @@
             String delegatePackage, String scope) {
         Objects.requireNonNull(who, "ComponentName is null");
 
-        final int userId = mInjector.userHandleGetCallingUserId();
+        final CallerIdentity caller = getCallerIdentity(who);
+        // Ensure calling process is device/profile owner.
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+
         synchronized (getLockObject()) {
-            // Ensure calling process is device/profile owner.
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-            final DevicePolicyData policy = getUserData(userId);
+            final DevicePolicyData policy = getUserData(caller.getUserId());
 
             if (delegatePackage != null) {
                 // Set package as a delegate for scope if it is not already one.
@@ -5658,7 +5717,7 @@
      */
     @Override
     public boolean setAlwaysOnVpnPackage(ComponentName who, String vpnPackage, boolean lockdown,
-            List<String> lockdownWhitelist)
+            List<String> lockdownAllowlist)
             throws SecurityException {
         Objects.requireNonNull(who, "ComponentName is null");
 
@@ -5673,10 +5732,10 @@
                         DevicePolicyManager.ERROR_VPN_PACKAGE_NOT_FOUND, vpnPackage);
             }
 
-            if (vpnPackage != null && lockdown && lockdownWhitelist != null) {
-                for (String packageName : lockdownWhitelist) {
+            if (vpnPackage != null && lockdown && lockdownAllowlist != null) {
+                for (String packageName : lockdownAllowlist) {
                     if (!isPackageInstalledForUser(packageName, userId)) {
-                        Slog.w(LOG_TAG, "Non-existent package in VPN whitelist: " + packageName);
+                        Slog.w(LOG_TAG, "Non-existent package in VPN allowlist: " + packageName);
                         throw new ServiceSpecificException(
                                 DevicePolicyManager.ERROR_VPN_PACKAGE_NOT_FOUND, packageName);
                     }
@@ -5684,7 +5743,7 @@
             }
             // If some package is uninstalled after the check above, it will be ignored by CM.
             if (!mInjector.getConnectivityManager().setAlwaysOnVpnPackageForUser(
-                    userId, vpnPackage, lockdown, lockdownWhitelist)) {
+                    userId, vpnPackage, lockdown, lockdownAllowlist)) {
                 throw new UnsupportedOperationException();
             }
             DevicePolicyEventLogger
@@ -5692,7 +5751,7 @@
                     .setAdmin(caller.getComponentName())
                     .setStrings(vpnPackage)
                     .setBoolean(lockdown)
-                    .setInt(lockdownWhitelist != null ? lockdownWhitelist.size() : 0)
+                    .setInt(lockdownAllowlist != null ? lockdownAllowlist.size() : 0)
                     .write();
         });
         synchronized (getLockObject()) {
@@ -5732,8 +5791,9 @@
     public boolean isAlwaysOnVpnLockdownEnabled(ComponentName admin) throws SecurityException {
         Objects.requireNonNull(admin, "ComponentName is null");
 
-        final CallerIdentity caller = getCallerIdentity(admin);
-        Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)
+        final CallerIdentity caller = getNonPrivilegedOrAdminCallerIdentity(admin);
+        Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+                && (isDeviceOwner(caller) || isProfileOwner(caller)))
                 || hasCallingPermission(PERMISSION_MAINLINE_NETWORK_STACK));
 
         return mInjector.binderWithCleanCallingIdentity(
@@ -5750,7 +5810,7 @@
     }
 
     @Override
-    public List<String> getAlwaysOnVpnLockdownWhitelist(ComponentName admin)
+    public List<String> getAlwaysOnVpnLockdownAllowlist(ComponentName admin)
             throws SecurityException {
         Objects.requireNonNull(admin, "ComponentName is null");
 
@@ -5817,7 +5877,7 @@
                             + "organization-owned device.");
         }
         if ((flags & WIPE_RESET_PROTECTION_DATA) != 0) {
-            Preconditions.checkCallAuthorization(isCallerDeviceOwner(caller.getUid())
+            Preconditions.checkCallAuthorization(isDeviceOwner(caller)
                             || calledByProfileOwnerOnOrgOwnedDevice,
                     "Only device owners or profile owners of organization-owned device can set "
                             + "WIPE_RESET_PROTECTION_DATA");
@@ -5828,7 +5888,7 @@
             admin = getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_WIPE_DATA);
         }
         Preconditions.checkCallAuthorization(admin != null,
-                String.format("No active admin for user %d", caller.getUserId()));
+                "No active admin for user %d", caller.getUserId());
 
         if (TextUtils.isEmpty(wipeReasonForUser)) {
             if (calledByProfileOwnerOnOrgOwnedDevice && !calledOnParentInstance) {
@@ -6021,7 +6081,7 @@
         }
         Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
 
-        final CallerIdentity caller = getCallerIdentityOptionalAdmin(comp);
+        final CallerIdentity caller = getAdminCallerIdentity(comp);
         Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
         Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(BIND_DEVICE_ADMIN));
 
@@ -6054,8 +6114,8 @@
         Preconditions.checkCallAuthorization(isSystemUid(caller));
         // Managed Profile password can only be changed when it has a separate challenge.
         if (!isSeparateProfileChallengeEnabled(userId)) {
-            Preconditions.checkCallAuthorization(!isManagedProfile(userId), String.format("You can "
-                    + "not set the active password for a managed profile, userId = %d", userId));
+            Preconditions.checkCallAuthorization(!isManagedProfile(userId), "You can "
+                    + "not set the active password for a managed profile, userId = %d", userId);
         }
 
         DevicePolicyData policy = getUserData(userId);
@@ -6108,9 +6168,9 @@
         Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
         Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(BIND_DEVICE_ADMIN));
         if (!isSeparateProfileChallengeEnabled(userHandle)) {
-            Preconditions.checkCallAuthorization(!isManagedProfile(userHandle), String.format(
+            Preconditions.checkCallAuthorization(!isManagedProfile(userHandle),
                     "You can not report failed password attempt if separate profile challenge is "
-                            + "not in place for a managed profile, userId = %d", userHandle));
+                            + "not in place for a managed profile, userId = %d", userHandle);
         }
 
         boolean wipeData = false;
@@ -6461,12 +6521,12 @@
         }
         Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
 
-        final CallerIdentity caller = getCallerIdentityOptionalAdmin(who);
+        final CallerIdentity caller = getAdminCallerIdentity(who);
         Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
 
         synchronized (getLockObject()) {
             // Check for permissions if a particular caller is specified
-            if (who != null) {
+            if (caller.hasAdminComponent()) {
                 // When checking for a single caller, status is based on caller's request
                 ActiveAdmin ap = getActiveAdminUncheckedLocked(who, userHandle);
                 return ap != null ? ap.encryptionRequested : false;
@@ -6495,7 +6555,7 @@
         }
         Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
 
-        final CallerIdentity caller = getCallerIdentityOptionalPackage(callerPackage);
+        final CallerIdentity caller = getAdminCallerIdentityUsingPackage(callerPackage);
         Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
 
         // It's not critical here, but let's make sure the package name is correct, in case
@@ -6576,8 +6636,8 @@
         }
 
         synchronized (getLockObject()) {
-            ActiveAdmin ap = getActiveAdminForCallerLocked(who,
-                    DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, parent);
+            ActiveAdmin ap = getParentOfAdminIfRequired(getProfileOwnerOrDeviceOwnerLocked(caller),
+                    parent);
             if (ap.disableScreenCapture != disabled) {
                 ap.disableScreenCapture = disabled;
                 saveSettingsLocked(caller.getUserId());
@@ -7260,9 +7320,15 @@
 
     private boolean isDeviceOwner(CallerIdentity caller) {
         synchronized (getLockObject()) {
-            return mOwners.hasDeviceOwner()
-                    && mOwners.getDeviceOwnerUserId() == caller.getUserId()
-                    && mOwners.getDeviceOwnerComponent().equals(caller.getComponentName());
+            if (!mOwners.hasDeviceOwner() || mOwners.getDeviceOwnerUserId() != caller.getUserId()) {
+                return false;
+            }
+
+            if (caller.hasAdminComponent()) {
+                return mOwners.getDeviceOwnerComponent().equals(caller.getComponentName());
+            } else {
+                return isUidDeviceOwnerLocked(caller.getUid());
+            }
         }
     }
 
@@ -7292,8 +7358,43 @@
      * @return true if {@code identity} is a profile owner, false otherwise.
      */
     public boolean isProfileOwner(CallerIdentity caller) {
-        final ComponentName profileOwner = getProfileOwner(caller.getUserId());
-        return profileOwner != null && profileOwner.equals(caller.getComponentName());
+        synchronized (getLockObject()) {
+            final ComponentName profileOwner = getProfileOwner(caller.getUserId());
+            // No profile owner.
+            if (profileOwner == null) {
+                return false;
+            }
+            // The admin ComponentName was specified, check it directly.
+            if (caller.hasAdminComponent()) {
+                return profileOwner.equals(caller.getComponentName());
+            } else {
+                return isUidProfileOwnerLocked(caller.getUid());
+            }
+        }
+    }
+
+    /**
+     * Checks if the app uid provided is the profile owner. This method should only be called
+     * if no componentName is available.
+     *
+     * @param appUid UID of the caller.
+     * @return true if the caller is the profile owner
+     */
+    private boolean isUidProfileOwnerLocked(int appUid) {
+        ensureLocked();
+
+        final int userId = UserHandle.getUserId(appUid);
+        final ComponentName profileOwnerComponent = mOwners.getProfileOwnerComponent(userId);
+        if (profileOwnerComponent == null) {
+            return false;
+        }
+        for (ActiveAdmin admin : getUserData(userId).mAdminList) {
+            final ComponentName currentAdminComponent = admin.info.getComponent();
+            if (admin.getUid() == appUid && profileOwnerComponent.equals(currentAdminComponent)) {
+                return true;
+            }
+        }
+        return false;
     }
 
     private boolean hasProfileOwner(int userId) {
@@ -7603,8 +7704,7 @@
         enforceUserUnlocked(userId);
         synchronized (getLockObject()) {
             // Check if this is the profile owner who is calling
-            final ActiveAdmin admin =
-                    getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
 
             mInjector.binderWithCleanCallingIdentity(() -> {
                 clearProfileOwnerLocked(admin, userId);
@@ -7791,7 +7891,7 @@
                 // Current user has a managed-profile, but current user is not managed, so
                 // rather than moving to finalized state, go back to unmanaged once
                 // profile provisioning is complete.
-                if (newState == DevicePolicyManager.STATE_USER_UNMANAGED) {
+                if (newState == DevicePolicyManager.STATE_USER_PROFILE_FINALIZED) {
                     return;
                 }
                 break;
@@ -8319,8 +8419,10 @@
     }
 
     private void enforceCanCallLockTaskLocked(ComponentName who) {
-        getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-        final int userId =  mInjector.userHandleGetCallingUserId();
+        final CallerIdentity caller = getAdminCallerIdentity(who);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+
+        final int userId =  caller.getUserId();
         if (!canUserUseLockTaskLocked(userId)) {
             throw new SecurityException("User " + userId + " is not allowed to use lock task");
         }
@@ -8484,10 +8586,11 @@
     public void addPersistentPreferredActivity(ComponentName who, IntentFilter filter,
             ComponentName activity) {
         Objects.requireNonNull(who, "ComponentName is null");
-        final int userHandle = UserHandle.getCallingUserId();
-        synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+        final CallerIdentity caller = getCallerIdentity(who);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
 
+        final int userHandle = caller.getUserId();
+        synchronized (getLockObject()) {
             long id = mInjector.binderClearCallingIdentity();
             try {
                 mIPackageManager.addPersistentPreferredActivity(filter, activity, userHandle);
@@ -8510,10 +8613,11 @@
     @Override
     public void clearPackagePersistentPreferredActivities(ComponentName who, String packageName) {
         Objects.requireNonNull(who, "ComponentName is null");
-        final int userHandle = UserHandle.getCallingUserId();
-        synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+        final CallerIdentity caller = getCallerIdentity(who);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
 
+        final int userHandle = caller.getUserId();
+        synchronized (getLockObject()) {
             long id = mInjector.binderClearCallingIdentity();
             try {
                 mIPackageManager.clearPackagePersistentPreferredActivities(packageName, userHandle);
@@ -8612,7 +8716,7 @@
         Objects.requireNonNull(agent, "agent null");
         Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
 
-        final CallerIdentity caller = getCallerIdentityOptionalAdmin(admin);
+        final CallerIdentity caller = getAdminCallerIdentity(admin);
         Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
 
         synchronized (getLockObject()) {
@@ -8665,10 +8769,11 @@
     @Override
     public void setRestrictionsProvider(ComponentName who, ComponentName permissionProvider) {
         Objects.requireNonNull(who, "ComponentName is null");
-        synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+        final CallerIdentity caller = getCallerIdentity(who);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
 
-            int userHandle = UserHandle.getCallingUserId();
+        synchronized (getLockObject()) {
+            int userHandle = caller.getUserId();
             DevicePolicyData userData = getUserData(userHandle);
             userData.mRestrictionsProvider = permissionProvider;
             saveSettingsLocked(userHandle);
@@ -8687,10 +8792,10 @@
     @Override
     public void addCrossProfileIntentFilter(ComponentName who, IntentFilter filter, int flags) {
         Objects.requireNonNull(who, "ComponentName is null");
-        int callingUserId = UserHandle.getCallingUserId();
+        final CallerIdentity caller = getCallerIdentity(who);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+        int callingUserId = caller.getUserId();
         synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-
             long id = mInjector.binderClearCallingIdentity();
             try {
                 UserInfo parent = mUserManager.getProfileParent(callingUserId);
@@ -8736,9 +8841,11 @@
     @Override
     public void clearCrossProfileIntentFilters(ComponentName who) {
         Objects.requireNonNull(who, "ComponentName is null");
-        int callingUserId = UserHandle.getCallingUserId();
+        final CallerIdentity caller = getCallerIdentity(who);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+
+        int callingUserId = caller.getUserId();
         synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
             long id = mInjector.binderClearCallingIdentity();
             try {
                 UserInfo parent = mUserManager.getProfileParent(callingUserId);
@@ -9413,10 +9520,11 @@
     @Override
     public int logoutUser(ComponentName who) {
         Objects.requireNonNull(who, "ComponentName is null");
+        final CallerIdentity caller = getCallerIdentity(who);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
 
-        final int callingUserId = mInjector.userHandleGetCallingUserId();
+        final int callingUserId = caller.getUserId();
         synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
             if (!isUserAffiliatedWithDeviceLocked(callingUserId)) {
                 throw new SecurityException("Admin " + who +
                         " is neither the device owner or affiliated user's profile owner.");
@@ -9578,9 +9686,8 @@
 
         int userHandle = caller.getUserId();
         synchronized (getLockObject()) {
-            final ActiveAdmin activeAdmin =
-                    getActiveAdminForCallerLocked(who,
-                            DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, parent);
+            final ActiveAdmin activeAdmin = getParentOfAdminIfRequired(
+                    getProfileOwnerOrDeviceOwnerLocked(caller), parent);
 
             if (isDeviceOwner(caller)) {
                 if (!UserRestrictionsUtils.canDeviceOwnerChange(key)) {
@@ -9899,9 +10006,9 @@
         boolean result;
         synchronized (getLockObject()) {
             Preconditions.checkCallAuthorization(
-                    isUserAffiliatedWithDeviceLocked(caller.getUserId()), String.format(
+                    isUserAffiliatedWithDeviceLocked(caller.getUserId()),
                             "Admin %s is neither the device owner or "
-                                    + "affiliated user's profile owner.", who));
+                                    + "affiliated user's profile owner.", who);
             final long id = mInjector.binderClearCallingIdentity();
             try {
                 if (VERBOSE_LOG) {
@@ -10283,12 +10390,12 @@
         final CallerIdentity caller = getCallerIdentity(who);
         Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
         Preconditions.checkCallAuthorization(!isManagedProfile(caller.getUserId()),
-                String.format("User %d is not allowed to call setSecondaryLockscreenEnabled",
-                        caller.getUserId()));
+                "User %d is not allowed to call setSecondaryLockscreenEnabled",
+                        caller.getUserId());
         // Allow testOnly admins to bypass supervision config requirement.
         Preconditions.checkCallAuthorization(isAdminTestOnlyLocked(who, caller.getUserId())
-                        || isDefaultSupervisor(caller), String.format("Admin %s is not the "
-                + "default supervision component", caller.getComponentName()));
+                        || isDefaultSupervisor(caller), "Admin %s is not the "
+                + "default supervision component", caller.getComponentName());
 
         synchronized (getLockObject()) {
             DevicePolicyData policy = getUserData(caller.getUserId());
@@ -10473,7 +10580,7 @@
                 return;
             }
 
-            if (!GLOBAL_SETTINGS_WHITELIST.contains(setting)
+            if (!GLOBAL_SETTINGS_ALLOWLIST.contains(setting)
                     && !UserManager.isDeviceInDemoMode(mContext)) {
                 throw new SecurityException(String.format(
                         "Permission denial: device owners cannot update %1$s", setting));
@@ -10497,19 +10604,17 @@
     public void setSystemSetting(ComponentName who, String setting, String value) {
         Objects.requireNonNull(who, "ComponentName is null");
         Preconditions.checkStringNotEmpty(setting, "String setting is null or empty");
+        final CallerIdentity caller = getCallerIdentity(who);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
 
         synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-
-            if (!SYSTEM_SETTINGS_WHITELIST.contains(setting)) {
+            if (!SYSTEM_SETTINGS_ALLOWLIST.contains(setting)) {
                 throw new SecurityException(String.format(
                         "Permission denial: device owners cannot update %1$s", setting));
             }
 
-            final int callingUserId = mInjector.userHandleGetCallingUserId();
-
             mInjector.binderWithCleanCallingIdentity(() ->
-                mInjector.settingsSystemPutStringForUser(setting, value, callingUserId));
+                    mInjector.settingsSystemPutStringForUser(setting, value, caller.getUserId()));
         }
     }
 
@@ -10594,8 +10699,10 @@
             Slog.wtf(LOG_TAG, "Failed to resolve intent for location settings");
         }
 
+        // Simple notification clicks are immutable
         PendingIntent locationSettingsIntent = mInjector.pendingIntentGetActivityAsUser(mContext, 0,
-                intent, PendingIntent.FLAG_UPDATE_CURRENT, null, user);
+                intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE, null,
+                user);
         Notification notification = new Notification.Builder(mContext,
                 SystemNotificationChannels.DEVICE_ADMIN)
                 .setSmallIcon(R.drawable.ic_info_outline)
@@ -10655,18 +10762,18 @@
     @Override
     public void setSecureSetting(ComponentName who, String setting, String value) {
         Objects.requireNonNull(who, "ComponentName is null");
-        int callingUserId = mInjector.userHandleGetCallingUserId();
+        final CallerIdentity caller = getCallerIdentity(who);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
 
+        int callingUserId = caller.getUserId();
         synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-
             if (isDeviceOwner(who, callingUserId)) {
-                if (!SECURE_SETTINGS_DEVICEOWNER_WHITELIST.contains(setting)
+                if (!SECURE_SETTINGS_DEVICEOWNER_ALLOWLIST.contains(setting)
                         && !isCurrentUserDemo()) {
                     throw new SecurityException(String.format(
                             "Permission denial: Device owners cannot update %1$s", setting));
                 }
-            } else if (!SECURE_SETTINGS_WHITELIST.contains(setting) && !isCurrentUserDemo()) {
+            } else if (!SECURE_SETTINGS_ALLOWLIST.contains(setting) && !isCurrentUserDemo()) {
                 throw new SecurityException(String.format(
                         "Permission denial: Profile owners cannot update %1$s", setting));
             }
@@ -10751,8 +10858,10 @@
     @Override
     public void setMasterVolumeMuted(ComponentName who, boolean on) {
         Objects.requireNonNull(who, "ComponentName is null");
+        final CallerIdentity caller = getCallerIdentity(who);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+
         synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
             setUserRestriction(who, UserManager.DISALLOW_UNMUTE_DEVICE, on, /* parent */ false);
             DevicePolicyEventLogger
                     .createEvent(DevicePolicyEnums.SET_MASTER_VOLUME_MUTED)
@@ -10765,9 +10874,10 @@
     @Override
     public boolean isMasterVolumeMuted(ComponentName who) {
         Objects.requireNonNull(who, "ComponentName is null");
-        synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+        final CallerIdentity caller = getCallerIdentity(who);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
 
+        synchronized (getLockObject()) {
             AudioManager audioManager =
                     (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
             return audioManager.isMasterMute();
@@ -10776,13 +10886,13 @@
 
     @Override
     public void setUserIcon(ComponentName who, Bitmap icon) {
-        synchronized (getLockObject()) {
-            Objects.requireNonNull(who, "ComponentName is null");
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+        Objects.requireNonNull(who, "ComponentName is null");
+        final CallerIdentity caller = getCallerIdentity(who);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
 
-            int userId = UserHandle.getCallingUserId();
+        synchronized (getLockObject()) {
             mInjector.binderWithCleanCallingIdentity(
-                    () -> mUserManagerInternal.setUserIcon(userId, icon));
+                    () -> mUserManagerInternal.setUserIcon(caller.getUserId(), icon));
         }
         DevicePolicyEventLogger
                 .createEvent(DevicePolicyEnums.SET_USER_ICON)
@@ -10793,13 +10903,15 @@
     @Override
     public boolean setKeyguardDisabled(ComponentName who, boolean disabled) {
         Objects.requireNonNull(who, "ComponentName is null");
-        final int userId = mInjector.userHandleGetCallingUserId();
+        final CallerIdentity caller = getCallerIdentity(who);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+
+        final int userId = caller.getUserId();
         synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-            if (!isUserAffiliatedWithDeviceLocked(userId)) {
-                throw new SecurityException("Admin " + who +
-                        " is neither the device owner or affiliated user's profile owner.");
-            }
+            Preconditions.checkCallAuthorization(isUserAffiliatedWithDeviceLocked(userId),
+                    String.format(
+                            "Admin %s is neither the device owner or affiliated user's profile "
+                                    + "owner.", who));
         }
         if (isManagedProfile(userId)) {
             throw new SecurityException("Managed profile cannot disable keyguard");
@@ -10832,13 +10944,14 @@
 
     @Override
     public boolean setStatusBarDisabled(ComponentName who, boolean disabled) {
-        int userId = UserHandle.getCallingUserId();
+        final CallerIdentity caller = getAdminCallerIdentity(who);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+
+        int userId = caller.getUserId();
         synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-            if (!isUserAffiliatedWithDeviceLocked(userId)) {
-                throw new SecurityException("Admin " + who +
-                        " is neither the device owner or affiliated user's profile owner.");
-            }
+            Preconditions.checkCallAuthorization(isUserAffiliatedWithDeviceLocked(userId),
+                    "Admin " + who
+                            + " is neither the device owner or affiliated user's profile owner.");
             if (isManagedProfile(userId)) {
                 throw new SecurityException("Managed profile cannot disable status bar");
             }
@@ -11045,9 +11158,18 @@
         }
 
         @Override
-        public boolean isActiveAdminWithPolicy(int uid, int reqPolicy) {
+        public boolean isActiveDeviceOwner(int uid) {
             synchronized (getLockObject()) {
-                return getActiveAdminWithPolicyForUidLocked(null, reqPolicy, uid) != null;
+                return getActiveAdminWithPolicyForUidLocked(
+                        null, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER, uid) != null;
+            }
+        }
+
+        @Override
+        public boolean isActiveProfileOwner(int uid) {
+            synchronized (getLockObject()) {
+                return getActiveAdminWithPolicyForUidLocked(
+                        null, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, uid) != null;
             }
         }
 
@@ -11171,8 +11293,8 @@
                 return false;
             }
             if (isUserAffiliatedWithDevice(UserHandle.getUserId(callerUid))
-                    && isActiveAdminWithPolicy(callerUid,
-                            DeviceAdminInfo.USES_POLICY_PROFILE_OWNER)) {
+                    && (isActiveProfileOwner(callerUid)
+                        || isActiveDeviceOwner(callerUid))) {
                 // device owner or a profile owner affiliated with the device owner
                 return true;
             }
@@ -11570,32 +11692,24 @@
     }
 
     /**
-     * Checks if the caller of the method is the device owner app.
-     *
-     * @param callerUid UID of the caller.
-     * @return true if the caller is the device owner app
+     * Checks if any of the packages associated with the UID of the app provided is that
+     * of the device owner.
+     * @param appUid UID of the app to check.
+     * @return {@code true} if any of the packages are the device owner, {@code false} otherwise.
      */
-    @VisibleForTesting
-    boolean isCallerDeviceOwner(int callerUid) {
-        synchronized (getLockObject()) {
-            if (!mOwners.hasDeviceOwner()) {
-                return false;
-            }
-            if (UserHandle.getUserId(callerUid) != mOwners.getDeviceOwnerUserId()) {
-                return false;
-            }
-            final String deviceOwnerPackageName = mOwners.getDeviceOwnerComponent()
-                    .getPackageName();
-                try {
-                    String[] pkgs = mInjector.getIPackageManager().getPackagesForUid(callerUid);
-                    for (String pkg : pkgs) {
-                        if (deviceOwnerPackageName.equals(pkg)) {
-                            return true;
-                        }
-                    }
-                } catch (RemoteException e) {
-                    return false;
+    private boolean isUidDeviceOwnerLocked(int appUid) {
+        ensureLocked();
+        final String deviceOwnerPackageName = mOwners.getDeviceOwnerComponent()
+                .getPackageName();
+        try {
+            String[] pkgs = mInjector.getIPackageManager().getPackagesForUid(appUid);
+            for (String pkg : pkgs) {
+                if (deviceOwnerPackageName.equals(pkg)) {
+                    return true;
                 }
+            }
+        } catch (RemoteException e) {
+            return false;
         }
         return false;
     }
@@ -12272,8 +12386,8 @@
         final CallerIdentity caller = getCallerIdentity();
         Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userId));
         Preconditions.checkCallAuthorization(canManageUsers(caller));
-        Preconditions.checkCallAuthorization(isManagedProfile(userId), String.format("You can not "
-                + "set organization color outside a managed profile, userId = %d", userId));
+        Preconditions.checkCallAuthorization(isManagedProfile(userId), "You can not "
+                + "set organization color outside a managed profile, userId = %d", userId);
 
         synchronized (getLockObject()) {
             ActiveAdmin admin = getProfileOwnerAdminLocked(userId);
@@ -12307,8 +12421,8 @@
 
         final CallerIdentity caller = getCallerIdentity();
         Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
-        Preconditions.checkCallAuthorization(isManagedProfile(userHandle), String.format("You can "
-                + "not get organization color outside a managed profile, userId = %d", userHandle));
+        Preconditions.checkCallAuthorization(isManagedProfile(userHandle), "You can "
+                + "not get organization color outside a managed profile, userId = %d", userHandle);
 
         synchronized (getLockObject()) {
             ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userHandle);
@@ -12373,9 +12487,9 @@
 
         final CallerIdentity caller = getCallerIdentity();
         Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
-        Preconditions.checkCallAuthorization(isManagedProfile(userHandle), String.format(
+        Preconditions.checkCallAuthorization(isManagedProfile(userHandle),
                 "You can not get organization name outside a managed profile, userId = %d",
-                userHandle));
+                userHandle);
 
         synchronized (getLockObject()) {
             ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userHandle);
@@ -12391,7 +12505,7 @@
         Objects.requireNonNull(packageNames);
         final CallerIdentity caller = getCallerIdentity(who);
         Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller),
-                String.format("Admin %s does not own the profile", caller.getComponentName()));
+                "Admin %s does not own the profile", caller.getComponentName());
 
         if (!mHasFeature) {
             return packageNames;
@@ -12442,7 +12556,7 @@
         }
         final CallerIdentity caller = getCallerIdentity(who);
         Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller),
-                String.format("Admin %s does not own the profile", caller.getComponentName()));
+                "Admin %s does not own the profile", caller.getComponentName());
 
         synchronized (getLockObject()) {
             final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
@@ -12473,7 +12587,7 @@
     @Override
     public void markProfileOwnerOnOrganizationOwnedDevice(ComponentName who, int userId) {
         // As the caller is the system, it must specify the component name of the profile owner
-        // as a sanity / safety check.
+        // as a safety check.
         Objects.requireNonNull(who);
 
         if (!mHasFeature) {
@@ -12509,7 +12623,7 @@
     @GuardedBy("getLockObject()")
     private void markProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(
             ComponentName who, int userId) {
-        // Sanity check: Make sure that the user has a profile owner and that the specified
+        // Make sure that the user has a profile owner and that the specified
         // component is the profile owner of that user.
         if (!isProfileOwner(who, userId)) {
             throw new IllegalArgumentException(String.format(
@@ -12575,9 +12689,11 @@
         }
 
         final Set<String> affiliationIds = new ArraySet<>(ids);
-        final int callingUserId = mInjector.userHandleGetCallingUserId();
+        final CallerIdentity caller = getAdminCallerIdentity(admin);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+        final int callingUserId = caller.getUserId();
+
         synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
             getUserData(callingUserId).mAffiliationIds = affiliationIds;
             saveSettingsLocked(callingUserId);
             if (callingUserId != UserHandle.USER_SYSTEM && isDeviceOwner(admin, callingUserId)) {
@@ -12603,10 +12719,11 @@
         }
 
         Objects.requireNonNull(admin);
+        final CallerIdentity caller = getCallerIdentity(admin);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+
         synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-            return new ArrayList<String>(
-                    getUserData(mInjector.userHandleGetCallingUserId()).mAffiliationIds);
+            return new ArrayList<String>(getUserData(caller.getUserId()).mAffiliationIds);
         }
     }
 
@@ -13116,11 +13233,11 @@
             return Collections.emptyList();
         }
         Objects.requireNonNull(admin);
+        final CallerIdentity caller = getCallerIdentity(admin);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
 
         synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-
-            final int callingUserId = mInjector.userHandleGetCallingUserId();
+            final int callingUserId = caller.getUserId();
             return mInjector.binderWithCleanCallingIdentity(() -> {
                 ArrayList<UserHandle> targetUsers = new ArrayList<>();
                 if (!isDeviceOwner(admin, callingUserId)) {
@@ -13436,8 +13553,9 @@
         final PackageManagerInternal pm = mInjector.getPackageManagerInternal();
         final Intent intent = new Intent(DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG);
         intent.setPackage(pm.getSystemUiServiceComponent().getPackageName());
-        final PendingIntent pendingIntent = PendingIntent.getBroadcastAsUser(mContext, 0, intent, 0,
-                UserHandle.CURRENT);
+        // Simple notification clicks are immutable
+        final PendingIntent pendingIntent = PendingIntent.getBroadcastAsUser(mContext, 0, intent,
+                PendingIntent.FLAG_IMMUTABLE, UserHandle.CURRENT);
         Notification notification =
                 new Notification.Builder(mContext, SystemNotificationChannels.DEVICE_ADMIN)
                 .setSmallIcon(R.drawable.ic_info_outline)
@@ -13523,9 +13641,11 @@
         if (token == null || token.length < 32) {
             throw new IllegalArgumentException("token must be at least 32-byte long");
         }
+        final CallerIdentity caller = getAdminCallerIdentity(admin);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+
         synchronized (getLockObject()) {
-            final int userHandle = mInjector.userHandleGetCallingUserId();
-            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            final int userHandle = caller.getUserId();
 
             DevicePolicyData policy = getUserData(userHandle);
             return mInjector.binderWithCleanCallingIdentity(() -> {
@@ -13545,9 +13665,11 @@
         if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
             return false;
         }
+        final CallerIdentity caller = getAdminCallerIdentity(admin);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+
         synchronized (getLockObject()) {
-            final int userHandle = mInjector.userHandleGetCallingUserId();
-            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            final int userHandle = caller.getUserId();
 
             DevicePolicyData policy = getUserData(userHandle);
             if (policy.mPasswordTokenHandle != 0) {
@@ -13568,11 +13690,11 @@
         if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
             return false;
         }
-        synchronized (getLockObject()) {
-            final int userHandle = mInjector.userHandleGetCallingUserId();
-            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+        final CallerIdentity caller = getAdminCallerIdentity(admin);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
 
-            return isResetPasswordTokenActiveForUserLocked(userHandle);
+        synchronized (getLockObject()) {
+            return isResetPasswordTokenActiveForUserLocked(caller.getUserId());
         }
     }
 
@@ -13592,15 +13714,16 @@
             return false;
         }
         Objects.requireNonNull(token);
-        synchronized (getLockObject()) {
-            final int userHandle = mInjector.userHandleGetCallingUserId();
-            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
 
-            DevicePolicyData policy = getUserData(userHandle);
+        final CallerIdentity caller = getAdminCallerIdentity(admin);
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+
+        synchronized (getLockObject()) {
+            DevicePolicyData policy = getUserData(caller.getUserId());
             if (policy.mPasswordTokenHandle != 0) {
                 final String password = passwordOrNull != null ? passwordOrNull : "";
                 return resetPasswordInternal(password, policy.mPasswordTokenHandle, token,
-                        flags, mInjector.binderGetCallingUid());
+                        flags, caller);
             } else {
                 Slog.w(LOG_TAG, "No saved token handle");
             }
@@ -13915,9 +14038,11 @@
     @Override
     @Nullable
     public PersistableBundle getTransferOwnershipBundle() {
+        final CallerIdentity caller = getCallerIdentity();
+        Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+
         synchronized (getLockObject()) {
-            final int callingUserId = mInjector.userHandleGetCallingUserId();
-            getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            final int callingUserId = caller.getUserId();
             final File bundleFile = new File(
                     mInjector.environmentGetUserSystemDirectory(callingUserId),
                     TRANSFER_OWNERSHIP_PARAMETERS_XML);
@@ -14101,8 +14226,8 @@
         parametersFile.delete();
     }
 
-    private void maybeLogPasswordComplexitySet(ComponentName who, int userId, boolean parent,
-            PasswordPolicy passwordPolicy) {
+    private void logPasswordQualitySetIfSecurityLogEnabled(ComponentName who, int userId,
+            boolean parent, PasswordPolicy passwordPolicy) {
         if (SecurityLog.isLoggingEnabled()) {
             final int affectedUserId = parent ? getProfileParentId(userId) : userId;
             SecurityLog.writeEvent(SecurityLog.TAG_PASSWORD_COMPLEXITY_SET, who.getPackageName(),
@@ -14650,7 +14775,7 @@
         Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller));
 
         synchronized (getLockObject()) {
-            final ActiveAdmin admin = getProfileOwnerOfCallerLocked(caller);
+            final ActiveAdmin admin = getProfileOwnerLocked(caller);
             final long deadline = admin.mProfileOffDeadline;
             final int result = makeSuspensionReasons(admin.mSuspendPersonalApps,
                     deadline != 0 && mInjector.systemCurrentTimeMillis() > deadline);
@@ -14683,7 +14808,7 @@
 
         final int callingUserId = caller.getUserId();
         synchronized (getLockObject()) {
-            final ActiveAdmin admin = getProfileOwnerOfCallerLocked(caller);
+            final ActiveAdmin admin = getProfileOwnerLocked(caller);
             boolean shouldSaveSettings = false;
             if (admin.mSuspendPersonalApps != suspended) {
                 admin.mSuspendPersonalApps = suspended;
@@ -14821,9 +14946,11 @@
         final AlarmManager am = mInjector.getAlarmManager();
         final Intent intent = new Intent(ACTION_PROFILE_OFF_DEADLINE);
         intent.setPackage(mContext.getPackageName());
+        // Broadcast alarms sent by system are immutable
         final PendingIntent pi = mInjector.pendingIntentGetBroadcast(
                 mContext, REQUEST_PROFILE_OFF_DEADLINE, intent,
-                PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT);
+                PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT
+                        | PendingIntent.FLAG_IMMUTABLE);
 
         if (alarmTime == 0) {
             Slog.i(LOG_TAG, "Profile off deadline alarm is removed.");
@@ -14884,8 +15011,10 @@
         intent.setPackage(mContext.getPackageName());
         intent.putExtra(Intent.EXTRA_USER_HANDLE, profileUserId);
 
+        // Simple notification action button clicks are immutable
         final PendingIntent pendingIntent = mInjector.pendingIntentGetBroadcast(mContext,
-                0 /* requestCode */, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+                0 /* requestCode */, intent,
+                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
 
         final String buttonText =
                 mContext.getString(R.string.personal_apps_suspended_turn_profile_on);
@@ -14944,7 +15073,7 @@
 
         final int userId = caller.getUserId();
         synchronized (getLockObject()) {
-            final ActiveAdmin admin = getProfileOwnerOfCallerLocked(caller);
+            final ActiveAdmin admin = getProfileOwnerLocked(caller);
 
             // Ensure the timeout is long enough to avoid having bad user experience.
             if (timeoutMillis > 0 && timeoutMillis < MANAGED_PROFILE_MAXIMUM_TIME_OFF_THRESHOLD
@@ -14989,7 +15118,7 @@
         Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller));
 
         synchronized (getLockObject()) {
-            final ActiveAdmin admin = getProfileOwnerOfCallerLocked(caller);
+            final ActiveAdmin admin = getProfileOwnerLocked(caller);
             return admin.mProfileMaximumTimeOffMillis;
         }
     }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java
index 46c9aab..543f381 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java
@@ -137,8 +137,9 @@
             Slog.wtf(LOG_TAG, "Failed to resolve intent for remote bugreport dialog");
         }
 
+        // Simple notification clicks are immutable
         final PendingIntent pendingDialogIntent = PendingIntent.getActivityAsUser(mContext, type,
-                dialogIntent, 0, null, UserHandle.CURRENT);
+                dialogIntent, PendingIntent.FLAG_IMMUTABLE, null, UserHandle.CURRENT);
 
         final Notification.Builder builder =
                 new Notification.Builder(mContext, SystemNotificationChannels.DEVICE_ADMIN)
@@ -158,12 +159,14 @@
                         R.string.taking_remote_bugreport_notification_title))
                     .setProgress(0, 0, true);
         } else if (type == NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED) {
+            // Simple notification action button clicks are immutable
             final PendingIntent pendingIntentAccept = PendingIntent.getBroadcast(mContext,
                     NOTIFICATION_ID, new Intent(ACTION_BUGREPORT_SHARING_ACCEPTED),
-                    PendingIntent.FLAG_CANCEL_CURRENT);
+                    PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+            // Simple notification action button clicks are immutable
             final PendingIntent pendingIntentDecline = PendingIntent.getBroadcast(mContext,
                     NOTIFICATION_ID, new Intent(ACTION_BUGREPORT_SHARING_DECLINED),
-                    PendingIntent.FLAG_CANCEL_CURRENT);
+                    PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
             builder.addAction(new Notification.Action.Builder(null /* icon */, mContext.getString(
                         R.string.decline_remote_bugreport_action), pendingIntentDecline).build())
                     .addAction(new Notification.Action.Builder(null /* icon */, mContext.getString(
diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp
index 2f8825b..a31aac9 100644
--- a/services/incremental/BinderIncrementalService.cpp
+++ b/services/incremental/BinderIncrementalService.cpp
@@ -323,6 +323,22 @@
     return ok();
 }
 
+binder::Status BinderIncrementalService::registerStorageHealthListener(
+        int32_t storageId,
+        const ::android::os::incremental::StorageHealthCheckParams& healthCheckParams,
+        const ::android::sp<IStorageHealthListener>& healthListener, bool* _aidl_return) {
+    *_aidl_return = mImpl.registerStorageHealthListener(storageId,
+                                                        const_cast<StorageHealthCheckParams&&>(
+                                                                healthCheckParams),
+                                                        healthListener);
+    return ok();
+}
+
+binder::Status BinderIncrementalService::unregisterStorageHealthListener(int32_t storageId) {
+    mImpl.unregisterStorageHealthListener(storageId);
+    return ok();
+}
+
 } // namespace android::os::incremental
 
 jlong Incremental_IncrementalService_Start(JNIEnv* env) {
diff --git a/services/incremental/BinderIncrementalService.h b/services/incremental/BinderIncrementalService.h
index 0a89166..8afa0f7 100644
--- a/services/incremental/BinderIncrementalService.h
+++ b/services/incremental/BinderIncrementalService.h
@@ -89,6 +89,11 @@
                     progressListener,
             bool* _aidl_return) final;
     binder::Status unregisterLoadingProgressListener(int32_t storageId, bool* _aidl_return) final;
+    binder::Status registerStorageHealthListener(
+            int32_t storageId,
+            const ::android::os::incremental::StorageHealthCheckParams& healthCheckParams,
+            const ::android::sp<IStorageHealthListener>& healthListener, bool* _aidl_return) final;
+    binder::Status unregisterStorageHealthListener(int32_t storageId) final;
 
 private:
     android::incremental::IncrementalService mImpl;
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 5f145f3..599ac93 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -1801,6 +1801,31 @@
     return removeTimedJobs(*mProgressUpdateJobQueue, storage);
 }
 
+bool IncrementalService::registerStorageHealthListener(
+        StorageId storage, StorageHealthCheckParams&& healthCheckParams,
+        const StorageHealthListener& healthListener) {
+    DataLoaderStubPtr dataLoaderStub;
+    {
+        std::unique_lock l(mLock);
+        const auto& ifs = getIfsLocked(storage);
+        if (!ifs) {
+            return false;
+        }
+        dataLoaderStub = ifs->dataLoaderStub;
+        if (!dataLoaderStub) {
+            return false;
+        }
+    }
+    dataLoaderStub->setHealthListener(std::move(healthCheckParams), &healthListener);
+    return true;
+}
+
+void IncrementalService::unregisterStorageHealthListener(StorageId storage) {
+    StorageHealthCheckParams invalidCheckParams;
+    invalidCheckParams.blockedTimeoutMs = -1;
+    registerStorageHealthListener(storage, std::move(invalidCheckParams), {});
+}
+
 bool IncrementalService::perfLoggingEnabled() {
     static const bool enabled = base::GetBoolProperty("incremental.perflogging", false);
     return enabled;
@@ -2137,6 +2162,19 @@
 
 binder::Status IncrementalService::DataLoaderStub::reportStreamHealth(MountId mountId,
                                                                       int newStatus) {
+    if (!isValid()) {
+        return binder::Status::
+                fromServiceSpecificError(-EINVAL,
+                                         "reportStreamHealth came to invalid DataLoaderStub");
+    }
+    if (id() != mountId) {
+        LOG(ERROR) << "Mount ID mismatch: expected " << id() << ", but got: " << mountId;
+        return binder::Status::fromServiceSpecificError(-EPERM, "Mount ID mismatch.");
+    }
+    {
+        std::lock_guard lock(mMutex);
+        mStreamStatus = newStatus;
+    }
     return binder::Status::ok();
 }
 
@@ -2153,6 +2191,33 @@
     }
 }
 
+static int adjustHealthStatus(int healthStatus, int streamStatus) {
+    if (healthStatus == IStorageHealthListener::HEALTH_STATUS_OK) {
+        // everything is good; no need to change status
+        return healthStatus;
+    }
+    int newHeathStatus = healthStatus;
+    switch (streamStatus) {
+        case IDataLoaderStatusListener::STREAM_STORAGE_ERROR:
+            // storage is limited and storage not healthy
+            newHeathStatus = IStorageHealthListener::HEALTH_STATUS_UNHEALTHY_STORAGE;
+            break;
+        case IDataLoaderStatusListener::STREAM_INTEGRITY_ERROR:
+            // fall through
+        case IDataLoaderStatusListener::STREAM_SOURCE_ERROR:
+            // fall through
+        case IDataLoaderStatusListener::STREAM_TRANSPORT_ERROR:
+            if (healthStatus == IStorageHealthListener::HEALTH_STATUS_UNHEALTHY) {
+                newHeathStatus = IStorageHealthListener::HEALTH_STATUS_UNHEALTHY_TRANSPORT;
+            }
+            // pending/blocked status due to transportation issues is not regarded as unhealthy
+            break;
+        default:
+            break;
+    }
+    return newHeathStatus;
+}
+
 void IncrementalService::DataLoaderStub::updateHealthStatus(bool baseline) {
     LOG(DEBUG) << id() << ": updateHealthStatus" << (baseline ? " (baseline)" : "");
 
@@ -2232,6 +2297,8 @@
             checkBackAfter = unhealthyMonitoring;
             healthStatusToReport = IStorageHealthListener::HEALTH_STATUS_UNHEALTHY;
         }
+        // Adjust health status based on stream status
+        healthStatusToReport = adjustHealthStatus(healthStatusToReport, mStreamStatus);
         LOG(DEBUG) << id() << ": updateHealthStatus in " << double(checkBackAfter.count()) / 1000.0
                    << "secs";
         mService.addTimedJob(*mService.mTimedQueue, id(), checkBackAfter,
@@ -2321,6 +2388,18 @@
     mService.mLooper->wake();
 }
 
+void IncrementalService::DataLoaderStub::setHealthListener(
+        StorageHealthCheckParams&& healthCheckParams, const StorageHealthListener* healthListener) {
+    std::lock_guard lock(mMutex);
+    mHealthCheckParams = std::move(healthCheckParams);
+    if (healthListener == nullptr) {
+        // reset listener and params
+        mHealthListener = {};
+    } else {
+        mHealthListener = *healthListener;
+    }
+}
+
 void IncrementalService::DataLoaderStub::onDump(int fd) {
     dprintf(fd, "    dataLoader: {\n");
     dprintf(fd, "      currentStatus: %d\n", mCurrentStatus);
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index 504c02a..4c4b8bd 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -140,7 +140,10 @@
     bool registerLoadingProgressListener(StorageId storage,
                                          const StorageLoadingProgressListener& progressListener);
     bool unregisterLoadingProgressListener(StorageId storage);
-
+    bool registerStorageHealthListener(StorageId storage,
+                                       StorageHealthCheckParams&& healthCheckParams,
+                                       const StorageHealthListener& healthListener);
+    void unregisterStorageHealthListener(StorageId storage);
     RawMetadata getMetadata(StorageId storage, std::string_view path) const;
     RawMetadata getMetadata(StorageId storage, FileId node) const;
 
@@ -197,6 +200,8 @@
 
         MountId id() const { return mId.load(std::memory_order_relaxed); }
         const content::pm::DataLoaderParamsParcel& params() const { return mParams; }
+        void setHealthListener(StorageHealthCheckParams&& healthCheckParams,
+                               const StorageHealthListener* healthListener);
 
     private:
         binder::Status onStatusChanged(MountId mount, int newStatus) final;
@@ -251,6 +256,7 @@
             BootClockTsUs kernelTsUs;
         } mHealthBase = {TimePoint::max(), kMaxBootClockTsUs};
         StorageHealthCheckParams mHealthCheckParams;
+        int mStreamStatus = content::pm::IDataLoaderStatusListener::STREAM_HEALTHY;
     };
     using DataLoaderStubPtr = sp<DataLoaderStub>;
 
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index aec9fa1..867312e 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -177,6 +177,18 @@
         }
         return binder::Status::ok();
     }
+    binder::Status storageError(int32_t id) {
+        if (mListener) {
+            mListener->reportStreamHealth(id, IDataLoaderStatusListener::STREAM_STORAGE_ERROR);
+        }
+        return binder::Status::ok();
+    }
+    binder::Status transportError(int32_t id) {
+        if (mListener) {
+            mListener->reportStreamHealth(id, IDataLoaderStatusListener::STREAM_INTEGRITY_ERROR);
+        }
+        return binder::Status::ok();
+    }
     int32_t setStorageParams(bool enableReadLogs) {
         int32_t result = -1;
         EXPECT_NE(mServiceConnector.get(), nullptr);
@@ -1221,4 +1233,83 @@
     EXPECT_CALL(*listenerMock, onStorageLoadingProgressChanged(_, _)).Times(0);
     mIncrementalService->registerLoadingProgressListener(storageId, listener);
 }
+
+TEST_F(IncrementalServiceTest, testRegisterStorageHealthListenerSuccess) {
+    mIncFs->openMountSuccess();
+    sp<NiceMock<MockStorageHealthListener>> listener{new NiceMock<MockStorageHealthListener>};
+    sp<NiceMock<MockStorageHealthListener>> newListener{new NiceMock<MockStorageHealthListener>};
+    NiceMock<MockStorageHealthListener>* newListenerMock = newListener.get();
+
+    TemporaryDir tempDir;
+    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
+                                                       IncrementalService::CreateOptions::CreateNew,
+                                                       {}, StorageHealthCheckParams{}, listener);
+    ASSERT_GE(storageId, 0);
+    StorageHealthCheckParams newParams;
+    newParams.blockedTimeoutMs = 10000;
+    newParams.unhealthyTimeoutMs = 20000;
+    newParams.unhealthyMonitoringMs = 30000;
+    ASSERT_TRUE(mIncrementalService->registerStorageHealthListener(storageId, std::move(newParams),
+                                                                   newListener));
+
+    using MS = std::chrono::milliseconds;
+    using MCS = std::chrono::microseconds;
+
+    const auto blockedTimeout = MS(newParams.blockedTimeoutMs);
+    const auto unhealthyTimeout = MS(newParams.unhealthyTimeoutMs);
+
+    const uint64_t kFirstTimestampUs = 1000000000ll;
+    const uint64_t kBlockedTimestampUs =
+            kFirstTimestampUs - std::chrono::duration_cast<MCS>(blockedTimeout).count();
+    const uint64_t kUnhealthyTimestampUs =
+            kFirstTimestampUs - std::chrono::duration_cast<MCS>(unhealthyTimeout).count();
+
+    // test that old listener was not called
+    EXPECT_CALL(*listener.get(),
+                onHealthStatus(_, IStorageHealthListener::HEALTH_STATUS_READS_PENDING))
+            .Times(0);
+    EXPECT_CALL(*newListenerMock,
+                onHealthStatus(_, IStorageHealthListener::HEALTH_STATUS_READS_PENDING))
+            .Times(1);
+    EXPECT_CALL(*newListenerMock, onHealthStatus(_, IStorageHealthListener::HEALTH_STATUS_BLOCKED))
+            .Times(1);
+    EXPECT_CALL(*newListenerMock,
+                onHealthStatus(_, IStorageHealthListener::HEALTH_STATUS_UNHEALTHY_STORAGE))
+            .Times(1);
+    EXPECT_CALL(*newListenerMock,
+                onHealthStatus(_, IStorageHealthListener::HEALTH_STATUS_UNHEALTHY_TRANSPORT))
+            .Times(1);
+    mIncFs->waitForPendingReadsSuccess(kFirstTimestampUs);
+    mLooper->mCallback(-1, -1, mLooper->mCallbackData);
+
+    ASSERT_EQ(IStorageHealthListener::HEALTH_STATUS_READS_PENDING, newListener->mStatus);
+    ASSERT_EQ(storageId, newListener->mStorageId);
+
+    auto timedCallback = mTimedQueue->mWhat;
+    mTimedQueue->clearJob(storageId);
+
+    // test when health status is blocked with transport error
+    mDataLoader->transportError(storageId);
+    mIncFs->waitForPendingReadsSuccess(kBlockedTimestampUs);
+    timedCallback();
+    ASSERT_EQ(IStorageHealthListener::HEALTH_STATUS_BLOCKED, newListener->mStatus);
+    timedCallback = mTimedQueue->mWhat;
+    mTimedQueue->clearJob(storageId);
+
+    // test when health status is blocked with storage error
+    mDataLoader->storageError(storageId);
+    mIncFs->waitForPendingReadsSuccess(kBlockedTimestampUs);
+    timedCallback();
+    ASSERT_EQ(IStorageHealthListener::HEALTH_STATUS_UNHEALTHY_STORAGE, newListener->mStatus);
+    timedCallback = mTimedQueue->mWhat;
+    mTimedQueue->clearJob(storageId);
+
+    // test when health status is unhealthy with transport error
+    mDataLoader->transportError(storageId);
+    mIncFs->waitForPendingReadsSuccess(kUnhealthyTimestampUs);
+    timedCallback();
+    ASSERT_EQ(IStorageHealthListener::HEALTH_STATUS_UNHEALTHY_TRANSPORT, newListener->mStatus);
+    mTimedQueue->clearJob(storageId);
+}
+
 } // namespace android::os::incremental
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index eca9f15..975e226 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -47,7 +47,9 @@
 import android.database.sqlite.SQLiteGlobal;
 import android.graphics.GraphicsStatsService;
 import android.hardware.display.DisplayManagerInternal;
+import android.net.ConnectivityManager;
 import android.net.ConnectivityModuleConnector;
+import android.net.IConnectivityManager;
 import android.net.NetworkStackClient;
 import android.os.BaseBundle;
 import android.os.Binder;
@@ -103,10 +105,10 @@
 import com.android.server.clipboard.ClipboardService;
 import com.android.server.compat.PlatformCompat;
 import com.android.server.compat.PlatformCompatNative;
-import com.android.server.connectivity.IpConnectivityMetrics;
 import com.android.server.contentcapture.ContentCaptureManagerInternal;
 import com.android.server.coverage.CoverageService;
 import com.android.server.devicepolicy.DevicePolicyManagerService;
+import com.android.server.devicestate.DeviceStateManagerService;
 import com.android.server.display.DisplayManagerService;
 import com.android.server.display.color.ColorDisplayService;
 import com.android.server.dreams.DreamManagerService;
@@ -279,6 +281,8 @@
             "com.android.server.autofill.AutofillManagerService";
     private static final String CONTENT_CAPTURE_MANAGER_SERVICE_CLASS =
             "com.android.server.contentcapture.ContentCaptureManagerService";
+    private static final String MUSIC_RECOGNITION_MANAGER_SERVICE_CLASS =
+            "com.android.server.musicrecognition.MusicRecognitionManagerService";
     private static final String SYSTEM_CAPTIONS_MANAGER_SERVICE_CLASS =
             "com.android.server.systemcaptions.SystemCaptionsManagerService";
     private static final String TIME_ZONE_RULES_MANAGER_SERVICE_CLASS =
@@ -317,6 +321,10 @@
             "com.android.server.media.MediaSessionService";
     private static final String MEDIA_RESOURCE_MONITOR_SERVICE_CLASS =
             "com.android.server.media.MediaResourceMonitorService";
+    private static final String CONNECTIVITY_SERVICE_INITIALIZER_CLASS =
+            "com.android.server.ConnectivityServiceInitializer";
+    private static final String IP_CONNECTIVITY_METRICS_CLASS =
+            "com.android.server.connectivity.IpConnectivityMetrics";
 
     private static final String TETHERING_CONNECTOR_CLASS = "android.net.ITetheringConnector";
 
@@ -1035,7 +1043,7 @@
         IpSecService ipSecService = null;
         NetworkStatsService networkStats = null;
         NetworkPolicyManagerService networkPolicy = null;
-        ConnectivityService connectivity = null;
+        IConnectivityManager connectivity = null;
         NsdService serviceDiscovery = null;
         WindowManagerService wm = null;
         SerialService serial = null;
@@ -1225,7 +1233,7 @@
             }
 
             t.traceBegin("IpConnectivityMetrics");
-            mSystemServiceManager.startService(IpConnectivityMetrics.class);
+            mSystemServiceManager.startService(IP_CONNECTIVITY_METRICS_CLASS);
             t.traceEnd();
 
             t.traceBegin("NetworkWatchlistService");
@@ -1254,6 +1262,10 @@
             mSystemServiceManager.startService(AppIntegrityManagerService.class);
             t.traceEnd();
 
+            t.traceBegin("DeviceStateManagerService");
+            mSystemServiceManager.startService(DeviceStateManagerService.class);
+            t.traceEnd();
+
         } catch (Throwable e) {
             Slog.e("System", "******************************************");
             Slog.e("System", "************ Failure starting core service");
@@ -1410,6 +1422,17 @@
                 t.traceEnd();
             }
 
+            if (deviceHasConfigString(context,
+                    R.string.config_defaultMusicRecognitionService)) {
+                t.traceBegin("StartMusicRecognitionManagerService");
+                mSystemServiceManager.startService(MUSIC_RECOGNITION_MANAGER_SERVICE_CLASS);
+                t.traceEnd();
+            } else {
+                Slog.d(TAG,
+                        "MusicRecognitionManagerService not defined by OEM or disabled by flag");
+            }
+
+
             startContentCaptureService(context, t);
             startAttentionService(context, t);
 
@@ -1554,16 +1577,15 @@
             }
 
             t.traceBegin("StartConnectivityService");
-            try {
-                connectivity = new ConnectivityService(
-                        context, networkManagement, networkStats, networkPolicy);
-                ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity,
-                        /* allowIsolated= */ false,
-                        DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL);
-                networkPolicy.bindConnectivityManager(connectivity);
-            } catch (Throwable e) {
-                reportWtf("starting Connectivity Service", e);
-            }
+            // This has to be called after NetworkManagementService, NetworkStatsService
+            // and NetworkPolicyManager because ConnectivityService needs to take these
+            // services to initialize.
+            // TODO: Dynamically load service-connectivity.jar by using startServiceFromJar.
+            mSystemServiceManager.startService(CONNECTIVITY_SERVICE_INITIALIZER_CLASS);
+            connectivity = IConnectivityManager.Stub.asInterface(
+                    ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
+            // TODO: Use ConnectivityManager instead of ConnectivityService.
+            networkPolicy.bindConnectivityManager(connectivity);
             t.traceEnd();
 
             t.traceBegin("StartNsdService");
@@ -2252,7 +2274,6 @@
         final NetworkManagementService networkManagementF = networkManagement;
         final NetworkStatsService networkStatsF = networkStats;
         final NetworkPolicyManagerService networkPolicyF = networkPolicy;
-        final ConnectivityService connectivityF = connectivity;
         final CountryDetectorService countryDetectorF = countryDetector;
         final NetworkTimeUpdateService networkTimeUpdaterF = networkTimeUpdater;
         final InputManagerService inputManagerF = inputManager;
@@ -2261,6 +2282,8 @@
         final MmsServiceBroker mmsServiceF = mmsService;
         final IpSecService ipSecServiceF = ipSecService;
         final WindowManagerService windowManagerF = wm;
+        final ConnectivityManager connectivityF = (ConnectivityManager)
+                context.getSystemService(Context.CONNECTIVITY_SERVICE);
 
         // We now tell the activity manager it is okay to run third party
         // code.  It will call back into us once it has gotten to the state
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index 2cfdf3f..47505a3 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -667,7 +667,7 @@
         }
 
         // clear calling identity so bindService does not fail
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             client.addDeviceConnection(device, callback);
         } finally {
@@ -692,7 +692,7 @@
         }
 
         // clear calling identity so bindService does not fail
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             client.addDeviceConnection(device, callback);
         } finally {
diff --git a/services/musicrecognition/Android.bp b/services/musicrecognition/Android.bp
new file mode 100644
index 0000000..39b5bb6
--- /dev/null
+++ b/services/musicrecognition/Android.bp
@@ -0,0 +1,13 @@
+filegroup {
+    name: "services.musicsearch-sources",
+    srcs: ["java/**/*.java"],
+    path: "java",
+    visibility: ["//frameworks/base/services"],
+}
+
+java_library_static {
+    name: "services.musicsearch",
+    defaults: ["services_defaults"],
+    srcs: [":services.musicsearch-sources"],
+    libs: ["services.core", "app-compat-annotations"],
+}
\ No newline at end of file
diff --git a/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java b/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java
new file mode 100644
index 0000000..e258ef0
--- /dev/null
+++ b/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.musicrecognition;
+
+import static android.media.musicrecognition.MusicRecognitionManager.RECOGNITION_FAILED_SERVICE_KILLED;
+import static android.media.musicrecognition.MusicRecognitionManager.RECOGNITION_FAILED_SERVICE_UNAVAILABLE;
+import static android.media.musicrecognition.MusicRecognitionManager.RecognitionFailureCode;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppGlobals;
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.media.AudioRecord;
+import android.media.MediaMetadata;
+import android.media.musicrecognition.IMusicRecognitionManagerCallback;
+import android.media.musicrecognition.IMusicRecognitionServiceCallback;
+import android.media.musicrecognition.RecognitionRequest;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.util.Pair;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.infra.AbstractPerUserSystemService;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Handles per-user requests received by
+ * {@link MusicRecognitionManagerService}. Opens an audio stream from the
+ * dsp and writes it into a pipe to {@link RemoteMusicRecognitionService}.
+ */
+public final class MusicRecognitionManagerPerUserService extends
+        AbstractPerUserSystemService<MusicRecognitionManagerPerUserService,
+                MusicRecognitionManagerService>
+        implements RemoteMusicRecognitionService.Callbacks {
+
+    private static final String TAG = MusicRecognitionManagerPerUserService.class.getSimpleName();
+    // Number of bytes per sample of audio (which is a short).
+    private static final int BYTES_PER_SAMPLE = 2;
+    private static final int MAX_STREAMING_SECONDS = 24;
+
+    @Nullable
+    @GuardedBy("mLock")
+    private RemoteMusicRecognitionService mRemoteService;
+
+    private MusicRecognitionServiceCallback mRemoteServiceCallback =
+            new MusicRecognitionServiceCallback();
+    private IMusicRecognitionManagerCallback mCallback;
+
+    MusicRecognitionManagerPerUserService(
+            @NonNull MusicRecognitionManagerService primary,
+            @NonNull Object lock, int userId) {
+        super(primary, lock, userId);
+    }
+
+    @NonNull
+    @GuardedBy("mLock")
+    @Override
+    protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
+            throws PackageManager.NameNotFoundException {
+        ServiceInfo si;
+        try {
+            si = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
+                    PackageManager.GET_META_DATA, mUserId);
+        } catch (RemoteException e) {
+            throw new PackageManager.NameNotFoundException(
+                    "Could not get service for " + serviceComponent);
+        }
+        if (!Manifest.permission.BIND_MUSIC_RECOGNITION_SERVICE.equals(si.permission)) {
+            Slog.w(TAG, "MusicRecognitionService from '" + si.packageName
+                    + "' does not require permission "
+                    + Manifest.permission.BIND_MUSIC_RECOGNITION_SERVICE);
+            throw new SecurityException("Service does not require permission "
+                    + Manifest.permission.BIND_MUSIC_RECOGNITION_SERVICE);
+        }
+        // TODO(b/158194857): check process which owns the service has RECORD_AUDIO permission. How?
+        return si;
+    }
+
+    @GuardedBy("mLock")
+    @Nullable
+    private RemoteMusicRecognitionService ensureRemoteServiceLocked() {
+        if (mRemoteService == null) {
+            final String serviceName = getComponentNameLocked();
+            if (serviceName == null) {
+                if (mMaster.verbose) {
+                    Slog.v(TAG, "ensureRemoteServiceLocked(): not set");
+                }
+                return null;
+            }
+            ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName);
+
+            mRemoteService = new RemoteMusicRecognitionService(getContext(),
+                    serviceComponent, mUserId, this,
+                    mRemoteServiceCallback, mMaster.isBindInstantServiceAllowed(), mMaster.verbose);
+        }
+
+        return mRemoteService;
+    }
+
+    /**
+     * Read audio from the given capture session using an AudioRecord and writes it to a
+     * ParcelFileDescriptor.
+     */
+    @GuardedBy("mLock")
+    public void beginRecognitionLocked(
+            @NonNull RecognitionRequest recognitionRequest,
+            @NonNull IBinder callback) {
+        int maxAudioLengthSeconds = Math.min(recognitionRequest.getMaxAudioLengthSeconds(),
+                MAX_STREAMING_SECONDS);
+        mCallback = IMusicRecognitionManagerCallback.Stub.asInterface(callback);
+        AudioRecord audioRecord = createAudioRecord(recognitionRequest, maxAudioLengthSeconds);
+
+        mRemoteService = ensureRemoteServiceLocked();
+        if (mRemoteService == null) {
+            try {
+                mCallback.onRecognitionFailed(
+                        RECOGNITION_FAILED_SERVICE_UNAVAILABLE);
+            } catch (RemoteException e) {
+                // Ignored.
+            }
+            return;
+        }
+
+        Pair<ParcelFileDescriptor, ParcelFileDescriptor> clientPipe = createPipe();
+        if (clientPipe == null) {
+            try {
+                mCallback.onAudioStreamClosed();
+            } catch (RemoteException ignored) {
+                // Ignored.
+            }
+            return;
+        }
+        ParcelFileDescriptor audioSink = clientPipe.second;
+        ParcelFileDescriptor clientRead = clientPipe.first;
+
+        mMaster.mExecutorService.execute(() -> {
+            try (OutputStream fos =
+                        new ParcelFileDescriptor.AutoCloseOutputStream(audioSink)) {
+                int halfSecondBufferSize =
+                        audioRecord.getBufferSizeInFrames() / maxAudioLengthSeconds;
+                byte[] byteBuffer = new byte[halfSecondBufferSize];
+                int bytesRead = 0;
+                int totalBytesRead = 0;
+                int ignoreBytes =
+                        recognitionRequest.getIgnoreBeginningFrames() * BYTES_PER_SAMPLE;
+                audioRecord.startRecording();
+                while (bytesRead >= 0 && totalBytesRead
+                        < audioRecord.getBufferSizeInFrames() * BYTES_PER_SAMPLE) {
+                    bytesRead = audioRecord.read(byteBuffer, 0, byteBuffer.length);
+                    if (bytesRead > 0) {
+                        totalBytesRead += bytesRead;
+                        // If we are ignoring the first x bytes, update that counter.
+                        if (ignoreBytes > 0) {
+                            ignoreBytes -= bytesRead;
+                            // If we've dipped negative, we've skipped through all ignored bytes
+                            // and then some.  Write out the bytes we shouldn't have skipped.
+                            if (ignoreBytes < 0) {
+                                fos.write(byteBuffer, bytesRead + ignoreBytes, -ignoreBytes);
+                            }
+                        } else {
+                            fos.write(byteBuffer);
+                        }
+                    }
+                }
+                Slog.i(TAG, String.format("Streamed %s bytes from audio record", totalBytesRead));
+            } catch (IOException e) {
+                Slog.e(TAG, "Audio streaming stopped.", e);
+            } finally {
+                audioRecord.release();
+                try {
+                    mCallback.onAudioStreamClosed();
+                } catch (RemoteException ignored) {
+                    // Ignored.
+                }
+
+            }
+        });
+        // Send the pipe down to the lookup service while we write to it asynchronously.
+        mRemoteService.writeAudioToPipe(clientRead, recognitionRequest.getAudioFormat());
+    }
+
+    /**
+     * Callback invoked by {@link android.service.musicrecognition.MusicRecognitionService} to pass
+     * back the music search result.
+     */
+    private final class MusicRecognitionServiceCallback extends
+            IMusicRecognitionServiceCallback.Stub {
+        @Override
+        public void onRecognitionSucceeded(MediaMetadata result, Bundle extras) {
+            try {
+                sanitizeBundle(extras);
+                mCallback.onRecognitionSucceeded(result, extras);
+            } catch (RemoteException ignored) {
+                // Ignored.
+            }
+        }
+
+        @Override
+        public void onRecognitionFailed(@RecognitionFailureCode int failureCode) {
+            try {
+                mCallback.onRecognitionFailed(failureCode);
+            } catch (RemoteException ignored) {
+                // Ignored.
+            }
+        }
+    }
+
+    @Override
+    public void onServiceDied(@NonNull RemoteMusicRecognitionService service) {
+        try {
+            mCallback.onRecognitionFailed(RECOGNITION_FAILED_SERVICE_KILLED);
+        } catch (RemoteException e) {
+            // Ignored.
+        }
+        Slog.w(TAG, "remote service died: " + service);
+    }
+
+    /** Establishes an audio stream from the DSP audio source. */
+    private static AudioRecord createAudioRecord(
+            @NonNull RecognitionRequest recognitionRequest,
+            int maxAudioLengthSeconds) {
+        int sampleRate = recognitionRequest.getAudioFormat().getSampleRate();
+        int bufferSize = getBufferSizeInBytes(sampleRate, maxAudioLengthSeconds);
+        return new AudioRecord(recognitionRequest.getAudioAttributes(),
+                recognitionRequest.getAudioFormat(), bufferSize,
+                recognitionRequest.getCaptureSession());
+    }
+
+    /**
+     * Returns the number of bytes required to store {@code bufferLengthSeconds} of audio sampled at
+     * {@code sampleRate} Hz, using the format returned by DSP audio capture.
+     */
+    private static int getBufferSizeInBytes(int sampleRate, int bufferLengthSeconds) {
+        return BYTES_PER_SAMPLE * sampleRate * bufferLengthSeconds;
+    }
+
+    private static Pair<ParcelFileDescriptor, ParcelFileDescriptor> createPipe() {
+        ParcelFileDescriptor[] fileDescriptors;
+        try {
+            fileDescriptors = ParcelFileDescriptor.createPipe();
+        } catch (IOException e) {
+            Slog.e(TAG, "Failed to create audio stream pipe", e);
+            return null;
+        }
+
+        if (fileDescriptors.length != 2) {
+            Slog.e(TAG, "Failed to create audio stream pipe, "
+                    + "unexpected number of file descriptors");
+            return null;
+        }
+
+        if (!fileDescriptors[0].getFileDescriptor().valid()
+                || !fileDescriptors[1].getFileDescriptor().valid()) {
+            Slog.e(TAG, "Failed to create audio stream pipe, didn't "
+                    + "receive a pair of valid file descriptors.");
+            return null;
+        }
+
+        return Pair.create(fileDescriptors[0], fileDescriptors[1]);
+    }
+
+    /** Removes remote objects from the bundle. */
+    private static void sanitizeBundle(@Nullable Bundle bundle) {
+        if (bundle == null) {
+            return;
+        }
+
+        for (String key : bundle.keySet()) {
+            Object o = bundle.get(key);
+
+            if (o instanceof Bundle) {
+                sanitizeBundle((Bundle) o);
+            } else if (o instanceof IBinder || o instanceof ParcelFileDescriptor) {
+                bundle.remove(key);
+            }
+        }
+    }
+}
diff --git a/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerService.java b/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerService.java
new file mode 100644
index 0000000..b4cb337
--- /dev/null
+++ b/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerService.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.musicrecognition;
+
+import static android.content.PermissionChecker.PERMISSION_GRANTED;
+import static android.media.musicrecognition.MusicRecognitionManager.RECOGNITION_FAILED_SERVICE_UNAVAILABLE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.media.musicrecognition.IMusicRecognitionManager;
+import android.media.musicrecognition.IMusicRecognitionManagerCallback;
+import android.media.musicrecognition.RecognitionRequest;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+
+import com.android.server.infra.AbstractMasterSystemService;
+import com.android.server.infra.FrameworkResourcesServiceNameResolver;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * Service which allows a DSP audio event to be securely streamed to a designated {@link
+ * MusicRecognitionService}.
+ */
+public class MusicRecognitionManagerService extends
+        AbstractMasterSystemService<MusicRecognitionManagerService,
+                MusicRecognitionManagerPerUserService> {
+
+    private static final String TAG = MusicRecognitionManagerService.class.getSimpleName();
+
+    private MusicRecognitionManagerStub mMusicRecognitionManagerStub;
+    final ExecutorService mExecutorService = Executors.newCachedThreadPool();
+
+    /**
+     * Initializes the system service.
+     *
+     * Subclasses must define a single argument constructor that accepts the context
+     * and passes it to super.
+     *
+     * @param context The system server context.
+     */
+    public MusicRecognitionManagerService(@NonNull Context context) {
+        super(context, new FrameworkResourcesServiceNameResolver(context,
+                        com.android.internal.R.string.config_defaultMusicRecognitionService),
+                /** disallowProperty */null);
+    }
+
+    @Nullable
+    @Override
+    protected MusicRecognitionManagerPerUserService newServiceLocked(int resolvedUserId,
+            boolean disabled) {
+        return new MusicRecognitionManagerPerUserService(this, mLock, resolvedUserId);
+    }
+
+    @Override
+    public void onStart() {
+        mMusicRecognitionManagerStub = new MusicRecognitionManagerStub();
+        publishBinderService(Context.MUSIC_RECOGNITION_SERVICE, mMusicRecognitionManagerStub);
+    }
+
+    private void enforceCaller(String func) {
+        Context ctx = getContext();
+        if (ctx.checkCallingPermission(android.Manifest.permission.MANAGE_MUSIC_RECOGNITION)
+                == PERMISSION_GRANTED) {
+            return;
+        }
+
+        String msg = "Permission Denial: " + func + " from pid="
+                + Binder.getCallingPid()
+                + ", uid=" + Binder.getCallingUid()
+                + " doesn't hold " + android.Manifest.permission.MANAGE_MUSIC_RECOGNITION;
+        throw new SecurityException(msg);
+    }
+
+    final class MusicRecognitionManagerStub extends IMusicRecognitionManager.Stub {
+        @Override
+        public void beginRecognition(
+                @NonNull RecognitionRequest recognitionRequest,
+                @NonNull IBinder callback) {
+            enforceCaller("beginRecognition");
+
+            synchronized (mLock) {
+                final MusicRecognitionManagerPerUserService service = getServiceForUserLocked(
+                        UserHandle.getCallingUserId());
+                if (service != null) {
+                    service.beginRecognitionLocked(recognitionRequest, callback);
+                } else {
+                    try {
+                        IMusicRecognitionManagerCallback.Stub.asInterface(callback)
+                                .onRecognitionFailed(RECOGNITION_FAILED_SERVICE_UNAVAILABLE);
+                    } catch (RemoteException e) {
+                        // ignored.
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/services/musicrecognition/java/com/android/server/musicrecognition/RemoteMusicRecognitionService.java b/services/musicrecognition/java/com/android/server/musicrecognition/RemoteMusicRecognitionService.java
new file mode 100644
index 0000000..4814a82
--- /dev/null
+++ b/services/musicrecognition/java/com/android/server/musicrecognition/RemoteMusicRecognitionService.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.musicrecognition;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Context;
+import android.media.AudioFormat;
+import android.media.musicrecognition.IMusicRecognitionService;
+import android.media.musicrecognition.IMusicRecognitionServiceCallback;
+import android.media.musicrecognition.MusicRecognitionService;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.text.format.DateUtils;
+
+import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
+
+/** Remote connection to an instance of {@link MusicRecognitionService}. */
+public class RemoteMusicRecognitionService extends
+        AbstractMultiplePendingRequestsRemoteService<RemoteMusicRecognitionService,
+                IMusicRecognitionService> {
+
+    // Maximum time allotted for the remote service to return a result. Up to 24s of audio plus
+    // time to fingerprint and make rpcs.
+    private static final long TIMEOUT_IDLE_BIND_MILLIS = 40 * DateUtils.SECOND_IN_MILLIS;
+
+    // Allows the remote service to send back a result.
+    private final IMusicRecognitionServiceCallback mServerCallback;
+
+    public RemoteMusicRecognitionService(Context context, ComponentName serviceName,
+            int userId, MusicRecognitionManagerPerUserService perUserService,
+            IMusicRecognitionServiceCallback callback,
+            boolean bindInstantServiceAllowed, boolean verbose) {
+        super(context, MusicRecognitionService.ACTION_MUSIC_SEARCH_LOOKUP, serviceName, userId,
+                perUserService,
+                context.getMainThreadHandler(),
+                // Prevents the service from having its permissions stripped while in background.
+                Context.BIND_INCLUDE_CAPABILITIES | (bindInstantServiceAllowed
+                        ? Context.BIND_ALLOW_INSTANT : 0), verbose,
+                /* initialCapacity= */ 1);
+        mServerCallback = callback;
+    }
+
+    @NonNull
+    @Override
+    protected IMusicRecognitionService getServiceInterface(@NonNull IBinder service) {
+        return IMusicRecognitionService.Stub.asInterface(service);
+    }
+
+    @Override
+    protected long getTimeoutIdleBindMillis() {
+        return TIMEOUT_IDLE_BIND_MILLIS;
+    }
+
+    /**
+     * Required, but empty since we don't need to notify the callback implementation of the request
+     * results.
+     */
+    interface Callbacks extends VultureCallback<RemoteMusicRecognitionService> {}
+
+    /**
+     * Sends the given descriptor to the app's {@link MusicRecognitionService} to read the
+     * audio.
+     */
+    public void writeAudioToPipe(@NonNull ParcelFileDescriptor fd,
+            @NonNull AudioFormat audioFormat) {
+        scheduleAsyncRequest(
+                binder -> binder.onAudioStreamStarted(fd, audioFormat, mServerCallback));
+    }
+}
diff --git a/services/net/TEST_MAPPING b/services/net/TEST_MAPPING
new file mode 100644
index 0000000..7025dd1
--- /dev/null
+++ b/services/net/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "imports": [
+    {
+      "path": "frameworks/base/core/java/android/net"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/services/net/java/android/net/TcpKeepalivePacketData.java b/services/net/java/android/net/TcpKeepalivePacketData.java
index c0c386b..4875c7c 100644
--- a/services/net/java/android/net/TcpKeepalivePacketData.java
+++ b/services/net/java/android/net/TcpKeepalivePacketData.java
@@ -19,11 +19,12 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.net.util.IpUtils;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.system.OsConstants;
 
+import com.android.net.module.util.IpUtils;
+
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.nio.ByteBuffer;
diff --git a/services/net/java/android/net/ip/IpClientManager.java b/services/net/java/android/net/ip/IpClientManager.java
index db464e7..94bc1ec 100644
--- a/services/net/java/android/net/ip/IpClientManager.java
+++ b/services/net/java/android/net/ip/IpClientManager.java
@@ -87,6 +87,8 @@
         } catch (RemoteException e) {
             log("Error confirming IpClient configuration", e);
             return false;
+        } finally {
+            Binder.restoreCallingIdentity(token);
         }
     }
 
diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java
index 91cb481..7c3b7a6 100644
--- a/services/people/java/com/android/server/people/PeopleService.java
+++ b/services/people/java/com/android/server/people/PeopleService.java
@@ -122,7 +122,7 @@
 
         @Override
         public ParceledListSlice<ConversationChannel> getRecentConversations() {
-            enforceSystemOrRoot("get recent conversations");
+            enforceSystemRootOrSystemUI(getContext(), "get recent conversations");
             return new ParceledListSlice<>(
                     mDataManager.getRecentConversations(
                             Binder.getCallingUserHandle().getIdentifier()));
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index 87f2c58..7803e78 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -1037,14 +1037,15 @@
      * A {@link Runnable} that queries the Usage Stats Service for recent events for a specified
      * user.
      */
-    private class UsageStatsQueryRunnable implements Runnable {
+    private class UsageStatsQueryRunnable implements Runnable,
+            UsageStatsQueryHelper.EventListener {
 
         private final UsageStatsQueryHelper mUsageStatsQueryHelper;
         private long mLastEventTimestamp;
 
         private UsageStatsQueryRunnable(int userId) {
             mUsageStatsQueryHelper = mInjector.createUsageStatsQueryHelper(userId,
-                    (packageName) -> getPackage(packageName, userId));
+                    (packageName) -> getPackage(packageName, userId), this);
             mLastEventTimestamp = System.currentTimeMillis() - QUERY_EVENTS_MAX_AGE_MS;
         }
 
@@ -1054,6 +1055,17 @@
                 mLastEventTimestamp = mUsageStatsQueryHelper.getLastEventTimestamp();
             }
         }
+
+        @Override
+        public void onEvent(PackageData packageData, ConversationInfo conversationInfo,
+                Event event) {
+            if (event.getType() == Event.TYPE_IN_APP_CONVERSATION) {
+                ConversationInfo updated = new ConversationInfo.Builder(conversationInfo)
+                        .setLastEventTimestamp(event.getTimestamp())
+                        .build();
+                packageData.getConversationStore().addOrUpdate(updated);
+            }
+        }
     }
 
     /** A {@link BroadcastReceiver} that receives the intents for a specified user. */
@@ -1135,8 +1147,9 @@
         }
 
         UsageStatsQueryHelper createUsageStatsQueryHelper(@UserIdInt int userId,
-                Function<String, PackageData> packageDataGetter) {
-            return new UsageStatsQueryHelper(userId, packageDataGetter);
+                Function<String, PackageData> packageDataGetter,
+                UsageStatsQueryHelper.EventListener eventListener) {
+            return new UsageStatsQueryHelper(userId, packageDataGetter, eventListener);
         }
     }
 }
diff --git a/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java b/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java
index d008b72..aefb6e3 100644
--- a/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java
+++ b/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java
@@ -43,17 +43,24 @@
     private final Function<String, PackageData> mPackageDataGetter;
     // Activity name -> Conversation start event (LOCUS_ID_SET)
     private final Map<ComponentName, UsageEvents.Event> mConvoStartEvents = new ArrayMap<>();
+    private final EventListener mEventListener;
     private long mLastEventTimestamp;
 
+    interface EventListener {
+        void onEvent(PackageData packageData, ConversationInfo conversationInfo, Event event);
+    }
+
     /**
      * @param userId The user whose events are to be queried.
      * @param packageDataGetter The function to get {@link PackageData} with a package name.
+     * @param eventListener A listener that listens to the new event.
      */
     UsageStatsQueryHelper(@UserIdInt int userId,
-            Function<String, PackageData> packageDataGetter) {
+            Function<String, PackageData> packageDataGetter, EventListener eventListener) {
         mUsageStatsManagerInternal = getUsageStatsManagerInternal();
         mUserId = userId;
         mPackageDataGetter = packageDataGetter;
+        mEventListener = eventListener;
     }
 
     /**
@@ -198,21 +205,27 @@
     }
 
     private void addEventByShortcutId(PackageData packageData, String shortcutId, Event event) {
-        if (packageData.getConversationStore().getConversation(shortcutId) == null) {
+        ConversationInfo conversationInfo =
+                packageData.getConversationStore().getConversation(shortcutId);
+        if (conversationInfo == null) {
             return;
         }
         EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateEventHistory(
                 EventStore.CATEGORY_SHORTCUT_BASED, shortcutId);
         eventHistory.addEvent(event);
+        mEventListener.onEvent(packageData, conversationInfo, event);
     }
 
     private void addEventByLocusId(PackageData packageData, LocusId locusId, Event event) {
-        if (packageData.getConversationStore().getConversationByLocusId(locusId) == null) {
+        ConversationInfo conversationInfo =
+                packageData.getConversationStore().getConversationByLocusId(locusId);
+        if (conversationInfo == null) {
             return;
         }
         EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateEventHistory(
                 EventStore.CATEGORY_LOCUS_ID_BASED, locusId.getId());
         eventHistory.addEvent(event);
+        mEventListener.onEvent(packageData, conversationInfo, event);
     }
 
     private static UsageStatsManagerInternal getUsageStatsManagerInternal() {
diff --git a/services/profcollect/Android.bp b/services/profcollect/Android.bp
index 68fba55..b7be5d4 100644
--- a/services/profcollect/Android.bp
+++ b/services/profcollect/Android.bp
@@ -30,6 +30,7 @@
 
 java_library_static {
   name: "services.profcollect",
+  defaults: ["services_defaults"],
   srcs: [":services.profcollect-sources"],
   libs: ["services.core"],
 }
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index ddd1f75..d14ed5a 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -29,6 +29,8 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
+import android.os.UpdateEngine;
+import android.os.UpdateEngineCallback;
 import android.util.Log;
 
 import com.android.server.IoThread;
@@ -198,6 +200,7 @@
     // Event observers
     private void registerObservers() {
         registerAppLaunchObserver();
+        registerOTAObserver();
     }
 
     private final AppLaunchObserver mAppLaunchObserver = new AppLaunchObserver();
@@ -261,4 +264,33 @@
             // Ignored
         }
     }
+
+    private void registerOTAObserver() {
+        UpdateEngine updateEngine = new UpdateEngine();
+        updateEngine.bind(new UpdateEngineCallback() {
+            @Override
+            public void onStatusUpdate(int status, float percent) {
+                if (status == UpdateEngine.UpdateStatusConstants.UPDATED_NEED_REBOOT) {
+                    packProfileReport();
+                }
+            }
+
+            @Override
+            public void onPayloadApplicationComplete(int errorCode) {
+                // Ignored
+            }
+        });
+    }
+
+    private void packProfileReport() {
+        if (mIProfcollect == null) {
+            return;
+        }
+
+        try {
+            mIProfcollect.CreateProfileReport();
+        } catch (RemoteException e) {
+            Log.e(LOG_TAG, e.getMessage());
+        }
+    }
 }
diff --git a/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java b/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java
index 946d28e..62dbd89 100644
--- a/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java
+++ b/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java
@@ -76,7 +76,7 @@
         public boolean hasRestrictionsProvider() throws RemoteException {
             int userHandle = UserHandle.getCallingUserId();
             if (mDpm != null) {
-                long ident = Binder.clearCallingIdentity();
+                final long ident = Binder.clearCallingIdentity();
                 try {
                     return mDpm.getRestrictionsProvider(userHandle) != null;
                 } finally {
@@ -97,7 +97,7 @@
             int callingUid = Binder.getCallingUid();
             int userHandle = UserHandle.getUserId(callingUid);
             if (mDpm != null) {
-                long ident = Binder.clearCallingIdentity();
+                final long ident = Binder.clearCallingIdentity();
                 try {
                     ComponentName restrictionsProvider =
                             mDpm.getRestrictionsProvider(userHandle);
@@ -130,7 +130,7 @@
             }
             final int userHandle = UserHandle.getCallingUserId();
             if (mDpm != null) {
-                long ident = Binder.clearCallingIdentity();
+                final long ident = Binder.clearCallingIdentity();
                 try {
                     ComponentName restrictionsProvider =
                             mDpm.getRestrictionsProvider(userHandle);
@@ -163,7 +163,7 @@
             int callingUid = Binder.getCallingUid();
             int userHandle = UserHandle.getUserId(callingUid);
             if (mDpm != null) {
-                long ident = Binder.clearCallingIdentity();
+                final long ident = Binder.clearCallingIdentity();
                 try {
                     ComponentName permProvider = mDpm.getRestrictionsProvider(userHandle);
                     if (permProvider == null) {
diff --git a/services/robotests/backup/src/com/android/server/backup/BackupAgentTimeoutParametersTest.java b/services/robotests/backup/src/com/android/server/backup/BackupAgentTimeoutParametersTest.java
index 5b226f3..f58e295 100644
--- a/services/robotests/backup/src/com/android/server/backup/BackupAgentTimeoutParametersTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/BackupAgentTimeoutParametersTest.java
@@ -23,6 +23,7 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.os.Handler;
+import android.os.Process;
 import android.platform.test.annotations.Presubmit;
 import android.provider.Settings;
 
@@ -64,7 +65,7 @@
         long kvBackupAgentTimeoutMillis = mParameters.getKvBackupAgentTimeoutMillis();
         long fullBackupAgentTimeoutMillis = mParameters.getFullBackupAgentTimeoutMillis();
         long sharedBackupAgentTimeoutMillis = mParameters.getSharedBackupAgentTimeoutMillis();
-        long restoreAgentTimeoutMillis = mParameters.getRestoreAgentTimeoutMillis();
+        long restoreSessionTimeoutMillis = mParameters.getRestoreSessionTimeoutMillis();
         long restoreAgentFinishedTimeoutMillis = mParameters.getRestoreAgentFinishedTimeoutMillis();
 
         assertEquals(
@@ -77,14 +78,41 @@
                 BackupAgentTimeoutParameters.DEFAULT_SHARED_BACKUP_AGENT_TIMEOUT_MILLIS,
                 sharedBackupAgentTimeoutMillis);
         assertEquals(
-                BackupAgentTimeoutParameters.DEFAULT_RESTORE_AGENT_TIMEOUT_MILLIS,
-                restoreAgentTimeoutMillis);
+                BackupAgentTimeoutParameters.DEFAULT_RESTORE_SESSION_TIMEOUT_MILLIS,
+                restoreSessionTimeoutMillis);
         assertEquals(
                 BackupAgentTimeoutParameters.DEFAULT_RESTORE_AGENT_FINISHED_TIMEOUT_MILLIS,
                 restoreAgentFinishedTimeoutMillis);
     }
 
     @Test
+    public void
+            testGetRestoreAgentTimeout_afterConstructorWithStartForSystemAgent_returnsDefaultValue() {
+        mParameters.start();
+
+        // Numbers before FIRST_APPLICATION_UID are reserved as UIDs for system components.
+        long restoreTimeout =
+                mParameters.getRestoreAgentTimeoutMillis(Process.FIRST_APPLICATION_UID - 1);
+
+        assertThat(restoreTimeout)
+                .isEqualTo(
+                        BackupAgentTimeoutParameters.DEFAULT_RESTORE_SYSTEM_AGENT_TIMEOUT_MILLIS);
+    }
+
+    @Test
+    public void
+            testGetRestoreAgentTimeout_afterConstructorWithStartForAppAgent_returnsDefaultValue() {
+        mParameters.start();
+
+        // Numbers starting from FIRST_APPLICATION_UID are reserved for app UIDs.
+        long restoreTimeout =
+                mParameters.getRestoreAgentTimeoutMillis(Process.FIRST_APPLICATION_UID);
+
+        assertThat(restoreTimeout)
+                .isEqualTo(BackupAgentTimeoutParameters.DEFAULT_RESTORE_AGENT_TIMEOUT_MILLIS);
+    }
+
+    @Test
     public void testGetQuotaExceededTimeoutMillis_returnsDefaultValue() {
         mParameters.start();
 
diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index 09e3bfe..7d17109 100644
--- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -41,6 +41,7 @@
 import static com.android.server.backup.testing.Utils.transferStreamedData;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -2880,8 +2881,8 @@
     }
 
     private static IterableSubject assertDirectory(Path directory) throws IOException {
-        return assertThat(oneTimeIterable(Files.newDirectoryStream(directory).iterator()))
-                .named("directory " + directory);
+        return assertWithMessage("directory " + directory).that(
+                oneTimeIterable(Files.newDirectoryStream(directory).iterator()));
     }
 
     private static void assertJournalDoesNotContain(
diff --git a/services/robotests/backup/src/com/android/server/backup/testing/TestUtils.java b/services/robotests/backup/src/com/android/server/backup/testing/TestUtils.java
index 3fe1f3f..3114a75 100644
--- a/services/robotests/backup/src/com/android/server/backup/testing/TestUtils.java
+++ b/services/robotests/backup/src/com/android/server/backup/testing/TestUtils.java
@@ -17,6 +17,7 @@
 package com.android.server.backup.testing;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.robolectric.Shadows.shadowOf;
 
@@ -95,8 +96,8 @@
      * logcat before that.
      */
     public static void assertLogcatAtMost(String tag, int level) {
-        assertThat(ShadowLog.getLogsForTag(tag).stream().allMatch(logItem -> logItem.type <= level))
-                .named("All logs <= " + level)
+        assertWithMessage("All logs <= " + level).that(
+                ShadowLog.getLogsForTag(tag).stream().allMatch(logItem -> logItem.type <= level))
                 .isTrue();
     }
 
@@ -105,8 +106,8 @@
      * logcat before that.
      */
     public static void assertLogcatAtLeast(String tag, int level) {
-        assertThat(ShadowLog.getLogsForTag(tag).stream().anyMatch(logItem -> logItem.type >= level))
-                .named("Any log >= " + level)
+        assertWithMessage("Any log >= " + level).that(
+                ShadowLog.getLogsForTag(tag).stream().anyMatch(logItem -> logItem.type >= level))
                 .isTrue();
     }
 
@@ -121,11 +122,10 @@
      * that uses logcat before that.
      */
     public static void assertLogcat(String tag, int... logs) {
-        assertThat(
+        assertWithMessage("Log items (specified per level)").that(
                         ShadowLog.getLogsForTag(tag).stream()
                                 .map(logItem -> logItem.type)
                                 .collect(toSet()))
-                .named("Log items (specified per level)")
                 .containsExactly(IntStream.of(logs).boxed().toArray());
     }
 
@@ -135,15 +135,13 @@
 
     /** Declare shadow {@link ShadowEventLog} to use this. */
     public static void assertEventLogged(int tag, Object... values) {
-        assertThat(ShadowEventLog.getEntries())
-                .named("Event logs")
+        assertWithMessage("Event logs").that(ShadowEventLog.getEntries())
                 .contains(new ShadowEventLog.Entry(tag, Arrays.asList(values)));
     }
 
     /** Declare shadow {@link ShadowEventLog} to use this. */
     public static void assertEventNotLogged(int tag, Object... values) {
-        assertThat(ShadowEventLog.getEntries())
-                .named("Event logs")
+        assertWithMessage("Event logs").that(ShadowEventLog.getEntries())
                 .doesNotContain(new ShadowEventLog.Entry(tag, Arrays.asList(values)));
     }
 
diff --git a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
index 09552082..27b07c7 100644
--- a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
+++ b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
@@ -45,6 +45,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
+import org.mockito.Mockito
 import org.mockito.Mockito.any
 import org.mockito.Mockito.anyBoolean
 import org.mockito.Mockito.anyInt
@@ -341,7 +342,8 @@
             whenever(this.userManagerService) { mockUserManagerService }
             whenever(this.permissionManagerServiceInternal) { mockPermissionManagerService }
             whenever(this.settings) { mockSettings }
-            whenever(this.activityTaskManagerInternal) { mockActivityTaskManager }
+            whenever(this.getLocalService(ActivityTaskManagerInternal::class.java)) {
+                mockActivityTaskManager}
             whenever(this.appsFilter) { mockAppsFilter }
             whenever(this.context) { mockContext }
         }
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index d61783e..a5f0834 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -48,10 +48,13 @@
 import static com.android.server.alarm.AlarmManagerService.IS_WAKEUP_MASK;
 import static com.android.server.alarm.AlarmManagerService.TIME_CHANGED_MASK;
 import static com.android.server.alarm.AlarmManagerService.WORKING_INDEX;
+import static com.android.server.alarm.Constants.TEST_CALLING_PACKAGE;
+import static com.android.server.alarm.Constants.TEST_CALLING_UID;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
@@ -82,6 +85,7 @@
 import android.platform.test.annotations.Presubmit;
 import android.provider.DeviceConfig;
 import android.provider.Settings;
+import android.util.IndentingPrintWriter;
 import android.util.Log;
 import android.util.SparseArray;
 
@@ -108,6 +112,8 @@
 import org.mockito.quality.Strictness;
 import org.mockito.stubbing.Answer;
 
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.concurrent.Executor;
@@ -116,9 +122,7 @@
 @RunWith(AndroidJUnit4.class)
 public class AlarmManagerServiceTest {
     private static final String TAG = AlarmManagerServiceTest.class.getSimpleName();
-    private static final String TEST_CALLING_PACKAGE = "com.android.framework.test-package";
     private static final int SYSTEM_UI_UID = 12345;
-    private static final int TEST_CALLING_UID = 67890;
     private static final int TEST_CALLING_USER = UserHandle.getUserId(TEST_CALLING_UID);
 
     private long mAppStandbyWindow;
@@ -350,19 +354,31 @@
     }
 
     private void setTestAlarm(int type, long triggerTime, PendingIntent operation) {
-        setTestAlarm(type, triggerTime, operation, 0, TEST_CALLING_UID);
+        setTestAlarm(type, triggerTime, operation, 0, AlarmManager.FLAG_STANDALONE,
+                TEST_CALLING_UID);
     }
 
     private void setRepeatingTestAlarm(int type, long firstTrigger, long interval,
             PendingIntent pi) {
-        setTestAlarm(type, firstTrigger, pi, interval, TEST_CALLING_UID);
+        setTestAlarm(type, firstTrigger, pi, interval, AlarmManager.FLAG_STANDALONE,
+                TEST_CALLING_UID);
+    }
+
+    private void setIdleUntilAlarm(int type, long triggerTime, PendingIntent pi) {
+        setTestAlarm(type, triggerTime, pi, 0, AlarmManager.FLAG_IDLE_UNTIL, TEST_CALLING_UID);
+    }
+
+    private void setWakeFromIdle(int type, long triggerTime, PendingIntent pi) {
+        // Note: Only alarm clock alarms are allowed to include this flag in the actual service.
+        // But this is a unit test so we'll only test the flag for granularity and convenience.
+        setTestAlarm(type, triggerTime, pi, 0,
+                AlarmManager.FLAG_WAKE_FROM_IDLE | AlarmManager.FLAG_STANDALONE, TEST_CALLING_UID);
     }
 
     private void setTestAlarm(int type, long triggerTime, PendingIntent operation, long interval,
-            int callingUid) {
-        mService.setImpl(type, triggerTime, AlarmManager.WINDOW_EXACT, interval,
-                operation, null, "test", AlarmManager.FLAG_STANDALONE, null, null,
-                callingUid, TEST_CALLING_PACKAGE);
+            int flags, int callingUid) {
+        mService.setImpl(type, triggerTime, AlarmManager.WINDOW_EXACT, interval, operation, null,
+                "test", flags, null, null, callingUid, TEST_CALLING_PACKAGE);
     }
 
     private void setTestAlarmWithListener(int type, long triggerTime, IAlarmListener listener) {
@@ -1002,7 +1018,7 @@
         for (int i = 0; i < numAlarms; i++) {
             int mockUid = UserHandle.getUid(mockUserId, 1234 + i);
             setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + i + 10,
-                    getNewMockPendingIntent(mockUid), 0, mockUid);
+                    getNewMockPendingIntent(mockUid), 0, AlarmManager.FLAG_STANDALONE, mockUid);
         }
         assertEquals(numAlarms, mService.mAlarmsPerUid.size());
         mService.removeUserLocked(mockUserId);
@@ -1142,6 +1158,116 @@
         }
     }
 
+    @Test
+    public void singleIdleUntil() {
+        doReturn(0).when(mService).fuzzForDuration(anyLong());
+
+        final PendingIntent idleUntilPi6 = getNewMockPendingIntent();
+        setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 6, idleUntilPi6);
+
+        assertTrue(mService.mPendingIdleUntil.matches(idleUntilPi6, null));
+        assertEquals(mNowElapsedTest + 6, mTestTimer.getElapsed());
+        assertEquals(mNowElapsedTest + 6, mService.mPendingIdleUntil.getWhenElapsed());
+
+        final PendingIntent idleUntilPi2 = getNewMockPendingIntent();
+        setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 2, idleUntilPi2);
+
+        // The same mPendingIdleUntil should get updated, even with a different PendingIntent.
+        assertTrue(mService.mPendingIdleUntil.matches(idleUntilPi2, null));
+        assertEquals(mNowElapsedTest + 2, mTestTimer.getElapsed());
+        assertEquals(1, mService.mAlarmStore.size());
+
+        final PendingIntent idleUntilPi10 = getNewMockPendingIntent();
+        setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 10, idleUntilPi10);
+
+        // The same thing should happen even when the new alarm is in farther in the future.
+        assertTrue(mService.mPendingIdleUntil.matches(idleUntilPi10, null));
+        assertEquals(mNowElapsedTest + 10, mTestTimer.getElapsed());
+        assertEquals(1, mService.mAlarmStore.size());
+    }
+
+    @Test
+    public void nextWakeFromIdle() throws Exception {
+        assertNull(mService.mNextWakeFromIdle);
+
+        final PendingIntent wakeFromIdle6 = getNewMockPendingIntent();
+        final long trigger6 = mNowElapsedTest + 6;
+        setWakeFromIdle(ELAPSED_REALTIME_WAKEUP, trigger6, wakeFromIdle6);
+
+        assertTrue(mService.mNextWakeFromIdle.matches(wakeFromIdle6, null));
+        assertEquals(trigger6, mService.mNextWakeFromIdle.getWhenElapsed());
+        assertEquals(trigger6, mTestTimer.getElapsed());
+
+        final PendingIntent wakeFromIdle10 = getNewMockPendingIntent();
+        final long trigger10 = mNowElapsedTest + 10;
+        setWakeFromIdle(ELAPSED_REALTIME_WAKEUP, trigger10, wakeFromIdle10);
+
+        // mNextWakeFromIdle should not get updated.
+        assertTrue(mService.mNextWakeFromIdle.matches(wakeFromIdle6, null));
+        assertEquals(trigger6, mTestTimer.getElapsed());
+        assertEquals(trigger6, mService.mNextWakeFromIdle.getWhenElapsed());
+
+        final PendingIntent wakeFromIdle3 = getNewMockPendingIntent();
+        final long trigger3 = mNowElapsedTest + 3;
+        setWakeFromIdle(ELAPSED_REALTIME_WAKEUP, trigger3, wakeFromIdle3);
+
+        // mNextWakeFromIdle should always reflect the next earliest wake_from_idle alarm.
+        assertTrue(mService.mNextWakeFromIdle.matches(wakeFromIdle3, null));
+        assertEquals(trigger3, mTestTimer.getElapsed());
+        assertEquals(trigger3, mService.mNextWakeFromIdle.getWhenElapsed());
+
+        mNowElapsedTest = trigger3;
+        mTestTimer.expire();
+
+        assertTrue(mService.mNextWakeFromIdle.matches(wakeFromIdle6, null));
+        assertEquals(trigger6, mTestTimer.getElapsed());
+        assertEquals(trigger6, mService.mNextWakeFromIdle.getWhenElapsed());
+
+        mService.removeLocked(wakeFromIdle6, null);
+
+        assertTrue(mService.mNextWakeFromIdle.matches(wakeFromIdle10, null));
+        assertEquals(trigger10, mTestTimer.getElapsed());
+        assertEquals(trigger10, mService.mNextWakeFromIdle.getWhenElapsed());
+
+        mService.removeLocked(wakeFromIdle10, null);
+        assertNull(mService.mNextWakeFromIdle);
+    }
+
+    @Test
+    public void idleUntilBeforeWakeFromIdle() {
+        doReturn(0).when(mService).fuzzForDuration(anyLong());
+
+        final PendingIntent idleUntilPi = getNewMockPendingIntent();
+        final long requestedIdleUntil = mNowElapsedTest + 10;
+        setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, requestedIdleUntil, idleUntilPi);
+
+        assertEquals(requestedIdleUntil, mService.mPendingIdleUntil.getWhenElapsed());
+
+        final PendingIntent wakeFromIdle5 = getNewMockPendingIntent();
+        setWakeFromIdle(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 5, wakeFromIdle5);
+        assertEquals(mNowElapsedTest + 5, mService.mPendingIdleUntil.getWhenElapsed());
+
+        final PendingIntent wakeFromIdle8 = getNewMockPendingIntent();
+        setWakeFromIdle(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 8, wakeFromIdle8);
+        assertEquals(mNowElapsedTest + 5, mService.mPendingIdleUntil.getWhenElapsed());
+
+        final PendingIntent wakeFromIdle12 = getNewMockPendingIntent();
+        setWakeFromIdle(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 12, wakeFromIdle12);
+        assertEquals(mNowElapsedTest + 5, mService.mPendingIdleUntil.getWhenElapsed());
+
+        mService.removeLocked(wakeFromIdle5, null);
+        assertEquals(mNowElapsedTest + 8, mService.mPendingIdleUntil.getWhenElapsed());
+
+        mService.removeLocked(wakeFromIdle8, null);
+        assertEquals(requestedIdleUntil, mService.mPendingIdleUntil.getWhenElapsed());
+
+        mService.removeLocked(idleUntilPi, null);
+        assertNull(mService.mPendingIdleUntil);
+
+        setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 15, idleUntilPi);
+        assertEquals(mNowElapsedTest + 12, mService.mPendingIdleUntil.getWhenElapsed());
+    }
+
     @After
     public void tearDown() {
         if (mMockingSession != null) {
@@ -1149,4 +1275,12 @@
         }
         LocalServices.removeServiceForTest(AlarmManagerInternal.class);
     }
+
+    private void dumpAllAlarms(String tag, ArrayList<Alarm> alarms) {
+        System.out.println(tag + ": ");
+        IndentingPrintWriter ipw = new IndentingPrintWriter(new PrintWriter(System.out));
+        AlarmManagerService.dumpAlarmList(ipw, alarms, mNowElapsedTest,
+                new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"));
+        ipw.close();
+    }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java
index 9e43b4a..f0490ce 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java
@@ -16,6 +16,9 @@
 
 package com.android.server.alarm;
 
+import static com.android.server.alarm.Constants.TEST_CALLING_PACKAGE;
+import static com.android.server.alarm.Constants.TEST_CALLING_UID;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
@@ -35,9 +38,6 @@
 @Presubmit
 @RunWith(AndroidJUnit4.class)
 public class AlarmStoreTest {
-    private static final int TEST_CALLING_UID = 12345;
-    private static final String TEST_CALLING_PACKAGE = "android.alarm.unit.test";
-
     private AlarmStore mAlarmStore;
 
     @Before
@@ -45,22 +45,22 @@
         mAlarmStore = new BatchingAlarmStore(null);
     }
 
-    private static Alarm createAlarm(long whenElapsed, long windowLength, PendingIntent mockPi,
+    private static Alarm createAlarm(long whenElapsed, long windowLength,
             AlarmManager.AlarmClockInfo alarmClock) {
-        return createAlarm(AlarmManager.ELAPSED_REALTIME, whenElapsed, windowLength, mockPi,
+        return createAlarm(AlarmManager.ELAPSED_REALTIME, whenElapsed, windowLength,
                 alarmClock);
     }
 
     private static Alarm createWakeupAlarm(long whenElapsed, long windowLength,
-            PendingIntent mockPi, AlarmManager.AlarmClockInfo alarmClock) {
-        return createAlarm(AlarmManager.ELAPSED_REALTIME_WAKEUP, whenElapsed, windowLength, mockPi,
+            AlarmManager.AlarmClockInfo alarmClock) {
+        return createAlarm(AlarmManager.ELAPSED_REALTIME_WAKEUP, whenElapsed, windowLength,
                 alarmClock);
     }
 
     private static Alarm createAlarm(int type, long whenElapsed, long windowLength,
-            PendingIntent mockPi, AlarmManager.AlarmClockInfo alarmClock) {
-        return new Alarm(type, whenElapsed, whenElapsed, windowLength, whenElapsed + windowLength,
-                0, mockPi, null, null, null, 0, alarmClock, TEST_CALLING_UID, TEST_CALLING_PACKAGE);
+            AlarmManager.AlarmClockInfo alarmClock) {
+        return new Alarm(type, whenElapsed, whenElapsed, windowLength, 0, mock(PendingIntent.class),
+                null, null, null, 0, alarmClock, TEST_CALLING_UID, TEST_CALLING_PACKAGE);
     }
 
     private void addAlarmsToStore(Alarm... alarms) {
@@ -71,11 +71,11 @@
 
     @Test
     public void add() {
-        final Alarm a1 = createAlarm(1, 0, mock(PendingIntent.class), null);
+        final Alarm a1 = createAlarm(1, 0, null);
         mAlarmStore.add(a1);
         assertEquals(1, mAlarmStore.size());
 
-        final Alarm a2 = createAlarm(2, 0, mock(PendingIntent.class), null);
+        final Alarm a2 = createAlarm(2, 0, null);
         mAlarmStore.add(a2);
         assertEquals(2, mAlarmStore.size());
 
@@ -86,17 +86,17 @@
 
     @Test
     public void remove() {
-        final Alarm a1 = createAlarm(1, 0, mock(PendingIntent.class), null);
-        final Alarm a2 = createAlarm(2, 0, mock(PendingIntent.class), null);
-        final Alarm a5 = createAlarm(5, 0, mock(PendingIntent.class), null);
+        final Alarm a1 = createAlarm(1, 0, null);
+        final Alarm a2 = createAlarm(2, 0, null);
+        final Alarm a5 = createAlarm(5, 0, null);
         addAlarmsToStore(a1, a2, a5);
 
-        ArrayList<Alarm> removed = mAlarmStore.remove(a -> (a.whenElapsed < 4));
+        ArrayList<Alarm> removed = mAlarmStore.remove(a -> (a.getWhenElapsed() < 4));
         assertEquals(2, removed.size());
         assertEquals(1, mAlarmStore.size());
         assertTrue(removed.contains(a1) && removed.contains(a2));
 
-        final Alarm a8 = createAlarm(8, 0, mock(PendingIntent.class), null);
+        final Alarm a8 = createAlarm(8, 0, null);
         addAlarmsToStore(a8, a2, a1);
 
         removed = mAlarmStore.remove(unused -> false);
@@ -110,10 +110,10 @@
 
     @Test
     public void removePendingAlarms() {
-        final Alarm a1_11 = createAlarm(1, 10, mock(PendingIntent.class), null);
-        final Alarm a2_5 = createAlarm(2, 3, mock(PendingIntent.class), null);
-        final Alarm a6_9 = createAlarm(6, 3, mock(PendingIntent.class), null);
-        addAlarmsToStore(a2_5, a6_9, a1_11);
+        final Alarm a1to11 = createAlarm(1, 10, null);
+        final Alarm a2to5 = createAlarm(2, 3, null);
+        final Alarm a6to9 = createAlarm(6, 3, null);
+        addAlarmsToStore(a2to5, a6to9, a1to11);
 
         final ArrayList<Alarm> pendingAt0 = mAlarmStore.removePendingAlarms(0);
         assertEquals(0, pendingAt0.size());
@@ -121,24 +121,24 @@
 
         final ArrayList<Alarm> pendingAt3 = mAlarmStore.removePendingAlarms(3);
         assertEquals(2, pendingAt3.size());
-        assertTrue(pendingAt3.contains(a1_11) && pendingAt3.contains(a2_5));
+        assertTrue(pendingAt3.contains(a1to11) && pendingAt3.contains(a2to5));
         assertEquals(1, mAlarmStore.size());
 
-        addAlarmsToStore(a2_5, a1_11);
+        addAlarmsToStore(a2to5, a1to11);
         final ArrayList<Alarm> pendingAt7 = mAlarmStore.removePendingAlarms(7);
         assertEquals(3, pendingAt7.size());
-        assertTrue(pendingAt7.contains(a1_11) && pendingAt7.contains(a2_5) && pendingAt7.contains(
-                a6_9));
+        assertTrue(pendingAt7.contains(a1to11) && pendingAt7.contains(a2to5) && pendingAt7.contains(
+                a6to9));
         assertEquals(0, mAlarmStore.size());
     }
 
     @Test
     public void getNextWakeupDeliveryTime() {
-        final Alarm a1_10 = createAlarm(1, 9, mock(PendingIntent.class), null);
-        final Alarm a3_8_wakeup = createWakeupAlarm(3, 5, mock(PendingIntent.class), null);
-        final Alarm a6_wakeup = createWakeupAlarm(6, 0, mock(PendingIntent.class), null);
-        final Alarm a5 = createAlarm(5, 0, mock(PendingIntent.class), null);
-        addAlarmsToStore(a5, a6_wakeup, a3_8_wakeup, a1_10);
+        final Alarm a1to10 = createAlarm(1, 9, null);
+        final Alarm a3to8wakeup = createWakeupAlarm(3, 5, null);
+        final Alarm a6wakeup = createWakeupAlarm(6, 0, null);
+        final Alarm a5 = createAlarm(5, 0, null);
+        addAlarmsToStore(a5, a6wakeup, a3to8wakeup, a1to10);
 
         // The wakeup alarms are [6] and [3, 8], hence 6 is the latest time till when we can
         // defer delivering any wakeup alarm.
@@ -155,11 +155,11 @@
 
     @Test
     public void getNextDeliveryTime() {
-        final Alarm a1_10 = createAlarm(1, 9, mock(PendingIntent.class), null);
-        final Alarm a3_8_wakeup = createWakeupAlarm(3, 5, mock(PendingIntent.class), null);
-        final Alarm a6_wakeup = createWakeupAlarm(6, 0, mock(PendingIntent.class), null);
-        final Alarm a5 = createAlarm(5, 0, mock(PendingIntent.class), null);
-        addAlarmsToStore(a5, a6_wakeup, a3_8_wakeup, a1_10);
+        final Alarm a1to10 = createAlarm(1, 9, null);
+        final Alarm a3to8wakeup = createWakeupAlarm(3, 5, null);
+        final Alarm a6wakeup = createWakeupAlarm(6, 0, null);
+        final Alarm a5 = createAlarm(5, 0, null);
+        addAlarmsToStore(a5, a6wakeup, a3to8wakeup, a1to10);
 
         assertTrue(mAlarmStore.getNextDeliveryTime() <= 5);
 
@@ -168,24 +168,22 @@
     }
 
     @Test
-    public void recalculateAlarmDeliveries() {
-        final Alarm a5 = createAlarm(5, 0, mock(PendingIntent.class), null);
-        final Alarm a8 = createAlarm(8, 0, mock(PendingIntent.class), null);
-        final Alarm a10 = createAlarm(10, 0, mock(PendingIntent.class), null);
+    public void updateAlarmDeliveries() {
+        final Alarm a5 = createAlarm(5, 0, null);
+        final Alarm a8 = createAlarm(8, 0, null);
+        final Alarm a10 = createAlarm(10, 0, null);
         addAlarmsToStore(a8, a10, a5);
 
         assertEquals(5, mAlarmStore.getNextDeliveryTime());
 
-        mAlarmStore.recalculateAlarmDeliveries(a -> {
-            a.whenElapsed += 3;
-            a.maxWhenElapsed = a.whenElapsed;
+        mAlarmStore.updateAlarmDeliveries(a -> {
+            a.setPolicyElapsed(Alarm.REQUESTER_POLICY_INDEX, a.getWhenElapsed() + 3);
             return true;
         });
         assertEquals(8, mAlarmStore.getNextDeliveryTime());
 
-        mAlarmStore.recalculateAlarmDeliveries(a -> {
-            a.whenElapsed = 20 - a.whenElapsed;
-            a.maxWhenElapsed = a.whenElapsed;
+        mAlarmStore.updateAlarmDeliveries(a -> {
+            a.setPolicyElapsed(Alarm.REQUESTER_POLICY_INDEX, 20 - a.getWhenElapsed());
             return true;
         });
         assertEquals(7, mAlarmStore.getNextDeliveryTime());
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmTest.java
new file mode 100644
index 0000000..efcfae3
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.alarm;
+
+import static android.app.AlarmManager.ELAPSED_REALTIME;
+
+import static com.android.server.alarm.Alarm.APP_STANDBY_POLICY_INDEX;
+import static com.android.server.alarm.Alarm.REQUESTER_POLICY_INDEX;
+import static com.android.server.alarm.Constants.TEST_CALLING_PACKAGE;
+import static com.android.server.alarm.Constants.TEST_CALLING_UID;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+import android.app.PendingIntent;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class AlarmTest {
+
+    private Alarm createDefaultAlarm(long requestedElapsed, long windowLength) {
+        return new Alarm(ELAPSED_REALTIME, 0, requestedElapsed, windowLength, 0,
+                mock(PendingIntent.class), null, null, null, 0, null, TEST_CALLING_UID,
+                TEST_CALLING_PACKAGE);
+    }
+
+    @Test
+    public void initSetsOnlyRequesterPolicy() {
+        final Alarm a = createDefaultAlarm(4567, 2);
+        assertEquals(4567, a.getPolicyElapsed(REQUESTER_POLICY_INDEX));
+        assertEquals(0, a.getPolicyElapsed(APP_STANDBY_POLICY_INDEX));
+    }
+
+    @Test
+    public void whenElapsed() {
+        final Alarm a = createDefaultAlarm(0, 0);
+
+        a.setPolicyElapsed(REQUESTER_POLICY_INDEX, 4);
+        a.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 10);
+        assertEquals(10, a.getWhenElapsed());
+
+        a.setPolicyElapsed(REQUESTER_POLICY_INDEX, 12);
+        assertEquals(12, a.getWhenElapsed());
+
+        a.setPolicyElapsed(REQUESTER_POLICY_INDEX, 7);
+        assertEquals(10, a.getWhenElapsed());
+
+        a.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 2);
+        assertEquals(7, a.getWhenElapsed());
+
+        a.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 7);
+        assertEquals(7, a.getWhenElapsed());
+    }
+
+    @Test
+    public void maxWhenElapsed() {
+        final Alarm a = createDefaultAlarm(10, 12);
+        assertEquals(22, a.getMaxWhenElapsed());
+
+        a.setPolicyElapsed(REQUESTER_POLICY_INDEX, 15);
+        assertEquals(27, a.getMaxWhenElapsed());
+
+        a.setPolicyElapsed(REQUESTER_POLICY_INDEX, 2);
+        assertEquals(14, a.getMaxWhenElapsed());
+
+        a.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 5);
+        assertEquals(14, a.getMaxWhenElapsed());
+
+        a.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 16);
+        assertEquals(16, a.getMaxWhenElapsed());
+
+        a.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 12);
+        assertEquals(14, a.getMaxWhenElapsed());
+    }
+
+    @Test
+    public void setPolicyElapsed() {
+        final Alarm exactAlarm = createDefaultAlarm(10, 0);
+
+        assertTrue(exactAlarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, 4));
+        assertTrue(exactAlarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 10));
+
+        assertFalse(exactAlarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, 8));
+        assertFalse(exactAlarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, 10));
+        assertFalse(exactAlarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 8));
+
+        assertTrue(exactAlarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, 7));
+
+        final Alarm inexactAlarm = createDefaultAlarm(10, 5);
+
+        assertTrue(inexactAlarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, 4));
+        assertTrue(inexactAlarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 10));
+
+        // whenElapsed won't change, but maxWhenElapsed will.
+        assertTrue(inexactAlarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, 8));
+        assertTrue(inexactAlarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, 10));
+
+        assertFalse(inexactAlarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 8));
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/BackgroundRestrictedAlarmsTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/BackgroundRestrictedAlarmsTest.java
index 6465739..5bb6a42 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/BackgroundRestrictedAlarmsTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/BackgroundRestrictedAlarmsTest.java
@@ -45,7 +45,7 @@
         }
         uidAlarms.add(new Alarm(
                 removeIt ? RTC : RTC_WAKEUP,
-                0, 0, 0, 0, 0, null, null, null, null, 0, null, uid, name));
+                0, 0, 0, 0, null, null, null, null, 0, null, uid, name));
         return all;
     }
 
diff --git a/media/java/android/media/tv/TvChannelInfo.aidl b/services/tests/mockingservicestests/src/com/android/server/alarm/Constants.java
similarity index 70%
copy from media/java/android/media/tv/TvChannelInfo.aidl
copy to services/tests/mockingservicestests/src/com/android/server/alarm/Constants.java
index 71cd0a7..2552db8 100644
--- a/media/java/android/media/tv/TvChannelInfo.aidl
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/Constants.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open 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,6 +14,9 @@
  * limitations under the License.
  */
 
-package android.media.tv;
+package com.android.server.alarm;
 
-parcelable TvChannelInfo;
+public interface Constants {
+    String TEST_CALLING_PACKAGE = "com.android.framework.test-package";
+    int TEST_CALLING_UID = 67890;
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java b/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java
index 04e8b63..b85da94 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java
@@ -34,12 +34,12 @@
 import android.os.HandlerThread;
 import android.os.Process;
 import android.platform.test.annotations.Presubmit;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 
 import com.android.dx.mockito.inline.extended.StaticMockitoSession;
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
-import com.android.server.am.ActivityManagerService.Injector;
 import com.android.server.appop.AppOpsService;
 import com.android.server.wm.ActivityTaskManagerService;
 
@@ -55,7 +55,11 @@
 import org.mockito.MockitoAnnotations;
 import org.mockito.quality.Strictness;
 
+import java.io.ByteArrayInputStream;
 import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
 
 @Presubmit
 public class AppChildProcessTest {
@@ -68,6 +72,7 @@
 
     private Context mContext = getInstrumentation().getTargetContext();
     private TestInjector mInjector;
+    private PhantomTestInjector mPhantomInjector;
     private ActivityManagerService mAms;
     private ProcessList mProcessList;
     private PhantomProcessList mPhantomProcessList;
@@ -94,6 +99,7 @@
         mProcessList = spy(pList);
 
         mInjector = new TestInjector(mContext);
+        mPhantomInjector = new PhantomTestInjector();
         mAms = new ActivityManagerService(mInjector, mServiceThreadRule.getThread());
         mAms.mActivityTaskManager = new ActivityTaskManagerService(mContext);
         mAms.mActivityTaskManager.initialize(null, null, mContext.getMainLooper());
@@ -101,6 +107,7 @@
         mAms.mPackageManagerInt = mPackageManagerInt;
         pList.mService = mAms;
         mPhantomProcessList = mAms.mPhantomProcessList;
+        mPhantomProcessList.mInjector = mPhantomInjector;
         doReturn(new ComponentName("", "")).when(mPackageManagerInt).getSystemUiServiceComponent();
         doReturn(false).when(() -> Process.supportsPidFd());
         // Remove stale instance of PackageManagerInternal if there is any
@@ -136,47 +143,60 @@
         final String child2ProcessName = "test1_child1_child2";
         final String nativeProcessName = "test_native";
 
-        makeParent(zygote64Pid, initPid);
-        makeParent(zygote32Pid, initPid);
+        makeProcess(rootUid, zygote64Pid, zygote64ProcessName);
+        makeParent(rootUid, zygote64Pid, initPid);
+        makeProcess(rootUid, zygote32Pid, zygote32ProcessName);
+        makeParent(rootUid, zygote32Pid, initPid);
 
         makeAppProcess(app1Pid, app1Uid, app1ProcessName, app1ProcessName);
-        makeParent(app1Pid, zygote64Pid);
         makeAppProcess(app2Pid, app2Uid, app2ProcessName, app2ProcessName);
-        makeParent(app2Pid, zygote64Pid);
+
+        mPhantomProcessList.lookForPhantomProcessesLocked();
 
         assertEquals(0, mPhantomProcessList.mPhantomProcesses.size());
 
         // Verify zygote itself isn't a phantom process
         assertEquals(null, mPhantomProcessList.getOrCreatePhantomProcessIfNeededLocked(
-                zygote64ProcessName, rootUid, zygote64Pid));
+                zygote64ProcessName, rootUid, zygote64Pid, false));
         assertEquals(null, mPhantomProcessList.getOrCreatePhantomProcessIfNeededLocked(
-                zygote32ProcessName, rootUid, zygote32Pid));
+                zygote32ProcessName, rootUid, zygote32Pid, false));
         // Verify none of the app isn't a phantom process
         assertEquals(null, mPhantomProcessList.getOrCreatePhantomProcessIfNeededLocked(
-                app1ProcessName, app1Uid, app1Pid));
+                app1ProcessName, app1Uid, app1Pid, false));
         assertEquals(null, mPhantomProcessList.getOrCreatePhantomProcessIfNeededLocked(
-                app2ProcessName, app2Uid, app2Pid));
+                app2ProcessName, app2Uid, app2Pid, false));
 
         // "Fork" an app child process
-        makeParent(child1Pid, app1Pid);
+        makeProcess(app1Uid, child1Pid, child1ProcessName);
+        makeParent(app1Uid, child1Pid, app1Pid);
+        mPhantomProcessList.lookForPhantomProcessesLocked();
+
         PhantomProcessRecord pr = mPhantomProcessList
-                .getOrCreatePhantomProcessIfNeededLocked(child1ProcessName, app1Uid, child1Pid);
+                .getOrCreatePhantomProcessIfNeededLocked(
+                        child1ProcessName, app1Uid, child1Pid, true);
         assertTrue(pr != null);
         assertEquals(1, mPhantomProcessList.mPhantomProcesses.size());
         assertEquals(pr, mPhantomProcessList.mPhantomProcesses.valueAt(0));
         verifyPhantomProcessRecord(pr, child1ProcessName, app1Uid, child1Pid);
 
         // Create another native process from init
-        makeParent(nativePid, initPid);
+        makeProcess(rootUid, nativePid, nativeProcessName);
+        makeParent(rootUid, nativePid, initPid);
+        mPhantomProcessList.lookForPhantomProcessesLocked();
+
         assertEquals(null, mPhantomProcessList.getOrCreatePhantomProcessIfNeededLocked(
-                nativeProcessName, rootUid, nativePid));
+                nativeProcessName, rootUid, nativePid, false));
         assertEquals(1, mPhantomProcessList.mPhantomProcesses.size());
         assertEquals(pr, mPhantomProcessList.mPhantomProcesses.valueAt(0));
 
         // "Fork" another app child process
-        makeParent(child2Pid, child1Pid);
+        makeProcess(app1Uid, child2Pid, child2ProcessName);
+        makeParent(app1Uid, child2Pid, app1Pid);
+        mPhantomProcessList.lookForPhantomProcessesLocked();
+
         PhantomProcessRecord pr2 = mPhantomProcessList
-                .getOrCreatePhantomProcessIfNeededLocked(child2ProcessName, app1Uid, child2Pid);
+                .getOrCreatePhantomProcessIfNeededLocked(
+                        child2ProcessName, app1Uid, child2Pid, false);
         assertTrue(pr2 != null);
         assertEquals(2, mPhantomProcessList.mPhantomProcesses.size());
         verifyPhantomProcessRecord(pr2, child2ProcessName, app1Uid, child2Pid);
@@ -197,19 +217,27 @@
         assertEquals(pid, pr.mPid);
     }
 
+    private void makeProcess(int uid, int pid, String processName) {
+        doReturn(uid).when(() -> Process.getUidForPid(eq(pid)));
+        mPhantomInjector.mPidToName.put(pid, processName);
+    }
+
     private void makeAppProcess(int pid, int uid, String packageName, String processName) {
+        makeProcess(uid, pid, processName);
         ApplicationInfo ai = new ApplicationInfo();
         ai.packageName = packageName;
+        ai.uid = uid;
         ProcessRecord app = new ProcessRecord(mAms, ai, processName, uid);
         app.pid = pid;
         mAms.mPidsSelfLocked.doAddInternal(app);
+        mPhantomInjector.addToProcess(uid, pid, pid);
     }
 
-    private void makeParent(int pid, int ppid) {
-        doReturn(ppid).when(() -> Process.getParentPid(eq(pid)));
+    private void makeParent(int uid, int pid, int ppid) {
+        mPhantomInjector.addToProcess(uid, ppid, pid);
     }
 
-    private class TestInjector extends Injector {
+    private class TestInjector extends ActivityManagerService.Injector {
         TestInjector(Context context) {
             super(context);
         }
@@ -230,6 +258,56 @@
         }
     }
 
+    private class PhantomTestInjector extends PhantomProcessList.Injector {
+        ArrayMap<String, InputStream> mPathToInput = new ArrayMap<>();
+        ArrayMap<String, StringBuffer> mPathToData = new ArrayMap<>();
+        ArrayMap<InputStream, StringBuffer> mInputToData = new ArrayMap<>();
+        ArrayMap<Integer, String> mPidToName = new ArrayMap<>();
+
+        @Override
+        InputStream openCgroupProcs(String path) throws FileNotFoundException, SecurityException {
+            InputStream input = mPathToInput.get(path);
+            if (input != null) {
+                return input;
+            }
+            input = new ByteArrayInputStream(new byte[8]); // buf size doesn't matter here
+            mPathToInput.put(path, input);
+            StringBuffer sb = mPathToData.get(path);
+            if (sb == null) {
+                sb = new StringBuffer();
+                mPathToData.put(path, sb);
+            }
+            mInputToData.put(input, sb);
+            return input;
+        }
+
+        @Override
+        int readCgroupProcs(InputStream input, byte[] buf, int offset, int len) throws IOException {
+            StringBuffer sb = mInputToData.get(input);
+            if (sb == null) {
+                return -1;
+            }
+            byte[] avail = sb.toString().getBytes();
+            System.arraycopy(avail, 0, buf, offset, Math.min(len, avail.length));
+            return Math.min(len, avail.length);
+        }
+
+        @Override
+        String getProcessName(final int pid) {
+            return mPidToName.get(pid);
+        }
+
+        void addToProcess(int uid, int pid, int newPid) {
+            final String path = PhantomProcessList.getCgroupFilePath(uid, pid);
+            StringBuffer sb = mPathToData.get(path);
+            if (sb == null) {
+                sb = new StringBuffer();
+                mPathToData.put(path, sb);
+            }
+            sb.append(newPid).append('\n');
+        }
+    }
+
     static class ServiceThreadRule implements TestRule {
         private ServiceThread mThread;
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index a250c21..d032dbd 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -457,12 +457,12 @@
     public void testUpdateOomAdj_DoOne_HeavyWeight() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-        doReturn(true).when(sService.mAtmInternal).isHeavyWeightProcess(any(
-                WindowProcessController.class));
+        doReturn(mock(WindowProcessController.class)).when(app).getWindowProcessController();
+        WindowProcessController wpc = app.getWindowProcessController();
+        doReturn(true).when(wpc).isHeavyWeightProcess();
         sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
-        doReturn(false).when(sService.mAtmInternal).isHeavyWeightProcess(any(
-                WindowProcessController.class));
+        doReturn(false).when(wpc).isHeavyWeightProcess();
 
         assertProcStates(app, PROCESS_STATE_HEAVY_WEIGHT, HEAVY_WEIGHT_APP_ADJ,
                 SCHED_GROUP_BACKGROUND);
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
index 9d2393b..0ab1501 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -83,6 +83,8 @@
 
     private LinkedList<DisplayAddress.Physical> mAddresses = new LinkedList<>();
 
+    private Injector mInjector;
+
     @Before
     public void setUp() throws Exception {
         mMockitoSession = mockitoSession()
@@ -94,8 +96,9 @@
         doReturn(mMockedResources).when(mMockedContext).getResources();
         LocalServices.removeServiceForTest(LightsManager.class);
         LocalServices.addService(LightsManager.class, mMockedLightsManager);
+        mInjector = new Injector();
         mAdapter = new LocalDisplayAdapter(mMockedSyncRoot, mMockedContext, mHandler,
-                mListener);
+                mListener, mInjector);
         spyOn(mAdapter);
         doReturn(mMockedContext).when(mAdapter).getOverlayContext();
     }
@@ -222,7 +225,7 @@
         display.configs = configs;
         display.activeConfig = 1;
         setUpDisplay(display);
-        mAdapter.registerLocked();
+        mInjector.getTransmitter().sendHotplug(display, /* connected */ true);
         waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
 
         assertThat(SurfaceControl.getActiveConfig(display.token)).isEqualTo(1);
@@ -272,7 +275,7 @@
                 new int[Display.HdrCapabilities.HDR_TYPE_HDR10_PLUS], 1000, 1000, 0);
         display.hdrCapabilities = changedHdrCapabilities;
         setUpDisplay(display);
-        mAdapter.registerLocked();
+        mInjector.getTransmitter().sendHotplug(display, /* connected */ true);
         waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
 
         assertThat(mListener.addedDisplays.size()).isEqualTo(1);
@@ -308,7 +311,7 @@
         final int[] changedColorModes = new int[]{Display.COLOR_MODE_DEFAULT};
         display.colorModes = changedColorModes;
         setUpDisplay(display);
-        mAdapter.registerLocked();
+        mInjector.getTransmitter().sendHotplug(display, /* connected */ true);
         waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
 
         assertThat(mListener.addedDisplays.size()).isEqualTo(1);
@@ -322,6 +325,35 @@
         assertThat(displayDeviceInfo.supportedColorModes).isEqualTo(changedColorModes);
     }
 
+    @Test
+    public void testDisplayChange_withStaleDesiredDisplayConfigSpecs() throws Exception {
+        SurfaceControl.DisplayConfig[] configs = new SurfaceControl.DisplayConfig[]{
+                createFakeDisplayConfig(1920, 1080, 60f),
+                createFakeDisplayConfig(1920, 1080, 50f)
+        };
+        final int activeConfig = 0;
+        FakeDisplay display = new FakeDisplay(PORT_A, configs, activeConfig);
+        display.desiredDisplayConfigSpecs.defaultConfig = 1;
+
+        setUpDisplay(display);
+        updateAvailableDisplays();
+        mAdapter.registerLocked();
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+        // Change the display
+        display.configs = new SurfaceControl.DisplayConfig[]{
+                createFakeDisplayConfig(1920, 1080, 60f)
+        };
+        // SurfaceFlinger can return a stale defaultConfig. Make sure this doesn't
+        // trigger ArrayOutOfBoundsException.
+        display.desiredDisplayConfigSpecs.defaultConfig = 1;
+
+        setUpDisplay(display);
+        updateAvailableDisplays();
+        mInjector.getTransmitter().sendHotplug(display, /* connected */ true);
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+    }
+
     private void assertDisplayDpi(DisplayDeviceInfo info, int expectedPort,
                                   float expectedXdpi,
                                   float expectedYDpi,
@@ -356,6 +388,8 @@
         public int[] colorModes = new int[]{ Display.COLOR_MODE_DEFAULT };
         public Display.HdrCapabilities hdrCapabilities = new Display.HdrCapabilities(new int[0],
                 1000, 1000, 0);
+        public SurfaceControl.DesiredDisplayConfigSpecs desiredDisplayConfigSpecs =
+                new SurfaceControl.DesiredDisplayConfigSpecs(0, 60.f, 60.f, 60.f, 60.f);
 
         private FakeDisplay(int port) {
             this.address = createDisplayAddress(port);
@@ -387,7 +421,7 @@
                 () -> SurfaceControl.getDisplayColorModes(display.token));
         doReturn(display.hdrCapabilities).when(
                 () -> SurfaceControl.getHdrCapabilities(display.token));
-        doReturn(new SurfaceControl.DesiredDisplayConfigSpecs(0, 60.f, 60.f, 60.f, 60.f))
+        doReturn(display.desiredDisplayConfigSpecs)
                 .when(() -> SurfaceControl.getDesiredDisplayConfigSpecs(display.token));
     }
 
@@ -422,7 +456,7 @@
         return config;
     }
 
-    private void waitForHandlerToComplete(Handler handler, long waitTimeMs)
+    private static void waitForHandlerToComplete(Handler handler, long waitTimeMs)
             throws InterruptedException {
         final Object lock = new Object();
         synchronized (lock) {
@@ -435,6 +469,35 @@
         }
     }
 
+    private class HotplugTransmitter {
+        private final Handler mHandler;
+        private final LocalDisplayAdapter.DisplayEventListener mListener;
+
+        HotplugTransmitter(Looper looper, LocalDisplayAdapter.DisplayEventListener listener) {
+            mHandler = new Handler(looper);
+            mListener = listener;
+        }
+
+        public void sendHotplug(FakeDisplay display, boolean connected)
+                throws InterruptedException {
+            mHandler.post(() -> mListener.onHotplug(/* timestampNanos = */ 0,
+                    display.address.getPhysicalDisplayId(), connected));
+            waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+        }
+    }
+
+    private class Injector extends LocalDisplayAdapter.Injector {
+        private HotplugTransmitter mTransmitter;
+        @Override
+        public void setDisplayEventListenerLocked(Looper looper,
+                LocalDisplayAdapter.DisplayEventListener listener) {
+            mTransmitter = new HotplugTransmitter(looper, listener);
+        }
+        public HotplugTransmitter getTransmitter() {
+            return mTransmitter;
+        }
+    }
+
     private class TestListener implements DisplayAdapter.Listener {
         public ArrayList<DisplayDevice> addedDisplays = new ArrayList<>();
         public ArrayList<DisplayDevice> changedDisplays = new ArrayList<>();
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/LocationFudgerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/LocationFudgerTest.java
index a0f48c6..d67eddd 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/LocationFudgerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/LocationFudgerTest.java
@@ -79,7 +79,7 @@
             Location coarse = mFudger.createCoarse(fine);
 
             assertThat(coarse).isNotNull();
-            assertThat(coarse).isNotSameAs(fine);
+            assertThat(coarse).isNotSameInstanceAs(fine);
             assertThat(coarse.hasBearing()).isFalse();
             assertThat(coarse.hasSpeed()).isFalse();
             assertThat(coarse.hasAltitude()).isFalse();
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java
index be258dc..3aedd3c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java
@@ -66,6 +66,7 @@
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.WorkSource;
 import android.platform.test.annotations.Presubmit;
 import android.util.Log;
 
@@ -111,6 +112,7 @@
     private static final CallerIdentity IDENTITY = CallerIdentity.forTest(CURRENT_USER, 1,
             "mypackage",
             "attribution");
+    private static final WorkSource WORK_SOURCE = new WorkSource(IDENTITY.getUid());
 
     private Random mRandom;
 
@@ -202,22 +204,20 @@
     @Test
     public void testIsEnabled() {
         assertThat(mManager.isEnabled(CURRENT_USER)).isTrue();
+        assertThat(mManager.isEnabled(OTHER_USER)).isTrue();
 
         mInjector.getSettingsHelper().setLocationEnabled(false, CURRENT_USER);
         assertThat(mManager.isEnabled(CURRENT_USER)).isFalse();
+        assertThat(mManager.isEnabled(OTHER_USER)).isTrue();
 
         mInjector.getSettingsHelper().setLocationEnabled(true, CURRENT_USER);
         mProvider.setAllowed(false);
         assertThat(mManager.isEnabled(CURRENT_USER)).isFalse();
+        assertThat(mManager.isEnabled(OTHER_USER)).isFalse();
 
         mProvider.setAllowed(true);
-        mInjector.getUserInfoHelper().setCurrentUserId(OTHER_USER);
-        assertThat(mManager.isEnabled(CURRENT_USER)).isFalse();
-        assertThat(mManager.isEnabled(OTHER_USER)).isTrue();
-
-        mInjector.getUserInfoHelper().setCurrentUserId(CURRENT_USER);
         assertThat(mManager.isEnabled(CURRENT_USER)).isTrue();
-        assertThat(mManager.isEnabled(OTHER_USER)).isFalse();
+        assertThat(mManager.isEnabled(OTHER_USER)).isTrue();
     }
 
     @Test
@@ -237,23 +237,15 @@
         mProvider.setAllowed(false);
         verify(listener, timeout(TIMEOUT_MS).times(2)).onProviderEnabledChanged(NAME, CURRENT_USER,
                 false);
+        verify(listener, timeout(TIMEOUT_MS).times(1)).onProviderEnabledChanged(NAME, OTHER_USER,
+                false);
 
         mProvider.setAllowed(true);
         verify(listener, timeout(TIMEOUT_MS).times(2)).onProviderEnabledChanged(NAME, CURRENT_USER,
                 true);
-
-        mInjector.getUserInfoHelper().setCurrentUserId(OTHER_USER);
-        verify(listener, timeout(TIMEOUT_MS).times(3)).onProviderEnabledChanged(NAME, CURRENT_USER,
-                false);
         verify(listener, timeout(TIMEOUT_MS).times(1)).onProviderEnabledChanged(NAME, OTHER_USER,
                 true);
 
-        mInjector.getUserInfoHelper().setCurrentUserId(CURRENT_USER);
-        verify(listener, timeout(TIMEOUT_MS).times(3)).onProviderEnabledChanged(NAME, CURRENT_USER,
-                true);
-        verify(listener, timeout(TIMEOUT_MS).times(1)).onProviderEnabledChanged(NAME, OTHER_USER,
-                false);
-
         mManager.removeEnabledListener(listener);
         mInjector.getSettingsHelper().setLocationEnabled(false, CURRENT_USER);
         verifyNoMoreInteractions(listener);
@@ -343,7 +335,7 @@
     @Test
     public void testPassive_Listener() throws Exception {
         ILocationListener listener = createMockLocationListener();
-        LocationRequest request = new LocationRequest.Builder(0).build();
+        LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
         mPassive.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
 
         Location loc = createLocation(NAME, mRandom);
@@ -368,8 +360,11 @@
         ArgumentCaptor<Location> locationCaptor = ArgumentCaptor.forClass(Location.class);
 
         ILocationListener listener = createMockLocationListener();
-        mManager.registerLocationRequest(new LocationRequest.Builder(0).build(), IDENTITY,
-                PERMISSION_FINE, listener);
+        mManager.registerLocationRequest(
+                new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(),
+                IDENTITY,
+                PERMISSION_FINE,
+                listener);
 
         Location loc = createLocation(NAME, mRandom);
         mProvider.setProviderLocation(loc);
@@ -397,16 +392,6 @@
         mProvider.setAllowed(true);
         verify(listener, timeout(TIMEOUT_MS).times(2)).onProviderEnabledChanged(NAME, true);
 
-        mInjector.getUserInfoHelper().setCurrentUserId(OTHER_USER);
-        verify(listener, timeout(TIMEOUT_MS).times(3)).onProviderEnabledChanged(NAME, false);
-        loc = createLocation(NAME, mRandom);
-        mProvider.setProviderLocation(loc);
-        verify(listener, times(1)).onLocationChanged(any(Location.class),
-                nullable(IRemoteCallback.class));
-
-        mInjector.getUserInfoHelper().setCurrentUserId(CURRENT_USER);
-        verify(listener, timeout(TIMEOUT_MS).times(3)).onProviderEnabledChanged(NAME, true);
-
         loc = createLocation(NAME, mRandom);
         mProvider.setProviderLocation(loc);
         verify(listener, times(2)).onLocationChanged(locationCaptor.capture(),
@@ -422,8 +407,11 @@
                 "attribution");
 
         ILocationListener listener = createMockLocationListener();
-        mManager.registerLocationRequest(new LocationRequest.Builder(0).build(), identity,
-                PERMISSION_FINE, listener);
+        mManager.registerLocationRequest(
+                new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(),
+                identity,
+                PERMISSION_FINE,
+                listener);
 
         Location loc = createLocation(NAME, mRandom);
         mProvider.setProviderLocation(loc);
@@ -435,8 +423,11 @@
     @Test
     public void testRegisterListener_Unregister() throws Exception {
         ILocationListener listener = createMockLocationListener();
-        mManager.registerLocationRequest(new LocationRequest.Builder(0).build(), IDENTITY,
-                PERMISSION_FINE, listener);
+        mManager.registerLocationRequest(
+                new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(),
+                IDENTITY,
+                PERMISSION_FINE,
+                listener);
         mManager.unregisterLocationRequest(listener);
 
         mProvider.setProviderLocation(createLocation(NAME, mRandom));
@@ -453,8 +444,11 @@
                 "attribution");
 
         ILocationListener listener = createMockLocationListener();
-        mManager.registerLocationRequest(new LocationRequest.Builder(0).build(), identity,
-                PERMISSION_FINE, listener);
+        mManager.registerLocationRequest(
+                new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(),
+                identity,
+                PERMISSION_FINE,
+                listener);
 
         CountDownLatch blocker = new CountDownLatch(1);
         IN_PROCESS_EXECUTOR.execute(() -> {
@@ -475,7 +469,10 @@
     @Test
     public void testRegisterListener_NumUpdates() throws Exception {
         ILocationListener listener = createMockLocationListener();
-        LocationRequest request = new LocationRequest.Builder(0).setMaxUpdates(5).build();
+        LocationRequest request = new LocationRequest.Builder(0)
+                .setMaxUpdates(5)
+                .setWorkSource(WORK_SOURCE)
+                .build();
         mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
 
         mProvider.setProviderLocation(createLocation(NAME, mRandom));
@@ -492,7 +489,10 @@
     @Test
     public void testRegisterListener_ExpiringAlarm() throws Exception {
         ILocationListener listener = createMockLocationListener();
-        LocationRequest request = new LocationRequest.Builder(0).setDurationMillis(5000).build();
+        LocationRequest request = new LocationRequest.Builder(0)
+                .setDurationMillis(5000)
+                .setWorkSource(WORK_SOURCE)
+                .build();
         mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
 
         mInjector.getAlarmHelper().incrementAlarmTime(5000);
@@ -504,7 +504,10 @@
     @Test
     public void testRegisterListener_ExpiringNoAlarm() throws Exception {
         ILocationListener listener = createMockLocationListener();
-        LocationRequest request = new LocationRequest.Builder(0).setDurationMillis(25).build();
+        LocationRequest request = new LocationRequest.Builder(0)
+                .setDurationMillis(25)
+                .setWorkSource(WORK_SOURCE)
+                .build();
         mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
 
         Thread.sleep(25);
@@ -517,8 +520,10 @@
     @Test
     public void testRegisterListener_FastestInterval() throws Exception {
         ILocationListener listener = createMockLocationListener();
-        LocationRequest request = new LocationRequest.Builder(5000).setMinUpdateIntervalMillis(
-                5000).build();
+        LocationRequest request = new LocationRequest.Builder(5000)
+                .setMinUpdateIntervalMillis(5000)
+                .setWorkSource(WORK_SOURCE)
+                .build();
         mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
 
         mProvider.setProviderLocation(createLocation(NAME, mRandom));
@@ -531,8 +536,10 @@
     @Test
     public void testRegisterListener_SmallestDisplacement() throws Exception {
         ILocationListener listener = createMockLocationListener();
-        LocationRequest request = new LocationRequest.Builder(5000).setMinUpdateDistanceMeters(
-                1f).build();
+        LocationRequest request = new LocationRequest.Builder(5000)
+                .setMinUpdateDistanceMeters(1f)
+                .setWorkSource(WORK_SOURCE)
+                .build();
         mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
 
         Location loc = createLocation(NAME, mRandom);
@@ -546,7 +553,7 @@
     @Test
     public void testRegisterListener_NoteOpFailure() throws Exception {
         ILocationListener listener = createMockLocationListener();
-        LocationRequest request = new LocationRequest.Builder(0).build();
+        LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
         mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
 
         mInjector.getAppOpsHelper().setAppOpAllowed(OP_FINE_LOCATION, IDENTITY.getPackageName(),
@@ -564,8 +571,11 @@
                 "attribution");
 
         ILocationListener listener = createMockLocationListener();
-        mManager.registerLocationRequest(new LocationRequest.Builder(0).build(), identity,
-                PERMISSION_FINE, listener);
+        mManager.registerLocationRequest(
+                new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(),
+                identity,
+                PERMISSION_FINE,
+                listener);
 
         CountDownLatch blocker = new CountDownLatch(1);
         IN_PROCESS_EXECUTOR.execute(() -> {
@@ -592,8 +602,8 @@
         ArgumentCaptor<Location> locationCaptor = ArgumentCaptor.forClass(Location.class);
 
         ILocationCallback listener = createMockGetCurrentLocationListener();
-        LocationRequest locationRequest = new LocationRequest.Builder(0).build();
-        mManager.getCurrentLocation(locationRequest, IDENTITY, PERMISSION_FINE, listener);
+        LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
+        mManager.getCurrentLocation(request, IDENTITY, PERMISSION_FINE, listener);
 
         Location loc = createLocation(NAME, mRandom);
         mProvider.setProviderLocation(loc);
@@ -606,8 +616,8 @@
     @Test
     public void testGetCurrentLocation_Cancel() throws Exception {
         ILocationCallback listener = createMockGetCurrentLocationListener();
-        LocationRequest locationRequest = new LocationRequest.Builder(0).build();
-        ICancellationSignal cancellationSignal = mManager.getCurrentLocation(locationRequest,
+        LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
+        ICancellationSignal cancellationSignal = mManager.getCurrentLocation(request,
                 IDENTITY, PERMISSION_FINE, listener);
 
         cancellationSignal.cancel();
@@ -619,8 +629,8 @@
     @Test
     public void testGetCurrentLocation_ProviderDisabled() throws Exception {
         ILocationCallback listener = createMockGetCurrentLocationListener();
-        LocationRequest locationRequest = new LocationRequest.Builder(0).build();
-        mManager.getCurrentLocation(locationRequest, IDENTITY, PERMISSION_FINE, listener);
+        LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
+        mManager.getCurrentLocation(request, IDENTITY, PERMISSION_FINE, listener);
 
         mProvider.setProviderAllowed(false);
         mProvider.setProviderAllowed(true);
@@ -633,8 +643,8 @@
         mProvider.setProviderAllowed(false);
 
         ILocationCallback listener = createMockGetCurrentLocationListener();
-        LocationRequest locationRequest = new LocationRequest.Builder(0).build();
-        mManager.getCurrentLocation(locationRequest, IDENTITY, PERMISSION_FINE, listener);
+        LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
+        mManager.getCurrentLocation(request, IDENTITY, PERMISSION_FINE, listener);
 
         mProvider.setProviderAllowed(true);
         mProvider.setProviderLocation(createLocation(NAME, mRandom));
@@ -649,8 +659,8 @@
         mProvider.setProviderLocation(loc);
 
         ILocationCallback listener = createMockGetCurrentLocationListener();
-        LocationRequest locationRequest = new LocationRequest.Builder(0).build();
-        mManager.getCurrentLocation(locationRequest, IDENTITY, PERMISSION_FINE, listener);
+        LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
+        mManager.getCurrentLocation(request, IDENTITY, PERMISSION_FINE, listener);
 
         verify(listener, times(1)).onLocation(locationCaptor.capture());
         assertThat(locationCaptor.getValue()).isEqualTo(loc);
@@ -659,8 +669,8 @@
     @Test
     public void testGetCurrentLocation_Timeout() throws Exception {
         ILocationCallback listener = createMockGetCurrentLocationListener();
-        LocationRequest locationRequest = new LocationRequest.Builder(0).build();
-        mManager.getCurrentLocation(locationRequest, IDENTITY, PERMISSION_FINE, listener);
+        LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
+        mManager.getCurrentLocation(request, IDENTITY, PERMISSION_FINE, listener);
 
         mInjector.getAlarmHelper().incrementAlarmTime(60000);
         verify(listener, times(1)).onLocation(isNull());
@@ -674,7 +684,7 @@
                 IDENTITY.getPackageName())).isFalse();
 
         ILocationListener listener = createMockLocationListener();
-        LocationRequest request = new LocationRequest.Builder(0).build();
+        LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
         mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
 
         assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_LOCATION,
@@ -700,26 +710,26 @@
     @Test
     public void testProviderRequest() {
         assertThat(mProvider.getRequest().isActive()).isFalse();
-        assertThat(mProvider.getRequest().getLocationRequests()).isEmpty();
 
         ILocationListener listener1 = createMockLocationListener();
-        LocationRequest request1 = new LocationRequest.Builder(5).build();
+        LocationRequest request1 = new LocationRequest.Builder(5).setWorkSource(
+                WORK_SOURCE).build();
         mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
 
         assertThat(mProvider.getRequest().isActive()).isTrue();
-        assertThat(mProvider.getRequest().getLocationRequests()).containsExactly(request1);
         assertThat(mProvider.getRequest().isLocationSettingsIgnored()).isFalse();
         assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5);
         assertThat(mProvider.getRequest().isLowPower()).isFalse();
         assertThat(mProvider.getRequest().getWorkSource()).isNotNull();
 
         ILocationListener listener2 = createMockLocationListener();
-        LocationRequest request2 = new LocationRequest.Builder(1).setLowPower(true).build();
+        LocationRequest request2 = new LocationRequest.Builder(1)
+                .setLowPower(true)
+                .setWorkSource(WORK_SOURCE)
+                .build();
         mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2);
 
         assertThat(mProvider.getRequest().isActive()).isTrue();
-        assertThat(mProvider.getRequest().getLocationRequests()).containsExactly(request1,
-                request2);
         assertThat(mProvider.getRequest().isLocationSettingsIgnored()).isFalse();
         assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(1);
         assertThat(mProvider.getRequest().isLowPower()).isFalse();
@@ -728,7 +738,6 @@
         mManager.unregisterLocationRequest(listener1);
 
         assertThat(mProvider.getRequest().isActive()).isTrue();
-        assertThat(mProvider.getRequest().getLocationRequests()).containsExactly(request2);
         assertThat(mProvider.getRequest().isLocationSettingsIgnored()).isFalse();
         assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(1);
         assertThat(mProvider.getRequest().isLowPower()).isTrue();
@@ -737,7 +746,6 @@
         mManager.unregisterLocationRequest(listener2);
 
         assertThat(mProvider.getRequest().isActive()).isFalse();
-        assertThat(mProvider.getRequest().getLocationRequests()).isEmpty();
     }
 
     @Test
@@ -745,7 +753,9 @@
         mProvider.setProviderLocation(createLocation(NAME, mRandom));
 
         ILocationListener listener1 = createMockLocationListener();
-        LocationRequest request1 = new LocationRequest.Builder(60000).build();
+        LocationRequest request1 = new LocationRequest.Builder(60000)
+                .setWorkSource(WORK_SOURCE)
+                .build();
         mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
 
         verify(listener1).onLocationChanged(any(Location.class), nullable(IRemoteCallback.class));
@@ -762,7 +772,9 @@
         mProvider.setProviderLocation(createLocation(NAME, mRandom));
 
         ILocationListener listener1 = createMockLocationListener();
-        LocationRequest request1 = new LocationRequest.Builder(60000).build();
+        LocationRequest request1 = new LocationRequest.Builder(60000)
+                .setWorkSource(WORK_SOURCE)
+                .build();
 
         mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
         assertThat(mProvider.getRequest().isActive()).isFalse();
@@ -777,7 +789,9 @@
     @Test
     public void testProviderRequest_BackgroundThrottle() {
         ILocationListener listener1 = createMockLocationListener();
-        LocationRequest request1 = new LocationRequest.Builder(5).build();
+        LocationRequest request1 = new LocationRequest.Builder(5)
+                .setWorkSource(WORK_SOURCE)
+                .build();
         mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
 
         assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5);
@@ -793,7 +807,9 @@
                 Collections.singleton(IDENTITY.getPackageName()));
 
         ILocationListener listener1 = createMockLocationListener();
-        LocationRequest request1 = new LocationRequest.Builder(5).build();
+        LocationRequest request1 = new LocationRequest.Builder(5)
+                .setWorkSource(WORK_SOURCE)
+                .build();
         mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
 
         assertThat(mProvider.getRequest().isActive()).isTrue();
@@ -801,8 +817,10 @@
         assertThat(mProvider.getRequest().isLocationSettingsIgnored()).isFalse();
 
         ILocationListener listener2 = createMockLocationListener();
-        LocationRequest request2 = new LocationRequest.Builder(1).setLocationSettingsIgnored(
-                true).build();
+        LocationRequest request2 = new LocationRequest.Builder(1)
+                .setLocationSettingsIgnored(true)
+                .setWorkSource(WORK_SOURCE)
+                .build();
         mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2);
 
         assertThat(mProvider.getRequest().isActive()).isTrue();
@@ -816,18 +834,21 @@
                 Collections.singleton(IDENTITY.getPackageName()));
 
         ILocationListener listener1 = createMockLocationListener();
-        LocationRequest request1 = new LocationRequest.Builder(1).build();
+        LocationRequest request1 = new LocationRequest.Builder(1)
+                .setWorkSource(WORK_SOURCE)
+                .build();
         mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
 
         ILocationListener listener2 = createMockLocationListener();
-        LocationRequest request2 = new LocationRequest.Builder(5).setLocationSettingsIgnored(
-                true).build();
+        LocationRequest request2 = new LocationRequest.Builder(5)
+                .setLocationSettingsIgnored(true)
+                .setWorkSource(WORK_SOURCE)
+                .build();
         mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2);
 
         mInjector.getSettingsHelper().setLocationEnabled(false, IDENTITY.getUserId());
 
         assertThat(mProvider.getRequest().isActive()).isTrue();
-        assertThat(mProvider.getRequest().getLocationRequests()).containsExactly(request2);
         assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5);
         assertThat(mProvider.getRequest().isLocationSettingsIgnored()).isTrue();
     }
@@ -838,8 +859,10 @@
                 Collections.singleton(IDENTITY.getPackageName()));
 
         ILocationListener listener = createMockLocationListener();
-        LocationRequest request = new LocationRequest.Builder(1).setLocationSettingsIgnored(
-                true).build();
+        LocationRequest request = new LocationRequest.Builder(1)
+                .setLocationSettingsIgnored(true)
+                .setWorkSource(WORK_SOURCE)
+                .build();
         mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
 
         mInjector.getSettingsHelper().setIgnoreSettingsPackageWhitelist(Collections.emptySet());
@@ -855,8 +878,10 @@
                 Collections.singleton(IDENTITY.getPackageName()));
 
         ILocationListener listener1 = createMockLocationListener();
-        LocationRequest request1 = new LocationRequest.Builder(5).setLocationSettingsIgnored(
-                true).build();
+        LocationRequest request1 = new LocationRequest.Builder(5)
+                .setLocationSettingsIgnored(true)
+                .setWorkSource(WORK_SOURCE)
+                .build();
         mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
 
         assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5);
@@ -871,7 +896,7 @@
                 LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF);
 
         ILocationListener listener = createMockLocationListener();
-        LocationRequest request = new LocationRequest.Builder(5).build();
+        LocationRequest request = new LocationRequest.Builder(5).setWorkSource(WORK_SOURCE).build();
         mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
 
         assertThat(mProvider.getRequest().isActive()).isTrue();
diff --git a/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java b/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java
index 968a402..3ace3f4 100644
--- a/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java
+++ b/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java
@@ -27,8 +27,6 @@
 import androidx.test.filters.MediumTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.server.BluetoothAirplaneModeListener.AirplaneModeHelper;
-
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
@@ -41,7 +39,7 @@
     private Context mContext;
     private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener;
     private BluetoothAdapter mBluetoothAdapter;
-    private AirplaneModeHelper mHelper;
+    private BluetoothModeChangeHelper mHelper;
 
     @Mock BluetoothManagerService mBluetoothManagerService;
 
@@ -49,7 +47,7 @@
     public void setUp() throws Exception {
         mContext = InstrumentationRegistry.getTargetContext();
 
-        mHelper = mock(AirplaneModeHelper.class);
+        mHelper = mock(BluetoothModeChangeHelper.class);
         when(mHelper.getSettingsInt(BluetoothAirplaneModeListener.TOAST_COUNT))
                 .thenReturn(BluetoothAirplaneModeListener.MAX_TOAST_COUNT);
         doNothing().when(mHelper).setSettingsInt(anyString(), anyInt());
diff --git a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
index c91bb93..d2d85c8 100644
--- a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
@@ -223,6 +223,25 @@
     }
 
     @Test
+    public void testInterceptPowerKeyDown_firstPowerDown_panicGestureNotLaunched() {
+        withPanicGestureEnabledSettingValue(true);
+        mGestureLauncherService.updatePanicButtonGestureEnabled();
+
+        long eventTime = INITIAL_EVENT_TIME_MILLIS
+                + GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS - 1;
+        KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+                IGNORED_REPEAT);
+        boolean interactive = true;
+        MutableBoolean outLaunched = new MutableBoolean(true);
+        boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+                outLaunched);
+
+        assertFalse(intercepted);
+        assertFalse(outLaunched.value);
+        verify(mMetricsLogger).histogram("power_double_tap_interval", (int) eventTime);
+    }
+
+    @Test
     public void testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOffInteractive() {
         withCameraDoubleTapPowerEnableConfigValue(false);
         withCameraDoubleTapPowerDisableSettingValue(1);
@@ -405,6 +424,200 @@
     }
 
     @Test
+    public void
+            testInterceptPowerKeyDown_fiveInboundPresses_cameraAndPanicEnabled_bothLaunch() {
+        withCameraDoubleTapPowerEnableConfigValue(true);
+        withCameraDoubleTapPowerDisableSettingValue(0);
+        withPanicGestureEnabledSettingValue(true);
+        mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+        mGestureLauncherService.updatePanicButtonGestureEnabled();
+        withUserSetupCompleteValue(true);
+
+        // First button press does nothing
+        long eventTime = INITIAL_EVENT_TIME_MILLIS;
+        KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+                IGNORED_REPEAT);
+        boolean interactive = true;
+        MutableBoolean outLaunched = new MutableBoolean(true);
+        boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+                outLaunched);
+        assertFalse(intercepted);
+        assertFalse(outLaunched.value);
+
+        final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+
+        // 2nd button triggers camera
+        eventTime += interval;
+        keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+                IGNORED_REPEAT);
+        outLaunched.value = false;
+        intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+                outLaunched);
+        assertTrue(intercepted);
+        assertTrue(outLaunched.value);
+
+        // Camera checks
+        verify(mStatusBarManagerInternal).onCameraLaunchGestureDetected(
+                StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP);
+        verify(mMetricsLogger)
+            .action(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE, (int) interval);
+
+        final ArgumentCaptor<Integer> cameraIntervalCaptor = ArgumentCaptor.forClass(Integer.class);
+        verify(mMetricsLogger, times(2)).histogram(
+                eq("power_double_tap_interval"), cameraIntervalCaptor.capture());
+        List<Integer> cameraIntervals = cameraIntervalCaptor.getAllValues();
+        assertEquals((int) INITIAL_EVENT_TIME_MILLIS, cameraIntervals.get(0).intValue());
+        assertEquals((int) interval, cameraIntervals.get(1).intValue());
+
+        final ArgumentCaptor<Integer> tapCountCaptor = ArgumentCaptor.forClass(Integer.class);
+        verify(mMetricsLogger, times(2)).histogram(
+                eq("power_consecutive_short_tap_count"), tapCountCaptor.capture());
+        List<Integer> tapCounts = tapCountCaptor.getAllValues();
+        assertEquals(1, tapCounts.get(0).intValue());
+        assertEquals(2, tapCounts.get(1).intValue());
+
+        // Continue the button presses for the panic gesture.
+
+        // Presses 3 and 4 should not trigger any gesture
+        for (int i = 0; i < 2; i++) {
+            eventTime += interval;
+            keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+                    IGNORED_REPEAT);
+            outLaunched.value = false;
+            intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+                    outLaunched);
+            assertTrue(intercepted);
+            assertFalse(outLaunched.value);
+        }
+
+        // Fifth button press should trigger the panic flow
+        eventTime += interval;
+        keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+                IGNORED_REPEAT);
+        outLaunched.value = false;
+        intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+                outLaunched);
+        assertTrue(intercepted);
+        assertTrue(outLaunched.value);
+
+        // TODO (b/169960245) Verify metric event equiv. to ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE
+        verify(mStatusBarManagerInternal).onEmergencyActionLaunchGestureDetected();
+
+        final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
+        verify(mMetricsLogger, times(5)).histogram(
+                eq("power_double_tap_interval"), intervalCaptor.capture());
+        List<Integer> intervals = intervalCaptor.getAllValues();
+        assertEquals((int) INITIAL_EVENT_TIME_MILLIS, intervals.get(0).intValue());
+        assertEquals((int) interval, intervals.get(1).intValue());
+    }
+
+    @Test
+    public void
+            testInterceptPowerKeyDown_fiveInboundPresses_panicGestureEnabled_launchesPanicFlow() {
+        withPanicGestureEnabledSettingValue(true);
+        mGestureLauncherService.updatePanicButtonGestureEnabled();
+        withUserSetupCompleteValue(true);
+
+        // First button press does nothing
+        long eventTime = INITIAL_EVENT_TIME_MILLIS;
+        KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+                IGNORED_REPEAT);
+        boolean interactive = true;
+        MutableBoolean outLaunched = new MutableBoolean(true);
+        boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+                outLaunched);
+        assertFalse(intercepted);
+        assertFalse(outLaunched.value);
+
+        final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+        // 3 more button presses which should not trigger any gesture (camera gesture disabled)
+        for (int i = 0; i < 3; i++) {
+            eventTime += interval;
+            keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+                    IGNORED_REPEAT);
+            outLaunched.value = false;
+            intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+                    outLaunched);
+            assertTrue(intercepted);
+            assertFalse(outLaunched.value);
+        }
+
+        // Fifth button press should trigger the panic flow
+        eventTime += interval;
+        keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+                IGNORED_REPEAT);
+        outLaunched.value = false;
+        intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+                outLaunched);
+        assertTrue(outLaunched.value);
+        assertTrue(intercepted);
+
+        // TODO (b/169960245) Verify metric event equiv. to ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE
+        verify(mStatusBarManagerInternal).onEmergencyActionLaunchGestureDetected();
+
+        final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
+        verify(mMetricsLogger, times(5)).histogram(
+                eq("power_double_tap_interval"), intervalCaptor.capture());
+        List<Integer> intervals = intervalCaptor.getAllValues();
+        assertEquals((int) INITIAL_EVENT_TIME_MILLIS, intervals.get(0).intValue());
+        assertEquals((int) interval, intervals.get(1).intValue());
+    }
+
+    @Test
+    public void
+            testInterceptPowerKeyDown_tenInboundPresses_panicGestureEnabled_pressesIntercepted() {
+        withPanicGestureEnabledSettingValue(true);
+        mGestureLauncherService.updatePanicButtonGestureEnabled();
+        withUserSetupCompleteValue(true);
+
+        // First button press does nothing
+        long eventTime = INITIAL_EVENT_TIME_MILLIS;
+        KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+                IGNORED_REPEAT);
+        boolean interactive = true;
+        MutableBoolean outLaunched = new MutableBoolean(true);
+        boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+                outLaunched);
+        assertFalse(intercepted);
+        assertFalse(outLaunched.value);
+
+        final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+        // 3 more button presses which should not trigger any gesture, but intercepts action.
+        for (int i = 0; i < 3; i++) {
+            eventTime += interval;
+            keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+                    IGNORED_REPEAT);
+            outLaunched.value = false;
+            intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+                    outLaunched);
+            assertTrue(intercepted);
+            assertFalse(outLaunched.value);
+        }
+
+        // Fifth button press should trigger the panic flow
+        eventTime += interval;
+        keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+                IGNORED_REPEAT);
+        outLaunched.value = false;
+        intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+                outLaunched);
+        assertTrue(outLaunched.value);
+        assertTrue(intercepted);
+
+        // 5 more button presses which should not trigger any gesture, but intercepts action.
+        for (int i = 0; i < 5; i++) {
+            eventTime += interval;
+            keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+                    IGNORED_REPEAT);
+            outLaunched.value = false;
+            intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+                    outLaunched);
+            assertTrue(intercepted);
+            assertFalse(outLaunched.value);
+        }
+    }
+
+    @Test
     public void testInterceptPowerKeyDown_longpress() {
         withCameraDoubleTapPowerEnableConfigValue(true);
         withCameraDoubleTapPowerDisableSettingValue(0);
diff --git a/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java
index 8a22a2f..044bdba 100644
--- a/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java
@@ -16,10 +16,16 @@
 
 package com.android.server;
 
+import static com.android.server.testutils.TestUtils.assertExpectException;
+
 import static org.junit.Assert.assertArrayEquals;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.os.CombinedVibrationEffect;
+import android.os.Process;
+import android.os.VibrationAttributes;
+import android.os.VibrationEffect;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.InstrumentationRegistry;
@@ -40,9 +46,16 @@
 @Presubmit
 public class VibratorManagerServiceTest {
 
-    @Rule public MockitoRule rule = MockitoJUnit.rule();
+    private static final int UID = Process.ROOT_UID;
+    private static final String PACKAGE_NAME = "package";
+    private static final VibrationAttributes ALARM_ATTRS =
+            new VibrationAttributes.Builder().setUsage(VibrationAttributes.USAGE_ALARM).build();
 
-    @Mock private VibratorManagerService.NativeWrapper mNativeWrapperMock;
+    @Rule
+    public MockitoRule rule = MockitoJUnit.rule();
+
+    @Mock
+    private VibratorManagerService.NativeWrapper mNativeWrapperMock;
 
     @Before
     public void setUp() throws Exception {
@@ -72,7 +85,26 @@
 
     @Test
     public void getVibratorIds_withNonEmptyResultFromNative_returnsSameArray() {
-        when(mNativeWrapperMock.getVibratorIds()).thenReturn(new int[]{ 1, 2 });
-        assertArrayEquals(new int[]{ 1, 2 }, createService().getVibratorIds());
+        when(mNativeWrapperMock.getVibratorIds()).thenReturn(new int[]{1, 2});
+        assertArrayEquals(new int[]{1, 2}, createService().getVibratorIds());
+    }
+
+    @Test
+    public void vibrate_isUnsupported() {
+        VibratorManagerService service = createService();
+        CombinedVibrationEffect effect = CombinedVibrationEffect.createSynced(
+                VibrationEffect.get(VibrationEffect.EFFECT_CLICK));
+        assertExpectException(UnsupportedOperationException.class,
+                "Not implemented",
+                () -> service.vibrate(UID, PACKAGE_NAME, effect, ALARM_ATTRS, "reason", service));
+    }
+
+    @Test
+    public void cancelVibrate_isUnsupported() {
+        VibratorManagerService service = createService();
+        CombinedVibrationEffect effect = CombinedVibrationEffect.createSynced(
+                VibrationEffect.get(VibrationEffect.EFFECT_CLICK));
+        assertExpectException(UnsupportedOperationException.class,
+                "Not implemented", () -> service.cancelVibrate(service));
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
index 8d4f2aa..8d60de9 100644
--- a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
@@ -24,6 +24,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.intThat;
 import static org.mockito.ArgumentMatchers.notNull;
@@ -36,11 +37,13 @@
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
+import android.app.AppOpsManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.pm.PackageManagerInternal;
 import android.hardware.vibrator.IVibrator;
+import android.media.AudioAttributes;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IVibratorStateListener;
@@ -104,6 +107,7 @@
     @Mock private PowerManagerInternal mPowerManagerInternalMock;
     @Mock private PowerSaveState mPowerSaveStateMock;
     @Mock private Vibrator mVibratorMock;
+    @Mock private AppOpsManager mAppOpsManagerMock;
     @Mock private VibratorService.NativeWrapper mNativeWrapperMock;
     @Mock private IVibratorStateListener mVibratorStateListenerMock;
     @Mock private IBinder mVibratorStateListenerBinderMock;
@@ -121,6 +125,7 @@
 
         when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
         when(mContextSpy.getSystemService(eq(Context.VIBRATOR_SERVICE))).thenReturn(mVibratorMock);
+        when(mContextSpy.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManagerMock);
         when(mVibratorMock.getDefaultHapticFeedbackIntensity())
                 .thenReturn(Vibrator.VIBRATION_INTENSITY_MEDIUM);
         when(mVibratorMock.getDefaultNotificationVibrationIntensity())
@@ -133,6 +138,14 @@
         when(mPowerManagerInternalMock.getLowPowerState(PowerManager.ServiceType.VIBRATION))
                 .thenReturn(mPowerSaveStateMock);
 
+        setVibrationIntensityUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
+        setVibrationIntensityUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
+                Vibrator.VIBRATION_INTENSITY_MEDIUM);
+        setVibrationIntensityUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
+                Vibrator.VIBRATION_INTENSITY_MEDIUM);
+        setVibrationIntensityUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
+                Vibrator.VIBRATION_INTENSITY_MEDIUM);
+
         addLocalServiceMock(PackageManagerInternal.class, mPackageManagerInternalMock);
         addLocalServiceMock(PowerManagerInternal.class, mPowerManagerInternalMock);
         FakeSettingsProvider.clearSettingsProvider();
@@ -286,6 +299,55 @@
     }
 
     @Test
+    public void vibrate_withAudioAttributes_usesOriginalAudioUsageInAppOpsManager() {
+        VibratorService service = createService();
+        service.systemReady();
+
+        VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
+        AudioAttributes audioAttributes = new AudioAttributes.Builder()
+                .setUsage(AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY).build();
+        VibrationAttributes vibrationAttributes = new VibrationAttributes.Builder(
+                audioAttributes, effect).build();
+
+        vibrate(service, effect, vibrationAttributes);
+
+        verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
+                eq(AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY), anyInt(), anyString());
+    }
+
+    @Test
+    public void vibrate_withVibrationAttributes_usesCorrespondingAudioUsageInAppOpsManager() {
+        VibratorService service = createService();
+        service.systemReady();
+
+        vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), ALARM_ATTRS);
+        vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_TICK), NOTIFICATION_ATTRS);
+        vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), RINGTONE_ATTRS);
+        vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_TICK), HAPTIC_FEEDBACK_ATTRS);
+        vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
+                new VibrationAttributes.Builder().setUsage(
+                        VibrationAttributes.USAGE_COMMUNICATION_REQUEST).build());
+        vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_TICK),
+                new VibrationAttributes.Builder().setUsage(
+                        VibrationAttributes.USAGE_UNKNOWN).build());
+
+        InOrder inOrderVerifier = inOrder(mAppOpsManagerMock);
+        inOrderVerifier.verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
+                eq(AudioAttributes.USAGE_ALARM), anyInt(), anyString());
+        inOrderVerifier.verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
+                eq(AudioAttributes.USAGE_NOTIFICATION), anyInt(), anyString());
+        inOrderVerifier.verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
+                eq(AudioAttributes.USAGE_NOTIFICATION_RINGTONE), anyInt(), anyString());
+        inOrderVerifier.verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
+                eq(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION), anyInt(), anyString());
+        inOrderVerifier.verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
+                eq(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST),
+                anyInt(), anyString());
+        inOrderVerifier.verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
+                eq(AudioAttributes.USAGE_UNKNOWN), anyInt(), anyString());
+    }
+
+    @Test
     public void vibrate_withOneShotAndAmplitudeControl_turnsVibratorOnAndSetsAmplitude() {
         mockVibratorCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
         VibratorService service = createService();
@@ -367,7 +429,7 @@
 
         // Wait for VibrateThread to turn vibrator ON with total timing and no callback.
         Thread.sleep(5);
-        verify(mNativeWrapperMock).vibratorOn(eq(30L), eq(0L));
+        verify(mNativeWrapperMock).vibratorOn(eq(30L), anyLong());
 
         // First amplitude set right away.
         verify(mNativeWrapperMock).vibratorSetAmplitude(eq(100));
@@ -434,8 +496,8 @@
         Thread.sleep(15);
         InOrder inOrderVerifier = inOrder(mNativeWrapperMock);
         inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
-        inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(3L), eq(0L));
-        inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(2L), eq(0L));
+        inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(3L), anyLong());
+        inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(2L), anyLong());
         inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
     }
 
@@ -637,6 +699,11 @@
         vibrate(service, effect, ALARM_ATTRS);
     }
 
+    private void vibrate(VibratorService service, VibrationEffect effect, AudioAttributes attrs) {
+        VibrationAttributes attributes = new VibrationAttributes.Builder(attrs, effect).build();
+        vibrate(service, effect, attributes);
+    }
+
     private void vibrate(VibratorService service, VibrationEffect effect,
             VibrationAttributes attributes) {
         service.vibrate(UID, PACKAGE_NAME, effect, attributes, "some reason", service);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
index 6acd9b6..9ba9188 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.accessibility.magnification;
 
+import static com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationRequestObserver;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertThat;
@@ -93,6 +95,10 @@
     final Context mMockContext = mock(Context.class);
     final AccessibilityManagerService mMockAms = mock(AccessibilityManagerService.class);
     final WindowManagerInternal mMockWindowManager = mock(WindowManagerInternal.class);
+    private final MagnificationAnimationCallback mAnimationCallback = mock(
+            MagnificationAnimationCallback.class);
+    private final MagnificationRequestObserver mRequestObserver = mock(
+            MagnificationRequestObserver.class);
     final MessageCapturingHandler mMessageCapturingHandler = new MessageCapturingHandler(null);
 
     ValueAnimator mMockValueAnimator;
@@ -100,12 +106,10 @@
     ValueAnimator.AnimatorListener mStateListener;
 
     FullScreenMagnificationController mFullScreenMagnificationController;
-    MagnificationAnimationCallback mAnimationCallback;
 
     @Before
     public void setUp() {
         Looper looper = InstrumentationRegistry.getContext().getMainLooper();
-        mAnimationCallback = Mockito.mock(MagnificationAnimationCallback.class);
         // Pretending ID of the Thread associated with looper as main thread ID in controller
         when(mMockContext.getMainLooper()).thenReturn(looper);
         when(mMockControllerCtx.getContext()).thenReturn(mMockContext);
@@ -116,7 +120,7 @@
         initMockWindowManager();
 
         mFullScreenMagnificationController = new FullScreenMagnificationController(
-                mMockControllerCtx, new Object());
+                mMockControllerCtx, new Object(), mRequestObserver);
     }
 
     @After
@@ -345,6 +349,7 @@
         verify(mMockAms).notifyMagnificationChanged(displayId,
                 INITIAL_MAGNIFICATION_REGION, scale, newCenter.x, newCenter.y);
         verify(mMockValueAnimator).start();
+        verify(mRequestObserver).onRequestMagnificationSpec(displayId, SERVICE_ID_1);
 
         // Initial value
         when(mMockValueAnimator.getAnimatedFraction()).thenReturn(0.0f);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index 008cbed..1cdd873 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -53,6 +53,7 @@
 
 import com.android.server.accessibility.AccessibilityManagerService;
 import com.android.server.accessibility.EventStreamTransformation;
+import com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationRequestObserver;
 import com.android.server.accessibility.magnification.MagnificationGestureHandler.ScaleChangedListener;
 import com.android.server.testutils.OffsettableClock;
 import com.android.server.testutils.TestHandler;
@@ -126,6 +127,8 @@
     FullScreenMagnificationController mFullScreenMagnificationController;
     @Mock
     ScaleChangedListener mMockScaleChangedListener;
+    @Mock
+    MagnificationRequestObserver mMagnificationRequestObserver;
 
     private OffsettableClock mClock;
     private FullScreenMagnificationGestureHandler mMgh;
@@ -148,7 +151,7 @@
         when(mockController.getAnimationDuration()).thenReturn(1000L);
         when(mockWindowManager.setMagnificationCallbacks(eq(DISPLAY_0), any())).thenReturn(true);
         mFullScreenMagnificationController = new FullScreenMagnificationController(mockController,
-                new Object()) {
+                new Object(), mMagnificationRequestObserver) {
             @Override
             public boolean magnificationRegionContains(int displayId, float x, float y) {
                 return true;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index 3e34f8a..e82ff34 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -250,6 +250,17 @@
         verify(mScreenMagnificationController).setUserId(CURRENT_USER_ID);
         verify(mWindowMagnificationManager).setUserId(CURRENT_USER_ID);
     }
+
+    @Test
+    public void onMagnificationRequest_windowMagnifying_disableWindow() throws RemoteException {
+        setMagnificationEnabled(MODE_WINDOW);
+
+        mMagnificationController.onRequestMagnificationSpec(TEST_DISPLAY, 1);
+        mMockConnection.invokeCallbacks();
+
+        assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+    }
+
     private void setMagnificationEnabled(int mode) throws RemoteException {
 
         setMagnificationEnabled(mode, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y);
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
index d57fd4b..3b4699e 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
@@ -62,6 +62,8 @@
                 .getContext();
         doReturn(mServiceThreadRule.getThread().getThreadHandler()).when(mMockInjector)
                 .getUiHandler(any());
+        final ProcessList dummyList = new ProcessList();
+        doReturn(dummyList).when(mMockInjector).getProcessList(any());
         mAms = new ActivityManagerService(mMockInjector, mServiceThreadRule.getThread());
         mAmi = mAms.new LocalService();
     }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 03dce4c..241b5a9 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -76,6 +76,7 @@
 import com.android.server.LocalServices;
 import com.android.server.am.ProcessList.IsolatedUidRange;
 import com.android.server.am.ProcessList.IsolatedUidRangeAllocator;
+import com.android.server.am.UidObserverController.ChangeRecord;
 import com.android.server.appop.AppOpsService;
 import com.android.server.wm.ActivityTaskManagerService;
 
@@ -539,15 +540,15 @@
             ActivityManager.PROCESS_STATE_CACHED_ACTIVITY,
             ActivityManager.PROCESS_STATE_TOP
         };
-        final Map<Integer, UidRecord.ChangeItem> changeItems = new HashMap<>();
+        final Map<Integer, ChangeRecord> changeItems = new HashMap<>();
         for (int i = 0; i < changesForPendingUidRecords.length; ++i) {
-            final UidRecord.ChangeItem pendingChange = new UidRecord.ChangeItem();
+            final ChangeRecord pendingChange = new ChangeRecord();
             pendingChange.change = changesForPendingUidRecords[i];
             pendingChange.uid = i;
-            pendingChange.processState = procStatesForPendingUidRecords[i];
+            pendingChange.procState = procStatesForPendingUidRecords[i];
             pendingChange.procStateSeq = i;
             changeItems.put(changesForPendingUidRecords[i], pendingChange);
-            mAms.mUidObserverController.mPendingUidChanges.add(pendingChange);
+            addPendingUidChange(pendingChange);
         }
 
         mAms.mUidObserverController.dispatchUidsChanged();
@@ -602,7 +603,7 @@
                 verifyObserverReceivedChanges(observerToTest, changesToVerify, changeItems,
                         (observer, changeItem) -> {
                             verify(observer).onUidStateChanged(changeItem.uid,
-                                    changeItem.processState, changeItem.procStateSeq,
+                                    changeItem.procState, changeItem.procStateSeq,
                                     ActivityManager.PROCESS_CAPABILITY_NONE);
                         });
             }
@@ -612,14 +613,14 @@
     }
 
     private interface ObserverChangesVerifier {
-        void verify(IUidObserver observer, UidRecord.ChangeItem changeItem) throws RemoteException;
+        void verify(IUidObserver observer, ChangeRecord changeItem) throws RemoteException;
     }
 
     private void verifyObserverReceivedChanges(IUidObserver observer, int[] changesToVerify,
-            Map<Integer, UidRecord.ChangeItem> changeItems, ObserverChangesVerifier verifier)
+            Map<Integer, ChangeRecord> changeItems, ObserverChangesVerifier verifier)
             throws RemoteException {
         for (int change : changesToVerify) {
-            final UidRecord.ChangeItem changeItem = changeItems.get(change);
+            final ChangeRecord changeItem = changeItems.get(change);
             verifier.verify(observer, changeItem);
         }
     }
@@ -641,59 +642,59 @@
         // So, resetting the mock here.
         Mockito.reset(observer);
 
-        final UidRecord.ChangeItem changeItem = new UidRecord.ChangeItem();
+        final ChangeRecord changeItem = new ChangeRecord();
         changeItem.uid = TEST_UID;
         changeItem.change = UidRecord.CHANGE_PROCSTATE;
-        changeItem.processState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
+        changeItem.procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
         changeItem.procStateSeq = 111;
-        mAms.mUidObserverController.mPendingUidChanges.add(changeItem);
+        addPendingUidChange(changeItem);
         mAms.mUidObserverController.dispatchUidsChanged();
         // First process state message is always delivered regardless of whether the process state
         // change is above or below the cutpoint (PROCESS_STATE_SERVICE).
         verify(observer).onUidStateChanged(TEST_UID,
-                changeItem.processState, changeItem.procStateSeq,
+                changeItem.procState, changeItem.procStateSeq,
                 ActivityManager.PROCESS_CAPABILITY_NONE);
         verifyNoMoreInteractions(observer);
 
-        changeItem.processState = ActivityManager.PROCESS_STATE_RECEIVER;
-        mAms.mUidObserverController.mPendingUidChanges.add(changeItem);
+        changeItem.procState = ActivityManager.PROCESS_STATE_RECEIVER;
+        addPendingUidChange(changeItem);
         mAms.mUidObserverController.dispatchUidsChanged();
         // Previous process state change is below cutpoint (PROCESS_STATE_SERVICE) and
         // the current process state change is also below cutpoint, so no callback will be invoked.
         verifyNoMoreInteractions(observer);
 
-        changeItem.processState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
-        mAms.mUidObserverController.mPendingUidChanges.add(changeItem);
+        changeItem.procState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+        addPendingUidChange(changeItem);
         mAms.mUidObserverController.dispatchUidsChanged();
         // Previous process state change is below cutpoint (PROCESS_STATE_SERVICE) and
         // the current process state change is above cutpoint, so callback will be invoked with the
         // current process state change.
         verify(observer).onUidStateChanged(TEST_UID,
-                changeItem.processState, changeItem.procStateSeq,
+                changeItem.procState, changeItem.procStateSeq,
                 ActivityManager.PROCESS_CAPABILITY_NONE);
         verifyNoMoreInteractions(observer);
 
-        changeItem.processState = ActivityManager.PROCESS_STATE_TOP;
-        mAms.mUidObserverController.mPendingUidChanges.add(changeItem);
+        changeItem.procState = ActivityManager.PROCESS_STATE_TOP;
+        addPendingUidChange(changeItem);
         mAms.mUidObserverController.dispatchUidsChanged();
         // Previous process state change is above cutpoint (PROCESS_STATE_SERVICE) and
         // the current process state change is also above cutpoint, so no callback will be invoked.
         verifyNoMoreInteractions(observer);
 
-        changeItem.processState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
-        mAms.mUidObserverController.mPendingUidChanges.add(changeItem);
+        changeItem.procState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+        addPendingUidChange(changeItem);
         mAms.mUidObserverController.dispatchUidsChanged();
         // Previous process state change is above cutpoint (PROCESS_STATE_SERVICE) and
         // the current process state change is below cutpoint, so callback will be invoked with the
         // current process state change.
         verify(observer).onUidStateChanged(TEST_UID,
-                changeItem.processState, changeItem.procStateSeq,
+                changeItem.procState, changeItem.procStateSeq,
                 ActivityManager.PROCESS_CAPABILITY_NONE);
         verifyNoMoreInteractions(observer);
     }
 
     /**
-     * This test verifies that {@link ActivityManagerService#mValidateUids} which is a
+     * This test verifies that {@link UidObserverController#getValidateUidsForTest()} which is a
      * part of dumpsys is correctly updated.
      */
     @Test
@@ -707,45 +708,45 @@
             ActivityManager.PROCESS_STATE_SERVICE,
             ActivityManager.PROCESS_STATE_RECEIVER
         };
-        final ArrayList<UidRecord.ChangeItem> pendingItemsForUids =
+        final ArrayList<ChangeRecord> pendingItemsForUids =
                 new ArrayList<>(changesForPendingItems.length);
         for (int i = 0; i < changesForPendingItems.length; ++i) {
-            final UidRecord.ChangeItem item = new UidRecord.ChangeItem();
+            final ChangeRecord item = new ChangeRecord();
             item.uid = i;
             item.change = changesForPendingItems[i];
-            item.processState = procStatesForPendingItems[i];
+            item.procState = procStatesForPendingItems[i];
             pendingItemsForUids.add(i, item);
         }
 
         // Verify that when there no observers listening to uid state changes, then there will
         // be no changes to validateUids.
-        mAms.mUidObserverController.mPendingUidChanges.addAll(pendingItemsForUids);
+        addPendingUidChanges(pendingItemsForUids);
         mAms.mUidObserverController.dispatchUidsChanged();
         assertEquals("No observers registered, so validateUids should be empty",
-                0, mAms.mUidObserverController.mValidateUids.size());
+                0, mAms.mUidObserverController.getValidateUidsForTest().size());
 
         final IUidObserver observer = mock(IUidObserver.Stub.class);
         when(observer.asBinder()).thenReturn((IBinder) observer);
         mAms.registerUidObserver(observer, 0, 0, null);
         // Verify that when observers are registered, then validateUids is correctly updated.
-        mAms.mUidObserverController.mPendingUidChanges.addAll(pendingItemsForUids);
+        addPendingUidChanges(pendingItemsForUids);
         mAms.mUidObserverController.dispatchUidsChanged();
         for (int i = 0; i < pendingItemsForUids.size(); ++i) {
-            final UidRecord.ChangeItem item = pendingItemsForUids.get(i);
+            final ChangeRecord item = pendingItemsForUids.get(i);
             final UidRecord validateUidRecord =
-                    mAms.mUidObserverController.mValidateUids.get(item.uid);
+                    mAms.mUidObserverController.getValidateUidsForTest().get(item.uid);
             if ((item.change & UidRecord.CHANGE_GONE) != 0) {
                 assertNull("validateUidRecord should be null since the change is either "
                         + "CHANGE_GONE or CHANGE_GONE_IDLE", validateUidRecord);
             } else {
                 assertNotNull("validateUidRecord should not be null since the change is neither "
                         + "CHANGE_GONE nor CHANGE_GONE_IDLE", validateUidRecord);
-                assertEquals("processState: " + item.processState + " curProcState: "
+                assertEquals("processState: " + item.procState + " curProcState: "
                         + validateUidRecord.getCurProcState() + " should have been equal",
-                        item.processState, validateUidRecord.getCurProcState());
-                assertEquals("processState: " + item.processState + " setProcState: "
+                        item.procState, validateUidRecord.getCurProcState());
+                assertEquals("processState: " + item.procState + " setProcState: "
                         + validateUidRecord.getCurProcState() + " should have been equal",
-                        item.processState, validateUidRecord.setProcState);
+                        item.procState, validateUidRecord.setProcState);
                 if (item.change == UidRecord.CHANGE_IDLE) {
                     assertTrue("UidRecord.idle should be updated to true for CHANGE_IDLE",
                             validateUidRecord.idle);
@@ -759,19 +760,19 @@
         // Verify that when uid state changes to CHANGE_GONE or CHANGE_GONE_IDLE, then it
         // will be removed from validateUids.
         assertNotEquals("validateUids should not be empty", 0,
-                mAms.mUidObserverController.mValidateUids.size());
+                mAms.mUidObserverController.getValidateUidsForTest().size());
         for (int i = 0; i < pendingItemsForUids.size(); ++i) {
-            final UidRecord.ChangeItem item = pendingItemsForUids.get(i);
+            final ChangeRecord item = pendingItemsForUids.get(i);
             // Assign CHANGE_GONE_IDLE to some items and CHANGE_GONE to the others, using even/odd
             // distribution for this assignment.
             item.change = (i % 2) == 0 ? (UidRecord.CHANGE_GONE | UidRecord.CHANGE_IDLE)
                     : UidRecord.CHANGE_GONE;
         }
-        mAms.mUidObserverController.mPendingUidChanges.addAll(pendingItemsForUids);
+        addPendingUidChanges(pendingItemsForUids);
         mAms.mUidObserverController.dispatchUidsChanged();
         assertEquals("validateUids should be empty, size="
-                + mAms.mUidObserverController.mValidateUids.size(),
-                        0, mAms.mUidObserverController.mValidateUids.size());
+                + mAms.mUidObserverController.getValidateUidsForTest().size(),
+                        0, mAms.mUidObserverController.getValidateUidsForTest().size());
     }
 
     @Test
@@ -784,8 +785,6 @@
 
         // Add a pending change for TEST_UID and verify enqueueUidChangeLocked still works as
         // expected.
-        final UidRecord.ChangeItem changeItem = new UidRecord.ChangeItem();
-        uidRecord.pendingChange = changeItem;
         uidRecord.curProcStateSeq = TEST_PROC_STATE_SEQ2;
         verifyLastProcStateSeqUpdated(uidRecord, -1, TEST_PROC_STATE_SEQ2);
     }
@@ -793,7 +792,7 @@
     @Test
     public void testEnqueueUidChangeLocked_nullUidRecord() {
         // Use "null" uidRecord to make sure there is no crash.
-        mAms.mUidObserverController.enqueueUidChangeLocked(null, TEST_UID, UidRecord.CHANGE_ACTIVE);
+        mAms.enqueueUidChangeLocked(null, TEST_UID, UidRecord.CHANGE_ACTIVE);
     }
 
     private void verifyLastProcStateSeqUpdated(UidRecord uidRecord, int uid, long curProcstateSeq) {
@@ -802,7 +801,7 @@
             final int changeToDispatch = UID_RECORD_CHANGES[i];
             // Reset lastProcStateSeqDispatchToObservers after every test.
             uidRecord.lastDispatchedProcStateSeq = 0;
-            mAms.mUidObserverController.enqueueUidChangeLocked(uidRecord, uid, changeToDispatch);
+            mAms.enqueueUidChangeLocked(uidRecord, uid, changeToDispatch);
             // Verify there is no effect on curProcStateSeq.
             assertEquals(curProcstateSeq, uidRecord.curProcStateSeq);
             if ((changeToDispatch & UidRecord.CHANGE_GONE) != 0) {
@@ -833,16 +832,17 @@
 
             // Reset the current state
             mHandler.reset();
-            uidRecord.pendingChange = null;
-            mAms.mUidObserverController.mPendingUidChanges.clear();
+            clearPendingUidChanges();
+            uidRecord.pendingChange.isPending = false;
 
-            mAms.mUidObserverController.enqueueUidChangeLocked(uidRecord, -1, changeToDispatch);
+            mAms.enqueueUidChangeLocked(uidRecord, -1, changeToDispatch);
 
-            // Verify that UidRecord.pendingChange is updated correctly.
-            assertNotNull(uidRecord.pendingChange);
-            assertEquals(TEST_UID, uidRecord.pendingChange.uid);
-            assertEquals(expectedProcState, uidRecord.pendingChange.processState);
-            assertEquals(TEST_PROC_STATE_SEQ1, uidRecord.pendingChange.procStateSeq);
+            // Verify that pendingChange is updated correctly.
+            final ChangeRecord pendingChange = uidRecord.pendingChange;
+            assertTrue(pendingChange.isPending);
+            assertEquals(TEST_UID, pendingChange.uid);
+            assertEquals(expectedProcState, pendingChange.procState);
+            assertEquals(TEST_PROC_STATE_SEQ1, pendingChange.procStateSeq);
 
             // TODO: Verify that DISPATCH_UIDS_CHANGED_UI_MSG is posted to handler.
         }
@@ -923,6 +923,23 @@
         mAms.mProcessList.mActiveUids.clear();
     }
 
+    private void addPendingUidChange(ChangeRecord record) {
+        mAms.mUidObserverController.getPendingUidChangesForTest().add(record);
+    }
+
+    private void addPendingUidChanges(ArrayList<ChangeRecord> changes) {
+        final ArrayList<ChangeRecord> pendingChanges =
+                mAms.mUidObserverController.getPendingUidChangesForTest();
+        for (int i = 0; i < changes.size(); ++i) {
+            final ChangeRecord record = changes.get(i);
+            pendingChanges.add(record);
+        }
+    }
+
+    private void clearPendingUidChanges() {
+        mAms.mUidObserverController.getPendingUidChangesForTest().clear();
+    }
+
     private static class TestHandler extends Handler {
         private static final long WAIT_FOR_MSG_TIMEOUT_MS = 4000; // 4 sec
         private static final long WAIT_FOR_MSG_INTERVAL_MS = 400; // 0.4 sec
diff --git a/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java b/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java
index 4221575..693bc7d 100644
--- a/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java
@@ -23,7 +23,7 @@
 import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
@@ -68,6 +68,7 @@
 
     private ActivityManagerService mAms;
     @Mock private Context mContext;
+    @Mock private Resources mResources;
 
     private MockContentResolver mContentResolver;
     private CoreSettingsObserver mCoreSettingsObserver;
@@ -94,7 +95,10 @@
         mContentResolver = new MockContentResolver(mContext);
         mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
         when(mContext.getContentResolver()).thenReturn(mContentResolver);
-        when(mContext.getResources()).thenReturn(mock(Resources.class));
+        when(mContext.getResources()).thenReturn(mResources);
+        // To prevent NullPointerException at the constructor of ActivityManagerConstants.
+        when(mResources.getStringArray(anyInt())).thenReturn(new String[0]);
+        when(mResources.getIntArray(anyInt())).thenReturn(new int[0]);
 
         mAms = new ActivityManagerService(new TestInjector(mContext),
                 mServiceThreadRule.getThread());
diff --git a/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java
new file mode 100644
index 0000000..1de5f6f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL;
+import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION;
+import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE;
+import static android.app.ActivityManager.PROCESS_STATE_CACHED_RECENT;
+import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND;
+import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+import static android.app.ActivityManager.PROCESS_STATE_RECEIVER;
+import static android.app.ActivityManager.PROCESS_STATE_SERVICE;
+import static android.app.ActivityManager.PROCESS_STATE_TOP;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.app.IUidObserver;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.DebugUtils;
+import android.util.Pair;
+import android.util.SparseArray;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.am.UidObserverController.ChangeRecord;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+
+@SmallTest
+public class UidObserverControllerTest {
+    private static final int TEST_UID1 = 1111;
+    private static final int TEST_UID2 = 2222;
+    private static final int TEST_UID3 = 3333;
+
+    private static final String TEST_PKG1 = "com.example1";
+    private static final String TEST_PKG2 = "com.example2";
+    private static final String TEST_PKG3 = "com.example3";
+
+    private UidObserverController mUidObserverController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mUidObserverController = new UidObserverController(mock(Handler.class));
+    }
+
+    @Test
+    public void testEnqueueUidChange() {
+        int change = mUidObserverController.enqueueUidChange(null, TEST_UID1,
+                UidRecord.CHANGE_ACTIVE, PROCESS_STATE_FOREGROUND_SERVICE,
+                PROCESS_CAPABILITY_ALL, 0, false);
+        assertEquals("expected=ACTIVE,actual=" + changeToStr(change),
+                UidRecord.CHANGE_ACTIVE, change);
+        assertPendingChange(TEST_UID1, UidRecord.CHANGE_ACTIVE, PROCESS_STATE_FOREGROUND_SERVICE,
+                PROCESS_CAPABILITY_ALL, 0, false, null);
+        final ChangeRecord record1 = getLatestPendingChange(TEST_UID1);
+        assertNull(getLatestPendingChange(TEST_UID2));
+
+        final ChangeRecord record2 = new ChangeRecord();
+        change = mUidObserverController.enqueueUidChange(record2, TEST_UID2,
+                UidRecord.CHANGE_CACHED, PROCESS_STATE_CACHED_RECENT, PROCESS_CAPABILITY_NONE,
+                99, true);
+        assertEquals("expected=ACTIVE,actual=" + changeToStr(change),
+                UidRecord.CHANGE_CACHED, change);
+        assertPendingChange(TEST_UID1, UidRecord.CHANGE_ACTIVE, PROCESS_STATE_FOREGROUND_SERVICE,
+                PROCESS_CAPABILITY_ALL, 0, false, null);
+        assertPendingChange(TEST_UID2, UidRecord.CHANGE_CACHED, PROCESS_STATE_CACHED_RECENT,
+                PROCESS_CAPABILITY_NONE, 99, true, record2);
+
+        change = mUidObserverController.enqueueUidChange(record1, TEST_UID1,
+                UidRecord.CHANGE_UNCACHED, PROCESS_STATE_TOP, PROCESS_CAPABILITY_ALL, 0, false);
+        assertEquals("expected=ACTIVE|UNCACHED,actual=" + changeToStr(change),
+                UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_UNCACHED, change);
+        assertPendingChange(TEST_UID1, UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_UNCACHED,
+                PROCESS_STATE_TOP, PROCESS_CAPABILITY_ALL, 0, false, record1);
+        assertPendingChange(TEST_UID2, UidRecord.CHANGE_CACHED, PROCESS_STATE_CACHED_RECENT,
+                PROCESS_CAPABILITY_NONE, 99, true, record2);
+    }
+
+    @Test
+    public void testMergeWithPendingChange() {
+        final SparseArray<Pair<Integer, Integer>> changesToVerify = new SparseArray<>();
+
+        changesToVerify.put(UidRecord.CHANGE_ACTIVE,
+                Pair.create(UidRecord.CHANGE_ACTIVE, UidRecord.CHANGE_IDLE));
+        changesToVerify.put(UidRecord.CHANGE_IDLE,
+                Pair.create(UidRecord.CHANGE_IDLE, UidRecord.CHANGE_ACTIVE));
+        changesToVerify.put(UidRecord.CHANGE_CACHED,
+                Pair.create(UidRecord.CHANGE_CACHED, UidRecord.CHANGE_UNCACHED));
+        changesToVerify.put(UidRecord.CHANGE_UNCACHED,
+                Pair.create(UidRecord.CHANGE_UNCACHED, UidRecord.CHANGE_CACHED));
+        changesToVerify.put(UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_UNCACHED,
+                Pair.create(UidRecord.CHANGE_ACTIVE, UidRecord.CHANGE_UNCACHED));
+        changesToVerify.put(UidRecord.CHANGE_IDLE | UidRecord.CHANGE_CACHED,
+                Pair.create(UidRecord.CHANGE_IDLE, UidRecord.CHANGE_CACHED));
+        changesToVerify.put(UidRecord.CHANGE_GONE,
+                Pair.create(UidRecord.CHANGE_GONE, UidRecord.CHANGE_ACTIVE));
+        changesToVerify.put(UidRecord.CHANGE_GONE,
+                Pair.create(UidRecord.CHANGE_GONE, UidRecord.CHANGE_CACHED));
+
+        for (int i = 0; i < changesToVerify.size(); ++i) {
+            final int expectedChange = changesToVerify.keyAt(i);
+            final int currentChange = changesToVerify.valueAt(i).first;
+            final int pendingChange = changesToVerify.valueAt(i).second;
+            assertEquals("current=" + changeToStr(currentChange) + ", pending="
+                            + changeToStr(pendingChange) + "exp=" + changeToStr(expectedChange),
+                    expectedChange, UidObserverController.mergeWithPendingChange(
+                    currentChange, pendingChange));
+        }
+    }
+
+    @Test
+    public void testDispatchUidsChanged() throws RemoteException {
+        addPendingChange(TEST_UID1, UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_PROCSTATE,
+                PROCESS_STATE_TOP, 0, PROCESS_CAPABILITY_ALL, false);
+
+        final IUidObserver observer1 = mock(IUidObserver.Stub.class);
+        registerObserver(observer1,
+                ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_ACTIVE,
+                PROCESS_STATE_IMPORTANT_FOREGROUND, TEST_PKG2, TEST_UID2);
+        final IUidObserver observer2 = mock(IUidObserver.Stub.class);
+        registerObserver(observer2, ActivityManager.UID_OBSERVER_PROCSTATE,
+                PROCESS_STATE_SERVICE, TEST_PKG3, TEST_UID3);
+
+        mUidObserverController.dispatchUidsChanged();
+        verify(observer1).onUidStateChanged(TEST_UID1, PROCESS_STATE_TOP,
+                0, PROCESS_CAPABILITY_ALL);
+        verify(observer1).onUidActive(TEST_UID1);
+        verifyNoMoreInteractions(observer1);
+        verify(observer2).onUidStateChanged(TEST_UID1, PROCESS_STATE_TOP,
+                0, PROCESS_CAPABILITY_ALL);
+        verifyNoMoreInteractions(observer2);
+
+        addPendingChange(TEST_UID1, UidRecord.CHANGE_PROCSTATE, PROCESS_STATE_IMPORTANT_BACKGROUND,
+                99, PROCESS_CAPABILITY_FOREGROUND_LOCATION, false);
+        mUidObserverController.dispatchUidsChanged();
+        verify(observer1).onUidStateChanged(TEST_UID1, PROCESS_STATE_IMPORTANT_BACKGROUND,
+                99, PROCESS_CAPABILITY_FOREGROUND_LOCATION);
+        verifyNoMoreInteractions(observer1);
+        verifyNoMoreInteractions(observer2);
+
+        addPendingChange(TEST_UID1, UidRecord.CHANGE_PROCSTATE, PROCESS_STATE_RECEIVER,
+                111, PROCESS_CAPABILITY_NONE, false);
+        mUidObserverController.dispatchUidsChanged();
+        verify(observer2).onUidStateChanged(TEST_UID1, PROCESS_STATE_RECEIVER,
+                111, PROCESS_CAPABILITY_NONE);
+        verifyNoMoreInteractions(observer1);
+        verifyNoMoreInteractions(observer2);
+
+        unregisterObserver(observer1);
+
+        addPendingChange(TEST_UID1, UidRecord.CHANGE_PROCSTATE, PROCESS_STATE_TOP,
+                112, PROCESS_CAPABILITY_ALL, false);
+        mUidObserverController.dispatchUidsChanged();
+        verify(observer2).onUidStateChanged(TEST_UID1, PROCESS_STATE_TOP,
+                112, PROCESS_CAPABILITY_ALL);
+        verifyNoMoreInteractions(observer1);
+        verifyNoMoreInteractions(observer2);
+
+        unregisterObserver(observer2);
+
+        addPendingChange(TEST_UID1, UidRecord.CHANGE_PROCSTATE, PROCESS_STATE_CACHED_RECENT,
+                112, PROCESS_CAPABILITY_NONE, false);
+        mUidObserverController.dispatchUidsChanged();
+        verifyNoMoreInteractions(observer1);
+        verifyNoMoreInteractions(observer2);
+    }
+
+    private void registerObserver(IUidObserver observer, int which, int cutpoint,
+            String callingPackage, int callingUid) {
+        when(observer.asBinder()).thenReturn((IBinder) observer);
+        mUidObserverController.register(observer, which, cutpoint, callingPackage, callingUid);
+        Mockito.reset(observer);
+    }
+
+    private void unregisterObserver(IUidObserver observer) {
+        when(observer.asBinder()).thenReturn((IBinder) observer);
+        mUidObserverController.unregister(observer);
+        Mockito.reset(observer);
+    }
+
+    private void addPendingChange(int uid, int change, int procState, long procStateSeq,
+            int capability, boolean ephemeral) {
+        final ChangeRecord record = new ChangeRecord();
+        record.uid = uid;
+        record.change = change;
+        record.procState = procState;
+        record.procStateSeq = procStateSeq;
+        record.capability = capability;
+        record.ephemeral = ephemeral;
+        mUidObserverController.getPendingUidChangesForTest().add(record);
+    }
+
+    private void assertPendingChange(int uid, int change, int procState, long procStateSeq,
+            int capability, boolean ephemeral, ChangeRecord expectedRecord) {
+        final ChangeRecord record = getLatestPendingChange(uid);
+        assertNotNull(record);
+        if (expectedRecord != null) {
+            assertEquals(expectedRecord, record);
+        }
+        assertEquals(change, record.change);
+        assertEquals(procState, record.procState);
+        assertEquals(procStateSeq, record.procStateSeq);
+        assertEquals(capability, record.capability);
+        assertEquals(ephemeral, record.ephemeral);
+    }
+
+    private ChangeRecord getLatestPendingChange(int uid) {
+        final ArrayList<ChangeRecord> changeRecords = mUidObserverController
+                .getPendingUidChangesForTest();
+        for (int i = changeRecords.size() - 1; i >= 0; --i) {
+            final ChangeRecord record = changeRecords.get(i);
+            if (record.uid == uid) {
+                return record;
+            }
+        }
+        return null;
+    }
+
+    private static String changeToStr(int change) {
+        return DebugUtils.flagsToString(UidRecord.class, "CHANGE_", change);
+    }
+}
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 6f4ff35..04de6ca 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -153,7 +153,7 @@
             doNothing().when(mInjector).startHomeActivity(anyInt(), anyString());
             doReturn(false).when(mInjector).stackSupervisorSwitchUser(anyInt(), any());
             doNothing().when(mInjector).stackSupervisorResumeFocusedStackTopActivity();
-            doNothing().when(mInjector).systemServiceManagerCleanupUser(anyInt());
+            doNothing().when(mInjector).systemServiceManagerOnUserStopped(anyInt());
             doNothing().when(mInjector).activityManagerForceStopPackage(anyInt(), anyString());
             doNothing().when(mInjector).activityManagerOnUserStopped(anyInt());
             doNothing().when(mInjector).clearBroadcastQueueForUser(anyInt());
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/AppSearchImplTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/AppSearchImplTest.java
index 24f7830..081bfb5 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/AppSearchImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/AppSearchImplTest.java
@@ -50,8 +50,7 @@
 
     @Before
     public void setUp() throws Exception {
-        mAppSearchImpl = new AppSearchImpl(mTemporaryFolder.newFolder());
-        mAppSearchImpl.initialize();
+        mAppSearchImpl = AppSearchImpl.create(mTemporaryFolder.newFolder());
     }
 
     /**
@@ -288,7 +287,8 @@
         SchemaProto finalSchemaProto = schemaProto;
         AppSearchException e = expectThrows(AppSearchException.class, () ->
                 mAppSearchImpl.setSchema("database1", finalSchemaProto, /*forceOverride=*/false));
-        assertThat(e).hasMessageThat().isEqualTo("Schema is incompatible.");
+        assertThat(e).hasMessageThat().contains("Schema is incompatible");
+        assertThat(e).hasMessageThat().contains("Deleted types: [database1/Document]");
 
         // ForceOverride to delete.
         mAppSearchImpl.setSchema("database1", finalSchemaProto, /*forceOverride=*/true);
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/converter/GenericDocumentToProtoConverterTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/converter/GenericDocumentToProtoConverterTest.java
new file mode 100644
index 0000000..a95290d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/converter/GenericDocumentToProtoConverterTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.external.localbackend.converter;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.appsearch.GenericDocument;
+
+import com.android.server.appsearch.proto.DocumentProto;
+import com.android.server.appsearch.proto.PropertyProto;
+import com.android.server.appsearch.protobuf.ByteString;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+
+public class GenericDocumentToProtoConverterTest {
+    private static final byte[] BYTE_ARRAY_1 = new byte[]{(byte) 1, (byte) 2, (byte) 3};
+    private static final byte[] BYTE_ARRAY_2 = new byte[]{(byte) 4, (byte) 5, (byte) 6, (byte) 7};
+    private static final GenericDocument DOCUMENT_PROPERTIES_1 =
+            new GenericDocument.Builder<GenericDocument.Builder<?>>(
+                    "sDocumentProperties1", "sDocumentPropertiesSchemaType1")
+            .setCreationTimestampMillis(12345L)
+            .build();
+    private static final GenericDocument DOCUMENT_PROPERTIES_2 =
+            new GenericDocument.Builder<GenericDocument.Builder<?>>(
+                    "sDocumentProperties2", "sDocumentPropertiesSchemaType2")
+            .setCreationTimestampMillis(6789L)
+            .build();
+
+    @Test
+    public void testDocumentProtoConvert() {
+        GenericDocument document =
+                new GenericDocument.Builder<GenericDocument.Builder<?>>("uri1", "schemaType1")
+                        .setCreationTimestampMillis(5L)
+                        .setScore(1)
+                        .setTtlMillis(1L)
+                        .setNamespace("namespace")
+                        .setProperty("longKey1", 1L)
+                        .setProperty("doubleKey1", 1.0)
+                        .setProperty("booleanKey1", true)
+                        .setProperty("stringKey1", "test-value1")
+                        .setProperty("byteKey1", BYTE_ARRAY_1, BYTE_ARRAY_2)
+                        .setProperty("documentKey1", DOCUMENT_PROPERTIES_1)
+                        .setProperty(GenericDocument.PROPERTIES_FIELD, DOCUMENT_PROPERTIES_2)
+                        .build();
+
+        // Create the Document proto. Need to sort the property order by key.
+        DocumentProto.Builder documentProtoBuilder = DocumentProto.newBuilder()
+                .setUri("uri1")
+                .setSchema("schemaType1")
+                .setCreationTimestampMs(5L)
+                .setScore(1)
+                .setTtlMs(1L)
+                .setNamespace("namespace");
+        HashMap<String, PropertyProto.Builder> propertyProtoMap = new HashMap<>();
+        propertyProtoMap.put("longKey1",
+                PropertyProto.newBuilder().setName("longKey1").addInt64Values(1L));
+        propertyProtoMap.put("doubleKey1",
+                PropertyProto.newBuilder().setName("doubleKey1").addDoubleValues(1.0));
+        propertyProtoMap.put("booleanKey1",
+                PropertyProto.newBuilder().setName("booleanKey1").addBooleanValues(true));
+        propertyProtoMap.put("stringKey1",
+                PropertyProto.newBuilder().setName("stringKey1").addStringValues("test-value1"));
+        propertyProtoMap.put("byteKey1",
+                PropertyProto.newBuilder().setName("byteKey1")
+                        .addBytesValues(ByteString.copyFrom(BYTE_ARRAY_1))
+                        .addBytesValues(ByteString.copyFrom(BYTE_ARRAY_2)));
+        propertyProtoMap.put("documentKey1",
+                PropertyProto.newBuilder().setName("documentKey1")
+                        .addDocumentValues(
+                                GenericDocumentToProtoConverter.convert(DOCUMENT_PROPERTIES_1)));
+        propertyProtoMap.put(GenericDocument.PROPERTIES_FIELD,
+                PropertyProto.newBuilder().setName(GenericDocument.PROPERTIES_FIELD)
+                        .addDocumentValues(
+                                GenericDocumentToProtoConverter.convert(DOCUMENT_PROPERTIES_2)));
+        List<String> sortedKey = new ArrayList<>(propertyProtoMap.keySet());
+        Collections.sort(sortedKey);
+        for (String key : sortedKey) {
+            documentProtoBuilder.addProperties(propertyProtoMap.get(key));
+        }
+        DocumentProto documentProto = documentProtoBuilder.build();
+        assertThat(GenericDocumentToProtoConverter.convert(document))
+                .isEqualTo(documentProto);
+        assertThat(document).isEqualTo(GenericDocumentToProtoConverter.convert(documentProto));
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/converter/SchemaToProtoConverterTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/converter/SchemaToProtoConverterTest.java
new file mode 100644
index 0000000..8b2fd1c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/converter/SchemaToProtoConverterTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.external.localbackend.converter;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.appsearch.AppSearchSchema;
+
+import com.android.server.appsearch.proto.IndexingConfig;
+import com.android.server.appsearch.proto.PropertyConfigProto;
+import com.android.server.appsearch.proto.SchemaTypeConfigProto;
+import com.android.server.appsearch.proto.TermMatchType;
+
+import org.junit.Test;
+
+public class SchemaToProtoConverterTest {
+    @Test
+    public void testGetProto_Email() {
+        AppSearchSchema emailSchema = new AppSearchSchema.Builder("Email")
+                .addProperty(new AppSearchSchema.PropertyConfig.Builder("subject")
+                        .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+                        .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                        .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build()
+                ).addProperty(new AppSearchSchema.PropertyConfig.Builder("body")
+                        .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+                        .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                        .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build()
+                ).build();
+
+        SchemaTypeConfigProto expectedEmailProto = SchemaTypeConfigProto.newBuilder()
+                .setSchemaType("Email")
+                .addProperties(PropertyConfigProto.newBuilder()
+                        .setPropertyName("subject")
+                        .setDataType(PropertyConfigProto.DataType.Code.STRING)
+                        .setSchemaType("")
+                        .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
+                        .setIndexingConfig(
+                                com.android.server.appsearch.proto.IndexingConfig.newBuilder()
+                                        .setTokenizerType(IndexingConfig.TokenizerType.Code.PLAIN)
+                                        .setTermMatchType(TermMatchType.Code.PREFIX)
+                        )
+                ).addProperties(PropertyConfigProto.newBuilder()
+                        .setPropertyName("body")
+                        .setDataType(PropertyConfigProto.DataType.Code.STRING)
+                        .setSchemaType("")
+                        .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
+                        .setIndexingConfig(
+                                com.android.server.appsearch.proto.IndexingConfig.newBuilder()
+                                        .setTokenizerType(IndexingConfig.TokenizerType.Code.PLAIN)
+                                        .setTermMatchType(TermMatchType.Code.PREFIX)
+                        )
+                ).build();
+
+        assertThat(SchemaToProtoConverter.convert(emailSchema)).isEqualTo(expectedEmailProto);
+    }
+
+    @Test
+    public void testGetProto_MusicRecording() {
+        AppSearchSchema musicRecordingSchema = new AppSearchSchema.Builder("MusicRecording")
+                .addProperty(new AppSearchSchema.PropertyConfig.Builder("artist")
+                        .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+                        .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+                        .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build()
+                ).addProperty(new AppSearchSchema.PropertyConfig.Builder("pubDate")
+                        .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
+                        .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                        .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
+                        .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
+                        .build()
+                ).build();
+
+        SchemaTypeConfigProto expectedMusicRecordingProto = SchemaTypeConfigProto.newBuilder()
+                .setSchemaType("MusicRecording")
+                .addProperties(PropertyConfigProto.newBuilder()
+                        .setPropertyName("artist")
+                        .setDataType(PropertyConfigProto.DataType.Code.STRING)
+                        .setSchemaType("")
+                        .setCardinality(PropertyConfigProto.Cardinality.Code.REPEATED)
+                        .setIndexingConfig(
+                                com.android.server.appsearch.proto.IndexingConfig.newBuilder()
+                                        .setTokenizerType(IndexingConfig.TokenizerType.Code.PLAIN)
+                                        .setTermMatchType(TermMatchType.Code.PREFIX)
+                        )
+                ).addProperties(PropertyConfigProto.newBuilder()
+                        .setPropertyName("pubDate")
+                        .setDataType(PropertyConfigProto.DataType.Code.INT64)
+                        .setSchemaType("")
+                        .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
+                        .setIndexingConfig(
+                                com.android.server.appsearch.proto.IndexingConfig.newBuilder()
+                                        .setTokenizerType(IndexingConfig.TokenizerType.Code.NONE)
+                                        .setTermMatchType(TermMatchType.Code.UNKNOWN)
+                        )
+                ).build();
+
+        assertThat(SchemaToProtoConverter.convert(musicRecordingSchema))
+                .isEqualTo(expectedMusicRecordingProto);
+    }
+}
diff --git a/core/tests/coretests/src/android/app/appsearch/SnippetTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/converter/SnippetTest.java
similarity index 75%
rename from core/tests/coretests/src/android/app/appsearch/SnippetTest.java
rename to services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/converter/SnippetTest.java
index 95f5b10..e9357aa 100644
--- a/core/tests/coretests/src/android/app/appsearch/SnippetTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/converter/SnippetTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright 2020 The Android Open 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,24 +14,23 @@
  * limitations under the License.
  */
 
-package android.app.appsearch;
+package com.android.server.appsearch.external.localbackend.converter;
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.app.appsearch.proto.DocumentProto;
-import android.app.appsearch.proto.PropertyProto;
-import android.app.appsearch.proto.SearchResultProto;
-import android.app.appsearch.proto.SnippetMatchProto;
-import android.app.appsearch.proto.SnippetProto;
+import android.app.appsearch.SearchResult;
 
-import androidx.test.filters.SmallTest;
+import com.android.server.appsearch.proto.DocumentProto;
+import com.android.server.appsearch.proto.PropertyProto;
+import com.android.server.appsearch.proto.SearchResultProto;
+import com.android.server.appsearch.proto.SnippetMatchProto;
+import com.android.server.appsearch.proto.SnippetProto;
 
 import org.junit.Test;
 
-@SmallTest
 public class SnippetTest {
 
-    // TODO(sidchhabra): Add tests for Double and Long Snippets.
+    // TODO(tytytyww): Add tests for Double and Long Snippets.
     @Test
     public void testSingleStringSnippet() {
 
@@ -74,22 +73,26 @@
         SearchResultProto searchResultProto = SearchResultProto.newBuilder()
                 .addResults(resultProto)
                 .build();
-        SearchResults searchResults = new SearchResults(searchResultProto);
 
         // Making ResultReader and getting Snippet values.
-        while (searchResults.hasNext()) {
-            SearchResults.Result result = searchResults.next();
-            MatchInfo match = result.getMatchInfo().get(0);
+        for (SearchResultProto.ResultProto proto : searchResultProto.getResultsList()) {
+            SearchResult result = SearchResultToProtoConverter.convertSearchResult(proto);
+            SearchResult.MatchInfo match = result.getMatches().get(0);
             assertThat(match.getPropertyPath()).isEqualTo(propertyKeyString);
             assertThat(match.getFullText()).isEqualTo(propertyValueString);
             assertThat(match.getExactMatch()).isEqualTo(exactMatch);
+            assertThat(match.getExactMatchPosition()).isEqualTo(
+                    new SearchResult.MatchRange(/*lower=*/29, /*upper=*/32));
+            assertThat(match.getFullText()).isEqualTo(propertyValueString);
+            assertThat(match.getSnippetPosition()).isEqualTo(
+                    new SearchResult.MatchRange(/*lower=*/26, /*upper=*/32));
             assertThat(match.getSnippet()).isEqualTo(window);
         }
     }
 
-    // TODO(sidchhabra): Add tests for Double and Long Snippets.
+    // TODO(tytytyww): Add tests for Double and Long Snippets.
     @Test
-    public void testNoSnippets() {
+    public void testNoSnippets() throws Exception {
 
         final String propertyKeyString = "content";
         final String propertyValueString = "A commonly used fake word is foo.\n"
@@ -117,16 +120,15 @@
         SearchResultProto searchResultProto = SearchResultProto.newBuilder()
                 .addResults(resultProto)
                 .build();
-        SearchResults searchResults = new SearchResults(searchResultProto);
 
-        while (searchResults.hasNext()) {
-            SearchResults.Result result = searchResults.next();
-            assertThat(result.getMatchInfo()).isEqualTo(null);
+        for (SearchResultProto.ResultProto proto : searchResultProto.getResultsList()) {
+            SearchResult result = SearchResultToProtoConverter.convertSearchResult(proto);
+            assertThat(result.getMatches()).isEqualTo(null);
         }
     }
 
     @Test
-    public void testMultipleStringSnippet() {
+    public void testMultipleStringSnippet() throws Exception {
         final String searchWord = "Test";
 
         // Building the SearchResult received from query.
@@ -178,22 +180,29 @@
         SearchResultProto searchResultProto = SearchResultProto.newBuilder()
                 .addResults(resultProto)
                 .build();
-        SearchResults searchResults = new SearchResults(searchResultProto);
 
         // Making ResultReader and getting Snippet values.
-        while (searchResults.hasNext()) {
-            SearchResults.Result result = searchResults.next();
+        for (SearchResultProto.ResultProto proto : searchResultProto.getResultsList()) {
+            SearchResult result = SearchResultToProtoConverter.convertSearchResult(proto);
 
-            MatchInfo match1 = result.getMatchInfo().get(0);
+            SearchResult.MatchInfo match1 = result.getMatches().get(0);
             assertThat(match1.getPropertyPath()).isEqualTo("sender.name");
             assertThat(match1.getFullText()).isEqualTo("Test Name Jr.");
+            assertThat(match1.getExactMatchPosition()).isEqualTo(
+                    new SearchResult.MatchRange(/*lower=*/0, /*upper=*/4));
             assertThat(match1.getExactMatch()).isEqualTo("Test");
+            assertThat(match1.getSnippetPosition()).isEqualTo(
+                    new SearchResult.MatchRange(/*lower=*/0, /*upper=*/9));
             assertThat(match1.getSnippet()).isEqualTo("Test Name");
 
-            MatchInfo match2 = result.getMatchInfo().get(1);
+            SearchResult.MatchInfo match2 = result.getMatches().get(1);
             assertThat(match2.getPropertyPath()).isEqualTo("sender.email");
             assertThat(match2.getFullText()).isEqualTo("TestNameJr@gmail.com");
+            assertThat(match2.getExactMatchPosition()).isEqualTo(
+                    new SearchResult.MatchRange(/*lower=*/0, /*upper=*/20));
             assertThat(match2.getExactMatch()).isEqualTo("TestNameJr@gmail.com");
+            assertThat(match2.getSnippetPosition()).isEqualTo(
+                    new SearchResult.MatchRange(/*lower=*/0, /*upper=*/20));
             assertThat(match2.getSnippet()).isEqualTo("TestNameJr@gmail.com");
         }
     }
diff --git a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
index 922d715..9ee1205 100644
--- a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
@@ -116,7 +116,7 @@
 
     @Test
     public void testCheckAttention_returnFalseWhenPowerManagerNotInteract() throws RemoteException {
-        doReturn(true).when(mSpyAttentionManager).isAttentionServiceSupported();
+        mSpyAttentionManager.mIsServiceEnabled = true;
         doReturn(false).when(mMockIPowerManager).isInteractive();
         AttentionCallbackInternal callback = Mockito.mock(AttentionCallbackInternal.class);
         assertThat(mSpyAttentionManager.checkAttention(mTimeout, callback)).isFalse();
@@ -124,7 +124,7 @@
 
     @Test
     public void testCheckAttention_callOnSuccess() throws RemoteException {
-        doReturn(true).when(mSpyAttentionManager).isAttentionServiceSupported();
+        mSpyAttentionManager.mIsServiceEnabled = true;
         doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
         doReturn(true).when(mMockIPowerManager).isInteractive();
         doNothing().when(mSpyAttentionManager).freeIfInactiveLocked();
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/HardwareAuthTokenUtilsTest.java b/services/tests/servicestests/src/com/android/server/biometrics/HardwareAuthTokenUtilsTest.java
new file mode 100644
index 0000000..84987e6
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/HardwareAuthTokenUtilsTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.biometrics;
+
+import static junit.framework.Assert.assertEquals;
+
+import android.hardware.keymaster.HardwareAuthToken;
+import android.hardware.keymaster.Timestamp;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+@Presubmit
+@SmallTest
+public class HardwareAuthTokenUtilsTest {
+
+    @Test
+    public void testByteArrayLoopBack() {
+        final byte[] hat = new byte[69];
+        for (int i = 0; i < 69; i++) {
+            hat[i] = (byte) i;
+        }
+
+        final HardwareAuthToken hardwareAuthToken = HardwareAuthTokenUtils.toHardwareAuthToken(hat);
+        final byte[] hat2 = HardwareAuthTokenUtils.toByteArray(hardwareAuthToken);
+
+        for (int i = 0; i < hat.length; i++) {
+            assertEquals(hat[i], hat2[i]);
+        }
+    }
+
+    @Test
+    public void testHardwareAuthTokenLoopBack() {
+        final long testChallenge = 1000L;
+        final long testUserId = 2000L;
+        final long testAuthenticatorId = 3000L;
+        final int testAuthenticatorType = 4000;
+        final long testTimestamp = 5000L;
+
+        final HardwareAuthToken hardwareAuthToken = new HardwareAuthToken();
+        hardwareAuthToken.challenge = testChallenge;
+        hardwareAuthToken.userId = testUserId;
+        hardwareAuthToken.authenticatorId = testAuthenticatorId;
+        hardwareAuthToken.authenticatorType = testAuthenticatorType;
+        hardwareAuthToken.timestamp = new Timestamp();
+        hardwareAuthToken.timestamp.milliSeconds = testTimestamp;
+        hardwareAuthToken.mac = new byte[32];
+
+        for (int i = 0; i < hardwareAuthToken.mac.length; i++) {
+            hardwareAuthToken.mac[i] = (byte) i;
+        }
+
+        final byte[] hat = HardwareAuthTokenUtils.toByteArray(hardwareAuthToken);
+        final HardwareAuthToken hardwareAuthToken2 =
+                HardwareAuthTokenUtils.toHardwareAuthToken(hat);
+
+        assertEquals(testChallenge, hardwareAuthToken2.challenge);
+        assertEquals(testUserId, hardwareAuthToken2.userId);
+        assertEquals(testAuthenticatorId, hardwareAuthToken2.authenticatorId);
+        assertEquals(testAuthenticatorType, hardwareAuthToken2.authenticatorType);
+        assertEquals(testTimestamp, hardwareAuthToken2.timestamp.milliSeconds);
+
+        for (int i = 0; i < hardwareAuthToken.mac.length; i++) {
+            assertEquals(hardwareAuthToken.mac[i], hardwareAuthToken2.mac[i]);
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
index 2cbe7be..870fe4a 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
@@ -39,58 +39,79 @@
         return new CompatConfigBuilder(buildClassifier, context);
     }
 
-    CompatConfigBuilder addTargetSdkChangeWithId(int sdk, long id) {
-        mChanges.add(new CompatChange(id, "", sdk, false, false, ""));
+    CompatConfigBuilder addEnableAfterSdkChangeWithId(int sdk, long id) {
+        mChanges.add(new CompatChange(id, "", sdk, -1, false, false, ""));
         return this;
     }
 
-    CompatConfigBuilder addTargetSdkDisabledChangeWithId(int sdk, long id) {
-        mChanges.add(new CompatChange(id, "", sdk, true, false, ""));
+    CompatConfigBuilder addEnableAfterSdkChangeWithIdAndName(int sdk, long id, String name) {
+        mChanges.add(new CompatChange(id, name, sdk, -1, false, false, ""));
         return this;
     }
 
-    CompatConfigBuilder addTargetSdkChangeWithIdAndName(int sdk, long id, String name) {
-        mChanges.add(new CompatChange(id, name, sdk, false, false, ""));
+    CompatConfigBuilder addEnableAfterSdkChangeWithIdDefaultDisabled(int sdk, long id) {
+        mChanges.add(new CompatChange(id, "", sdk, -1, true, false, ""));
         return this;
     }
 
-    CompatConfigBuilder addTargetSdkChangeWithIdAndDescription(int sdk, long id,
+    CompatConfigBuilder addEnableAfterSdkChangeWithIdAndDescription(int sdk, long id,
             String description) {
-        mChanges.add(new CompatChange(id, "", sdk, false, false, description));
+        mChanges.add(new CompatChange(id, "", sdk, -1, false, false, description));
+        return this;
+    }
+
+    CompatConfigBuilder addEnableSinceSdkChangeWithId(int sdk, long id) {
+        mChanges.add(new CompatChange(id, "", -1, sdk, false, false, ""));
+        return this;
+    }
+
+    CompatConfigBuilder addEnableSinceSdkChangeWithIdAndName(int sdk, long id, String name) {
+        mChanges.add(new CompatChange(id, name, -1, sdk, false, false, ""));
+        return this;
+    }
+
+    CompatConfigBuilder addEnableSinceSdkChangeWithIdDefaultDisabled(int sdk, long id) {
+        mChanges.add(new CompatChange(id, "", -1, sdk, true, false, ""));
+        return this;
+    }
+
+    CompatConfigBuilder addEnableSinceSdkChangeWithIdAndDescription(int sdk, long id,
+            String description) {
+        mChanges.add(new CompatChange(id, "", -1, sdk, false, false, description));
         return this;
     }
 
     CompatConfigBuilder addEnabledChangeWithId(long id) {
-        mChanges.add(new CompatChange(id, "", -1, false, false, ""));
+        mChanges.add(new CompatChange(id, "", -1, -1, false, false, ""));
         return this;
     }
 
     CompatConfigBuilder addEnabledChangeWithIdAndName(long id, String name) {
-        mChanges.add(new CompatChange(id, name, -1, false, false, ""));
+        mChanges.add(new CompatChange(id, name, -1, -1, false, false, ""));
         return this;
     }
     CompatConfigBuilder addEnabledChangeWithIdAndDescription(long id, String description) {
-        mChanges.add(new CompatChange(id, "", -1, false, false, description));
+        mChanges.add(new CompatChange(id, "", -1, -1, false, false, description));
         return this;
     }
 
     CompatConfigBuilder addDisabledChangeWithId(long id) {
-        mChanges.add(new CompatChange(id, "", -1, true, false, ""));
+        mChanges.add(new CompatChange(id, "", -1, -1, true, false, ""));
         return this;
     }
 
     CompatConfigBuilder addDisabledChangeWithIdAndName(long id, String name) {
-        mChanges.add(new CompatChange(id, name, -1, true, false, ""));
+        mChanges.add(new CompatChange(id, name, -1, -1, true, false, ""));
         return this;
     }
 
     CompatConfigBuilder addDisabledChangeWithIdAndDescription(long id, String description) {
-        mChanges.add(new CompatChange(id, "", -1, true, false, description));
+        mChanges.add(new CompatChange(id, "", -1, -1, true, false, description));
         return this;
     }
 
     CompatConfigBuilder addLoggingOnlyChangeWithId(long id) {
-        mChanges.add(new CompatChange(id, "", -1, false, true, ""));
+        mChanges.add(new CompatChange(id, "", -1, -1, false, true, ""));
         return this;
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
index 8be9213..e588370 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -98,7 +98,7 @@
     @Test
     public void testTargetSdkChangeDisabled() throws Exception {
         CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
-                .addTargetSdkChangeWithId(2, 1234L)
+                .addEnableAfterSdkChangeWithId(2, 1234L)
                 .build();
 
         assertThat(compatConfig.isChangeEnabled(1234L,
@@ -109,7 +109,7 @@
     @Test
     public void testTargetSdkChangeEnabled() throws Exception {
         CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
-                .addTargetSdkChangeWithId(2, 1234L)
+                .addEnableAfterSdkChangeWithId(2, 1234L)
                 .build();
 
         assertThat(compatConfig.isChangeEnabled(1234L,
@@ -119,7 +119,7 @@
     @Test
     public void testDisabledOverrideTargetSdkChange() throws Exception {
         CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
-                .addTargetSdkDisabledChangeWithId(2, 1234L)
+                .addEnableAfterSdkChangeWithIdDefaultDisabled(2, 1234L)
                 .build();
 
         assertThat(compatConfig.isChangeEnabled(1234L,
@@ -293,8 +293,8 @@
         CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
                 .addEnabledChangeWithId(1L)
                 .addDisabledChangeWithId(2L)
-                .addTargetSdkChangeWithId(3, 3L)
-                .addTargetSdkChangeWithId(4, 4L)
+                .addEnableSinceSdkChangeWithId(3, 3L)
+                .addEnableSinceSdkChangeWithId(4, 4L)
                 .build();
         ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
                 .withPackageName("foo.bar")
@@ -314,8 +314,8 @@
         CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
                 .addEnabledChangeWithId(1L)
                 .addDisabledChangeWithId(2L)
-                .addTargetSdkChangeWithId(3, 3L)
-                .addTargetSdkChangeWithId(4, 4L)
+                .addEnableSinceSdkChangeWithId(3, 3L)
+                .addEnableSinceSdkChangeWithId(4, 4L)
                 .build();
         ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
                 .withPackageName("foo.bar")
diff --git a/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java
index d45589d..c53b29a 100644
--- a/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java
@@ -87,9 +87,9 @@
     public void getOverrideAllowedState_debugBuildAnyChangeDebugApp_allowOverride()
             throws Exception {
         CompatConfig config = CompatConfigBuilder.create(debuggableBuild(), mContext)
-                    .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1)
-                    .addTargetSdkChangeWithId(TARGET_SDK, 2)
-                    .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3)
+                    .addEnableAfterSdkChangeWithId(TARGET_SDK_BEFORE, 1)
+                    .addEnableAfterSdkChangeWithId(TARGET_SDK, 2)
+                    .addEnableAfterSdkChangeWithId(TARGET_SDK_AFTER, 3)
                     .addEnabledChangeWithId(4)
                     .addDisabledChangeWithId(5)
                     .addLoggingOnlyChangeWithId(6).build();
@@ -131,9 +131,9 @@
     public void getOverrideAllowedState_debugBuildAnyChangeReleaseApp_allowOverride()
             throws Exception {
         CompatConfig config = CompatConfigBuilder.create(debuggableBuild(), mContext)
-                    .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1)
-                    .addTargetSdkChangeWithId(TARGET_SDK, 2)
-                    .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3)
+                    .addEnableAfterSdkChangeWithId(TARGET_SDK_BEFORE, 1)
+                    .addEnableAfterSdkChangeWithId(TARGET_SDK, 2)
+                    .addEnableAfterSdkChangeWithId(TARGET_SDK_AFTER, 3)
                     .addEnabledChangeWithId(4)
                     .addDisabledChangeWithId(5)
                     .addLoggingOnlyChangeWithId(6).build();
@@ -174,9 +174,9 @@
     public void getOverrideAllowedState_betaBuildTargetSdkChangeDebugApp_allowOverride()
             throws Exception {
         CompatConfig config = CompatConfigBuilder.create(betaBuild(), mContext)
-                        .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1)
-                        .addTargetSdkChangeWithId(TARGET_SDK, 2)
-                        .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3)
+                        .addEnableAfterSdkChangeWithId(TARGET_SDK_BEFORE, 1)
+                        .addEnableAfterSdkChangeWithId(TARGET_SDK, 2)
+                        .addEnableAfterSdkChangeWithId(TARGET_SDK_AFTER, 3)
                         .addDisabledChangeWithId(4).build();
         IOverrideValidator overrideValidator = config.getOverrideValidator();
         when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
@@ -245,9 +245,9 @@
     public void getOverrideAllowedState_betaBuildAnyChangeReleaseApp_rejectOverride()
             throws Exception {
         CompatConfig config = CompatConfigBuilder.create(betaBuild(), mContext)
-                        .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1)
-                        .addTargetSdkChangeWithId(TARGET_SDK, 2)
-                        .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3)
+                        .addEnableAfterSdkChangeWithId(TARGET_SDK_BEFORE, 1)
+                        .addEnableAfterSdkChangeWithId(TARGET_SDK, 2)
+                        .addEnableAfterSdkChangeWithId(TARGET_SDK_AFTER, 3)
                         .addEnabledChangeWithId(4)
                         .addDisabledChangeWithId(5)
                         .addLoggingOnlyChangeWithId(6).build();
@@ -288,8 +288,8 @@
     public void getOverrideAllowedState_finalBuildTargetSdkChangeDebugAppOptin_allowOverride()
             throws Exception {
         CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext)
-                        .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 1)
-                        .addTargetSdkChangeWithId(TARGET_SDK, 2).build();
+                        .addEnableAfterSdkChangeWithId(TARGET_SDK_AFTER, 1)
+                        .addEnableAfterSdkChangeWithId(TARGET_SDK, 2).build();
         IOverrideValidator overrideValidator = config.getOverrideValidator();
         when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
                 .thenReturn(ApplicationInfoBuilder.create()
@@ -313,7 +313,7 @@
     public void getOverrideAllowedState_finalBuildTargetSdkChangeDebugAppOptout_rejectOverride()
             throws Exception {
         CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext)
-                        .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1).build();
+                        .addEnableAfterSdkChangeWithId(TARGET_SDK_BEFORE, 1).build();
         IOverrideValidator overrideValidator = config.getOverrideValidator();
         when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
                 .thenReturn(ApplicationInfoBuilder.create()
@@ -371,9 +371,9 @@
     public void getOverrideAllowedState_finalBuildAnyChangeReleaseApp_rejectOverride()
             throws Exception {
         CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext)
-                        .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1)
-                        .addTargetSdkChangeWithId(TARGET_SDK, 2)
-                        .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3)
+                        .addEnableAfterSdkChangeWithId(TARGET_SDK_BEFORE, 1)
+                        .addEnableAfterSdkChangeWithId(TARGET_SDK, 2)
+                        .addEnableAfterSdkChangeWithId(TARGET_SDK_AFTER, 3)
                         .addEnabledChangeWithId(4)
                         .addDisabledChangeWithId(5)
                         .addLoggingOnlyChangeWithId(6).build();
diff --git a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
index cef02ff..64014ba 100644
--- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
@@ -84,22 +84,22 @@
         mCompatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
                 .addEnabledChangeWithId(1L)
                 .addDisabledChangeWithIdAndName(2L, "change2")
-                .addTargetSdkChangeWithIdAndDescription(Build.VERSION_CODES.O, 3L, "description")
-                .addTargetSdkChangeWithId(Build.VERSION_CODES.P, 4L)
-                .addTargetSdkChangeWithId(Build.VERSION_CODES.Q, 5L)
-                .addTargetSdkChangeWithId(Build.VERSION_CODES.R, 6L)
+                .addEnableAfterSdkChangeWithIdAndDescription(Build.VERSION_CODES.O, 3L, "desc")
+                .addEnableAfterSdkChangeWithId(Build.VERSION_CODES.P, 4L)
+                .addEnableAfterSdkChangeWithId(Build.VERSION_CODES.Q, 5L)
+                .addEnableAfterSdkChangeWithId(Build.VERSION_CODES.R, 6L)
                 .addLoggingOnlyChangeWithId(7L)
                 .build();
         mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
         assertThat(mPlatformCompat.listAllChanges()).asList().containsExactly(
-                new CompatibilityChangeInfo(1L, "", -1, false, false, ""),
-                new CompatibilityChangeInfo(2L, "change2", -1, true, false, ""),
-                new CompatibilityChangeInfo(3L, "", Build.VERSION_CODES.O, false, false,
-                        "description"),
-                new CompatibilityChangeInfo(4L, "", Build.VERSION_CODES.P, false, false, ""),
-                new CompatibilityChangeInfo(5L, "", Build.VERSION_CODES.Q, false, false, ""),
-                new CompatibilityChangeInfo(6L, "", Build.VERSION_CODES.R, false, false, ""),
-                new CompatibilityChangeInfo(7L, "", -1, false, true, ""));
+                new CompatibilityChangeInfo(1L, "", -1, -1, false, false, ""),
+                new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, ""),
+                new CompatibilityChangeInfo(3L, "", Build.VERSION_CODES.O, -1, false, false,
+                        "desc"),
+                new CompatibilityChangeInfo(4L, "", Build.VERSION_CODES.P, -1, false, false, ""),
+                new CompatibilityChangeInfo(5L, "", Build.VERSION_CODES.Q, -1, false, false, ""),
+                new CompatibilityChangeInfo(6L, "", Build.VERSION_CODES.R, -1, false, false, ""),
+                new CompatibilityChangeInfo(7L, "", -1, -1, false, true, ""));
     }
 
     @Test
@@ -107,18 +107,18 @@
         mCompatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
                 .addEnabledChangeWithId(1L)
                 .addDisabledChangeWithIdAndName(2L, "change2")
-                .addTargetSdkChangeWithIdAndDescription(Build.VERSION_CODES.O, 3L, "description")
-                .addTargetSdkChangeWithId(Build.VERSION_CODES.P, 4L)
-                .addTargetSdkChangeWithId(Build.VERSION_CODES.Q, 5L)
-                .addTargetSdkChangeWithId(Build.VERSION_CODES.R, 6L)
+                .addEnableAfterSdkChangeWithIdAndDescription(Build.VERSION_CODES.O, 3L, "desc")
+                .addEnableAfterSdkChangeWithId(Build.VERSION_CODES.P, 4L)
+                .addEnableAfterSdkChangeWithId(Build.VERSION_CODES.Q, 5L)
+                .addEnableAfterSdkChangeWithId(Build.VERSION_CODES.R, 6L)
                 .addLoggingOnlyChangeWithId(7L)
                 .build();
         mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
         assertThat(mPlatformCompat.listUIChanges()).asList().containsExactly(
-                new CompatibilityChangeInfo(1L, "", -1, false, false, ""),
-                new CompatibilityChangeInfo(2L, "change2", -1, true, false, ""),
-                new CompatibilityChangeInfo(4L, "", Build.VERSION_CODES.P, false, false, ""),
-                new CompatibilityChangeInfo(5L, "", Build.VERSION_CODES.Q, false, false, ""));
+                new CompatibilityChangeInfo(1L, "", -1, -1, false, false, ""),
+                new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, ""),
+                new CompatibilityChangeInfo(4L, "", Build.VERSION_CODES.P, -1, false, false, ""),
+                new CompatibilityChangeInfo(5L, "", Build.VERSION_CODES.Q, -1, false, false, ""));
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 631b4d4..c4f7b95 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -2814,7 +2814,7 @@
 
         exerciseUserProvisioningTransitions(CALLER_USER_HANDLE,
                 DevicePolicyManager.STATE_USER_PROFILE_COMPLETE,
-                DevicePolicyManager.STATE_USER_UNMANAGED);
+                DevicePolicyManager.STATE_USER_PROFILE_FINALIZED);
     }
 
     public void testSetUserProvisioningState_managedProfileFromSetupWizard_managedProfile()
@@ -5728,6 +5728,7 @@
         // Device owner should be allowed to request Device ID attestation.
         dpms.enforceCallerCanRequestDeviceIdAttestation(dpms.getCallerIdentity(admin1));
 
+        mContext.binder.callingUid = DpmMockContext.ANOTHER_UID;
         // Another package must not be allowed to request Device ID attestation.
         assertExpectException(SecurityException.class, null,
                 () -> dpms.enforceCallerCanRequestDeviceIdAttestation(
@@ -5757,6 +5758,7 @@
         dpms.enforceCallerCanRequestDeviceIdAttestation(dpms.getCallerIdentity(admin1));
 
         // But not another package.
+        mContext.binder.callingUid = DpmMockContext.ANOTHER_UID;
         assertExpectException(SecurityException.class, null,
                 () -> dpms.enforceCallerCanRequestDeviceIdAttestation(
                         dpms.getCallerIdentity(null, admin2.getPackageName())));
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 09b6d7b..cb49a51 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -113,7 +113,7 @@
         }
 
         public void withCleanCallingIdentity(@NonNull FunctionalUtils.ThrowingRunnable action) {
-            long callingIdentity = clearCallingIdentity();
+            final long callingIdentity = clearCallingIdentity();
             Throwable throwableToPropagate = null;
             try {
                 action.runOrThrow();
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
new file mode 100644
index 0000000..058794a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.devicestate;
+
+import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertThrows;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Unit tests for {@link DeviceStateManagerService}.
+ * <p/>
+ * Run with <code>atest DeviceStateManagerServiceTest</code>.
+ */
+@RunWith(AndroidJUnit4.class)
+public final class DeviceStateManagerServiceTest {
+    private static final int DEFAULT_DEVICE_STATE = 0;
+    private static final int OTHER_DEVICE_STATE = 1;
+    private static final int UNSUPPORTED_DEVICE_STATE = 999;
+
+    private TestDeviceStatePolicy mPolicy;
+    private TestDeviceStateProvider mProvider;
+    private DeviceStateManagerService mService;
+
+    @Before
+    public void setup() {
+        mProvider = new TestDeviceStateProvider();
+        mPolicy = new TestDeviceStatePolicy(mProvider);
+        mService = new DeviceStateManagerService(InstrumentationRegistry.getContext(), mPolicy);
+        mService.onStart();
+    }
+
+    @Test
+    public void requestStateChange() {
+        assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
+        assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
+        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE);
+
+        mProvider.notifyRequestState(OTHER_DEVICE_STATE);
+        assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
+        assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
+        assertEquals(mService.getRequestedState(), OTHER_DEVICE_STATE);
+        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), OTHER_DEVICE_STATE);
+    }
+
+    @Test
+    public void requestStateChange_pendingState() {
+        mPolicy.blockConfigure();
+
+        mProvider.notifyRequestState(OTHER_DEVICE_STATE);
+        assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getPendingState(), OTHER_DEVICE_STATE);
+        assertEquals(mService.getRequestedState(), OTHER_DEVICE_STATE);
+        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), OTHER_DEVICE_STATE);
+
+        mProvider.notifyRequestState(DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getPendingState(), OTHER_DEVICE_STATE);
+        assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
+        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), OTHER_DEVICE_STATE);
+
+        mPolicy.resumeConfigure();
+        assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
+        assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
+        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE);
+    }
+
+    @Test
+    public void requestStateChange_unsupportedState() {
+        mProvider.notifyRequestState(UNSUPPORTED_DEVICE_STATE);
+        assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
+        assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
+        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE);
+    }
+
+    @Test
+    public void requestStateChange_invalidState() {
+        assertThrows(IllegalArgumentException.class, () -> {
+            mProvider.notifyRequestState(INVALID_DEVICE_STATE);
+        });
+    }
+
+    @Test
+    public void supportedStatesChanged() {
+        assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
+        assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
+
+        mProvider.notifySupportedDeviceStates(new int []{ DEFAULT_DEVICE_STATE });
+
+        // The current committed and requests states do not change because the current state remains
+        // supported.
+        assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
+        assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
+    }
+
+    @Test
+    public void supportedStatesChanged_invalidState() {
+        assertThrows(IllegalArgumentException.class, () -> {
+            mProvider.notifySupportedDeviceStates(new int []{ INVALID_DEVICE_STATE });
+        });
+    }
+
+    @Test
+    public void supportedStatesChanged_unsupportedRequestedState() {
+        assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
+        assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
+
+        mProvider.notifySupportedDeviceStates(new int []{ OTHER_DEVICE_STATE });
+
+        // The current requested state is cleared because it is no longer supported.
+        assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
+        assertEquals(mService.getRequestedState(), INVALID_DEVICE_STATE);
+
+        mProvider.notifyRequestState(OTHER_DEVICE_STATE);
+
+        assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
+        assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
+        assertEquals(mService.getRequestedState(), OTHER_DEVICE_STATE);
+    }
+
+    private static final class TestDeviceStatePolicy implements DeviceStatePolicy {
+        private final DeviceStateProvider mProvider;
+        private int mLastDeviceStateRequestedToConfigure = INVALID_DEVICE_STATE;
+        private boolean mConfigureBlocked = false;
+        private Runnable mPendingConfigureCompleteRunnable;
+
+        TestDeviceStatePolicy(DeviceStateProvider provider) {
+            mProvider = provider;
+        }
+
+        @Override
+        public DeviceStateProvider getDeviceStateProvider() {
+            return mProvider;
+        }
+
+        public void blockConfigure() {
+            mConfigureBlocked = true;
+        }
+
+        public void resumeConfigure() {
+            mConfigureBlocked = false;
+            if (mPendingConfigureCompleteRunnable != null) {
+                Runnable onComplete = mPendingConfigureCompleteRunnable;
+                mPendingConfigureCompleteRunnable = null;
+                onComplete.run();
+            }
+        }
+
+        public int getMostRecentRequestedStateToConfigure() {
+            return mLastDeviceStateRequestedToConfigure;
+        }
+
+        @Override
+        public void configureDeviceForState(int state, Runnable onComplete) {
+            if (mPendingConfigureCompleteRunnable != null) {
+                throw new IllegalStateException("configureDeviceForState() called while configure"
+                        + " is pending");
+            }
+
+            mLastDeviceStateRequestedToConfigure = state;
+            if (mConfigureBlocked) {
+                mPendingConfigureCompleteRunnable = onComplete;
+                return;
+            }
+            onComplete.run();
+        }
+    }
+
+    private static final class TestDeviceStateProvider implements DeviceStateProvider {
+        private int[] mSupportedDeviceStates = new int[]{ DEFAULT_DEVICE_STATE,
+                OTHER_DEVICE_STATE };
+        private int mCurrentDeviceState = DEFAULT_DEVICE_STATE;
+        private Listener mListener;
+
+        @Override
+        public void setListener(Listener listener) {
+            if (mListener != null) {
+                throw new IllegalArgumentException("Provider already has listener set.");
+            }
+
+            mListener = listener;
+            mListener.onSupportedDeviceStatesChanged(mSupportedDeviceStates);
+            mListener.onStateChanged(mCurrentDeviceState);
+        }
+
+        public void notifySupportedDeviceStates(int[] supportedDeviceStates) {
+            mSupportedDeviceStates = supportedDeviceStates;
+            mListener.onSupportedDeviceStatesChanged(supportedDeviceStates);
+        }
+
+        public void notifyRequestState(int state) {
+            mCurrentDeviceState = state;
+            mListener.onStateChanged(state);
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index b69cc47..ec747ac 100644
--- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -83,8 +83,8 @@
                 BRIGHTNESS_MAX_FLOAT, DOZE_SCALE_FACTOR, LIGHT_SENSOR_RATE,
                 INITIAL_LIGHT_SENSOR_RATE, BRIGHTENING_LIGHT_DEBOUNCE_CONFIG,
                 DARKENING_LIGHT_DEBOUNCE_CONFIG, RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG,
-                mAmbientBrightnessThresholds, mScreenBrightnessThresholds, mContext,
-                mDisplayDeviceConfig);
+                mAmbientBrightnessThresholds, mScreenBrightnessThresholds, mContext
+        );
         controller.setLoggingEnabled(true);
 
         // Configure the brightness controller and grab an instance of the sensor listener,
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index da25fd6..13b0198 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -343,7 +343,8 @@
         displayDeviceInfo2.copyFrom(displayDeviceInfo);
         displayDeviceInfo2.displayCutout = null;
         displayDevice.setDisplayDeviceInfo(displayDeviceInfo2);
-        displayManager.handleDisplayDeviceChanged(displayDevice);
+        displayManager.getDisplayDeviceRepository()
+                .onDisplayDeviceEvent(displayDevice, DisplayAdapter.DISPLAY_DEVICE_EVENT_CHANGED);
 
         handler.runWithScissors(() -> {
         }, 0 /* now */);
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
index b312e52..ac45017 100644
--- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
@@ -28,6 +28,9 @@
 import org.junit.Before;
 import org.junit.Test;
 
+import java.io.InputStream;
+import java.io.OutputStream;
+
 public class LogicalDisplayTest {
     private static final int DISPLAY_ID = 0;
     private static final int LAYER_STACK = 0;
@@ -50,13 +53,21 @@
         when(mDisplayDevice.getDisplayDeviceInfoLocked()).thenReturn(displayDeviceInfo);
 
         DisplayDeviceRepository repo = new DisplayDeviceRepository(
-                new DisplayManagerService.SyncRoot(), new DisplayDeviceRepository.Listener() {
+                new DisplayManagerService.SyncRoot(),
+                new PersistentDataStore(new PersistentDataStore.Injector() {
                     @Override
-                    public void onDisplayDeviceEventLocked(DisplayDevice device, int event) {}
+                    public InputStream openRead() {
+                        return null;
+                    }
 
                     @Override
-                    public void onTraversalRequested() {}
-                });
+                    public OutputStream startWrite() {
+                        return null;
+                    }
+
+                    @Override
+                    public void finishWrite(OutputStream os, boolean success) {}
+                }));
         repo.onDisplayDeviceEvent(mDisplayDevice, DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED);
         mLogicalDisplay.updateLocked(repo);
     }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
new file mode 100644
index 0000000..bbda168
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.hdmi;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertThrows;
+
+import android.content.Context;
+import android.hardware.hdmi.HdmiControlManager;
+import android.platform.test.annotations.Presubmit;
+import android.provider.Settings.Global;
+import android.sysprop.HdmiProperties;
+import android.util.Slog;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.hdmi.cec.config.CecSettings;
+import com.android.server.hdmi.cec.config.XmlParser;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import javax.xml.datatype.DatatypeConfigurationException;
+
+@SmallTest
+@Presubmit
+@RunWith(JUnit4.class)
+public final class HdmiCecConfigTest {
+    private static final String TAG = "HdmiCecConfigTest";
+
+    private Context mContext;
+
+    @Mock private HdmiCecConfig.StorageAdapter mStorageAdapter;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mContext = InstrumentationRegistry.getTargetContext();
+    }
+
+    @Test
+    public void getAllCecSettings_Empty() {
+        HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+                + "<cec-settings>"
+                + "</cec-settings>", null);
+        assertThat(hdmiCecConfig.getAllSettings()).isEmpty();
+    }
+
+    @Test
+    public void getAllCecSettings_BasicSanity() {
+        HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+                + "<cec-settings>"
+                + "  <setting name=\"hdmi_cec_enabled\""
+                + "           user-configurable=\"true\">"
+                + "    <allowed-values>"
+                + "      <value string-value=\"0\" />"
+                + "      <value string-value=\"1\" />"
+                + "    </allowed-values>"
+                + "    <default-value string-value=\"1\" />"
+                + "  </setting>"
+                + "  <setting name=\"send_standby_on_sleep\""
+                + "           user-configurable=\"false\">"
+                + "    <allowed-values>"
+                + "      <value string-value=\"to_tv\" />"
+                + "      <value string-value=\"broadcast\" />"
+                + "      <value string-value=\"none\" />"
+                + "    </allowed-values>"
+                + "    <default-value string-value=\"to_tv\" />"
+                + "  </setting>"
+                + "</cec-settings>", null);
+        assertThat(hdmiCecConfig.getAllSettings())
+                .containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
+                                 HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP);
+    }
+
+    @Test
+    public void getUserCecSettings_Empty() {
+        HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+                + "<cec-settings>"
+                + "</cec-settings>", null);
+        assertThat(hdmiCecConfig.getUserSettings()).isEmpty();
+    }
+
+    @Test
+    public void getUserCecSettings_OnlyMasterXml() {
+        HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+                + "<cec-settings>"
+                + "  <setting name=\"hdmi_cec_enabled\""
+                + "           user-configurable=\"true\">"
+                + "    <allowed-values>"
+                + "      <value string-value=\"0\" />"
+                + "      <value string-value=\"1\" />"
+                + "    </allowed-values>"
+                + "    <default-value string-value=\"1\" />"
+                + "  </setting>"
+                + "  <setting name=\"send_standby_on_sleep\""
+                + "           user-configurable=\"true\">"
+                + "    <allowed-values>"
+                + "      <value string-value=\"to_tv\" />"
+                + "      <value string-value=\"broadcast\" />"
+                + "      <value string-value=\"none\" />"
+                + "    </allowed-values>"
+                + "    <default-value string-value=\"to_tv\" />"
+                + "  </setting>"
+                + "</cec-settings>", null);
+        assertThat(hdmiCecConfig.getUserSettings())
+                .containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
+                                 HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP);
+    }
+
+    @Test
+    public void getUserCecSettings_WithOverride() {
+        HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+                + "<cec-settings>"
+                + "  <setting name=\"hdmi_cec_enabled\""
+                + "           user-configurable=\"true\">"
+                + "    <allowed-values>"
+                + "      <value string-value=\"0\" />"
+                + "      <value string-value=\"1\" />"
+                + "    </allowed-values>"
+                + "    <default-value string-value=\"1\" />"
+                + "  </setting>"
+                + "  <setting name=\"send_standby_on_sleep\""
+                + "           user-configurable=\"true\">"
+                + "    <allowed-values>"
+                + "      <value string-value=\"to_tv\" />"
+                + "      <value string-value=\"broadcast\" />"
+                + "      <value string-value=\"none\" />"
+                + "    </allowed-values>"
+                + "    <default-value string-value=\"to_tv\" />"
+                + "  </setting>"
+                + "</cec-settings>",
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+                + "<cec-settings>"
+                + "  <setting name=\"send_standby_on_sleep\""
+                + "           user-configurable=\"false\">"
+                + "    <allowed-values>"
+                + "      <value string-value=\"to_tv\" />"
+                + "      <value string-value=\"broadcast\" />"
+                + "      <value string-value=\"none\" />"
+                + "    </allowed-values>"
+                + "    <default-value string-value=\"to_tv\" />"
+                + "  </setting>"
+                + "</cec-settings>");
+        assertThat(hdmiCecConfig.getUserSettings())
+                .containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED);
+    }
+
+    @Test
+    public void getAllowedValues_InvalidSetting() {
+        HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+                + "<cec-settings>"
+                + "</cec-settings>", null);
+        assertThrows(IllegalArgumentException.class,
+                () -> hdmiCecConfig.getAllowedValues("foo"));
+    }
+
+    @Test
+    public void getAllowedValues_BasicSanity() {
+        HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+                + "<cec-settings>"
+                + "  <setting name=\"send_standby_on_sleep\""
+                + "           user-configurable=\"true\">"
+                + "    <allowed-values>"
+                + "      <value string-value=\"to_tv\" />"
+                + "      <value string-value=\"broadcast\" />"
+                + "      <value string-value=\"none\" />"
+                + "    </allowed-values>"
+                + "    <default-value string-value=\"to_tv\" />"
+                + "  </setting>"
+                + "</cec-settings>", null);
+        assertThat(hdmiCecConfig.getAllowedValues(
+                    HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP))
+                .containsExactly(HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV,
+                                 HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST,
+                                 HdmiControlManager.SEND_STANDBY_ON_SLEEP_NONE);
+    }
+
+    @Test
+    public void getDefaultValue_InvalidSetting() {
+        HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+                + "<cec-settings>"
+                + "</cec-settings>", null);
+        assertThrows(IllegalArgumentException.class,
+                () -> hdmiCecConfig.getDefaultValue("foo"));
+    }
+
+    @Test
+    public void getDefaultValue_BasicSanity() {
+        HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+                + "<cec-settings>"
+                + "  <setting name=\"send_standby_on_sleep\""
+                + "           user-configurable=\"true\">"
+                + "    <allowed-values>"
+                + "      <value string-value=\"to_tv\" />"
+                + "      <value string-value=\"broadcast\" />"
+                + "      <value string-value=\"none\" />"
+                + "    </allowed-values>"
+                + "    <default-value string-value=\"to_tv\" />"
+                + "  </setting>"
+                + "</cec-settings>", null);
+        assertThat(hdmiCecConfig.getDefaultValue(
+                    HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP))
+                .isEqualTo(HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV);
+    }
+
+    @Test
+    public void getValue_InvalidSetting() {
+        HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+                + "<cec-settings>"
+                + "</cec-settings>", null);
+        assertThrows(IllegalArgumentException.class,
+                () -> hdmiCecConfig.getValue(mContext, "foo"));
+    }
+
+    @Test
+    public void getValue_GlobalSetting_BasicSanity() {
+        when(mStorageAdapter.retrieveGlobalSetting(mContext,
+                  Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP,
+                  HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV))
+            .thenReturn(HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST);
+        HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+                + "<cec-settings>"
+                + "  <setting name=\"send_standby_on_sleep\""
+                + "           user-configurable=\"true\">"
+                + "    <allowed-values>"
+                + "      <value string-value=\"to_tv\" />"
+                + "      <value string-value=\"broadcast\" />"
+                + "      <value string-value=\"none\" />"
+                + "    </allowed-values>"
+                + "    <default-value string-value=\"to_tv\" />"
+                + "  </setting>"
+                + "</cec-settings>", null);
+        assertThat(hdmiCecConfig.getValue(mContext,
+                    HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP))
+                .isEqualTo(HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST);
+    }
+
+    @Test
+    public void getValue_SystemProperty_BasicSanity() {
+        when(mStorageAdapter.retrieveSystemProperty(
+                  HdmiCecConfig.SYSPROP_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+                  HdmiProperties.power_state_change_on_active_source_lost_values
+                      .NONE.name().toLowerCase()))
+                .thenReturn(HdmiProperties.power_state_change_on_active_source_lost_values
+                       .STANDBY_NOW.name().toLowerCase());
+        HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+                + "<cec-settings>"
+                + "  <setting name=\"power_state_change_on_active_source_lost\""
+                + "           user-configurable=\"false\">"
+                + "    <allowed-values>"
+                + "      <value string-value=\"none\" />"
+                + "      <value string-value=\"standby_now\" />"
+                + "    </allowed-values>"
+                + "    <default-value string-value=\"none\" />"
+                + "  </setting>"
+                + "</cec-settings>", null);
+        assertThat(hdmiCecConfig.getValue(mContext,
+                    HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST))
+                .isEqualTo(HdmiProperties.power_state_change_on_active_source_lost_values
+                        .STANDBY_NOW.name().toLowerCase());
+    }
+
+    @Test
+    public void setValue_InvalidSetting() {
+        HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+                + "<cec-settings>"
+                + "</cec-settings>", null);
+        assertThrows(IllegalArgumentException.class,
+                () -> hdmiCecConfig.setValue(mContext, "foo", "bar"));
+    }
+
+    @Test
+    public void setValue_NotConfigurable() {
+        HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+                + "<cec-settings>"
+                + "  <setting name=\"send_standby_on_sleep\""
+                + "           user-configurable=\"false\">"
+                + "    <allowed-values>"
+                + "      <value string-value=\"to_tv\" />"
+                + "      <value string-value=\"broadcast\" />"
+                + "      <value string-value=\"none\" />"
+                + "    </allowed-values>"
+                + "    <default-value string-value=\"to_tv\" />"
+                + "  </setting>"
+                + "</cec-settings>", null);
+        assertThrows(IllegalArgumentException.class,
+                () -> hdmiCecConfig.setValue(mContext,
+                        HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP,
+                        HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST));
+    }
+
+    @Test
+    public void setValue_InvalidValue() {
+        HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+                + "<cec-settings>"
+                + "  <setting name=\"send_standby_on_sleep\""
+                + "           user-configurable=\"true\">"
+                + "    <allowed-values>"
+                + "      <value string-value=\"to_tv\" />"
+                + "      <value string-value=\"broadcast\" />"
+                + "      <value string-value=\"none\" />"
+                + "    </allowed-values>"
+                + "    <default-value string-value=\"to_tv\" />"
+                + "  </setting>"
+                + "</cec-settings>", null);
+        assertThrows(IllegalArgumentException.class,
+                () -> hdmiCecConfig.setValue(mContext,
+                        HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP,
+                        "bar"));
+    }
+
+    @Test
+    public void setValue_GlobalSetting_BasicSanity() {
+        HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+                + "<cec-settings>"
+                + "  <setting name=\"send_standby_on_sleep\""
+                + "           user-configurable=\"true\">"
+                + "    <allowed-values>"
+                + "      <value string-value=\"to_tv\" />"
+                + "      <value string-value=\"broadcast\" />"
+                + "      <value string-value=\"none\" />"
+                + "    </allowed-values>"
+                + "    <default-value string-value=\"to_tv\" />"
+                + "  </setting>"
+                + "</cec-settings>", null);
+        hdmiCecConfig.setValue(mContext,
+                               HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP,
+                               HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST);
+        verify(mStorageAdapter).storeGlobalSetting(mContext,
+                  Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP,
+                  HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST);
+    }
+
+    @Test
+    public void setValue_SystemProperty_BasicSanity() {
+        HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+                + "<cec-settings>"
+                + "  <setting name=\"power_state_change_on_active_source_lost\""
+                + "           user-configurable=\"true\">"
+                + "    <allowed-values>"
+                + "      <value string-value=\"none\" />"
+                + "      <value string-value=\"standby_now\" />"
+                + "    </allowed-values>"
+                + "    <default-value string-value=\"none\" />"
+                + "  </setting>"
+                + "</cec-settings>", null);
+        hdmiCecConfig.setValue(mContext,
+                  HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+                  HdmiProperties.power_state_change_on_active_source_lost_values
+                      .STANDBY_NOW.name().toLowerCase());
+        verify(mStorageAdapter).storeSystemProperty(
+                  HdmiCecConfig.SYSPROP_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+                  HdmiProperties.power_state_change_on_active_source_lost_values
+                      .STANDBY_NOW.name().toLowerCase());
+    }
+
+    private HdmiCecConfig createHdmiCecConfig(String productConfigXml, String vendorOverrideXml) {
+        CecSettings productConfig = null;
+        CecSettings vendorOverride = null;
+        try {
+            productConfig = XmlParser.read(new ByteArrayInputStream(productConfigXml.getBytes()));
+            if (vendorOverrideXml != null) {
+                vendorOverride = XmlParser.read(
+                        new ByteArrayInputStream(vendorOverrideXml.getBytes()));
+            }
+        } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
+            Slog.e(TAG, "Encountered an error while reading/parsing CEC config strings", e);
+        }
+        return new HdmiCecConfig(productConfig, vendorOverride, mStorageAdapter);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index 7cbf571..ef98b98 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -814,7 +814,7 @@
         mTestLooper.dispatchAll();
 
         assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isTrue();
-        assertThat(mNativeWrapper.getResultMessages()).containsAllOf(pressed, released);
+        assertThat(mNativeWrapper.getResultMessages()).containsAtLeast(pressed, released);
     }
 
     @Test
@@ -834,7 +834,7 @@
         mTestLooper.dispatchAll();
 
         assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isTrue();
-        assertThat(mNativeWrapper.getResultMessages()).containsAllOf(pressed, released);
+        assertThat(mNativeWrapper.getResultMessages()).containsAtLeast(pressed, released);
     }
 
     @Test
@@ -853,7 +853,7 @@
         mTestLooper.dispatchAll();
 
         assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
-        assertThat(mNativeWrapper.getResultMessages()).containsAllOf(pressed, released);
+        assertThat(mNativeWrapper.getResultMessages()).containsAtLeast(pressed, released);
     }
 
     @Test
@@ -872,7 +872,7 @@
         mTestLooper.dispatchAll();
 
         assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
-        assertThat(mNativeWrapper.getResultMessages()).containsAllOf(pressed, released);
+        assertThat(mNativeWrapper.getResultMessages()).containsAtLeast(pressed, released);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
new file mode 100644
index 0000000..5a05fc6
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.hdmi;
+
+import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_DESTINATION;
+import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_PARAMETER_SHORT;
+import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_SOURCE;
+import static com.android.server.hdmi.HdmiCecMessageValidator.OK;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.test.TestLooper;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import com.google.common.truth.IntegerSubject;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link com.android.server.hdmi.HdmiCecMessageValidator} class. */
+@SmallTest
+@Presubmit
+@RunWith(JUnit4.class)
+public class HdmiCecMessageValidatorTest {
+
+    private HdmiCecMessageValidator mHdmiCecMessageValidator;
+    private TestLooper mTestLooper = new TestLooper();
+
+    @Before
+    public void setUp() throws Exception {
+        HdmiControlService mHdmiControlService = new HdmiControlService(
+                InstrumentationRegistry.getTargetContext());
+
+        mHdmiControlService.setIoLooper(mTestLooper.getLooper());
+        mHdmiCecMessageValidator = new HdmiCecMessageValidator(mHdmiControlService);
+    }
+
+    @Test
+    public void isValid_giveDevicePowerStatus() {
+        assertMessageValidity("04:8F").isEqualTo(OK);
+
+        assertMessageValidity("0F:8F").isEqualTo(ERROR_DESTINATION);
+        assertMessageValidity("F4:8F").isEqualTo(ERROR_SOURCE);
+    }
+
+    @Test
+    public void isValid_reportPowerStatus() {
+        assertMessageValidity("04:90:00").isEqualTo(OK);
+
+        assertMessageValidity("0F:90:00").isEqualTo(ERROR_DESTINATION);
+        assertMessageValidity("F0:90").isEqualTo(ERROR_SOURCE);
+        assertMessageValidity("04:90").isEqualTo(ERROR_PARAMETER_SHORT);
+    }
+
+    private IntegerSubject assertMessageValidity(String message) {
+        return assertThat(mHdmiCecMessageValidator.isValid(buildMessage(message)));
+    }
+
+    /**
+     * Build a CEC message from a hex byte string with bytes separated by {@code :}.
+     *
+     * <p>This format is used by both cec-client and www.cec-o-matic.com
+     */
+    private static HdmiCecMessage buildMessage(String message) {
+        String[] parts = message.split(":");
+        int src = Integer.parseInt(parts[0].substring(0, 1), 16);
+        int dest = Integer.parseInt(parts[0].substring(1, 2), 16);
+        int opcode = Integer.parseInt(parts[1], 16);
+        byte[] params = new byte[parts.length - 2];
+        for (int i = 0; i < params.length; i++) {
+            params[i] = (byte) Integer.parseInt(parts[i + 2], 16);
+        }
+        return new HdmiCecMessage(src, dest, opcode, params);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/inputmethod/MultiClientInputMethodManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/inputmethod/MultiClientInputMethodManagerServiceTest.java
index cdff97b..9ab762a 100644
--- a/services/tests/servicestests/src/com/android/server/inputmethod/MultiClientInputMethodManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/inputmethod/MultiClientInputMethodManagerServiceTest.java
@@ -76,7 +76,7 @@
 
         // Act and assert
         assertThat(MultiClientInputMethodManagerService.resolveMultiClientImeService(
-                asList(imeService))).isSameAs(imeService);
+                asList(imeService))).isSameInstanceAs(imeService);
     }
 
     private ResolveInfo buildResolveInfo(String permission, int flags) {
diff --git a/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java b/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java
index 41be54a..f26e094 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java
@@ -194,7 +194,7 @@
         assertThat(rulesFetched.size())
                 .isEqualTo(INDEXING_BLOCK_SIZE * 2 + unindexedRuleCount);
         assertThat(rulesFetched)
-                .containsAllOf(
+                .containsAtLeast(
                         getPackageNameIndexedRule(installedPackageName),
                         getAppCertificateIndexedRule(installedAppCertificate));
     }
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/TestSupport.java b/services/tests/servicestests/src/com/android/server/location/timezone/TestSupport.java
index 192ade7..4810563 100644
--- a/services/tests/servicestests/src/com/android/server/location/timezone/TestSupport.java
+++ b/services/tests/servicestests/src/com/android/server/location/timezone/TestSupport.java
@@ -45,6 +45,7 @@
         return new ConfigurationInternal.Builder(userId)
                 .setUserConfigAllowed(true)
                 .setAutoDetectionSupported(true)
+                .setGeoDetectionSupported(true)
                 .setAutoDetectionEnabled(true)
                 .setLocationEnabled(true)
                 .setGeoDetectionEnabled(geoDetectionEnabled)
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelperTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelperTest.java
index 6921bb2..8d5687c 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelperTest.java
@@ -71,7 +71,7 @@
         Map<String, Pair<SecretKey, byte[]>> filteredKeys =
                 mHelper.keepOnlyWhitelistedInsecureKeys(rawKeys);
         assertThat(filteredKeys.entrySet()).containsExactlyElementsIn(expectedResult.entrySet());
-        assertThat(filteredKeys.entrySet()).containsAllIn(rawKeys.entrySet());
+        assertThat(filteredKeys.entrySet()).containsAtLeastElementsIn(rawKeys.entrySet());
     }
 
     @Test
@@ -85,7 +85,7 @@
         Map<String, Pair<SecretKey, byte[]>> filteredKeys =
                 mHelper.keepOnlyWhitelistedInsecureKeys(rawKeys);
         assertThat(filteredKeys.entrySet()).containsExactlyElementsIn(expectedResult.entrySet());
-        assertThat(rawKeys.entrySet()).containsAllIn(filteredKeys.entrySet());
+        assertThat(rawKeys.entrySet()).containsAtLeastElementsIn(filteredKeys.entrySet());
     }
 
     @Test
@@ -100,7 +100,7 @@
         Map<String, Pair<SecretKey, byte[]>> filteredKeys =
                 mHelper.keepOnlyWhitelistedInsecureKeys(rawKeys);
         assertThat(filteredKeys.entrySet()).containsExactlyElementsIn(expectedResult.entrySet());
-        assertThat(rawKeys.entrySet()).containsAllIn(filteredKeys.entrySet());
+        assertThat(rawKeys.entrySet()).containsAtLeastElementsIn(filteredKeys.entrySet());
     }
 
     @Test
@@ -122,7 +122,7 @@
         Map<String, Pair<SecretKey, byte[]>> filteredKeys =
                 mHelper.keepOnlyWhitelistedInsecureKeys(rawKeys);
         assertThat(filteredKeys.entrySet()).containsExactlyElementsIn(expectedResult.entrySet());
-        assertThat(rawKeys.entrySet()).containsAllIn(filteredKeys.entrySet());
+        assertThat(rawKeys.entrySet()).containsAtLeastElementsIn(filteredKeys.entrySet());
     }
 
     @Test
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 9836c64..b0cb2ea 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
@@ -70,7 +70,7 @@
         CertXml certXml = CertXml.parse(certXmlBytes);
         List<X509Certificate> endpointCerts = certXml.getAllEndpointCerts();
         assertThat(endpointCerts).hasSize(3);
-        assertThat(endpointCerts).containsAllOf(TestData.LEAF_CERT_1, TestData.LEAF_CERT_2);
+        assertThat(endpointCerts).containsAtLeast(TestData.LEAF_CERT_1, TestData.LEAF_CERT_2);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt b/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt
index 154d42c..3a292de 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt
@@ -72,7 +72,8 @@
         @JvmStatic
         fun checkAllCasesHandled() {
             // Assert that all states have been tested at least once.
-            assertThat(CASES.map { it.state }.distinct()).containsAllIn(ActorState.values())
+            assertThat(CASES.map { it.state }.distinct())
+                    .containsAtLeastElementsIn(ActorState.values())
         }
 
         @BeforeClass
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java
index e281f2b..391611b 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java
@@ -35,8 +35,8 @@
 @RunWith(AndroidJUnit4.class)
 public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceImplTestsBase {
 
-    private static final String OVERLAY = "com.dummy.overlay";
-    private static final String TARGET = "com.dummy.target";
+    private static final String OVERLAY = "com.test.overlay";
+    private static final String TARGET = "com.test.target";
     private static final int USER = 0;
 
     private static final String OVERLAY2 = OVERLAY + "2";
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
index c1d862a..4f882ce 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
@@ -39,8 +39,8 @@
 @RunWith(AndroidJUnit4.class)
 public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTestsBase {
 
-    private static final String OVERLAY = "com.dummy.overlay";
-    private static final String TARGET = "com.dummy.target";
+    private static final String OVERLAY = "com.test.overlay";
+    private static final String TARGET = "com.test.target";
     private static final int USER = 0;
 
     private static final String OVERLAY2 = OVERLAY + "2";
@@ -50,7 +50,7 @@
     private static final String OVERLAY3 = OVERLAY + "3";
     private static final int USER3 = USER2 + 1;
 
-    private static final String CONFIG_SIGNATURE_REFERENCE_PKG = "com.dummy.ref";
+    private static final String CONFIG_SIGNATURE_REFERENCE_PKG = "com.test.ref";
     private static final String CERT_CONFIG_OK = "config_certificate_ok";
     private static final String CERT_CONFIG_NOK = "config_certificate_nok";
 
@@ -149,7 +149,7 @@
         installNewPackage(overlay(OVERLAY, TARGET), USER);
         assertState(STATE_MISSING_TARGET, OVERLAY, USER);
 
-        final DummyDeviceState.PackageBuilder target = target(TARGET);
+        final FakeDeviceState.PackageBuilder target = target(TARGET);
         installNewPackage(target, USER);
         assertState(STATE_DISABLED, OVERLAY, USER);
 
@@ -169,9 +169,9 @@
 
     @Test
     public void testOnOverlayPackageUpgraded() {
-        final DummyListener listener = getListener();
-        final DummyDeviceState.PackageBuilder target = target(TARGET);
-        final DummyDeviceState.PackageBuilder overlay = overlay(OVERLAY, TARGET);
+        final FakeListener listener = getListener();
+        final FakeDeviceState.PackageBuilder target = target(TARGET);
+        final FakeDeviceState.PackageBuilder overlay = overlay(OVERLAY, TARGET);
         installNewPackage(target, USER);
         installNewPackage(overlay, USER);
         listener.count = 0;
@@ -181,7 +181,7 @@
         // upgrade to a version where the overlay has changed its target
         // expect once for the old target package, once for the new target package
         listener.count = 0;
-        final DummyDeviceState.PackageBuilder overlay2 = overlay(OVERLAY, "some.other.target");
+        final FakeDeviceState.PackageBuilder overlay2 = overlay(OVERLAY, "some.other.target");
         upgradePackage(overlay2, USER);
         assertEquals(3, listener.count);
 
@@ -193,7 +193,7 @@
     @Test
     public void testListener() {
         final OverlayManagerServiceImpl impl = getImpl();
-        final DummyListener listener = getListener();
+        final FakeListener listener = getListener();
         installNewPackage(overlay(OVERLAY, TARGET), USER);
         assertEquals(1, listener.count);
         listener.count = 0;
@@ -219,12 +219,12 @@
         installNewPackage(target(TARGET), USER);
         installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_OK), USER);
 
-        final DummyIdmapDaemon idmapd = getIdmapd();
-        final DummyDeviceState state = getState();
+        final FakeIdmapDaemon idmapd = getIdmapd();
+        final FakeDeviceState state = getState();
         String overlayPath = state.select(OVERLAY, USER).apkPath;
         assertTrue(idmapd.idmapExists(overlayPath, USER));
 
-        DummyIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath);
+        FakeIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath);
         assertTrue((CONFIG_SIGNATURE & idmap.policies) == CONFIG_SIGNATURE);
     }
 
@@ -237,12 +237,12 @@
         installNewPackage(target(TARGET), USER);
         installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
 
-        final DummyIdmapDaemon idmapd = getIdmapd();
-        final DummyDeviceState state = getState();
+        final FakeIdmapDaemon idmapd = getIdmapd();
+        final FakeDeviceState state = getState();
         String overlayPath = state.select(OVERLAY, USER).apkPath;
         assertTrue(idmapd.idmapExists(overlayPath, USER));
 
-        DummyIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath);
+        FakeIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath);
         assertTrue((CONFIG_SIGNATURE & idmap.policies) == 0);
     }
 
@@ -252,12 +252,12 @@
         installNewPackage(target(TARGET), USER);
         installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
 
-        final DummyIdmapDaemon idmapd = getIdmapd();
-        final DummyDeviceState state = getState();
+        final FakeIdmapDaemon idmapd = getIdmapd();
+        final FakeDeviceState state = getState();
         String overlayPath = state.select(OVERLAY, USER).apkPath;
         assertTrue(idmapd.idmapExists(overlayPath, USER));
 
-        DummyIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath);
+        FakeIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath);
         assertTrue((CONFIG_SIGNATURE & idmap.policies) == 0);
     }
 
@@ -266,12 +266,12 @@
         installNewPackage(target(TARGET), USER);
         installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
 
-        final DummyIdmapDaemon idmapd = getIdmapd();
-        final DummyDeviceState state = getState();
+        final FakeIdmapDaemon idmapd = getIdmapd();
+        final FakeDeviceState state = getState();
         String overlayPath = state.select(OVERLAY, USER).apkPath;
         assertTrue(idmapd.idmapExists(overlayPath, USER));
 
-        DummyIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath);
+        FakeIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath);
         assertTrue((CONFIG_SIGNATURE & idmap.policies) == 0);
     }
 
@@ -284,12 +284,12 @@
         installNewPackage(target(TARGET), USER);
         installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
 
-        final DummyIdmapDaemon idmapd = getIdmapd();
-        final DummyDeviceState state = getState();
+        final FakeIdmapDaemon idmapd = getIdmapd();
+        final FakeDeviceState state = getState();
         String overlayPath = state.select(OVERLAY, USER).apkPath;
         assertTrue(idmapd.idmapExists(overlayPath, USER));
 
-        DummyIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath);
+        FakeIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath);
         assertTrue((CONFIG_SIGNATURE & idmap.policies) == 0);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
index 2faf29f..006dda0 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
@@ -48,19 +48,19 @@
 /** Base class for creating {@link OverlayManagerServiceImplTests} tests. */
 class OverlayManagerServiceImplTestsBase {
     private OverlayManagerServiceImpl mImpl;
-    private DummyDeviceState mState;
-    private DummyListener mListener;
-    private DummyPackageManagerHelper mPackageManager;
-    private DummyIdmapDaemon mIdmapDaemon;
+    private FakeDeviceState mState;
+    private FakeListener mListener;
+    private FakePackageManagerHelper mPackageManager;
+    private FakeIdmapDaemon mIdmapDaemon;
     private OverlayConfig mOverlayConfig;
     private String mConfigSignaturePackageName;
 
     @Before
     public void setUp() {
-        mState = new DummyDeviceState();
-        mListener = new DummyListener();
-        mPackageManager = new DummyPackageManagerHelper(mState);
-        mIdmapDaemon = new DummyIdmapDaemon(mState);
+        mState = new FakeDeviceState();
+        mListener = new FakeListener();
+        mPackageManager = new FakePackageManagerHelper(mState);
+        mIdmapDaemon = new FakeIdmapDaemon(mState);
         mOverlayConfig = mock(OverlayConfig.class);
         when(mOverlayConfig.getPriority(any())).thenReturn(OverlayConfig.DEFAULT_PRIORITY);
         when(mOverlayConfig.isEnabled(any())).thenReturn(false);
@@ -81,15 +81,15 @@
         return mImpl;
     }
 
-    DummyListener getListener() {
+    FakeListener getListener() {
         return mListener;
     }
 
-    DummyIdmapDaemon getIdmapd() {
+    FakeIdmapDaemon getIdmapd() {
         return mIdmapDaemon;
     }
 
-    DummyDeviceState getState() {
+    FakeDeviceState getState() {
         return mState;
     }
 
@@ -116,27 +116,27 @@
         assertEquals(expected, actual);
     }
 
-    DummyDeviceState.PackageBuilder app(String packageName) {
-        return new DummyDeviceState.PackageBuilder(packageName, null /* targetPackageName */,
+    FakeDeviceState.PackageBuilder app(String packageName) {
+        return new FakeDeviceState.PackageBuilder(packageName, null /* targetPackageName */,
                 null /* targetOverlayableName */, "data");
     }
 
-    DummyDeviceState.PackageBuilder target(String packageName) {
-        return new DummyDeviceState.PackageBuilder(packageName, null /* targetPackageName */,
+    FakeDeviceState.PackageBuilder target(String packageName) {
+        return new FakeDeviceState.PackageBuilder(packageName, null /* targetPackageName */,
                 null /* targetOverlayableName */, "");
     }
 
-    DummyDeviceState.PackageBuilder overlay(String packageName, String targetPackageName) {
+    FakeDeviceState.PackageBuilder overlay(String packageName, String targetPackageName) {
         return overlay(packageName, targetPackageName, null /* targetOverlayableName */);
     }
 
-    DummyDeviceState.PackageBuilder overlay(String packageName, String targetPackageName,
+    FakeDeviceState.PackageBuilder overlay(String packageName, String targetPackageName,
             String targetOverlayableName) {
-        return new DummyDeviceState.PackageBuilder(packageName, targetPackageName,
+        return new FakeDeviceState.PackageBuilder(packageName, targetPackageName,
                 targetOverlayableName, "");
     }
 
-    void addPackage(DummyDeviceState.PackageBuilder pkg, int userId) {
+    void addPackage(FakeDeviceState.PackageBuilder pkg, int userId) {
         mState.add(pkg, userId);
     }
 
@@ -155,7 +155,7 @@
      *
      * @throws IllegalStateException if the package is currently installed
      */
-    void installNewPackage(DummyDeviceState.PackageBuilder pkg, int userId) {
+    void installNewPackage(FakeDeviceState.PackageBuilder pkg, int userId) {
         if (mState.select(pkg.packageName, userId) != null) {
             throw new IllegalStateException("package " + pkg.packageName + " already installed");
         }
@@ -178,8 +178,8 @@
      *
      * @throws IllegalStateException if the package is not currently installed
      */
-    void upgradePackage(DummyDeviceState.PackageBuilder pkg, int userId) {
-        final DummyDeviceState.Package replacedPackage = mState.select(pkg.packageName, userId);
+    void upgradePackage(FakeDeviceState.PackageBuilder pkg, int userId) {
+        final FakeDeviceState.Package replacedPackage = mState.select(pkg.packageName, userId);
         if (replacedPackage == null) {
             throw new IllegalStateException("package " + pkg.packageName + " not installed");
         }
@@ -204,7 +204,7 @@
      * @throws IllegalStateException if the package is not currently installed
      */
     void uninstallPackage(String packageName, int userId) {
-        final DummyDeviceState.Package pkg = mState.select(packageName, userId);
+        final FakeDeviceState.Package pkg = mState.select(packageName, userId);
         if (pkg == null) {
             throw new IllegalStateException("package " + packageName+ " not installed");
         }
@@ -217,7 +217,7 @@
     }
 
     /** Represents the state of packages installed on a fake device. */
-    static class DummyDeviceState {
+    static class FakeDeviceState {
         private ArrayMap<String, Package> mPackages = new ArrayMap<>();
 
         void add(PackageBuilder pkgBuilder, int userId) {
@@ -333,16 +333,16 @@
         }
     }
 
-    final class DummyPackageManagerHelper implements PackageManagerHelper {
-        private final DummyDeviceState mState;
+    final class FakePackageManagerHelper implements PackageManagerHelper {
+        private final FakeDeviceState mState;
 
-        private DummyPackageManagerHelper(DummyDeviceState state) {
+        private FakePackageManagerHelper(FakeDeviceState state) {
             mState = state;
         }
 
         @Override
         public PackageInfo getPackageInfo(@NonNull String packageName, int userId) {
-            final DummyDeviceState.Package pkg = mState.select(packageName, userId);
+            final FakeDeviceState.Package pkg = mState.select(packageName, userId);
             if (pkg == null) {
                 return null;
             }
@@ -353,15 +353,15 @@
             pi.packageName = pkg.packageName;
             pi.overlayTarget = pkg.targetPackageName;
             pi.targetOverlayableName = pkg.targetOverlayableName;
-            pi.overlayCategory = "dummy-category-" + pkg.targetPackageName;
+            pi.overlayCategory = "Fake-category-" + pkg.targetPackageName;
             return pi;
         }
 
         @Override
         public boolean signaturesMatching(@NonNull String packageName1,
                 @NonNull String packageName2, int userId) {
-            final DummyDeviceState.Package pkg1 = mState.select(packageName1, userId);
-            final DummyDeviceState.Package pkg2 = mState.select(packageName2, userId);
+            final FakeDeviceState.Package pkg1 = mState.select(packageName1, userId);
+            final FakeDeviceState.Package pkg2 = mState.select(packageName2, userId);
             return pkg1 != null && pkg2 != null && pkg1.certificate.equals(pkg2.certificate);
         }
 
@@ -382,7 +382,7 @@
         @Override
         public OverlayableInfo getOverlayableForTarget(@NonNull String packageName,
                 @NonNull String targetOverlayableName, int userId) {
-            final DummyDeviceState.Package pkg = mState.select(packageName, userId);
+            final FakeDeviceState.Package pkg = mState.select(packageName, userId);
             if (pkg == null || !pkg.overlayableNames.contains(targetOverlayableName)) {
                 return null;
             }
@@ -403,7 +403,7 @@
 
         @Override
         public boolean doesTargetDefineOverlayable(String targetPackageName, int userId) {
-            final DummyDeviceState.Package pkg = mState.select(targetPackageName, userId);
+            final FakeDeviceState.Package pkg = mState.select(targetPackageName, userId);
             return pkg != null && pkg.overlayableNames.contains(targetPackageName);
         }
 
@@ -413,16 +413,16 @@
         }
     }
 
-    static class DummyIdmapDaemon extends IdmapDaemon {
-        private final DummyDeviceState mState;
+    static class FakeIdmapDaemon extends IdmapDaemon {
+        private final FakeDeviceState mState;
         private final ArrayMap<String, IdmapHeader> mIdmapFiles = new ArrayMap<>();
 
-        DummyIdmapDaemon(DummyDeviceState state) {
+        FakeIdmapDaemon(FakeDeviceState state) {
             this.mState = state;
         }
 
         private int getCrc(@NonNull final String path) {
-            final DummyDeviceState.Package pkg = mState.selectFromPath(path);
+            final FakeDeviceState.Package pkg = mState.selectFromPath(path);
             Assert.assertNotNull(pkg);
             return pkg.versionCode;
         }
@@ -486,7 +486,7 @@
         }
     }
 
-    static class DummyListener implements OverlayManagerServiceImpl.OverlayChangeListener {
+    static class FakeListener implements OverlayManagerServiceImpl.OverlayChangeListener {
         public int count;
 
         public void onOverlaysChanged(@NonNull String targetPackage, int userId) {
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java
index 146f60a..9ef7557 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java
@@ -50,55 +50,55 @@
     private OverlayManagerSettings mSettings;
 
     private static final OverlayInfo OVERLAY_A0 = new OverlayInfo(
-            "com.dummy.overlay_a",
-            "com.dummy.target",
+            "com.test.overlay_a",
+            "com.test.target",
             null,
             "some-category",
-            "/data/app/com.dummy.overlay_a-1/base.apk",
+            "/data/app/com.test.overlay_a-1/base.apk",
             STATE_DISABLED,
             0,
             0,
             true);
 
     private static final OverlayInfo OVERLAY_B0 = new OverlayInfo(
-            "com.dummy.overlay_b",
-            "com.dummy.target",
+            "com.test.overlay_b",
+            "com.test.target",
             null,
             "some-category",
-            "/data/app/com.dummy.overlay_b-1/base.apk",
+            "/data/app/com.test.overlay_b-1/base.apk",
             STATE_DISABLED,
             0,
             0,
             true);
 
     private static final OverlayInfo OVERLAY_C0 = new OverlayInfo(
-            "com.dummy.overlay_c",
-            "com.dummy.target",
+            "com.test.overlay_c",
+            "com.test.target",
             null,
             "some-category",
-            "/data/app/com.dummy.overlay_c-1/base.apk",
+            "/data/app/com.test.overlay_c-1/base.apk",
             STATE_DISABLED,
             0,
             0,
             true);
 
     private static final OverlayInfo OVERLAY_A1 = new OverlayInfo(
-            "com.dummy.overlay_a",
-            "com.dummy.target",
+            "com.test.overlay_a",
+            "com.test.target",
             null,
             "some-category",
-            "/data/app/com.dummy.overlay_a-1/base.apk",
+            "/data/app/com.test.overlay_a-1/base.apk",
             STATE_DISABLED,
             1,
             0,
             true);
 
     private static final OverlayInfo OVERLAY_B1 = new OverlayInfo(
-            "com.dummy.overlay_b",
-            "com.dummy.target",
+            "com.test.overlay_b",
+            "com.test.target",
             null,
             "some-category",
-            "/data/app/com.dummy.overlay_b-1/base.apk",
+            "/data/app/com.test.overlay_b-1/base.apk",
             STATE_DISABLED,
             1,
             0,
@@ -230,11 +230,11 @@
         assertListsAreEqual(list, OVERLAY_A0, OVERLAY_C0, OVERLAY_B0);
 
         OverlayInfo otherTarget = new OverlayInfo(
-                "com.dummy.overlay_other",
-                "com.dummy.some.other.target",
+                "com.test.overlay_other",
+                "com.test.some.other.target",
                 null,
                 "some-category",
-                "/data/app/com.dummy.overlay_other-1/base.apk",
+                "/data/app/com.test.overlay_other-1/base.apk",
                 STATE_DISABLED,
                 0,
                 0,
@@ -350,7 +350,7 @@
         ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes("utf-8"));
 
         mSettings.restore(is);
-        assertDoesNotContain(mSettings, "com.dummy.overlay", 0);
+        assertDoesNotContain(mSettings, "com.test.overlay", 0);
     }
 
     @Test
@@ -359,27 +359,27 @@
         final String xml =
                 "<?xml version='1.0' encoding='utf-8' standalone='yes'?>\n"
                 + "<overlays version='" + version + "'>\n"
-                + "<item packageName='com.dummy.overlay'\n"
+                + "<item packageName='com.test.overlay'\n"
                 + "      userId='1234'\n"
-                + "      targetPackageName='com.dummy.target'\n"
-                + "      baseCodePath='/data/app/com.dummy.overlay-1/base.apk'\n"
+                + "      targetPackageName='com.test.target'\n"
+                + "      baseCodePath='/data/app/com.test.overlay-1/base.apk'\n"
                 + "      state='" + STATE_DISABLED + "'\n"
                 + "      isEnabled='false'\n"
-                + "      category='dummy-category'\n"
+                + "      category='test-category'\n"
                 + "      isStatic='false'\n"
                 + "      priority='0' />\n"
                 + "</overlays>\n";
         ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes("utf-8"));
 
         mSettings.restore(is);
-        OverlayInfo oi = mSettings.getOverlayInfo("com.dummy.overlay", 1234);
+        OverlayInfo oi = mSettings.getOverlayInfo("com.test.overlay", 1234);
         assertNotNull(oi);
-        assertEquals("com.dummy.overlay", oi.packageName);
-        assertEquals("com.dummy.target", oi.targetPackageName);
-        assertEquals("/data/app/com.dummy.overlay-1/base.apk", oi.baseCodePath);
+        assertEquals("com.test.overlay", oi.packageName);
+        assertEquals("com.test.target", oi.targetPackageName);
+        assertEquals("/data/app/com.test.overlay-1/base.apk", oi.baseCodePath);
         assertEquals(1234, oi.userId);
         assertEquals(STATE_DISABLED, oi.state);
-        assertFalse(mSettings.getEnabled("com.dummy.overlay", 1234));
+        assertFalse(mSettings.getEnabled("com.test.overlay", 1234));
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index f37054d..9ce4ee0 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -41,6 +41,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
@@ -100,6 +101,7 @@
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
+import java.util.function.Function;
 
 @RunWith(JUnit4.class)
 public final class DataManagerTest {
@@ -660,6 +662,26 @@
     }
 
     @Test
+    public void testConversationLastEventTimestampUpdate() {
+        mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+        ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+                buildPerson());
+        mDataManager.addOrUpdateConversationInfo(shortcut);
+
+        PackageData packageData = mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY);
+        ConversationInfo conversationInfo =
+                packageData.getConversationStore().getConversation(TEST_SHORTCUT_ID);
+        Event event = new Event(123L, Event.TYPE_IN_APP_CONVERSATION);
+
+        mInjector.mUsageStatsQueryHelper.mEventListener.onEvent(packageData, conversationInfo,
+                event);
+        ConversationInfo newConversationInfo =
+                packageData.getConversationStore().getConversation(TEST_SHORTCUT_ID);
+        assertEquals(123L, newConversationInfo.getLastEventTimestamp());
+    }
+
+    @Test
     public void testDeleteUninstalledPackageDataOnPackageRemoved() {
         mDataManager.onUserUnlocked(USER_ID_PRIMARY);
 
@@ -1122,6 +1144,18 @@
         }
     }
 
+    private class TestUsageStatsQueryHelper extends UsageStatsQueryHelper {
+
+        private final EventListener mEventListener;
+
+        TestUsageStatsQueryHelper(int userId,
+                Function<String, PackageData> packageDataGetter,
+                EventListener eventListener) {
+            super(userId, packageDataGetter, eventListener);
+            mEventListener = eventListener;
+        }
+    }
+
     private class TestInjector extends DataManager.Injector {
 
         private final TestContactsQueryHelper mContactsQueryHelper =
@@ -1129,6 +1163,7 @@
         private TestCallLogQueryHelper mCallLogQueryHelper;
         private TestMmsQueryHelper mMmsQueryHelper;
         private TestSmsQueryHelper mSmsQueryHelper;
+        private TestUsageStatsQueryHelper mUsageStatsQueryHelper;
 
         @Override
         ScheduledExecutorService createScheduledExecutor() {
@@ -1165,5 +1200,14 @@
             mSmsQueryHelper = new TestSmsQueryHelper(context, eventConsumer);
             return mSmsQueryHelper;
         }
+
+        @Override
+        UsageStatsQueryHelper createUsageStatsQueryHelper(@UserIdInt int userId,
+                Function<String, PackageData> packageDataGetter,
+                UsageStatsQueryHelper.EventListener eventListener) {
+            mUsageStatsQueryHelper =
+                    new TestUsageStatsQueryHelper(userId, packageDataGetter, eventListener);
+            return mUsageStatsQueryHelper;
+        }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java
index e968607..baa74b7 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java
@@ -26,6 +26,7 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.annotation.NonNull;
@@ -72,6 +73,8 @@
 
     @Mock
     private UsageStatsManagerInternal mUsageStatsManagerInternal;
+    @Mock
+    private UsageStatsQueryHelper.EventListener mEventListener;
 
     private TestPackageData mPackageData;
     private UsageStatsQueryHelper mHelper;
@@ -93,7 +96,7 @@
                 .setLocusId(LOCUS_ID_1)
                 .build();
 
-        mHelper = new UsageStatsQueryHelper(USER_ID_PRIMARY, pkg -> mPackageData);
+        mHelper = new UsageStatsQueryHelper(USER_ID_PRIMARY, pkg -> mPackageData, mEventListener);
     }
 
     @After
@@ -131,6 +134,8 @@
                 Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
         assertEquals(1, events.size());
         assertEquals(createInAppConversationEvent(100_000L, 10), events.get(0));
+        verify(mEventListener).onEvent(
+                mPackageData, mPackageData.mConversationStore.mConversationInfo, events.get(0));
     }
 
     @Test
@@ -145,6 +150,8 @@
                 Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
         assertEquals(1, events.size());
         assertEquals(createInAppConversationEvent(100_000L, 10), events.get(0));
+        verify(mEventListener).onEvent(
+                mPackageData, mPackageData.mConversationStore.mConversationInfo, events.get(0));
     }
 
     @Test
@@ -159,6 +166,8 @@
                 Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
         assertEquals(1, events.size());
         assertEquals(createInAppConversationEvent(100_000L, 10), events.get(0));
+        verify(mEventListener).onEvent(
+                mPackageData, mPackageData.mConversationStore.mConversationInfo, events.get(0));
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index e4acdfe..f78c01a 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -177,6 +177,12 @@
         }
 
         @Override
+        public Context createContextAsUser(UserHandle user, int flags) {
+            when(mMockPackageManager.getUserId()).thenReturn(user.getIdentifier());
+            return this;
+        }
+
+        @Override
         public void unregisterReceiver(BroadcastReceiver receiver) {
             // ignore.
         }
@@ -939,7 +945,7 @@
             assertEquals(Process.SYSTEM_UID, mInjectedCallingUid);
 
             final String packageName = (String) pmInvocation.getArguments()[0];
-            final int userId = (Integer) pmInvocation.getArguments()[1];
+            final int userId =  mMockPackageManager.getUserId();
 
             final Resources res = mock(Resources.class);
 
@@ -971,7 +977,7 @@
                 return Integer.parseInt(entryName.substring(1)) + ressIdOffset;
             }).when(res).getIdentifier(anyStringOrNull(), anyStringOrNull(), anyStringOrNull());
             return res;
-        }).when(mMockPackageManager).getResourcesForApplicationAsUser(anyString(), anyInt());
+        }).when(mMockPackageManager).getResourcesForApplication(anyString());
     }
 
     protected static UserInfo withProfileGroupId(UserInfo in, int groupId) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java b/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java
index 62e135b..86758f1 100644
--- a/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java
@@ -20,7 +20,6 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import android.content.pm.IDataLoaderStatusListener;
 import android.content.pm.PackageManager;
 import android.os.ConditionVariable;
 import android.os.incremental.IStorageHealthListener;
@@ -113,96 +112,30 @@
     }
 
     /**
-     * Test that the package is still startable when Incremental Storage is at blocked status.
+     * Test that the package becomes unstartable when health status indicate storage issues.
      */
     @Test
     public void testStartableTransition_IncrementalStorageBlocked() {
         mIncrementalStates.onStorageHealthStatusChanged(
-                IStorageHealthListener.HEALTH_STATUS_BLOCKED);
-        // Test that package is still startable
-        assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
-        assertTrue(mIncrementalStates.isStartable());
+                IStorageHealthListener.HEALTH_STATUS_UNHEALTHY_STORAGE);
+        // Test that package is now unstartable
+        assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
+        assertFalse(mIncrementalStates.isStartable());
+        assertEquals(PackageManager.UNSTARTABLE_REASON_INSUFFICIENT_STORAGE,
+                mUnstartableReason.get());
     }
 
     /**
-     * Test that the package is still startable when Data Loader has unknown transportation issues.
-     */
-    @Test
-    public void testStartableTransition_DataLoaderTransportError() {
-        mIncrementalStates.onStreamStatusChanged(
-                IDataLoaderStatusListener.STREAM_TRANSPORT_ERROR);
-        // Test that package is still startable
-        assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
-        assertTrue(mIncrementalStates.isStartable());
-    }
-
-    /**
-     * Test that the package becomes unstartable when Data Loader has data integrity issues.
+     * Test that the package becomes unstartable when health status indicates transport issues.
      */
     @Test
     public void testStartableTransition_DataLoaderIntegrityError() {
-        mIncrementalStates.onStreamStatusChanged(
-                IDataLoaderStatusListener.STREAM_INTEGRITY_ERROR);
-        // Test that package is now unstartable
-        assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
-        assertFalse(mIncrementalStates.isStartable());
-        assertEquals(PackageManager.UNSTARTABLE_REASON_DATALOADER_TRANSPORT,
-                mUnstartableReason.get());
-    }
-
-    /**
-     * Test that the package becomes unstartable when Data Loader has data source issues.
-     */
-    @Test
-    public void testStartableTransition_DataLoaderSourceError() {
-        mIncrementalStates.onStreamStatusChanged(
-                IDataLoaderStatusListener.STREAM_SOURCE_ERROR);
-        // Test that package is now unstartable
-        assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
-        assertFalse(mIncrementalStates.isStartable());
-        assertEquals(PackageManager.UNSTARTABLE_REASON_DATALOADER_TRANSPORT,
-                mUnstartableReason.get());
-    }
-
-    /**
-     * Test that the package becomes unstartable when Data Loader hits limited storage while
-     * Incremental storage has a pending reads.
-     */
-    @Test
-    public void testStartableTransition_DataLoaderStorageErrorWhenIncrementalStoragePending()
-            throws InterruptedException {
-        mIncrementalStates.onStreamStatusChanged(
-                IDataLoaderStatusListener.STREAM_STORAGE_ERROR);
-        // Test that package is still startable
-        assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
-        assertTrue(mIncrementalStates.isStartable());
         mIncrementalStates.onStorageHealthStatusChanged(
-                IStorageHealthListener.HEALTH_STATUS_READS_PENDING);
+                IStorageHealthListener.HEALTH_STATUS_UNHEALTHY_TRANSPORT);
         // Test that package is now unstartable
         assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
         assertFalse(mIncrementalStates.isStartable());
-        assertEquals(PackageManager.UNSTARTABLE_REASON_DATALOADER_STORAGE,
-                mUnstartableReason.get());
-    }
-
-    /**
-     * Test that the package becomes unstartable when Data Loader hits limited storage while
-     * Incremental storage is at blocked status.
-     */
-    @Test
-    public void testStartableTransition_DataLoaderStorageErrorWhenIncrementalStorageBlocked()
-            throws InterruptedException {
-        mIncrementalStates.onStreamStatusChanged(
-                IDataLoaderStatusListener.STREAM_STORAGE_ERROR);
-        // Test that package is still startable
-        assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
-        assertTrue(mIncrementalStates.isStartable());
-        mIncrementalStates.onStorageHealthStatusChanged(
-                IStorageHealthListener.HEALTH_STATUS_BLOCKED);
-        // Test that package is now unstartable
-        assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
-        assertFalse(mIncrementalStates.isStartable());
-        assertEquals(PackageManager.UNSTARTABLE_REASON_DATALOADER_STORAGE,
+        assertEquals(PackageManager.UNSTARTABLE_REASON_CONNECTION_ERROR,
                 mUnstartableReason.get());
     }
 
@@ -227,42 +160,18 @@
     }
 
     /**
-     * Test that the package becomes unstartable when Data Loader has data integrity issue, and it
-     * becomes startable again when Data Loader is healthy again.
+     * Test that the package becomes unstartable when health status indicates transportation issue,
+     * and it becomes startable again when health status is ok again.
      */
     @Test
     public void testStartableTransition_DataLoaderUnhealthyBackToHealthy()
             throws InterruptedException {
-        mIncrementalStates.onStreamStatusChanged(IDataLoaderStatusListener.STREAM_INTEGRITY_ERROR);
-        // Test that package is unstartable
-        assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
-        assertFalse(mIncrementalStates.isStartable());
-
-        mIncrementalStates.onStreamStatusChanged(IDataLoaderStatusListener.STREAM_HEALTHY);
-        // Test that package is now startable
-        assertTrue(mStartableCalled.block(WAIT_TIMEOUT_MILLIS));
-        assertTrue(mIncrementalStates.isStartable());
-    }
-
-    /**
-     * Test that the package becomes unstartable when both Incremental Storage and Data Loader
-     * are unhealthy, and it becomes startable again when both Incremental Storage and Data Loader
-     * are healthy again.
-     */
-    @Test
-    public void testStartableTransition_DataLoaderAndIncrementalStorageUnhealthyBackToHealthy()
-            throws InterruptedException {
         mIncrementalStates.onStorageHealthStatusChanged(
-                IStorageHealthListener.HEALTH_STATUS_UNHEALTHY);
-        mIncrementalStates.onStreamStatusChanged(IDataLoaderStatusListener.STREAM_INTEGRITY_ERROR);
+                IStorageHealthListener.HEALTH_STATUS_UNHEALTHY_TRANSPORT);
         // Test that package is unstartable
         assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
         assertFalse(mIncrementalStates.isStartable());
 
-        mIncrementalStates.onStreamStatusChanged(IDataLoaderStatusListener.STREAM_HEALTHY);
-        // Test that package is still unstartable
-        assertFalse(mStartableCalled.block(WAIT_TIMEOUT_MILLIS));
-        assertFalse(mIncrementalStates.isStartable());
         mIncrementalStates.onStorageHealthStatusChanged(IStorageHealthListener.HEALTH_STATUS_OK);
         // Test that package is now startable
         assertTrue(mStartableCalled.block(WAIT_TIMEOUT_MILLIS));
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
index 35d6f47..d54a40e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
@@ -16,12 +16,21 @@
 
 package com.android.server.pm;
 
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static java.lang.reflect.Modifier.isFinal;
+import static java.lang.reflect.Modifier.isPublic;
+import static java.lang.reflect.Modifier.isStatic;
+
 import android.content.IIntentReceiver;
+import android.content.pm.PackageManagerInternal;
 import android.os.Bundle;
 import android.util.SparseArray;
 
 import androidx.test.runner.AndroidJUnit4;
 
+import com.google.android.collect.Lists;
+
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
@@ -29,6 +38,12 @@
 import org.junit.runner.RunWith;
 
 import java.io.File;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Pattern;
 
 // runtest -c com.android.server.pm.PackageManagerServiceTest frameworks-services
 // bit FrameworksServicesTests:com.android.server.pm.PackageManagerServiceTest
@@ -133,4 +148,51 @@
             }
         }
     }
+
+    @Test
+    public void testKnownPackageToString_shouldNotGetUnknown() {
+        final List<String> packageNames = new ArrayList<>();
+        for (int i = 0; i <= PackageManagerInternal.LAST_KNOWN_PACKAGE; i++) {
+            packageNames.add(PackageManagerInternal.knownPackageToString(i));
+        }
+        assertWithMessage(
+                "The Ids of KnownPackage should be continuous and the string representation "
+                        + "should not be unknown.").that(
+                packageNames).containsNoneIn(Lists.newArrayList("Unknown"));
+    }
+
+    @Test
+    public void testKnownPackage_lastKnownPackageIsTheLast() throws Exception {
+        final List<Integer> knownPackageIds = getKnownPackageIdsList();
+        assertWithMessage(
+                "The last KnownPackage Id should be assigned to PackageManagerInternal"
+                        + ".LAST_KNOWN_PACKAGE.").that(
+                knownPackageIds.get(knownPackageIds.size() - 1)).isEqualTo(
+                PackageManagerInternal.LAST_KNOWN_PACKAGE);
+    }
+
+    @Test
+    public void testKnownPackage_IdsShouldBeUniqueAndContinuous() throws Exception {
+        final List<Integer> knownPackageIds = getKnownPackageIdsList();
+        for (int i = 0, size = knownPackageIds.size(); i < size - 1; i++) {
+            assertWithMessage(
+                    "The KnownPackage Ids should be unique and continuous. KnownPackageIds = "
+                            + Arrays.toString(knownPackageIds.toArray())).that(
+                    knownPackageIds.get(i) + 1).isEqualTo(knownPackageIds.get(i + 1));
+        }
+    }
+
+    private List<Integer> getKnownPackageIdsList() throws IllegalAccessException {
+        final ArrayList<Integer> knownPackageIds = new ArrayList<>();
+        final Field[] allFields = PackageManagerInternal.class.getDeclaredFields();
+        for (Field field : allFields) {
+            final int modifier = field.getModifiers();
+            if (isPublic(modifier) && isStatic(modifier) && isFinal(modifier)
+                    && Pattern.matches("PACKAGE(_[A-Z]+)+", field.getName())) {
+                knownPackageIds.add(field.getInt(null));
+            }
+        }
+        Collections.sort(knownPackageIds);
+        return knownPackageIds;
+    }
 }
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 bc74783..6255630 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -891,8 +891,10 @@
             assertNotSame(origPkgSetting.mimeGroups, testPkgSetting.mimeGroups);
         }
         assertThat(origPkgSetting.mimeGroups, is(testPkgSetting.mimeGroups));
-        assertNotSame(origPkgSetting.mPermissionsState, testPkgSetting.mPermissionsState);
-        assertThat(origPkgSetting.mPermissionsState, is(testPkgSetting.mPermissionsState));
+        assertNotSame(origPkgSetting.mLegacyPermissionsState,
+                testPkgSetting.mLegacyPermissionsState);
+        assertThat(origPkgSetting.mLegacyPermissionsState,
+                is(testPkgSetting.mLegacyPermissionsState));
         assertThat(origPkgSetting.name, is(testPkgSetting.name));
         // mOldCodePaths is _not_ copied
         // assertNotSame(origPkgSetting.mOldCodePaths, testPkgSetting.mOldCodePaths);
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
index 0ccc026..2250185 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
@@ -17,6 +17,7 @@
 package com.android.server.pm;
 
 import static android.content.pm.UserInfo.FLAG_DEMO;
+import static android.content.pm.UserInfo.FLAG_DISABLED;
 import static android.content.pm.UserInfo.FLAG_EPHEMERAL;
 import static android.content.pm.UserInfo.FLAG_FULL;
 import static android.content.pm.UserInfo.FLAG_GUEST;
@@ -166,6 +167,23 @@
         assertTrue(mUserManagerService.isUserOfType(testId, typeName));
     }
 
+    /** Test UserInfo.supportsSwitchTo() for partial user. */
+    @Test
+    public void testSupportSwitchTo_partial() throws Exception {
+        UserInfo userInfo = createUser(100, FLAG_FULL, null);
+        userInfo.partial = true;
+        assertFalse("Switching to a partial user should be disabled",
+                userInfo.supportsSwitchTo());
+    }
+
+    /** Test UserInfo.supportsSwitchTo() for disabled user. */
+    @Test
+    public void testSupportSwitchTo_disabled() throws Exception {
+        UserInfo userInfo = createUser(100, FLAG_DISABLED, null);
+        assertFalse("Switching to a DISABLED user should be disabled",
+                userInfo.supportsSwitchTo());
+    }
+
     /** Test UserInfo.supportsSwitchTo() for precreated users. */
     @Test
     public void testSupportSwitchTo_preCreated() throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 44bb58f..b190339 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -41,6 +41,7 @@
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Slog;
 
+import androidx.annotation.Nullable;
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
@@ -58,6 +59,8 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 
+import javax.annotation.concurrent.GuardedBy;
+
 /** Test {@link UserManager} functionality. */
 @RunWith(AndroidJUnit4.class)
 public final class UserManagerTest {
@@ -134,7 +137,7 @@
     @SmallTest
     @Test
     public void testHasSystemUser() throws Exception {
-        assertThat(findUser(UserHandle.USER_SYSTEM)).isTrue();
+        assertThat(hasUser(UserHandle.USER_SYSTEM)).isTrue();
     }
 
     @MediumTest
@@ -164,9 +167,9 @@
         assertThat(user1).isNotNull();
         assertThat(user2).isNotNull();
 
-        assertThat(findUser(UserHandle.USER_SYSTEM)).isTrue();
-        assertThat(findUser(user1.id)).isTrue();
-        assertThat(findUser(user2.id)).isTrue();
+        assertThat(hasUser(UserHandle.USER_SYSTEM)).isTrue();
+        assertThat(hasUser(user1.id)).isTrue();
+        assertThat(hasUser(user2.id)).isTrue();
     }
 
     @MediumTest
@@ -175,7 +178,7 @@
         UserInfo userInfo = createUser("Guest 1", UserInfo.FLAG_GUEST);
         removeUser(userInfo.id);
 
-        assertThat(findUser(userInfo.id)).isFalse();
+        assertThat(hasUser(userInfo.id)).isFalse();
     }
 
     @MediumTest
@@ -199,7 +202,7 @@
             }
         }
 
-        assertThat(findUser(userInfo.id)).isFalse();
+        assertThat(hasUser(userInfo.id)).isFalse();
     }
 
     @MediumTest
@@ -208,6 +211,79 @@
         assertThrows(IllegalArgumentException.class, () -> mUserManager.removeUser(null));
     }
 
+    @MediumTest
+    @Test
+    public void testRemoveUserOrSetEphemeral_restrictedReturnsError() throws Exception {
+        final int currentUser = ActivityManager.getCurrentUser();
+        final UserInfo user1 = createUser("User 1", /* flags= */ 0);
+        mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_USER, /* value= */ true,
+                asHandle(currentUser));
+        try {
+            assertThat(mUserManager.removeUserOrSetEphemeral(user1.id)).isEqualTo(
+                    UserManager.REMOVE_RESULT_ERROR);
+        } finally {
+            mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_USER, /* value= */ false,
+                    asHandle(currentUser));
+        }
+
+        assertThat(hasUser(user1.id)).isTrue();
+        assertThat(getUser(user1.id).isEphemeral()).isFalse();
+    }
+
+    @MediumTest
+    @Test
+    public void testRemoveUserOrSetEphemeral_systemUserReturnsError() throws Exception {
+        assertThat(mUserManager.removeUserOrSetEphemeral(UserHandle.USER_SYSTEM)).isEqualTo(
+                UserManager.REMOVE_RESULT_ERROR);
+
+        assertThat(hasUser(UserHandle.USER_SYSTEM)).isTrue();
+    }
+
+    @MediumTest
+    @Test
+    public void testRemoveUserOrSetEphemeral_invalidUserReturnsError() throws Exception {
+        assertThat(hasUser(Integer.MAX_VALUE)).isFalse();
+        assertThat(mUserManager.removeUserOrSetEphemeral(Integer.MAX_VALUE)).isEqualTo(
+                UserManager.REMOVE_RESULT_ERROR);
+    }
+
+    @MediumTest
+    @Test
+    public void testRemoveUserOrSetEphemeral_currentUserSetEphemeral() throws Exception {
+        final int startUser = ActivityManager.getCurrentUser();
+        final UserInfo user1 = createUser("User 1", /* flags= */ 0);
+        // Switch to the user just created.
+        switchUser(user1.id, null, /* ignoreHandle= */ true);
+
+        assertThat(mUserManager.removeUserOrSetEphemeral(user1.id)).isEqualTo(
+                UserManager.REMOVE_RESULT_SET_EPHEMERAL);
+
+        assertThat(hasUser(user1.id)).isTrue();
+        assertThat(getUser(user1.id).isEphemeral()).isTrue();
+
+        // Switch back to the starting user.
+        switchUser(startUser, null, /* ignoreHandle= */ true);
+
+        // User is removed once switch is complete
+        synchronized (mUserRemoveLock) {
+            waitForUserRemovalLocked(user1.id);
+        }
+        assertThat(hasUser(user1.id)).isFalse();
+    }
+
+    @MediumTest
+    @Test
+    public void testRemoveUserOrSetEphemeral_nonCurrentUserRemoved() throws Exception {
+        final UserInfo user1 = createUser("User 1", /* flags= */ 0);
+        synchronized (mUserRemoveLock) {
+            assertThat(mUserManager.removeUserOrSetEphemeral(user1.id)).isEqualTo(
+                    UserManager.REMOVE_RESULT_REMOVED);
+            waitForUserRemovalLocked(user1.id);
+        }
+
+        assertThat(hasUser(user1.id)).isFalse();
+    }
+
     /** Tests creating a FULL user via specifying userType. */
     @MediumTest
     @Test
@@ -608,15 +684,20 @@
                 () -> mUserManager.getUserCreationTime(asHandle(user.id)));
     }
 
-    private boolean findUser(int id) {
+    @Nullable
+    private UserInfo getUser(int id) {
         List<UserInfo> list = mUserManager.getUsers();
 
         for (UserInfo user : list) {
             if (user.id == id) {
-                return true;
+                return user;
             }
         }
-        return false;
+        return null;
+    }
+
+    private boolean hasUser(int id) {
+        return getUser(id) != null;
     }
 
     @MediumTest
@@ -639,7 +720,7 @@
         UserInfo user1 = createUser("User 1", 0);
         UserInfo user2 = createUser("User 2", 0);
         long[] serialNumbersOfUsers = mUserManager.getSerialNumbersOfUsers(false);
-        assertThat(serialNumbersOfUsers).asList().containsAllOf(
+        assertThat(serialNumbersOfUsers).asList().containsAtLeast(
                 (long) user1.serialNumber, (long) user2.serialNumber);
     }
 
@@ -918,17 +999,22 @@
     private void removeUser(int userId) {
         synchronized (mUserRemoveLock) {
             mUserManager.removeUser(userId);
-            long time = System.currentTimeMillis();
-            while (mUserManager.getUserInfo(userId) != null) {
-                try {
-                    mUserRemoveLock.wait(REMOVE_CHECK_INTERVAL_MILLIS);
-                } catch (InterruptedException ie) {
-                    Thread.currentThread().interrupt();
-                    return;
-                }
-                if (System.currentTimeMillis() - time > REMOVE_TIMEOUT_MILLIS) {
-                    fail("Timeout waiting for removeUser. userId = " + userId);
-                }
+            waitForUserRemovalLocked(userId);
+        }
+    }
+
+    @GuardedBy("mUserRemoveLock")
+    private void waitForUserRemovalLocked(int userId) {
+        long time = System.currentTimeMillis();
+        while (mUserManager.getUserInfo(userId) != null) {
+            try {
+                mUserRemoveLock.wait(REMOVE_CHECK_INTERVAL_MILLIS);
+            } catch (InterruptedException ie) {
+                Thread.currentThread().interrupt();
+                return;
+            }
+            if (System.currentTimeMillis() - time > REMOVE_TIMEOUT_MILLIS) {
+                fail("Timeout waiting for removeUser. userId = " + userId);
             }
         }
     }
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 4381bfd..af161ee 100644
--- a/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java
@@ -39,7 +39,6 @@
 
 import android.attention.AttentionManagerInternal;
 import android.attention.AttentionManagerInternal.AttentionCallbackInternal;
-import android.content.pm.PackageManager;
 import android.os.PowerManager;
 import android.os.PowerManagerInternal;
 import android.os.SystemClock;
@@ -64,8 +63,6 @@
     private static final long DEFAULT_DIM_DURATION_MILLIS = 6_000L;
 
     @Mock
-    private PackageManager mPackageManager;
-    @Mock
     private AttentionManagerInternal mAttentionManagerInternal;
     @Mock
     private WindowManagerInternal mWindowManagerInternal;
@@ -80,9 +77,6 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        when(mPackageManager.getAttentionServicePackageName()).thenReturn("com.google.android.as");
-        when(mPackageManager.checkPermission(any(), any())).thenReturn(
-                PackageManager.PERMISSION_GRANTED);
         when(mAttentionManagerInternal.checkAttention(anyLong(), any()))
                 .thenReturn(true);
         when(mWindowManagerInternal.isKeyguardShowingAndNotOccluded()).thenReturn(false);
@@ -157,16 +151,6 @@
     }
 
     @Test
-    public void testOnUserActivity_doesntCheckIfNotSufficientPermissions() {
-        when(mPackageManager.checkPermission(any(), any())).thenReturn(
-                PackageManager.PERMISSION_DENIED);
-
-        long when = registerAttention();
-        verify(mAttentionManagerInternal, never()).checkAttention(anyLong(), any());
-        assertThat(mNextDimming).isEqualTo(when);
-    }
-
-    @Test
     public void testOnUserActivity_doesntCrashIfNoAttentionService() {
         mAttentionManagerInternal = null;
         registerAttention();
@@ -452,7 +436,6 @@
             super(AttentionDetectorTest.this.mOnUserAttention, new Object());
             mAttentionManager = mAttentionManagerInternal;
             mWindowManager = mWindowManagerInternal;
-            mPackageManager = AttentionDetectorTest.this.mPackageManager;
             mContentResolver = getContext().getContentResolver();
         }
 
diff --git a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
index 3221a4d..59aff8d 100644
--- a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
@@ -20,12 +20,16 @@
 import static org.junit.Assert.fail;
 
 import android.content.Context;
+import android.hardware.power.stats.ChannelInfo;
+import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyMeasurement;
 
 import androidx.test.InstrumentationRegistry;
 
 import com.android.server.SystemService;
 import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper;
-import com.android.server.powerstats.nano.PowerStatsServiceProto;
+import com.android.server.powerstats.nano.PowerStatsServiceMeterProto;
+import com.android.server.powerstats.nano.PowerStatsServiceModelProto;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -48,11 +52,12 @@
 public class PowerStatsServiceTest {
     private static final String TAG = PowerStatsServiceTest.class.getSimpleName();
     private static final String DATA_STORAGE_SUBDIR = "powerstatstest";
-    private static final String DATA_STORAGE_FILENAME = "test";
+    private static final String METER_FILENAME = "metertest";
+    private static final String MODEL_FILENAME = "modeltest";
     private static final String PROTO_OUTPUT_FILENAME = "powerstats.proto";
-    private static final String RAIL_NAME = "railname";
-    private static final String SUBSYS_NAME = "subsysname";
-    private static final int POWER_RAIL_COUNT = 8;
+    private static final String CHANNEL_NAME = "channelname";
+    private static final int ENERGY_METER_COUNT = 8;
+    private static final int ENERGY_CONSUMER_COUNT = 2;
 
     private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
     private PowerStatsService mService;
@@ -75,8 +80,13 @@
         }
 
         @Override
-        String createDataStorageFilename() {
-            return DATA_STORAGE_FILENAME;
+        String createMeterFilename() {
+            return METER_FILENAME;
+        }
+
+        @Override
+        String createModelFilename() {
+            return MODEL_FILENAME;
         }
 
         @Override
@@ -86,9 +96,10 @@
 
         @Override
         PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath,
-                String dataStorageFilename, IPowerStatsHALWrapper powerStatsHALWrapper) {
-            mPowerStatsLogger = new PowerStatsLogger(context, dataStoragePath, dataStorageFilename,
-                powerStatsHALWrapper);
+                String meterFilename, String modelFilename,
+                IPowerStatsHALWrapper powerStatsHALWrapper) {
+            mPowerStatsLogger = new PowerStatsLogger(context, dataStoragePath, meterFilename,
+                modelFilename, powerStatsHALWrapper);
             return mPowerStatsLogger;
         }
 
@@ -107,23 +118,48 @@
 
     public static final class TestPowerStatsHALWrapper implements IPowerStatsHALWrapper {
         @Override
-        public PowerStatsData.RailInfo[] readRailInfo() {
-            PowerStatsData.RailInfo[] railInfoArray = new PowerStatsData.RailInfo[POWER_RAIL_COUNT];
-            for (int i = 0; i < POWER_RAIL_COUNT; i++) {
-                railInfoArray[i] = new PowerStatsData.RailInfo(i, RAIL_NAME + i, SUBSYS_NAME + i,
-                    i);
+        public int[] getEnergyConsumerInfo() {
+            int[] energyConsumerInfoList = new int[ENERGY_CONSUMER_COUNT];
+            for (int i = 0; i < energyConsumerInfoList.length; i++) {
+                energyConsumerInfoList[i] = i;
             }
-            return railInfoArray;
+            return energyConsumerInfoList;
         }
 
         @Override
-        public PowerStatsData.EnergyData[] readEnergyData() {
-            PowerStatsData.EnergyData[] energyDataArray =
-              new PowerStatsData.EnergyData[POWER_RAIL_COUNT];
-            for (int i = 0; i < POWER_RAIL_COUNT; i++) {
-                energyDataArray[i] = new PowerStatsData.EnergyData(i, i, i);
+        public EnergyConsumerResult[] getEnergyConsumed() {
+            EnergyConsumerResult[] energyConsumedList =
+                new EnergyConsumerResult[ENERGY_CONSUMER_COUNT];
+            for (int i = 0; i < energyConsumedList.length; i++) {
+                energyConsumedList[i] = new EnergyConsumerResult();
+                energyConsumedList[i].energyConsumerId = i;
+                energyConsumedList[i].timestampMs = i;
+                energyConsumedList[i].energyUWs = i;
             }
-            return energyDataArray;
+            return energyConsumedList;
+        }
+
+        @Override
+        public ChannelInfo[] getEnergyMeterInfo() {
+            ChannelInfo[] energyMeterInfoList = new ChannelInfo[ENERGY_METER_COUNT];
+            for (int i = 0; i < energyMeterInfoList.length; i++) {
+                energyMeterInfoList[i] = new ChannelInfo();
+                energyMeterInfoList[i].channelId = i;
+                energyMeterInfoList[i].channelName = new String(CHANNEL_NAME + i);
+            }
+            return energyMeterInfoList;
+        }
+
+        @Override
+        public EnergyMeasurement[] readEnergyMeters() {
+            EnergyMeasurement[] energyMeasurementList = new EnergyMeasurement[ENERGY_METER_COUNT];
+            for (int i = 0; i < energyMeasurementList.length; i++) {
+                energyMeasurementList[i] = new EnergyMeasurement();
+                energyMeasurementList[i].channelId = i;
+                energyMeasurementList[i].timestampMs = i;
+                energyMeasurementList[i].energyUWs = i;
+            }
+            return energyMeasurementList;
         }
 
         @Override
@@ -138,7 +174,7 @@
     }
 
     @Test
-    public void testWrittenPowerStatsHALDataMatchesReadIncidentReportData()
+    public void testWrittenMeterDataMatchesReadIncidentReportData()
             throws InterruptedException, IOException {
         mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
 
@@ -152,36 +188,74 @@
         // Write on-device storage to an incident report.
         File incidentReport = new File(mDataStorageDir, PROTO_OUTPUT_FILENAME);
         FileOutputStream fos = new FileOutputStream(incidentReport);
-        mPowerStatsLogger.writeToFile(fos.getFD());
+        mPowerStatsLogger.writeMeterDataToFile(fos.getFD());
 
         // Read the incident report in to a byte array.
         FileInputStream fis = new FileInputStream(incidentReport);
         byte[] fileContent = new byte[(int) incidentReport.length()];
         fis.read(fileContent);
 
-        // Parse the incident data into a PowerStatsServiceProto object.
-        PowerStatsServiceProto pssProto = PowerStatsServiceProto.parseFrom(fileContent);
+        // Parse the incident data into a PowerStatsServiceMeterProto object.
+        PowerStatsServiceMeterProto pssProto = PowerStatsServiceMeterProto.parseFrom(fileContent);
 
-        // Validate the railInfo array matches what was written to on-device storage.
-        assertTrue(pssProto.railInfo.length == POWER_RAIL_COUNT);
-        for (int i = 0; i < pssProto.railInfo.length; i++) {
-            assertTrue(pssProto.railInfo[i].index == i);
-            assertTrue(pssProto.railInfo[i].railName.equals(RAIL_NAME + i));
-            assertTrue(pssProto.railInfo[i].subsysName.equals(SUBSYS_NAME + i));
-            assertTrue(pssProto.railInfo[i].samplingRate == i);
+        // Validate the channelInfo array matches what was written to on-device storage.
+        assertTrue(pssProto.channelInfo.length == ENERGY_METER_COUNT);
+        for (int i = 0; i < pssProto.channelInfo.length; i++) {
+            assertTrue(pssProto.channelInfo[i].channelId == i);
+            assertTrue(pssProto.channelInfo[i].channelName.equals(CHANNEL_NAME + i));
         }
 
-        // Validate the energyData array matches what was written to on-device storage.
-        assertTrue(pssProto.energyData.length == POWER_RAIL_COUNT);
-        for (int i = 0; i < pssProto.energyData.length; i++) {
-            assertTrue(pssProto.energyData[i].index == i);
-            assertTrue(pssProto.energyData[i].timestampMs == i);
-            assertTrue(pssProto.energyData[i].energyUws == i);
+        // Validate the energyMeasurement array matches what was written to on-device storage.
+        assertTrue(pssProto.energyMeasurement.length == ENERGY_METER_COUNT);
+        for (int i = 0; i < pssProto.energyMeasurement.length; i++) {
+            assertTrue(pssProto.energyMeasurement[i].channelId == i);
+            assertTrue(pssProto.energyMeasurement[i].timestampMs == i);
+            assertTrue(pssProto.energyMeasurement[i].energyUws == i);
         }
     }
 
     @Test
-    public void testCorruptOnDeviceStorage() throws IOException {
+    public void testWrittenModelDataMatchesReadIncidentReportData()
+            throws InterruptedException, IOException {
+        mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+
+        // Write data to on-device storage.
+        mTimerTrigger.logPowerStatsData();
+
+        // The above call puts a message on a handler.  Wait for
+        // it to be processed.
+        Thread.sleep(100);
+
+        // Write on-device storage to an incident report.
+        File incidentReport = new File(mDataStorageDir, PROTO_OUTPUT_FILENAME);
+        FileOutputStream fos = new FileOutputStream(incidentReport);
+        mPowerStatsLogger.writeModelDataToFile(fos.getFD());
+
+        // Read the incident report in to a byte array.
+        FileInputStream fis = new FileInputStream(incidentReport);
+        byte[] fileContent = new byte[(int) incidentReport.length()];
+        fis.read(fileContent);
+
+        // Parse the incident data into a PowerStatsServiceModelProto object.
+        PowerStatsServiceModelProto pssProto = PowerStatsServiceModelProto.parseFrom(fileContent);
+
+        // Validate the energyConsumerId array matches what was written to on-device storage.
+        assertTrue(pssProto.energyConsumerId.length == ENERGY_CONSUMER_COUNT);
+        for (int i = 0; i < pssProto.energyConsumerId.length; i++) {
+            assertTrue(pssProto.energyConsumerId[i].energyConsumerId == i);
+        }
+
+        // Validate the energyConsumerResult array matches what was written to on-device storage.
+        assertTrue(pssProto.energyConsumerResult.length == ENERGY_CONSUMER_COUNT);
+        for (int i = 0; i < pssProto.energyConsumerResult.length; i++) {
+            assertTrue(pssProto.energyConsumerResult[i].energyConsumerId == i);
+            assertTrue(pssProto.energyConsumerResult[i].timestampMs == i);
+            assertTrue(pssProto.energyConsumerResult[i].energyUws == i);
+        }
+    }
+
+    @Test
+    public void testCorruptOnDeviceMeterStorage() throws IOException {
         mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
 
         // Generate random array of bytes to emulate corrupt data.
@@ -191,7 +265,7 @@
 
         // Store corrupt data in on-device storage.  Add fake timestamp to filename
         // to match format expected by FileRotator.
-        File onDeviceStorageFile = new File(mDataStorageDir, DATA_STORAGE_FILENAME + ".1234-2234");
+        File onDeviceStorageFile = new File(mDataStorageDir, METER_FILENAME + ".1234-2234");
         FileOutputStream onDeviceStorageFos = new FileOutputStream(onDeviceStorageFile);
         onDeviceStorageFos.write(bytes);
         onDeviceStorageFos.close();
@@ -199,33 +273,72 @@
         // Write on-device storage to an incident report.
         File incidentReport = new File(mDataStorageDir, PROTO_OUTPUT_FILENAME);
         FileOutputStream incidentReportFos = new FileOutputStream(incidentReport);
-        mPowerStatsLogger.writeToFile(incidentReportFos.getFD());
+        mPowerStatsLogger.writeMeterDataToFile(incidentReportFos.getFD());
 
         // Read the incident report in to a byte array.
         FileInputStream fis = new FileInputStream(incidentReport);
         byte[] fileContent = new byte[(int) incidentReport.length()];
         fis.read(fileContent);
 
-        // Parse the incident data into a PowerStatsServiceProto object.
-        PowerStatsServiceProto pssProto = PowerStatsServiceProto.parseFrom(fileContent);
+        // Parse the incident data into a PowerStatsServiceMeterProto object.
+        PowerStatsServiceMeterProto pssProto = PowerStatsServiceMeterProto.parseFrom(fileContent);
 
-        // Valid railInfo data is written to the incident report in the call to
-        // mPowerStatsLogger.writeToFile().
-        assertTrue(pssProto.railInfo.length == POWER_RAIL_COUNT);
-        for (int i = 0; i < pssProto.railInfo.length; i++) {
-            assertTrue(pssProto.railInfo[i].index == i);
-            assertTrue(pssProto.railInfo[i].railName.equals(RAIL_NAME + i));
-            assertTrue(pssProto.railInfo[i].subsysName.equals(SUBSYS_NAME + i));
-            assertTrue(pssProto.railInfo[i].samplingRate == i);
+        // Valid channelInfo data is written to the incident report in the call to
+        // mPowerStatsLogger.writeMeterDataToFile().
+        assertTrue(pssProto.channelInfo.length == ENERGY_METER_COUNT);
+        for (int i = 0; i < pssProto.channelInfo.length; i++) {
+            assertTrue(pssProto.channelInfo[i].channelId == i);
+            assertTrue(pssProto.channelInfo[i].channelName.equals(CHANNEL_NAME + i));
         }
 
-        // No energyData should be written to the incident report since it
+        // No energyMeasurements should be written to the incident report since it
         // is all corrupt (random bytes generated above).
-        assertTrue(pssProto.energyData.length == 0);
+        assertTrue(pssProto.energyMeasurement.length == 0);
     }
 
     @Test
-    public void testNotEnoughBytesAfterLengthField() throws IOException {
+    public void testCorruptOnDeviceModelStorage() throws IOException {
+        mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+
+        // Generate random array of bytes to emulate corrupt data.
+        Random rd = new Random();
+        byte[] bytes = new byte[100];
+        rd.nextBytes(bytes);
+
+        // Store corrupt data in on-device storage.  Add fake timestamp to filename
+        // to match format expected by FileRotator.
+        File onDeviceStorageFile = new File(mDataStorageDir, MODEL_FILENAME + ".1234-2234");
+        FileOutputStream onDeviceStorageFos = new FileOutputStream(onDeviceStorageFile);
+        onDeviceStorageFos.write(bytes);
+        onDeviceStorageFos.close();
+
+        // Write on-device storage to an incident report.
+        File incidentReport = new File(mDataStorageDir, PROTO_OUTPUT_FILENAME);
+        FileOutputStream incidentReportFos = new FileOutputStream(incidentReport);
+        mPowerStatsLogger.writeModelDataToFile(incidentReportFos.getFD());
+
+        // Read the incident report in to a byte array.
+        FileInputStream fis = new FileInputStream(incidentReport);
+        byte[] fileContent = new byte[(int) incidentReport.length()];
+        fis.read(fileContent);
+
+        // Parse the incident data into a PowerStatsServiceModelProto object.
+        PowerStatsServiceModelProto pssProto = PowerStatsServiceModelProto.parseFrom(fileContent);
+
+        // Valid energyConsumerId data is written to the incident report in the call to
+        // mPowerStatsLogger.writeModelDataToFile().
+        assertTrue(pssProto.energyConsumerId.length == ENERGY_CONSUMER_COUNT);
+        for (int i = 0; i < pssProto.energyConsumerId.length; i++) {
+            assertTrue(pssProto.energyConsumerId[i].energyConsumerId == i);
+        }
+
+        // No energyConsumerResults should be written to the incident report since it
+        // is all corrupt (random bytes generated above).
+        assertTrue(pssProto.energyConsumerResult.length == 0);
+    }
+
+    @Test
+    public void testNotEnoughBytesAfterMeterLengthField() throws IOException {
         mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
 
         // Create corrupt data.
@@ -236,7 +349,7 @@
 
         // Store corrupt data in on-device storage.  Add fake timestamp to filename
         // to match format expected by FileRotator.
-        File onDeviceStorageFile = new File(mDataStorageDir, DATA_STORAGE_FILENAME + ".1234-2234");
+        File onDeviceStorageFile = new File(mDataStorageDir, METER_FILENAME + ".1234-2234");
         FileOutputStream onDeviceStorageFos = new FileOutputStream(onDeviceStorageFile);
         onDeviceStorageFos.write(data.toByteArray());
         onDeviceStorageFos.close();
@@ -244,28 +357,68 @@
         // Write on-device storage to an incident report.
         File incidentReport = new File(mDataStorageDir, PROTO_OUTPUT_FILENAME);
         FileOutputStream incidentReportFos = new FileOutputStream(incidentReport);
-        mPowerStatsLogger.writeToFile(incidentReportFos.getFD());
+        mPowerStatsLogger.writeMeterDataToFile(incidentReportFos.getFD());
 
         // Read the incident report in to a byte array.
         FileInputStream fis = new FileInputStream(incidentReport);
         byte[] fileContent = new byte[(int) incidentReport.length()];
         fis.read(fileContent);
 
-        // Parse the incident data into a PowerStatsServiceProto object.
-        PowerStatsServiceProto pssProto = PowerStatsServiceProto.parseFrom(fileContent);
+        // Parse the incident data into a PowerStatsServiceMeterProto object.
+        PowerStatsServiceMeterProto pssProto = PowerStatsServiceMeterProto.parseFrom(fileContent);
 
-        // Valid railInfo data is written to the incident report in the call to
-        // mPowerStatsLogger.writeToFile().
-        assertTrue(pssProto.railInfo.length == POWER_RAIL_COUNT);
-        for (int i = 0; i < pssProto.railInfo.length; i++) {
-            assertTrue(pssProto.railInfo[i].index == i);
-            assertTrue(pssProto.railInfo[i].railName.equals(RAIL_NAME + i));
-            assertTrue(pssProto.railInfo[i].subsysName.equals(SUBSYS_NAME + i));
-            assertTrue(pssProto.railInfo[i].samplingRate == i);
+        // Valid channelInfo data is written to the incident report in the call to
+        // mPowerStatsLogger.writeMeterDataToFile().
+        assertTrue(pssProto.channelInfo.length == ENERGY_METER_COUNT);
+        for (int i = 0; i < pssProto.channelInfo.length; i++) {
+            assertTrue(pssProto.channelInfo[i].channelId == i);
+            assertTrue(pssProto.channelInfo[i].channelName.equals(CHANNEL_NAME + i));
         }
 
-        // No energyData should be written to the incident report since the
+        // No energyMeasurements should be written to the incident report since the
         // input buffer had only length and no data.
-        assertTrue(pssProto.energyData.length == 0);
+        assertTrue(pssProto.energyMeasurement.length == 0);
+    }
+
+    @Test
+    public void testNotEnoughBytesAfterModelLengthField() throws IOException {
+        mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+
+        // Create corrupt data.
+        // Length field is correct, but there is no data following the length.
+        ByteArrayOutputStream data = new ByteArrayOutputStream();
+        data.write(ByteBuffer.allocate(4).putInt(50).array());
+        byte[] test = data.toByteArray();
+
+        // Store corrupt data in on-device storage.  Add fake timestamp to filename
+        // to match format expected by FileRotator.
+        File onDeviceStorageFile = new File(mDataStorageDir, MODEL_FILENAME + ".1234-2234");
+        FileOutputStream onDeviceStorageFos = new FileOutputStream(onDeviceStorageFile);
+        onDeviceStorageFos.write(data.toByteArray());
+        onDeviceStorageFos.close();
+
+        // Write on-device storage to an incident report.
+        File incidentReport = new File(mDataStorageDir, PROTO_OUTPUT_FILENAME);
+        FileOutputStream incidentReportFos = new FileOutputStream(incidentReport);
+        mPowerStatsLogger.writeModelDataToFile(incidentReportFos.getFD());
+
+        // Read the incident report in to a byte array.
+        FileInputStream fis = new FileInputStream(incidentReport);
+        byte[] fileContent = new byte[(int) incidentReport.length()];
+        fis.read(fileContent);
+
+        // Parse the incident data into a PowerStatsServiceModelProto object.
+        PowerStatsServiceModelProto pssProto = PowerStatsServiceModelProto.parseFrom(fileContent);
+
+        // Valid energyConsumerId data is written to the incident report in the call to
+        // mPowerStatsLogger.writeModelDataToFile().
+        assertTrue(pssProto.energyConsumerId.length == ENERGY_CONSUMER_COUNT);
+        for (int i = 0; i < pssProto.energyConsumerId.length; i++) {
+            assertTrue(pssProto.energyConsumerId[i].energyConsumerId == i);
+        }
+
+        // No energyConsumerResults should be written to the incident report since the
+        // input buffer had only length and no data.
+        assertTrue(pssProto.energyConsumerResult.length == 0);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
index b6a0979..eedc978 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
@@ -46,41 +46,25 @@
     private static final String INSTALLER = "some.installer";
 
     private static final Correspondence<VersionedPackage, VersionedPackage> VER_PKG_CORR =
-            new Correspondence<VersionedPackage, VersionedPackage>() {
-                @Override
-                public boolean compare(VersionedPackage a, VersionedPackage b) {
-                    if (a == null || b == null) {
-                        return a == b;
-                    }
-                    return a.equals(b);
+            Correspondence.from((VersionedPackage a, VersionedPackage b) -> {
+                if (a == null || b == null) {
+                    return a == b;
                 }
-
-                @Override
-                public String toString() {
-                    return "is the same as";
-                }
-            };
+                return a.equals(b);
+            }, "is the same as");
 
     private static final Correspondence<PackageRollbackInfo.RestoreInfo,
             PackageRollbackInfo.RestoreInfo>
             RESTORE_INFO_CORR =
-            new Correspondence<PackageRollbackInfo.RestoreInfo, PackageRollbackInfo.RestoreInfo>() {
-                @Override
-                public boolean compare(PackageRollbackInfo.RestoreInfo a,
-                        PackageRollbackInfo.RestoreInfo b) {
-                    if (a == null || b == null) {
-                        return a == b;
-                    }
-                    return a.userId == b.userId
-                            && a.appId == b.appId
-                            && Objects.equals(a.seInfo, b.seInfo);
+            Correspondence.from((PackageRollbackInfo.RestoreInfo a,
+                    PackageRollbackInfo.RestoreInfo b) -> {
+                if (a == null || b == null) {
+                    return a == b;
                 }
-
-                @Override
-                public String toString() {
-                    return "is the same as";
-                }
-            };
+                return a.userId == b.userId
+                        && a.appId == b.appId
+                        && Objects.equals(a.seInfo, b.seInfo);
+            }, "is the same as");
 
     private static final String JSON_ROLLBACK_NO_EXT = "{'info':{'rollbackId':123,'packages':"
             + "[{'versionRolledBackFrom':{'packageName':'blah','longVersionCode':55},"
diff --git a/services/tests/servicestests/src/com/android/server/storage/DiskStatsFileLoggerTest.java b/services/tests/servicestests/src/com/android/server/storage/DiskStatsFileLoggerTest.java
index 46224cb..8fb2e68 100644
--- a/services/tests/servicestests/src/com/android/server/storage/DiskStatsFileLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/storage/DiskStatsFileLoggerTest.java
@@ -132,7 +132,7 @@
                     appSizes.getLong(i), cacheSizes.getLong(i));
             apps.add(app);
         }
-        assertThat(apps).containsAllOf(new AppSizeGrouping("com.test.app", 1100, 20),
+        assertThat(apps).containsAtLeast(new AppSizeGrouping("com.test.app", 1100, 20),
                 new AppSizeGrouping("com.test.app2", 11, 2));
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
index 54b5bee..682a80c 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
@@ -47,6 +47,7 @@
         ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
                 .setUserConfigAllowed(true)
                 .setAutoDetectionSupported(true)
+                .setGeoDetectionSupported(true)
                 .setAutoDetectionEnabled(true)
                 .setLocationEnabled(true)
                 .setGeoDetectionEnabled(true)
@@ -91,7 +92,7 @@
             TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities();
             assertEquals(CAPABILITY_POSSESSED,
                     capabilities.getConfigureAutoDetectionEnabledCapability());
-            assertEquals(CAPABILITY_POSSESSED,
+            assertEquals(CAPABILITY_NOT_APPLICABLE,
                     capabilities.getConfigureGeoDetectionEnabledCapability());
             assertEquals(CAPABILITY_POSSESSED,
                     capabilities.getSuggestManualTimeZoneCapability());
@@ -108,6 +109,7 @@
         ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
                 .setUserConfigAllowed(false)
                 .setAutoDetectionSupported(true)
+                .setGeoDetectionSupported(true)
                 .setAutoDetectionEnabled(true)
                 .setLocationEnabled(true)
                 .setGeoDetectionEnabled(true)
@@ -169,6 +171,7 @@
         ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
                 .setUserConfigAllowed(true)
                 .setAutoDetectionSupported(false)
+                .setGeoDetectionSupported(false)
                 .setAutoDetectionEnabled(true)
                 .setLocationEnabled(true)
                 .setGeoDetectionEnabled(true)
@@ -220,4 +223,67 @@
             assertTrue(configuration.isGeoDetectionEnabled());
         }
     }
+
+    /**
+     * Tests when {@link ConfigurationInternal#isAutoDetectionSupported()} is true, but
+     * {@link ConfigurationInternal#isGeoDetectionSupported()} is false.
+     */
+    @Test
+    public void test_geoDetectNotSupported() {
+        ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
+                .setUserConfigAllowed(true)
+                .setAutoDetectionSupported(true)
+                .setGeoDetectionSupported(false)
+                .setAutoDetectionEnabled(true)
+                .setLocationEnabled(true)
+                .setGeoDetectionEnabled(true)
+                .build();
+        {
+            ConfigurationInternal autoOnConfig = new ConfigurationInternal.Builder(baseConfig)
+                    .setAutoDetectionEnabled(true)
+                    .build();
+            assertTrue(autoOnConfig.getAutoDetectionEnabledSetting());
+            assertTrue(autoOnConfig.getGeoDetectionEnabledSetting());
+            assertTrue(autoOnConfig.getAutoDetectionEnabledBehavior());
+            assertFalse(autoOnConfig.getGeoDetectionEnabledBehavior());
+
+            TimeZoneCapabilitiesAndConfig capabilitiesAndConfig =
+                    autoOnConfig.createCapabilitiesAndConfig();
+
+            TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities();
+            assertEquals(CAPABILITY_POSSESSED,
+                    capabilities.getConfigureAutoDetectionEnabledCapability());
+            assertEquals(CAPABILITY_NOT_SUPPORTED,
+                    capabilities.getConfigureGeoDetectionEnabledCapability());
+            assertEquals(CAPABILITY_NOT_APPLICABLE,
+                    capabilities.getSuggestManualTimeZoneCapability());
+
+            TimeZoneConfiguration configuration = capabilitiesAndConfig.getConfiguration();
+            assertTrue(configuration.isAutoDetectionEnabled());
+            assertTrue(configuration.isGeoDetectionEnabled());
+        }
+        {
+            ConfigurationInternal autoOffConfig = new ConfigurationInternal.Builder(baseConfig)
+                    .setAutoDetectionEnabled(false)
+                    .build();
+            assertFalse(autoOffConfig.getAutoDetectionEnabledSetting());
+            assertTrue(autoOffConfig.getGeoDetectionEnabledSetting());
+            assertFalse(autoOffConfig.getAutoDetectionEnabledBehavior());
+            assertFalse(autoOffConfig.getGeoDetectionEnabledBehavior());
+
+            TimeZoneCapabilitiesAndConfig capabilitiesAndConfig =
+                    autoOffConfig.createCapabilitiesAndConfig();
+
+            TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities();
+            assertEquals(CAPABILITY_POSSESSED,
+                    capabilities.getConfigureAutoDetectionEnabledCapability());
+            assertEquals(CAPABILITY_NOT_SUPPORTED,
+                    capabilities.getConfigureGeoDetectionEnabledCapability());
+            assertEquals(CAPABILITY_POSSESSED, capabilities.getSuggestManualTimeZoneCapability());
+
+            TimeZoneConfiguration configuration = capabilitiesAndConfig.getConfiguration();
+            assertFalse(configuration.isAutoDetectionEnabled());
+            assertTrue(configuration.isGeoDetectionEnabled());
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
index cb27657..d2452ea 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
@@ -365,6 +365,7 @@
         final boolean geoDetectionEnabled = autoDetectionEnabled;
         return new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
                 .setAutoDetectionSupported(true)
+                .setGeoDetectionSupported(true)
                 .setUserConfigAllowed(true)
                 .setAutoDetectionEnabled(autoDetectionEnabled)
                 .setLocationEnabled(geoDetectionEnabled)
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
index 296aa73..a6ffd20 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
@@ -91,6 +91,7 @@
             new ConfigurationInternal.Builder(USER_ID)
                     .setUserConfigAllowed(false)
                     .setAutoDetectionSupported(true)
+                    .setGeoDetectionSupported(true)
                     .setAutoDetectionEnabled(false)
                     .setLocationEnabled(true)
                     .setGeoDetectionEnabled(false)
@@ -100,6 +101,7 @@
             new ConfigurationInternal.Builder(USER_ID)
                     .setUserConfigAllowed(false)
                     .setAutoDetectionSupported(true)
+                    .setGeoDetectionSupported(true)
                     .setAutoDetectionEnabled(true)
                     .setLocationEnabled(true)
                     .setGeoDetectionEnabled(true)
@@ -109,32 +111,36 @@
             new ConfigurationInternal.Builder(USER_ID)
                     .setUserConfigAllowed(true)
                     .setAutoDetectionSupported(false)
+                    .setGeoDetectionSupported(false)
                     .setAutoDetectionEnabled(false)
                     .setLocationEnabled(true)
                     .setGeoDetectionEnabled(false)
                     .build();
 
+    private static final ConfigurationInternal CONFIG_INT_AUTO_SUPPORTED_GEO_NOT_SUPPORTED =
+            new ConfigurationInternal.Builder(USER_ID)
+                    .setUserConfigAllowed(true)
+                    .setAutoDetectionSupported(true)
+                    .setGeoDetectionSupported(false)
+                    .setAutoDetectionEnabled(true)
+                    .setLocationEnabled(true)
+                    .setGeoDetectionEnabled(true)
+                    .build();
+
     private static final ConfigurationInternal CONFIG_INT_AUTO_DISABLED_GEO_DISABLED =
             new ConfigurationInternal.Builder(USER_ID)
                     .setUserConfigAllowed(true)
                     .setAutoDetectionSupported(true)
+                    .setGeoDetectionSupported(true)
                     .setAutoDetectionEnabled(false)
                     .setLocationEnabled(true)
                     .setGeoDetectionEnabled(false)
                     .build();
 
-    private static final ConfigurationInternal CONFIG_INT_AUTO_DISABLED_GEO_ENABLED =
-            new ConfigurationInternal.Builder(USER_ID)
-                    .setUserConfigAllowed(true)
-                    .setAutoDetectionSupported(true)
-                    .setAutoDetectionEnabled(false)
-                    .setLocationEnabled(true)
-                    .setGeoDetectionEnabled(true)
-                    .build();
-
     private static final ConfigurationInternal CONFIG_INT_AUTO_ENABLED_GEO_DISABLED =
             new ConfigurationInternal.Builder(USER_ID)
                     .setAutoDetectionSupported(true)
+                    .setGeoDetectionSupported(true)
                     .setUserConfigAllowed(true)
                     .setAutoDetectionEnabled(true)
                     .setLocationEnabled(true)
@@ -144,6 +150,7 @@
     private static final ConfigurationInternal CONFIG_INT_AUTO_ENABLED_GEO_ENABLED =
             new ConfigurationInternal.Builder(USER_ID)
                     .setAutoDetectionSupported(true)
+                    .setGeoDetectionSupported(true)
                     .setUserConfigAllowed(true)
                     .setAutoDetectionEnabled(true)
                     .setLocationEnabled(true)
@@ -223,14 +230,14 @@
         // The settings should not have been changed: user shouldn't have the capabilities.
         script.verifyConfigurationNotChanged();
 
-        // Update the configuration with auto detection enabled.
+        // Try to update the configuration with auto detection enabled.
         script.simulateUpdateConfiguration(
                 USER_ID, CONFIG_AUTO_ENABLED,  false /* expectedResult */);
 
         // The settings should not have been changed: user shouldn't have the capabilities.
         script.verifyConfigurationNotChanged();
 
-        // Try to  update the configuration to enable geolocation time zone detection.
+        // Try to update the configuration to enable geolocation time zone detection.
         script.simulateUpdateConfiguration(
                 USER_ID, CONFIG_GEO_DETECTION_ENABLED,  false /* expectedResult */);
 
@@ -249,7 +256,7 @@
         // The settings should not have been changed: user shouldn't have the capabilities.
         script.verifyConfigurationNotChanged();
 
-        // Update the configuration with auto detection enabled.
+        // Try to update the configuration with auto detection enabled.
         script.simulateUpdateConfiguration(
                 USER_ID, CONFIG_AUTO_ENABLED, false /* expectedResult */);
 
@@ -258,6 +265,38 @@
     }
 
     @Test
+    public void testUpdateConfiguration_autoDetectSupportedGeoNotSupported() {
+        Script script = new Script().initializeConfig(CONFIG_INT_AUTO_SUPPORTED_GEO_NOT_SUPPORTED);
+
+        // Update the configuration with auto detection disabled.
+        script.simulateUpdateConfiguration(
+                USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */);
+
+        // The settings should have been changed and the StrategyListener onChange() called.
+        ConfigurationInternal expectedConfig =
+                new ConfigurationInternal.Builder(CONFIG_INT_AUTO_SUPPORTED_GEO_NOT_SUPPORTED)
+                        .setAutoDetectionEnabled(false)
+                        .build();
+        script.verifyConfigurationChangedAndReset(expectedConfig);
+
+        // Try to update the configuration with geo detection disabled.
+        script.simulateUpdateConfiguration(
+                USER_ID, CONFIG_GEO_DETECTION_DISABLED, false /* expectedResult */);
+
+        // The settings should not have been changed: user shouldn't have the capability to modify
+        // the setting when the feature is disabled.
+        script.verifyConfigurationNotChanged();
+
+        // Try to update the configuration with geo detection enabled.
+        script.simulateUpdateConfiguration(
+                USER_ID, CONFIG_GEO_DETECTION_ENABLED, false /* expectedResult */);
+
+        // The settings should not have been changed: user shouldn't have the capability to modify
+        // the setting when the feature is disabled.
+        script.verifyConfigurationNotChanged();
+    }
+
+    @Test
     public void testEmptyTelephonySuggestions() {
         TelephonyTimeZoneSuggestion slotIndex1TimeZoneSuggestion =
                 createEmptySlotIndex1Suggestion();
diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
index 7b07102..1055069 100644
--- a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
@@ -81,9 +81,7 @@
 
     // A correspondence to compare a FrontendResource and a TunerFrontendInfo.
     private static final Correspondence<FrontendResource, TunerFrontendInfo> FR_TFI_COMPARE =
-            new Correspondence<FrontendResource, TunerFrontendInfo>() {
-            @Override
-            public boolean compare(FrontendResource actual, TunerFrontendInfo expected) {
+            Correspondence.from((FrontendResource actual, TunerFrontendInfo expected) -> {
                 if (actual == null || expected == null) {
                     return (actual == null) && (expected == null);
                 }
@@ -91,13 +89,7 @@
                 return actual.getId() == expected.getId()
                         && actual.getType() == expected.getFrontendType()
                         && actual.getExclusiveGroupId() == expected.getExclusiveGroupId();
-            }
-
-            @Override
-            public String toString() {
-                return "is correctly configured from ";
-            }
-        };
+            },  "is correctly configured from ");
 
     @Before
     public void setUp() throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
index 8cba69f..3530e38 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
@@ -34,7 +34,6 @@
     List<Integer> mUsers = new ArrayList<>();
     // Package -> [user, package]
     Map<String, Map<Integer, PackageInfo>> mPackages = new HashMap();
-    private boolean mFallbackLogicEnabled;
     private final int mNumRelros;
     private final boolean mIsDebuggable;
     private int mMultiProcessSetting;
@@ -42,10 +41,9 @@
 
     public static final int PRIMARY_USER_ID = 0;
 
-    public TestSystemImpl(WebViewProviderInfo[] packageConfigs, boolean fallbackLogicEnabled,
-            int numRelros, boolean isDebuggable, boolean multiProcessDefault) {
+    public TestSystemImpl(WebViewProviderInfo[] packageConfigs, int numRelros, boolean isDebuggable,
+            boolean multiProcessDefault) {
         mPackageConfigs = packageConfigs;
-        mFallbackLogicEnabled = fallbackLogicEnabled;
         mNumRelros = numRelros;
         mIsDebuggable = isDebuggable;
         mUsers.add(PRIMARY_USER_ID);
@@ -78,16 +76,6 @@
     public void killPackageDependents(String packageName) {}
 
     @Override
-    public boolean isFallbackLogicEnabled() {
-        return mFallbackLogicEnabled;
-    }
-
-    @Override
-    public void enableFallbackLogic(boolean enable) {
-        mFallbackLogicEnabled = enable;
-    }
-
-    @Override
     public void enablePackageForAllUsers(Context context, String packageName, boolean enable) {
         for(int userId : mUsers) {
             enablePackageForUser(packageName, enable, userId);
diff --git a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
index bbfc5ab..ebe45a6 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
@@ -67,36 +67,30 @@
     }
 
     private void setupWithPackages(WebViewProviderInfo[] packages) {
-        setupWithAllParameters(packages, false /* fallbackLogicEnabled */, 1 /* numRelros */,
-                true /* isDebuggable */, false /* multiProcessDefault */);
-    }
-
-    private void setupWithPackagesAndFallbackLogic(WebViewProviderInfo[] packages) {
-        setupWithAllParameters(packages, true /* fallbackLogicEnabled */, 1 /* numRelros */,
-                true /* isDebuggable */, false /* multiProcessDefault */);
+        setupWithAllParameters(packages, 1 /* numRelros */, true /* isDebuggable */,
+                false /* multiProcessDefault */);
     }
 
     private void setupWithPackagesAndRelroCount(WebViewProviderInfo[] packages, int numRelros) {
-        setupWithAllParameters(packages, false /* fallbackLogicEnabled */, numRelros,
-                true /* isDebuggable */, false /* multiProcessDefault */);
+        setupWithAllParameters(packages, numRelros, true /* isDebuggable */,
+                false /* multiProcessDefault */);
     }
 
     private void setupWithPackagesNonDebuggable(WebViewProviderInfo[] packages) {
-        setupWithAllParameters(packages, false /* fallbackLogicEnabled */, 1 /* numRelros */,
-                false /* isDebuggable */, false /* multiProcessDefault */);
+        setupWithAllParameters(packages, 1 /* numRelros */, false /* isDebuggable */,
+                false /* multiProcessDefault */);
     }
 
     private void setupWithPackagesAndMultiProcess(WebViewProviderInfo[] packages,
             boolean multiProcessDefault) {
-        setupWithAllParameters(packages, false /* fallbackLogicEnabled */, 1 /* numRelros */,
-                true /* isDebuggable */, multiProcessDefault);
+        setupWithAllParameters(packages, 1 /* numRelros */, true /* isDebuggable */,
+                multiProcessDefault);
     }
 
-    private void setupWithAllParameters(WebViewProviderInfo[] packages,
-            boolean fallbackLogicEnabled, int numRelros, boolean isDebuggable,
-            boolean multiProcessDefault) {
-        TestSystemImpl testing = new TestSystemImpl(packages, fallbackLogicEnabled, numRelros,
-                isDebuggable, multiProcessDefault);
+    private void setupWithAllParameters(WebViewProviderInfo[] packages, int numRelros,
+            boolean isDebuggable, boolean multiProcessDefault) {
+        TestSystemImpl testing = new TestSystemImpl(packages, numRelros, isDebuggable,
+                multiProcessDefault);
         mTestSystemImpl = Mockito.spy(testing);
         mWebViewUpdateServiceImpl =
             new WebViewUpdateServiceImpl(null /*Context*/, mTestSystemImpl);
@@ -514,49 +508,29 @@
     }
 
     /**
-     * Scenario for testing migrating away from the fallback logic.
-     * We start with a primary package that's a disabled fallback, and an enabled secondary,
-     * so that the fallback being re-enabled will cause a provider switch, as that covers
-     * the most complex case.
+     * Scenario for testing re-enabling a fallback package.
      */
     @Test
-    public void testFallbackLogicMigration() {
-        String primaryPackage = "primary";
-        String secondaryPackage = "secondary";
+    public void testFallbackPackageEnabling() {
+        String testPackage = "testFallback";
         WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
             new WebViewProviderInfo(
-                    primaryPackage, "", true /* default available */, true /* fallback */, null),
-            new WebViewProviderInfo(
-                    secondaryPackage, "", true /* default available */, false /* fallback */,
-                    null)};
-        setupWithPackagesAndFallbackLogic(packages);
+                    testPackage, "", true /* default available */, true /* fallback */, null)};
+        setupWithPackages(packages);
         mTestSystemImpl.setPackageInfo(
-                createPackageInfo(primaryPackage, false /* enabled */ , true /* valid */,
-                    true /* installed */));
-        mTestSystemImpl.setPackageInfo(
-                createPackageInfo(secondaryPackage, true /* enabled */ , true /* valid */,
+                createPackageInfo(testPackage, false /* enabled */ , true /* valid */,
                     true /* installed */));
 
-        // Check that the boot time logic re-enables and chooses the primary, and disables the
-        // fallback logic.
+        // Check that the boot time logic re-enables the fallback package.
         runWebViewBootPreparationOnMainSync();
         Mockito.verify(mTestSystemImpl).enablePackageForAllUsers(
-                Matchers.anyObject(), Mockito.eq(primaryPackage), Mockito.eq(true));
-        checkPreparationPhasesForPackage(primaryPackage, 1);
-        assertFalse(mTestSystemImpl.isFallbackLogicEnabled());
+                Matchers.anyObject(), Mockito.eq(testPackage), Mockito.eq(true));
 
-        // Disable primary again
-        mTestSystemImpl.setPackageInfo(createPackageInfo(primaryPackage, false /* enabled */,
-                        true /* valid */, true /* installed */));
-        mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
-                WebViewUpdateService.PACKAGE_CHANGED, TestSystemImpl.PRIMARY_USER_ID);
-        checkPreparationPhasesForPackage(secondaryPackage, 1);
-
-        // Run boot logic again and check that we didn't re-enable the primary a second time.
-        runWebViewBootPreparationOnMainSync();
-        Mockito.verify(mTestSystemImpl, Mockito.times(1)).enablePackageForAllUsers(
-                Matchers.anyObject(), Mockito.eq(primaryPackage), Mockito.eq(true));
-        checkPreparationPhasesForPackage(secondaryPackage, 2);
+        // Fake the message about the enabling having changed the package state,
+        // and check we now use that package.
+        mWebViewUpdateServiceImpl.packageStateChanged(
+                testPackage, WebViewUpdateService.PACKAGE_CHANGED, TestSystemImpl.PRIMARY_USER_ID);
+        checkPreparationPhasesForPackage(testPackage, 1);
     }
 
     /**
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index abcc14c..9766cb5 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -402,31 +402,33 @@
     }
 
     private void verifyNeverVibrate() {
-        verify(mVibrator, never()).vibrate(anyInt(), anyString(), any(), anyString(), any());
+        verify(mVibrator, never()).vibrate(anyInt(), anyString(), any(), anyString(),
+                any(AudioAttributes.class));
     }
 
     private void verifyVibrate() {
         verify(mVibrator, times(1)).vibrate(anyInt(), anyString(), argThat(mVibrateOnceMatcher),
-                anyString(), any());
+                anyString(), any(AudioAttributes.class));
     }
 
     private void verifyVibrate(int times) {
-        verify(mVibrator, times(times)).vibrate(anyInt(), anyString(), any(), anyString(), any());
+        verify(mVibrator, times(times)).vibrate(anyInt(), anyString(), any(), anyString(),
+                any(AudioAttributes.class));
     }
 
     private void verifyVibrateLooped() {
         verify(mVibrator, times(1)).vibrate(anyInt(), anyString(), argThat(mVibrateLoopMatcher),
-                anyString(), any());
+                anyString(), any(AudioAttributes.class));
     }
 
     private void verifyDelayedVibrateLooped() {
         verify(mVibrator, timeout(MAX_VIBRATION_DELAY).times(1)).vibrate(anyInt(), anyString(),
-                argThat(mVibrateLoopMatcher), anyString(), any());
+                argThat(mVibrateLoopMatcher), anyString(), any(AudioAttributes.class));
     }
 
     private void verifyDelayedVibrate() {
         verify(mVibrator, timeout(MAX_VIBRATION_DELAY).times(1)).vibrate(anyInt(), anyString(),
-                argThat(mVibrateOnceMatcher), anyString(), any());
+                argThat(mVibrateOnceMatcher), anyString(), any(AudioAttributes.class));
     }
 
     private void verifyStopVibrate() {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index d7e431f..e304083 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -690,6 +690,39 @@
     }
 
     @Test
+    public void unbindOtherUserServices() throws PackageManager.NameNotFoundException {
+        Context context = mock(Context.class);
+        PackageManager pm = mock(PackageManager.class);
+        ApplicationInfo ai = new ApplicationInfo();
+        ai.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
+
+        when(context.getPackageName()).thenReturn(mContext.getPackageName());
+        when(context.getUserId()).thenReturn(mContext.getUserId());
+        when(context.getPackageManager()).thenReturn(pm);
+        when(pm.getApplicationInfo(anyString(), anyInt())).thenReturn(ai);
+
+        ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, mIpm,
+                APPROVAL_BY_COMPONENT);
+        ComponentName cn = ComponentName.unflattenFromString("a/a");
+
+        when(context.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(invocation -> {
+            Object[] args = invocation.getArguments();
+            ServiceConnection sc = (ServiceConnection) args[1];
+            sc.onServiceConnected(cn, mock(IBinder.class));
+            return true;
+        });
+
+        service.registerService(cn, 0);
+        service.registerService(cn, 10);
+        service.registerService(cn, 11);
+        service.unbindOtherUserServices(11);
+
+        assertFalse(service.isBound(cn, 0));
+        assertFalse(service.isBound(cn, 10));
+        assertTrue(service.isBound(cn, 11));
+    }
+
+    @Test
     public void testPackageUninstall_packageNoLongerInApprovedList() throws Exception {
         for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
             ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index ed6a20b..1100496 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -43,6 +43,9 @@
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
+import static android.app.PendingIntent.FLAG_IMMUTABLE;
+import static android.app.PendingIntent.FLAG_MUTABLE;
+import static android.app.PendingIntent.FLAG_ONE_SHOT;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
 import static android.content.pm.PackageManager.FEATURE_WATCH;
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
@@ -110,6 +113,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.IIntentSender;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
@@ -248,6 +252,11 @@
     Resources mResources;
     @Mock
     RankingHandler mRankingHandler;
+    @Mock
+    ActivityManagerInternal mAmi;
+
+    @Mock
+    IIntentSender pi1;
 
     private static final int MAX_POST_DELAY = 1000;
 
@@ -392,7 +401,6 @@
 
         DeviceIdleInternal deviceIdleInternal = mock(DeviceIdleInternal.class);
         when(deviceIdleInternal.getNotificationAllowlistDuration()).thenReturn(3000L);
-        ActivityManagerInternal activityManagerInternal = mock(ActivityManagerInternal.class);
 
         LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
         LocalServices.addService(UriGrantsManagerInternal.class, mUgmInternal);
@@ -403,7 +411,7 @@
         LocalServices.removeServiceForTest(DeviceIdleInternal.class);
         LocalServices.addService(DeviceIdleInternal.class, deviceIdleInternal);
         LocalServices.removeServiceForTest(ActivityManagerInternal.class);
-        LocalServices.addService(ActivityManagerInternal.class, activityManagerInternal);
+        LocalServices.addService(ActivityManagerInternal.class, mAmi);
 
         doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any());
 
@@ -477,7 +485,7 @@
                 mGroupHelper, mAm, mAtm, mAppUsageStats,
                 mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal,
                 mAppOpsManager, mUm, mHistoryManager, mStatsManager,
-                mock(TelephonyManager.class));
+                mock(TelephonyManager.class), mAmi);
         mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
 
         mService.setAudioManager(mAudioManager);
@@ -674,7 +682,8 @@
         }
         Notification.Builder nb = new Notification.Builder(mContext, channel.getId())
                 .setContentTitle("foo")
-                .setSmallIcon(android.R.drawable.sym_def_app_icon);
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .addAction(new Notification.Action.Builder(null, "test", null).build());
         if (extender != null) {
             nb.extend(extender);
         }
@@ -810,6 +819,7 @@
         PendingIntent pendingIntent = mock(PendingIntent.class);
         Intent intent = mock(Intent.class);
         when(pendingIntent.getIntent()).thenReturn(intent);
+        when(pendingIntent.getTarget()).thenReturn(pi1);
 
         ActivityInfo info = new ActivityInfo();
         info.resizeMode = RESIZE_MODE_RESIZEABLE;
@@ -1873,7 +1883,7 @@
     public void testGroupInstanceIds() throws Exception {
         final NotificationRecord group1 = generateNotificationRecord(
                 mTestNotificationChannel, 1, "group1", true);
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "testFindGroupNotificationsLocked",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "testGroupInstanceIds",
                 group1.getSbn().getId(), group1.getSbn().getNotification(),
                 group1.getSbn().getUserId());
         waitForIdle();
@@ -1881,7 +1891,7 @@
         // same group, child, should be returned
         final NotificationRecord group1Child = generateNotificationRecord(
                 mTestNotificationChannel, 2, "group1", false);
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "testFindGroupNotificationsLocked",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "testGroupInstanceIds",
                 group1Child.getSbn().getId(),
                 group1Child.getSbn().getNotification(), group1Child.getSbn().getUserId());
         waitForIdle();
@@ -7134,4 +7144,159 @@
         inOrder.verify(parent).recordDismissalSentiment(anyInt());
         inOrder.verify(child).recordDismissalSentiment(anyInt());
     }
+
+    @Test
+    public void testImmutableBubbleIntent() throws Exception {
+        when(mAmi.getPendingIntentFlags(pi1))
+                .thenReturn(FLAG_IMMUTABLE | FLAG_ONE_SHOT);
+        NotificationRecord r = generateMessageBubbleNotifRecord(true,
+                mTestNotificationChannel, 7, "testImmutableBubbleIntent", null, false);
+        try {
+            mBinderService.enqueueNotificationWithTag(PKG, PKG, r.getSbn().getTag(),
+                    r.getSbn().getId(), r.getNotification(), r.getSbn().getUserId());
+
+            waitForIdle();
+            fail("Allowed a bubble with an immutable intent to be posted");
+        } catch (IllegalArgumentException e) {
+            // good
+        }
+    }
+
+    @Test
+    public void testMutableBubbleIntent() throws Exception {
+        when(mAmi.getPendingIntentFlags(pi1))
+                .thenReturn(FLAG_MUTABLE | FLAG_ONE_SHOT);
+        NotificationRecord r = generateMessageBubbleNotifRecord(true,
+                mTestNotificationChannel, 7, "testMutableBubbleIntent", null, false);
+
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, r.getSbn().getTag(),
+                r.getSbn().getId(), r.getNotification(), r.getSbn().getUserId());
+
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(r.getSbn().getPackageName());
+        assertEquals(1, notifs.length);
+    }
+
+    @Test
+    public void testImmutableDirectReplyActionIntent() throws Exception {
+        when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
+                .thenReturn(FLAG_IMMUTABLE | FLAG_ONE_SHOT);
+        NotificationRecord r = generateMessageBubbleNotifRecord(false,
+                mTestNotificationChannel, 7, "testImmutableDirectReplyActionIntent", null, false);
+        try {
+            mBinderService.enqueueNotificationWithTag(PKG, PKG, r.getSbn().getTag(),
+                    r.getSbn().getId(), r.getNotification(), r.getSbn().getUserId());
+
+            waitForIdle();
+            fail("Allowed a direct reply with an immutable intent to be posted");
+        } catch (IllegalArgumentException e) {
+            // good
+        }
+    }
+
+    @Test
+    public void testMutableDirectReplyActionIntent() throws Exception {
+        when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
+                .thenReturn(FLAG_MUTABLE | FLAG_ONE_SHOT);
+        NotificationRecord r = generateMessageBubbleNotifRecord(false,
+                mTestNotificationChannel, 7, "testMutableDirectReplyActionIntent", null, false);
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, r.getSbn().getTag(),
+                r.getSbn().getId(), r.getNotification(), r.getSbn().getUserId());
+
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(r.getSbn().getPackageName());
+        assertEquals(1, notifs.length);
+    }
+
+    @Test
+    public void testImmutableDirectReplyContextualActionIntent() throws Exception {
+        when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
+                .thenReturn(FLAG_IMMUTABLE | FLAG_ONE_SHOT);
+        when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+
+        NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+        ArrayList<Notification.Action> extraAction = new ArrayList<>();
+        RemoteInput remoteInput = new RemoteInput.Builder("reply_key").setLabel("reply").build();
+        PendingIntent inputIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+        Icon icon = Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon);
+        Notification.Action replyAction = new Notification.Action.Builder(icon, "Reply",
+                inputIntent).addRemoteInput(remoteInput)
+                .build();
+        extraAction.add(replyAction);
+        Bundle signals = new Bundle();
+        signals.putParcelableArrayList(Adjustment.KEY_CONTEXTUAL_ACTIONS, extraAction);
+        Adjustment adjustment = new Adjustment(r.getSbn().getPackageName(), r.getKey(), signals, "",
+                r.getUser());
+        r.addAdjustment(adjustment);
+        r.applyAdjustments();
+
+        try {
+            mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), r.getSbn().getId(),
+                    r.getSbn().getTag(), r,false);
+            fail("Allowed a contextual direct reply with an immutable intent to be posted");
+        } catch (IllegalArgumentException e) {
+            // good
+        }
+    }
+
+    @Test
+    public void testMutableDirectReplyContextualActionIntent() throws Exception {
+        when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
+                .thenReturn(FLAG_MUTABLE | FLAG_ONE_SHOT);
+        when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+        NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+        ArrayList<Notification.Action> extraAction = new ArrayList<>();
+        RemoteInput remoteInput = new RemoteInput.Builder("reply_key").setLabel("reply").build();
+        PendingIntent inputIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+        Icon icon = Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon);
+        Notification.Action replyAction = new Notification.Action.Builder(icon, "Reply",
+                inputIntent).addRemoteInput(remoteInput)
+                .build();
+        extraAction.add(replyAction);
+        Bundle signals = new Bundle();
+        signals.putParcelableArrayList(Adjustment.KEY_CONTEXTUAL_ACTIONS, extraAction);
+        Adjustment adjustment = new Adjustment(r.getSbn().getPackageName(), r.getKey(), signals, "",
+                r.getUser());
+        r.addAdjustment(adjustment);
+        r.applyAdjustments();
+
+        mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), r.getSbn().getId(),
+                r.getSbn().getTag(), r,false);
+    }
+
+    @Test
+    public void testImmutableActionIntent() throws Exception {
+        when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
+                .thenReturn(FLAG_IMMUTABLE | FLAG_ONE_SHOT);
+        NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, r.getSbn().getTag(),
+                r.getSbn().getId(), r.getNotification(), r.getSbn().getUserId());
+
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(r.getSbn().getPackageName());
+        assertEquals(1, notifs.length);
+    }
+
+    @Test
+    public void testImmutableContextualActionIntent() throws Exception {
+        when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
+                .thenReturn(FLAG_IMMUTABLE | FLAG_ONE_SHOT);
+        when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+        NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+        ArrayList<Notification.Action> extraAction = new ArrayList<>();
+        extraAction.add(new Notification.Action(0, "hello", null));
+        Bundle signals = new Bundle();
+        signals.putParcelableArrayList(Adjustment.KEY_CONTEXTUAL_ACTIONS, extraAction);
+        Adjustment adjustment = new Adjustment(r.getSbn().getPackageName(), r.getKey(), signals, "",
+                r.getUser());
+        r.addAdjustment(adjustment);
+        r.applyAdjustments();
+
+        mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), r.getSbn().getId(),
+                    r.getSbn().getTag(), r,false);
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
index 3281c3f..a80f62a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
@@ -32,6 +32,7 @@
 import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
 import android.app.AppOpsManager;
 import android.app.IActivityManager;
 import android.app.IUriGrantsManager;
@@ -154,7 +155,8 @@
                     mock(DevicePolicyManagerInternal.class), mock(IUriGrantsManager.class),
                     mock(UriGrantsManagerInternal.class),
                     mock(AppOpsManager.class), mUm, mock(NotificationHistoryManager.class),
-                    mock(StatsManager.class), mock(TelephonyManager.class));
+                    mock(StatsManager.class), mock(TelephonyManager.class),
+                    mock(ActivityManagerInternal.class));
         } catch (SecurityException e) {
             if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
                 throw e;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java
index eca71b6..e5ae2d3 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java
@@ -305,6 +305,7 @@
         //when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(),
          //       anyString(), anyInt(), any())).thenReturn(true);
 
-        assertThat(mShortcutHelper.getValidShortcutInfo("a", "p", UserHandle.SYSTEM)).isSameAs(si);
+        assertThat(mShortcutHelper.getValidShortcutInfo("a", "p", UserHandle.SYSTEM))
+                .isSameInstanceAs(si);
     }
 }
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index e3c795d..9099272 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -39,6 +39,8 @@
     <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
     <uses-permission android:name="android.permission.STATUS_BAR" />
     <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT" />
+    <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
+    <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE" />
 
     <!-- TODO: Remove largeHeap hack when memory leak is fixed (b/123984854) -->
     <application android:debuggable="true"
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
index cf07183..f026b85 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
@@ -300,15 +300,15 @@
     }
 
     @Test
-    public void testRemoveStackInWindowingModes() {
-        removeStackTests(() -> mRootWindowContainer.removeStacksInWindowingModes(
+    public void testRemoveRootTaskInWindowingModes() {
+        removeStackTests(() -> mRootWindowContainer.removeRootTasksInWindowingModes(
                 WINDOWING_MODE_FULLSCREEN));
     }
 
     @Test
     public void testRemoveStackWithActivityTypes() {
-        removeStackTests(
-                () -> mRootWindowContainer.removeStacksWithActivityTypes(ACTIVITY_TYPE_STANDARD));
+        removeStackTests(() -> mRootWindowContainer.removeRootTasksWithActivityTypes(
+                ACTIVITY_TYPE_STANDARD));
     }
 
     private void removeStackTests(Runnable runnable) {
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 dd85484..dd4d718 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -1523,8 +1523,8 @@
             // Return error to skip unnecessary operation.
             doReturn(WindowManagerGlobal.ADD_STARTING_NOT_NEEDED).when(session).addToDisplay(
                     any() /* window */,  any() /* attrs */,
-                    anyInt() /* viewVisibility */, anyInt() /* displayId */, any() /* outFrame */,
-                    any() /* outContentInsets */, any() /* outStableInsets */,
+                    anyInt() /* viewVisibility */, anyInt() /* displayId */,
+                    any() /* requestedVisibility */, any() /* outFrame */,
                     any() /* outDisplayCutout */, any() /* outInputChannel */,
                     any() /* outInsetsState */, any() /* outActiveControls */);
             TaskSnapshotSurface.create(mAtm.mWindowManager, mActivity, snapshot);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index 0311657..3d31824 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -23,20 +23,24 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doCallRealMethod;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.when;
 
 import android.app.Activity;
+import android.app.ActivityManager;
 import android.app.PictureInPictureParams;
 import android.app.servertransaction.ClientTransaction;
 import android.app.servertransaction.EnterPipRequestedItem;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.IBinder;
+import android.os.PowerManager;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
 import android.view.IDisplayWindowListener;
@@ -50,6 +54,7 @@
 import org.mockito.MockitoSession;
 
 import java.util.ArrayList;
+import java.util.function.Consumer;
 
 /**
  * Tests for the {@link ActivityTaskManagerService} class.
@@ -160,7 +165,7 @@
         };
         mAtm.mWindowManager.registerDisplayWindowListener(listener);
         // Check that existing displays call added
-        assertEquals(1, added.size());
+        assertEquals(mRootWindowContainer.getChildCount(), added.size());
         assertEquals(0, changed.size());
         assertEquals(0, removed.size());
         added.clear();
@@ -233,7 +238,11 @@
                 .setTask(mRootWindowContainer.getDefaultTaskDisplayArea().getOrCreateRootHomeTask())
                 .build();
         final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        activity.setState(Task.ActivityState.RESUMED, "test");
         mSupervisor.endDeferResume();
+
+        assertEquals(activity.app, mAtm.mInternal.getTopApp());
+
         // Assume the activity is finishing and hidden because it was crashed.
         activity.finishing = true;
         activity.mVisibleRequested = false;
@@ -246,6 +255,45 @@
         mAtm.mInternal.handleAppDied(activity.app, false /* restarting */,
                 null /* finishInstrumentationCallback */);
         assertEquals(Task.ActivityState.RESUMED, homeActivity.getState());
+        assertEquals(homeActivity.app, mAtm.mInternal.getTopApp());
+    }
+
+    @Test
+    public void testUpdateSleep() {
+        doCallRealMethod().when(mWm.mRoot).hasAwakeDisplay();
+        mSupervisor.mGoingToSleepWakeLock = mock(PowerManager.WakeLock.class);
+        final ActivityRecord homeActivity = new ActivityBuilder(mAtm)
+                .setTask(mWm.mRoot.getDefaultTaskDisplayArea().getOrCreateRootHomeTask()).build();
+        final ActivityRecord topActivity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        topActivity.setState(Task.ActivityState.RESUMED, "test");
+
+        final Consumer<ActivityRecord> assertTopNonSleeping = activity -> {
+            assertFalse(mAtm.mInternal.isSleeping());
+            assertEquals(ActivityManager.PROCESS_STATE_TOP, mAtm.mInternal.getTopProcessState());
+            assertEquals(activity.app, mAtm.mInternal.getTopApp());
+        };
+        assertTopNonSleeping.accept(topActivity);
+
+        // Sleep all displays.
+        mWm.mRoot.forAllDisplays(display -> doReturn(true).when(display).shouldSleep());
+        mAtm.updateSleepIfNeededLocked();
+
+        assertEquals(Task.ActivityState.PAUSING, topActivity.getState());
+        assertTrue(mAtm.mInternal.isSleeping());
+        assertEquals(ActivityManager.PROCESS_STATE_TOP_SLEEPING,
+                mAtm.mInternal.getTopProcessState());
+        // The top app should not change while sleeping.
+        assertEquals(topActivity.app, mAtm.mInternal.getTopApp());
+
+        // Move the current top to back, the top app should update to the next activity.
+        topActivity.getRootTask().moveToBack("test", null /* self */);
+        assertEquals(homeActivity.app, mAtm.mInternal.getTopApp());
+
+        // Wake all displays.
+        mWm.mRoot.forAllDisplays(display -> doReturn(false).when(display).shouldSleep());
+        mAtm.updateSleepIfNeededLocked();
+
+        assertTopNonSleeping.accept(homeActivity);
     }
 }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
index e17601e..01c1f1f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -23,6 +23,8 @@
 import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
 import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS;
 import static com.android.server.wm.DisplayArea.Type.ANY;
@@ -37,14 +39,18 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyZeroInteractions;
 
+import android.content.pm.ActivityInfo;
 import android.graphics.Rect;
 import android.os.Binder;
 import android.platform.test.annotations.Presubmit;
 import android.view.SurfaceControl;
+import android.view.View;
+import android.view.WindowManager;
 
 import com.google.android.collect.Lists;
 
@@ -405,6 +411,55 @@
                 childBounds1, windowToken.getMaxBounds());
     }
 
+    @Test
+    public void testGetOrientation() {
+        final DisplayArea.Tokens area = new DisplayArea.Tokens(mWms, ABOVE_TASKS, "test");
+        final WindowToken token = createWindowToken(TYPE_APPLICATION_OVERLAY);
+        spyOn(token);
+        doReturn(mock(DisplayContent.class)).when(token).getDisplayContent();
+        doNothing().when(token).setParent(any());
+        final WindowState win = createWindowState(token);
+        spyOn(win);
+        doNothing().when(win).setParent(any());
+        win.mAttrs.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+        token.addChild(win, 0);
+        area.addChild(token);
+
+        doReturn(true).when(win).isVisible();
+
+        assertEquals("Visible window can request orientation",
+                ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE,
+                area.getOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR));
+
+        doReturn(false).when(win).isVisible();
+
+        assertEquals("Invisible window cannot request orientation",
+                ActivityInfo.SCREEN_ORIENTATION_NOSENSOR,
+                area.getOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR));
+    }
+
+    @Test
+    public void testSetIgnoreOrientationRequest() {
+        final DisplayArea.Tokens area = new DisplayArea.Tokens(mWms, ABOVE_TASKS, "test");
+        final WindowToken token = createWindowToken(TYPE_APPLICATION_OVERLAY);
+        spyOn(token);
+        doReturn(mock(DisplayContent.class)).when(token).getDisplayContent();
+        doNothing().when(token).setParent(any());
+        final WindowState win = createWindowState(token);
+        spyOn(win);
+        doNothing().when(win).setParent(any());
+        win.mAttrs.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+        token.addChild(win, 0);
+        area.addChild(token);
+        doReturn(true).when(win).isVisible();
+
+        assertEquals(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, area.getOrientation());
+
+        area.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+        assertEquals(ActivityInfo.SCREEN_ORIENTATION_UNSET, area.getOrientation());
+    }
+
     private static class TestDisplayArea<T extends WindowContainer> extends DisplayArea<T> {
         private TestDisplayArea(WindowManagerService wms, Rect bounds) {
             super(wms, ANY, "half display area");
@@ -417,6 +472,13 @@
         }
     }
 
+    private WindowState createWindowState(WindowToken token) {
+        return new WindowState(mWms, mock(Session.class), new TestIWindow(), token,
+                null /* parentWindow */, 0 /* appOp */, new WindowManager.LayoutParams(),
+                View.VISIBLE, 0 /* ownerId */, 0 /* showUserId */,
+                false /* ownerCanAddInternalSystemWindow */);
+    }
+
     private WindowToken createWindowToken(int type) {
         return new WindowToken(mWmsRule.getWindowManagerService(), new Binder(),
                 type, false /* persist */, null /* displayContent */,
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 4a90466..4d0d3b2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -526,7 +526,8 @@
         for (int i = 0; i < types.length; i++) {
             final int type = types[i];
             windows[i] = createWindow(null /* parent */, type, displayContent, "window-" + type);
-            windows[i].mHasSurface = false;
+            windows[i].setHasSurface(true);
+            windows[i].mWinAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING;
         }
         return windows;
     }
@@ -1583,6 +1584,28 @@
         assertFalse(publicDc.forceDesktopMode());
     }
 
+    @Test
+    public void testDisplaySettingsReappliedWhenDisplayChanged() {
+        final DisplayInfo displayInfo = new DisplayInfo();
+        displayInfo.copyFrom(mDisplayInfo);
+        final DisplayContent dc = createNewDisplay(displayInfo);
+
+        // Generate width/height/density values different from the default of the display.
+        final int forcedWidth = dc.mBaseDisplayWidth + 1;
+        final int forcedHeight = dc.mBaseDisplayHeight + 1;;
+        final int forcedDensity = dc.mBaseDisplayDensity + 1;;
+        // Update the forced size and density in settings and the unique id to simualate a display
+        // remap.
+        dc.mWmService.mDisplayWindowSettings.setForcedSize(dc, forcedWidth, forcedHeight);
+        dc.mWmService.mDisplayWindowSettings.setForcedDensity(dc, forcedDensity, 0 /* userId */);
+        dc.mCurrentUniqueDisplayId = mDisplayInfo.uniqueId + "-test";
+        // Trigger display changed.
+        dc.onDisplayChanged();
+        // Ensure overridden size and denisty match the most up-to-date values in settings for the
+        // display.
+        verifySizes(dc, forcedWidth, forcedHeight, forcedDensity);
+    }
+
     private boolean isOptionsPanelAtRight(int displayId) {
         return (mWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT;
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index 2920c1d..3598cd8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -157,6 +157,8 @@
         mDisplayBounds.set(0, 0, mFrames.mDisplayWidth, mFrames.mDisplayHeight);
         mDisplayContent.mDisplayFrames = mFrames;
         mDisplayContent.setBounds(mDisplayBounds);
+        mDisplayContent.getInsetsStateController().getRawInsetsState().setDisplayFrame(
+                mDisplayBounds);
     }
 
     private DisplayFrames createDisplayFrames() {
@@ -339,7 +341,7 @@
     @Test
     public void layoutWindowLw_fitInsetsIgnoringVisibility() {
         final InsetsState state =
-                mDisplayContent.getInsetsPolicy().getInsetsForDispatch(mWindow);
+                mDisplayContent.getInsetsPolicy().getInsetsForWindow(mWindow);
         state.getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
         state.getSource(InsetsState.ITYPE_NAVIGATION_BAR).setVisible(false);
         mWindow.mAttrs.setFitInsetsIgnoringVisibility(true);
@@ -359,7 +361,7 @@
     @Test
     public void layoutWindowLw_fitInsetsNotIgnoringVisibility() {
         final InsetsState state =
-                mDisplayContent.getInsetsPolicy().getInsetsForDispatch(mWindow);
+                mDisplayContent.getInsetsPolicy().getInsetsForWindow(mWindow);
         state.getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
         state.getSource(InsetsState.ITYPE_NAVIGATION_BAR).setVisible(false);
         mWindow.mAttrs.setFitInsetsIgnoringVisibility(false);
@@ -520,9 +522,11 @@
         mWindow.mAttrs.flags =
                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
-        mDisplayContent.getInsetsPolicy().getInsetsForDispatch(mWindow)
+        mDisplayContent.getInsetsPolicy().getInsetsForWindow(mWindow)
                 .getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
-        mWindow.getRequestedInsetsState().getSource(ITYPE_STATUS_BAR).setVisible(false);
+        final InsetsState requestedState = new InsetsState();
+        requestedState.getSource(ITYPE_STATUS_BAR).setVisible(false);
+        mWindow.updateRequestedVisibility(requestedState);
         addWindow(mWindow);
 
         mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
@@ -542,9 +546,11 @@
         mWindow.mAttrs.flags =
                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
-        mDisplayContent.getInsetsPolicy().getInsetsForDispatch(mWindow)
+        mDisplayContent.getInsetsPolicy().getInsetsForWindow(mWindow)
                 .getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
-        mWindow.getRequestedInsetsState().getSource(ITYPE_STATUS_BAR).setVisible(false);
+        final InsetsState requestedState = new InsetsState();
+        requestedState.getSource(ITYPE_STATUS_BAR).setVisible(false);
+        mWindow.updateRequestedVisibility(requestedState);
         mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
         addWindow(mWindow);
 
@@ -770,31 +776,30 @@
 
     @Test
     public void layoutHint_appWindow() {
-        mWindow.mAttrs.flags =
-                FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+        mWindow.mAttrs.setFitInsetsTypes(0);
 
         // Initialize DisplayFrames
         mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
 
         final Rect outFrame = new Rect();
-        final Rect outContentInsets = new Rect();
-        final Rect outStableInsets = new Rect();
         final DisplayCutout.ParcelableWrapper outDisplayCutout =
                 new DisplayCutout.ParcelableWrapper();
+        final InsetsState outState = new InsetsState();
 
         mDisplayPolicy.getLayoutHint(mWindow.mAttrs, null /* windowToken */, outFrame,
-                outContentInsets, outStableInsets, outDisplayCutout);
+                outDisplayCutout, outState, true /* localClient */);
 
-        assertThat(outFrame, is(mFrames.mUnrestricted));
-        assertThat(outContentInsets, is(new Rect(0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT)));
-        assertThat(outStableInsets, is(new Rect(0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT)));
+        assertThat(outFrame, is(outState.getDisplayFrame()));
         assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
+        assertThat(outState.getSource(ITYPE_STATUS_BAR).getFrame(),
+                is(new Rect(0, 0, DISPLAY_WIDTH, STATUS_BAR_HEIGHT)));
+        assertThat(outState.getSource(ITYPE_NAVIGATION_BAR).getFrame(),
+                is(new Rect(0, DISPLAY_HEIGHT - NAV_BAR_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT)));
     }
 
     @Test
     public void layoutHint_appWindowInTask() {
-        mWindow.mAttrs.flags =
-                FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+        mWindow.mAttrs.setFitInsetsTypes(0);
 
         // Initialize DisplayFrames
         mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
@@ -805,24 +810,24 @@
         task.getWindowConfiguration().setBounds(taskBounds);
 
         final Rect outFrame = new Rect();
-        final Rect outContentInsets = new Rect();
-        final Rect outStableInsets = new Rect();
         final DisplayCutout.ParcelableWrapper outDisplayCutout =
                 new DisplayCutout.ParcelableWrapper();
+        final InsetsState outState = new InsetsState();
 
-        mDisplayPolicy.getLayoutHint(mWindow.mAttrs, mWindow.mToken, outFrame,
-                outContentInsets, outStableInsets, outDisplayCutout);
+        mDisplayPolicy.getLayoutHint(mWindow.mAttrs, mWindow.mToken, outFrame, outDisplayCutout,
+                outState, true /* localClient */);
 
         assertThat(outFrame, is(taskBounds));
-        assertThat(outContentInsets, is(new Rect()));
-        assertThat(outStableInsets, is(new Rect()));
         assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
+        assertThat(outState.getSource(ITYPE_STATUS_BAR).getFrame(),
+                is(new Rect(0, 0, DISPLAY_WIDTH, STATUS_BAR_HEIGHT)));
+        assertThat(outState.getSource(ITYPE_NAVIGATION_BAR).getFrame(),
+                is(new Rect(0, DISPLAY_HEIGHT - NAV_BAR_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT)));
     }
 
     @Test
     public void layoutHint_appWindowInTask_outsideContentFrame() {
-        mWindow.mAttrs.flags =
-                FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+        mWindow.mAttrs.setFitInsetsTypes(0);
 
         // Initialize DisplayFrames
         mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
@@ -839,18 +844,19 @@
         task.getWindowConfiguration().setBounds(taskBounds);
 
         final Rect outFrame = new Rect();
-        final Rect outContentInsets = new Rect();
-        final Rect outStableInsets = new Rect();
         final DisplayCutout.ParcelableWrapper outDisplayCutout =
                 new DisplayCutout.ParcelableWrapper();
+        final InsetsState outState = new InsetsState();
 
-        mDisplayPolicy.getLayoutHint(mWindow.mAttrs, mWindow.mToken, outFrame, outContentInsets,
-                outStableInsets, outDisplayCutout);
+        mDisplayPolicy.getLayoutHint(mWindow.mAttrs, mWindow.mToken, outFrame, outDisplayCutout,
+                outState, true /* localClient */);
 
         assertThat(outFrame, is(taskBounds));
-        assertThat(outContentInsets, is(new Rect()));
-        assertThat(outStableInsets, is(new Rect()));
         assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
+        assertThat(outState.getSource(ITYPE_STATUS_BAR).getFrame(),
+                is(new Rect(0, 0, DISPLAY_WIDTH, STATUS_BAR_HEIGHT)));
+        assertThat(outState.getSource(ITYPE_NAVIGATION_BAR).getFrame(),
+                is(new Rect(0, DISPLAY_HEIGHT - NAV_BAR_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT)));
     }
 
     /**
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index a55423a..21bdc9e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -313,8 +313,8 @@
         // App requests to hide navigation bar.
         final InsetsState requestedState = new InsetsState();
         requestedState.getSource(ITYPE_NAVIGATION_BAR).setVisible(false);
-        mAppWindow.updateRequestedInsetsState(requestedState);
-        insetsPolicy.onInsetsModified(mAppWindow, requestedState);
+        mAppWindow.updateRequestedVisibility(requestedState);
+        insetsPolicy.onInsetsModified(mAppWindow);
         assertNotNull(displayPolicy.mInputConsumer);
 
         // App still requests to hide navigation bar, but without BEHAVIOR_SHOW_BARS_BY_TOUCH.
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 9b2a2db..e1aca55 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -664,20 +664,20 @@
     // Non-rotation API Tests
     // ========================
     @Test
-    public void testRespectsAppRequestedOrientationByDefault() throws Exception {
+    public void testIsNotFixedToUserRotationByDefault() throws Exception {
         mBuilder.build();
 
-        assertTrue("Display rotation should respect app requested orientation by"
-                + " default.", mTarget.respectAppRequestedOrientation());
+        assertFalse("Display rotation should respect app requested orientation by"
+                + " default.", mTarget.isFixedToUserRotation());
     }
 
     @Test
-    public void testNotRespectAppRequestedOrientation_FixedToUserRotation() throws Exception {
+    public void testIsFixedToUserRotation() throws Exception {
         mBuilder.build();
         mTarget.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED);
 
-        assertFalse("Display rotation shouldn't respect app requested orientation if"
-                + " fixed to user rotation.", mTarget.respectAppRequestedOrientation());
+        assertTrue("Display rotation shouldn't respect app requested orientation if"
+                + " fixed to user rotation.", mTarget.isFixedToUserRotation());
     }
 
     /**
diff --git a/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
new file mode 100644
index 0000000..0a960be
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.view.Display.DEFAULT_DISPLAY;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.view.WindowManager;
+
+import com.android.server.inputmethod.InputMethodManagerService;
+import com.android.server.inputmethod.InputMethodMenuController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+// TODO(b/157888351): Move the test to inputmethod package once we find the way to test the
+//  scenario there.
+/**
+ * Build/Install/Run:
+ *  atest WmTests:InputMethodMenuControllerTest
+ */
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class InputMethodMenuControllerTest extends WindowTestsBase {
+
+    private InputMethodMenuController mController;
+
+    @Before
+    public void setUp() {
+        mController = new InputMethodMenuController(mock(InputMethodManagerService.class));
+    }
+
+    @Test
+    public void testGetSettingsContext() {
+        final Context contextOnDefaultDisplay = mController.getSettingsContext(DEFAULT_DISPLAY);
+
+        assertImeSwitchContextMetricsValidity(contextOnDefaultDisplay, mDefaultDisplay);
+
+        // Obtain the context again and check they are the same instance and match the display
+        // metrics of the secondary display.
+        final Context contextOnSecondaryDisplay = mController.getSettingsContext(
+                mDisplayContent.getDisplayId());
+
+        assertImeSwitchContextMetricsValidity(contextOnSecondaryDisplay, mDisplayContent);
+        assertThat(contextOnDefaultDisplay.getActivityToken())
+                .isEqualTo(contextOnSecondaryDisplay.getActivityToken());
+    }
+
+    private void assertImeSwitchContextMetricsValidity(Context context, DisplayContent dc) {
+        assertThat(context.getDisplayId()).isEqualTo(dc.getDisplayId());
+
+        final Rect contextBounds = context.getSystemService(WindowManager.class)
+                .getMaximumWindowMetrics().getBounds();
+        final Rect imeContainerBounds = dc.getImeContainer().getBounds();
+        assertThat(contextBounds).isEqualTo(imeContainerBounds);
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index 8c02faf..a1606d3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -38,7 +38,6 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
@@ -180,7 +179,7 @@
         final WindowState fullscreenApp = addWindow(TYPE_APPLICATION, "fullscreenApp");
         final InsetsState requestedState = new InsetsState();
         requestedState.getSource(ITYPE_STATUS_BAR).setVisible(false);
-        fullscreenApp.updateRequestedInsetsState(requestedState);
+        fullscreenApp.updateRequestedVisibility(requestedState);
 
         // Add a non-fullscreen dialog window.
         final WindowState dialog = addWindow(TYPE_APPLICATION, "dialog");
@@ -215,7 +214,7 @@
         final WindowState newFocusedFullscreenApp = addWindow(TYPE_APPLICATION, "newFullscreenApp");
         final InsetsState newRequestedState = new InsetsState();
         newRequestedState.getSource(ITYPE_STATUS_BAR).setVisible(true);
-        newFocusedFullscreenApp.updateRequestedInsetsState(newRequestedState);
+        newFocusedFullscreenApp.updateRequestedVisibility(newRequestedState);
         // Make sure status bar is hidden by previous insets state.
         mDisplayContent.getInsetsPolicy().updateBarControlTarget(fullscreenApp);
 
@@ -232,20 +231,28 @@
     @UseTestDisplay(addWindows = W_ACTIVITY)
     @Test
     public void testShowTransientBars_bothCanBeTransient_appGetsBothFakeControls() {
-        addNonFocusableWindow(TYPE_STATUS_BAR, "statusBar")
-                .getControllableInsetProvider().getSource().setVisible(false);
-        addNonFocusableWindow(TYPE_NAVIGATION_BAR, "navBar")
-                .getControllableInsetProvider().getSource().setVisible(false);
+        final WindowState statusBar = addNonFocusableWindow(TYPE_STATUS_BAR, "statusBar");
+        statusBar.setHasSurface(true);
+        statusBar.getControllableInsetProvider().setServerVisible(true);
+        final WindowState navBar = addNonFocusableWindow(TYPE_NAVIGATION_BAR, "navBar");
+        navBar.setHasSurface(true);
+        navBar.getControllableInsetProvider().setServerVisible(true);
 
         final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy());
+        doNothing().when(policy).startAnimation(anyBoolean(), any());
 
-        doAnswer(invocation -> {
-            ((InsetsState) invocation.getArgument(2)).setSourceVisible(ITYPE_STATUS_BAR, true);
-            ((InsetsState) invocation.getArgument(2)).setSourceVisible(ITYPE_NAVIGATION_BAR, true);
-            return null;
-        }).when(policy).startAnimation(anyBoolean(), any(), any());
-
+        // Make both system bars invisible.
+        final InsetsState requestedState = new InsetsState();
+        requestedState.getSource(ITYPE_STATUS_BAR).setVisible(false);
+        requestedState.getSource(ITYPE_NAVIGATION_BAR).setVisible(false);
+        mAppWindow.updateRequestedVisibility(requestedState);
         policy.updateBarControlTarget(mAppWindow);
+        waitUntilWindowAnimatorIdle();
+        assertFalse(mDisplayContent.getInsetsStateController().getRawInsetsState()
+                .getSource(ITYPE_STATUS_BAR).isVisible());
+        assertFalse(mDisplayContent.getInsetsStateController().getRawInsetsState()
+                .getSource(ITYPE_NAVIGATION_BAR).isVisible());
+
         policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR});
         waitUntilWindowAnimatorIdle();
         final InsetsSourceControl[] controls =
@@ -272,7 +279,7 @@
                 .getControllableInsetProvider().setServerVisible(true);
 
         final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy());
-        doNothing().when(policy).startAnimation(anyBoolean(), any(), any());
+        doNothing().when(policy).startAnimation(anyBoolean(), any());
         policy.updateBarControlTarget(mAppWindow);
         policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR});
         waitUntilWindowAnimatorIdle();
@@ -301,7 +308,7 @@
                 .getControllableInsetProvider().getSource().setVisible(false);
 
         final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy());
-        doNothing().when(policy).startAnimation(anyBoolean(), any(), any());
+        doNothing().when(policy).startAnimation(anyBoolean(), any());
         policy.updateBarControlTarget(mAppWindow);
         policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR});
         waitUntilWindowAnimatorIdle();
@@ -314,7 +321,7 @@
             assertNull(controls[i].getLeash());
         }
 
-        final InsetsState state = policy.getInsetsForDispatch(mAppWindow);
+        final InsetsState state = policy.getInsetsForWindow(mAppWindow);
         state.setSourceVisible(ITYPE_STATUS_BAR, true);
         state.setSourceVisible(ITYPE_NAVIGATION_BAR, true);
 
@@ -326,7 +333,8 @@
         assertTrue(state.getSource(ITYPE_STATUS_BAR).isVisible());
         assertTrue(state.getSource(ITYPE_NAVIGATION_BAR).isVisible());
 
-        policy.onInsetsModified(mAppWindow, state);
+        mAppWindow.updateRequestedVisibility(state);
+        policy.onInsetsModified(mAppWindow);
         waitUntilWindowAnimatorIdle();
 
         controls = mDisplayContent.getInsetsStateController().getControlsForDispatch(mAppWindow);
@@ -348,7 +356,7 @@
         final WindowState app2 = addWindow(TYPE_APPLICATION, "app");
 
         final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy());
-        doNothing().when(policy).startAnimation(anyBoolean(), any(), any());
+        doNothing().when(policy).startAnimation(anyBoolean(), any());
         policy.updateBarControlTarget(app);
         policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR});
         final InsetsSourceControl[] controls =
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
index a0fa936..9830631 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
@@ -205,7 +205,8 @@
         mProvider.updateControlForTarget(target, false /* force */);
         InsetsState state = new InsetsState();
         state.getSource(ITYPE_STATUS_BAR).setVisible(false);
-        mProvider.onInsetsModified(target, state.getSource(ITYPE_STATUS_BAR));
+        target.updateRequestedVisibility(state);
+        mProvider.updateClientVisibility(target);
         assertFalse(mSource.isVisible());
     }
 
@@ -217,7 +218,8 @@
         mProvider.setWindow(statusBar, null, null);
         InsetsState state = new InsetsState();
         state.getSource(ITYPE_STATUS_BAR).setVisible(false);
-        mProvider.onInsetsModified(target, state.getSource(ITYPE_STATUS_BAR));
+        target.updateRequestedVisibility(state);
+        mProvider.updateClientVisibility(target);
         assertTrue(mSource.isVisible());
     }
 
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 e2cd8a9..90caf35 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -63,7 +63,7 @@
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
         getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
         statusBar.setControllableInsetProvider(getController().getSourceProvider(ITYPE_STATUS_BAR));
-        assertNotNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_STATUS_BAR));
+        assertNotNull(getController().getInsetsForWindow(app).peekSource(ITYPE_STATUS_BAR));
     }
 
     @Test
@@ -72,7 +72,7 @@
         mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_STATUS_BAR)
                 .setWindow(statusBar, null, null);
         statusBar.setControllableInsetProvider(getController().getSourceProvider(ITYPE_STATUS_BAR));
-        final InsetsState state = getController().getInsetsForDispatch(statusBar);
+        final InsetsState state = getController().getInsetsForWindow(statusBar);
         assertNull(state.peekSource(ITYPE_STATUS_BAR));
     }
 
@@ -88,8 +88,8 @@
         getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
         getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null);
         getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null);
-        assertNull(getController().getInsetsForDispatch(navBar).peekSource(ITYPE_IME));
-        assertNull(getController().getInsetsForDispatch(navBar).peekSource(ITYPE_STATUS_BAR));
+        assertNull(getController().getInsetsForWindow(navBar).peekSource(ITYPE_IME));
+        assertNull(getController().getInsetsForWindow(navBar).peekSource(ITYPE_STATUS_BAR));
     }
 
     @Test
@@ -102,8 +102,8 @@
         getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null);
         app.setWindowingMode(WINDOWING_MODE_PINNED);
 
-        assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_STATUS_BAR));
-        assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_NAVIGATION_BAR));
+        assertNull(getController().getInsetsForWindow(app).peekSource(ITYPE_STATUS_BAR));
+        assertNull(getController().getInsetsForWindow(app).peekSource(ITYPE_NAVIGATION_BAR));
     }
 
     @Test
@@ -116,8 +116,8 @@
         getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null);
         app.setWindowingMode(WINDOWING_MODE_FREEFORM);
 
-        assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_STATUS_BAR));
-        assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_NAVIGATION_BAR));
+        assertNull(getController().getInsetsForWindow(app).peekSource(ITYPE_STATUS_BAR));
+        assertNull(getController().getInsetsForWindow(app).peekSource(ITYPE_NAVIGATION_BAR));
     }
 
     @Test
@@ -131,8 +131,8 @@
         app.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
         app.setAlwaysOnTop(true);
 
-        assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_STATUS_BAR));
-        assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_NAVIGATION_BAR));
+        assertNull(getController().getInsetsForWindow(app).peekSource(ITYPE_STATUS_BAR));
+        assertNull(getController().getInsetsForWindow(app).peekSource(ITYPE_NAVIGATION_BAR));
     }
 
     @UseTestDisplay(addWindows = W_INPUT_METHOD)
@@ -147,8 +147,8 @@
         app2.mBehindIme = false;
 
         getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
-        assertFalse(getController().getInsetsForDispatch(app2).getSource(ITYPE_IME).isVisible());
-        assertTrue(getController().getInsetsForDispatch(app1).getSource(ITYPE_IME).isVisible());
+        assertFalse(getController().getInsetsForWindow(app2).getSource(ITYPE_IME).isVisible());
+        assertTrue(getController().getInsetsForWindow(app1).getSource(ITYPE_IME).isVisible());
     }
 
     @UseTestDisplay(addWindows = W_INPUT_METHOD)
@@ -160,7 +160,7 @@
         app.mBehindIme = true;
 
         getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
-        assertTrue(getController().getInsetsForDispatch(app).getSource(ITYPE_IME).isVisible());
+        assertTrue(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
     }
 
     @UseTestDisplay(addWindows = W_INPUT_METHOD)
@@ -172,7 +172,7 @@
         app.mBehindIme = false;
 
         getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
-        assertFalse(getController().getInsetsForDispatch(app).getSource(ITYPE_IME).isVisible());
+        assertFalse(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
     }
 
     @UseTestDisplay(addWindows = W_INPUT_METHOD)
@@ -189,8 +189,8 @@
         getController().onImeControlTargetChanged(mDisplayContent.mInputMethodInputTarget);
         final InsetsState requestedState = new InsetsState();
         requestedState.getSource(ITYPE_IME).setVisible(true);
-        mDisplayContent.mInputMethodInputTarget.updateRequestedInsetsState(requestedState);
-        getController().onInsetsModified(mDisplayContent.mInputMethodInputTarget, requestedState);
+        mDisplayContent.mInputMethodInputTarget.updateRequestedVisibility(requestedState);
+        getController().onInsetsModified(mDisplayContent.mInputMethodInputTarget);
 
         // Send our spy window (app) into the system so that we can detect the invocation.
         final WindowState win = createWindow(null, TYPE_APPLICATION, "app");
@@ -207,7 +207,7 @@
 
         // app won't get visible IME insets while above IME even when IME is visible.
         assertTrue(getController().getRawInsetsState().getSourceOrDefaultVisibility(ITYPE_IME));
-        assertFalse(getController().getInsetsForDispatch(app).getSource(ITYPE_IME).isVisible());
+        assertFalse(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
 
         // Reset invocation counter.
         clearInvocations(app);
@@ -221,7 +221,7 @@
         verify(app, atLeast(1)).notifyInsetsChanged();
 
         // app will get visible IME insets while below IME.
-        assertTrue(getController().getInsetsForDispatch(app).getSource(ITYPE_IME).isVisible());
+        assertTrue(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
     }
 
     @UseTestDisplay(addWindows = W_INPUT_METHOD)
@@ -238,8 +238,8 @@
         mDisplayContent.applySurfaceChangesTransaction();
 
         getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
-        assertTrue(getController().getInsetsForDispatch(app).getSource(ITYPE_IME).isVisible());
-        assertFalse(getController().getInsetsForDispatch(child).getSource(ITYPE_IME).isVisible());
+        assertTrue(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
+        assertFalse(getController().getInsetsForWindow(child).getSource(ITYPE_IME).isVisible());
     }
 
     @UseTestDisplay(addWindows = W_INPUT_METHOD)
@@ -257,8 +257,8 @@
         mDisplayContent.applySurfaceChangesTransaction();
 
         getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
-        assertTrue(getController().getInsetsForDispatch(app).getSource(ITYPE_IME).isVisible());
-        assertFalse(getController().getInsetsForDispatch(child).getSource(ITYPE_IME).isVisible());
+        assertTrue(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
+        assertFalse(getController().getInsetsForWindow(child).getSource(ITYPE_IME).isVisible());
     }
 
     @Test
@@ -278,7 +278,7 @@
 
         statusBarProvider.onPostLayout();
 
-        final InsetsState state = getController().getInsetsForDispatch(ime);
+        final InsetsState state = getController().getInsetsForWindow(ime);
         assertEquals(new Rect(0, 1, 2, 3), state.getSource(ITYPE_STATUS_BAR).getFrame());
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 7a30c37..bea0d8e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -736,7 +736,7 @@
     }
 
     /**
-     * Tests that tasks on singleTaskDisplay are not visible and not trimmed/removed.
+     * Tests that tasks on always on top multi-window tasks are not visible and not trimmed/removed.
      */
     @Test
     public void testVisibleTasks_alwaysOnTop() {
@@ -1154,22 +1154,22 @@
     }
 
     private void doTestRecentTasksApis(boolean expectCallable) {
-        assertSecurityException(expectCallable, () -> mAtm.removeStack(INVALID_STACK_ID));
+        assertSecurityException(expectCallable, () -> mAtm.removeTask(INVALID_STACK_ID));
         assertSecurityException(expectCallable,
-                () -> mAtm.removeStacksInWindowingModes(
+                () -> mAtm.removeRootTasksInWindowingModes(
                         new int[]{WINDOWING_MODE_UNDEFINED}));
         assertSecurityException(expectCallable,
-                () -> mAtm.removeStacksWithActivityTypes(
+                () -> mAtm.removeRootTasksWithActivityTypes(
                         new int[]{ACTIVITY_TYPE_UNDEFINED}));
         assertSecurityException(expectCallable, () -> mAtm.removeTask(0));
         assertSecurityException(expectCallable,
                 () -> mAtm.setTaskWindowingMode(0, WINDOWING_MODE_UNDEFINED, true));
         assertSecurityException(expectCallable,
-                () -> mAtm.moveTaskToStack(0, INVALID_STACK_ID, true));
+                () -> mAtm.moveTaskToRootTask(0, INVALID_STACK_ID, true));
         assertSecurityException(expectCallable,
                 () -> mAtm.setTaskWindowingModeSplitScreenPrimary(0, true));
         assertSecurityException(expectCallable,
-                () -> mAtm.moveTopActivityToPinnedStack(INVALID_STACK_ID, new Rect()));
+                () -> mAtm.moveTopActivityToPinnedRootTask(INVALID_STACK_ID, new Rect()));
         assertSecurityException(expectCallable, () -> mAtm.getAllRootTaskInfos());
         assertSecurityException(expectCallable,
                 () -> mAtm.getRootTaskInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_UNDEFINED));
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 7fb7d40..2985796 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -476,6 +476,26 @@
         assertFalse(wallpaperWindowToken.hasFixedRotationTransform());
     }
 
+    @Test
+    public void testIsAnimatingByRecents() {
+        final ActivityRecord homeActivity = createHomeActivity();
+        final Task rootTask = createTaskStackOnDisplay(mDefaultDisplay);
+        final Task childTask = createTaskInStack(rootTask, 0 /* userId */);
+        final Task leafTask = createTaskInStack(childTask, 0 /* userId */);
+        spyOn(leafTask);
+        doReturn(true).when(leafTask).isVisible();
+
+        initializeRecentsAnimationController(mController, homeActivity);
+
+        // Verify RecentsAnimationController will animate visible leaf task by default.
+        verify(mController).addAnimation(eq(leafTask), anyBoolean(), anyBoolean(), eq(null));
+        assertTrue(leafTask.isAnimatingByRecents());
+
+        // Make sure isAnimatingByRecents will also return true when it called by the parent task.
+        assertTrue(rootTask.isAnimatingByRecents());
+        assertTrue(childTask.isAnimatingByRecents());
+    }
+
     private ActivityRecord createHomeActivity() {
         final ActivityRecord homeActivity = new ActivityBuilder(mWm.mAtmService)
                 .setStack(mRootHomeTask)
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index f154073..7a1b17d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -584,6 +584,95 @@
         assertTrue(statusBarController.isTransparentAllowed(w));
     }
 
+    @Test
+    public void testDisplayIgnoreOrientationRequest_fixedOrientationAppLaunchedInTaskLetterbox() {
+        // Set up a display in landscape and ignoring orientation request.
+        setUpDisplaySizeWithApp(2800, 1400);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+        // Portrait fixed app without max aspect.
+        prepareUnresizable(0, SCREEN_ORIENTATION_PORTRAIT);
+
+        final Rect displayBounds = mActivity.mDisplayContent.getBounds();
+        final Rect taskBounds = mTask.getBounds();
+        final Rect activityBounds = mActivity.getBounds();
+
+        // Display shouldn't be rotated.
+        assertEquals(SCREEN_ORIENTATION_UNSPECIFIED,
+                mActivity.mDisplayContent.getLastOrientation());
+        assertTrue(displayBounds.width() > displayBounds.height());
+
+        // App should launch in task level letterboxing.
+        assertTrue(mTask.isTaskLetterboxed());
+        assertFalse(mActivity.inSizeCompatMode());
+        assertEquals(taskBounds, activityBounds);
+
+        // Task bounds should be 700x1400 with the ratio as the display.
+        assertEquals(displayBounds.height(), taskBounds.height());
+        assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(),
+                taskBounds.width());
+    }
+
+    @Test
+    public void testDisplayIgnoreOrientationRequest_taskLetterboxBecameSizeCompatAfterRotate() {
+        // Set up a display in landscape and ignoring orientation request.
+        setUpDisplaySizeWithApp(2800, 1400);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+        // Portrait fixed app without max aspect.
+        prepareUnresizable(0, SCREEN_ORIENTATION_PORTRAIT);
+
+        final Rect activityBounds = mActivity.getBounds();
+
+        // Rotate display to portrait.
+        rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
+
+        final Rect displayBounds = mActivity.mDisplayContent.getBounds();
+        final Rect newTaskBounds = mTask.getBounds();
+        final Rect newActivityBounds = mActivity.getBounds();
+        assertTrue(displayBounds.width() < displayBounds.height());
+
+        // App should be in size compat.
+        assertFalse(mTask.isTaskLetterboxed());
+        assertScaled();
+        assertEquals(activityBounds.width(), newActivityBounds.width());
+        assertEquals(activityBounds.height(), newActivityBounds.height());
+    }
+
+    @Test
+    public void testDisplayIgnoreOrientationRequest_sizeCompatAfterRotate() {
+        // Set up a display in portrait and ignoring orientation request.
+        setUpDisplaySizeWithApp(1400, 2800);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+        // Portrait fixed app without max aspect.
+        prepareUnresizable(0, SCREEN_ORIENTATION_PORTRAIT);
+
+        Rect displayBounds = mActivity.mDisplayContent.getBounds();
+        Rect activityBounds = mActivity.getBounds();
+
+        // App should launch in fullscreen.
+        assertFalse(mTask.isTaskLetterboxed());
+        assertFalse(mActivity.inSizeCompatMode());
+        assertEquals(displayBounds, activityBounds);
+
+        // Rotate display to landscape.
+        rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
+
+        displayBounds = mActivity.mDisplayContent.getBounds();
+        activityBounds = mActivity.getBounds();
+        assertTrue(displayBounds.width() > displayBounds.height());
+
+        // App should be in size compat.
+        assertFalse(mTask.isTaskLetterboxed());
+        assertScaled();
+
+        // App bounds should be 700x1400 with the ratio as the display.
+        assertEquals(displayBounds.height(), activityBounds.height());
+        assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(),
+                activityBounds.width());
+    }
+
     private static WindowState addWindowToActivity(ActivityRecord activity) {
         final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
         params.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
diff --git a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
index d0a5644..ecbfac8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
+++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
@@ -163,12 +163,6 @@
     }
 
     @Override
-    public SurfaceControl.Transaction setOverrideScalingMode(SurfaceControl sc,
-            int overrideScalingMode) {
-        return this;
-    }
-
-    @Override
     public SurfaceControl.Transaction setColor(SurfaceControl sc, float[] color) {
         return this;
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java b/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
new file mode 100644
index 0000000..7bac3e7
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
+import static com.android.server.wm.WindowContainer.SYNC_STATE_NONE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.notNull;
+import static org.mockito.Mockito.spy;
+
+import android.platform.test.annotations.Presubmit;
+import android.view.SurfaceControl;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+/**
+ * Test class for {@link BLASTSyncEngine}.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:SyncEngineTests
+ */
+@SmallTest
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class SyncEngineTests extends WindowTestsBase {
+
+    @Before
+    public void setUp() {
+        spyOn(mWm.mWindowPlacerLocked);
+    }
+
+    @Test
+    public void testTrivialSyncCallback() {
+        TestWindowContainer mockWC = new TestWindowContainer(mWm, false /* waiter */);
+
+        BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
+
+        BLASTSyncEngine.TransactionReadyListener listener = mock(
+                BLASTSyncEngine.TransactionReadyListener.class);
+
+        int id = bse.startSyncSet(listener);
+        bse.addToSyncSet(id, mockWC);
+        // Make sure a traversal is requested
+        verify(mWm.mWindowPlacerLocked, times(1)).requestTraversal();
+
+        bse.onSurfacePlacement();
+        verify(listener, times(0)).onTransactionReady(anyInt(), any());
+
+        bse.setReady(id);
+        // Make sure a traversal is requested
+        verify(mWm.mWindowPlacerLocked, times(2)).requestTraversal();
+        bse.onSurfacePlacement();
+        verify(listener, times(1)).onTransactionReady(eq(id), notNull());
+
+        // make sure it was cleaned-up (no second callback)
+        bse.onSurfacePlacement();
+        verify(listener, times(1)).onTransactionReady(anyInt(), any());
+    }
+
+    @Test
+    public void testWaitingSyncCallback() {
+        TestWindowContainer mockWC = new TestWindowContainer(mWm, true /* waiter */);
+
+        BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
+
+        BLASTSyncEngine.TransactionReadyListener listener = mock(
+                BLASTSyncEngine.TransactionReadyListener.class);
+
+        int id = bse.startSyncSet(listener);
+        bse.addToSyncSet(id, mockWC);
+        bse.setReady(id);
+        // Make sure traversals requested (one for add and another for setReady)
+        verify(mWm.mWindowPlacerLocked, times(2)).requestTraversal();
+        bse.onSurfacePlacement();
+        verify(listener, times(0)).onTransactionReady(anyInt(), any());
+
+        mockWC.onSyncFinishedDrawing();
+        // Make sure a (third) traversal is requested.
+        verify(mWm.mWindowPlacerLocked, times(3)).requestTraversal();
+        bse.onSurfacePlacement();
+        verify(listener, times(1)).onTransactionReady(eq(id), notNull());
+    }
+
+    @Test
+    public void testInvisibleSyncCallback() {
+        TestWindowContainer mockWC = new TestWindowContainer(mWm, true /* waiter */);
+
+        BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
+
+        BLASTSyncEngine.TransactionReadyListener listener = mock(
+                BLASTSyncEngine.TransactionReadyListener.class);
+
+        int id = bse.startSyncSet(listener);
+        bse.addToSyncSet(id, mockWC);
+        bse.setReady(id);
+        // Make sure traversals requested (one for add and another for setReady)
+        verify(mWm.mWindowPlacerLocked, times(2)).requestTraversal();
+        bse.onSurfacePlacement();
+        verify(listener, times(0)).onTransactionReady(anyInt(), any());
+
+        // Finish sync if invisible.
+        mockWC.mVisibleRequested = false;
+        bse.onSurfacePlacement();
+        verify(listener, times(1)).onTransactionReady(eq(id), notNull());
+        assertEquals(SYNC_STATE_NONE, mockWC.mSyncState);
+    }
+
+    @Test
+    public void testWaitForChildrenCallback() {
+        TestWindowContainer parentWC = new TestWindowContainer(mWm, true /* waiter */);
+        TestWindowContainer childWC = new TestWindowContainer(mWm, true /* waiter */);
+        TestWindowContainer childWC2 = new TestWindowContainer(mWm, true /* waiter */);
+        parentWC.addChild(childWC, POSITION_TOP);
+        parentWC.addChild(childWC2, POSITION_TOP);
+
+        BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
+
+        BLASTSyncEngine.TransactionReadyListener listener = mock(
+                BLASTSyncEngine.TransactionReadyListener.class);
+
+        int id = bse.startSyncSet(listener);
+        bse.addToSyncSet(id, parentWC);
+        bse.setReady(id);
+        bse.onSurfacePlacement();
+        verify(listener, times(0)).onTransactionReady(anyInt(), any());
+
+        parentWC.onSyncFinishedDrawing();
+        bse.onSurfacePlacement();
+        verify(listener, times(0)).onTransactionReady(anyInt(), any());
+
+        childWC.onSyncFinishedDrawing();
+        bse.onSurfacePlacement();
+        verify(listener, times(0)).onTransactionReady(anyInt(), any());
+
+        childWC2.onSyncFinishedDrawing();
+        bse.onSurfacePlacement();
+        verify(listener, times(1)).onTransactionReady(eq(id), notNull());
+        assertEquals(SYNC_STATE_NONE, parentWC.mSyncState);
+        assertEquals(SYNC_STATE_NONE, childWC.mSyncState);
+        assertEquals(SYNC_STATE_NONE, childWC2.mSyncState);
+    }
+
+    @Test
+    public void testWaitForParentCallback() {
+        TestWindowContainer parentWC = new TestWindowContainer(mWm, true /* waiter */);
+        TestWindowContainer childWC = new TestWindowContainer(mWm, true /* waiter */);
+        parentWC.addChild(childWC, POSITION_TOP);
+
+        BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
+
+        BLASTSyncEngine.TransactionReadyListener listener = mock(
+                BLASTSyncEngine.TransactionReadyListener.class);
+
+        int id = bse.startSyncSet(listener);
+        bse.addToSyncSet(id, parentWC);
+        bse.setReady(id);
+        bse.onSurfacePlacement();
+        verify(listener, times(0)).onTransactionReady(anyInt(), any());
+
+        childWC.onSyncFinishedDrawing();
+        bse.onSurfacePlacement();
+        verify(listener, times(0)).onTransactionReady(anyInt(), any());
+
+        parentWC.onSyncFinishedDrawing();
+        bse.onSurfacePlacement();
+        verify(listener, times(1)).onTransactionReady(eq(id), notNull());
+        assertEquals(SYNC_STATE_NONE, parentWC.mSyncState);
+        assertEquals(SYNC_STATE_NONE, childWC.mSyncState);
+    }
+
+    @Test
+    public void testFillsParent() {
+        TestWindowContainer parentWC = new TestWindowContainer(mWm, true /* waiter */);
+        TestWindowContainer topChildWC = new TestWindowContainer(mWm, true /* waiter */);
+        TestWindowContainer botChildWC = new TestWindowContainer(mWm, true /* waiter */);
+        topChildWC.mFillsParent = botChildWC.mFillsParent = true;
+        parentWC.addChild(topChildWC, POSITION_TOP);
+        parentWC.addChild(botChildWC, POSITION_BOTTOM);
+
+        BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
+
+        BLASTSyncEngine.TransactionReadyListener listener = mock(
+                BLASTSyncEngine.TransactionReadyListener.class);
+
+        int id = bse.startSyncSet(listener);
+        bse.addToSyncSet(id, parentWC);
+        bse.setReady(id);
+        bse.onSurfacePlacement();
+        verify(listener, times(0)).onTransactionReady(anyInt(), any());
+
+        parentWC.onSyncFinishedDrawing();
+        topChildWC.onSyncFinishedDrawing();
+        // Even though bottom isn't finished, we should see callback because it is occluded by top.
+        assertFalse(botChildWC.isSyncFinished());
+        bse.onSurfacePlacement();
+        verify(listener, times(1)).onTransactionReady(eq(id), notNull());
+
+        assertEquals(SYNC_STATE_NONE, parentWC.mSyncState);
+        assertEquals(SYNC_STATE_NONE, botChildWC.mSyncState);
+        assertEquals(SYNC_STATE_NONE, topChildWC.mSyncState);
+    }
+
+    @Test
+    public void testReparentOut() {
+        TestWindowContainer nonMemberParentWC = new TestWindowContainer(mWm, true /* waiter */);
+        TestWindowContainer parentWC = new TestWindowContainer(mWm, true /* waiter */);
+        TestWindowContainer topChildWC = new TestWindowContainer(mWm, true /* waiter */);
+        TestWindowContainer botChildWC = new TestWindowContainer(mWm, true /* waiter */);
+        parentWC.addChild(topChildWC, POSITION_TOP);
+        parentWC.addChild(botChildWC, POSITION_BOTTOM);
+
+        BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
+
+        BLASTSyncEngine.TransactionReadyListener listener = mock(
+                BLASTSyncEngine.TransactionReadyListener.class);
+
+        int id = bse.startSyncSet(listener);
+        bse.addToSyncSet(id, parentWC);
+        bse.setReady(id);
+        bse.onSurfacePlacement();
+        verify(listener, times(0)).onTransactionReady(anyInt(), any());
+
+        parentWC.onSyncFinishedDrawing();
+        topChildWC.onSyncFinishedDrawing();
+        bse.onSurfacePlacement();
+        verify(listener, times(0)).onTransactionReady(anyInt(), any());
+
+        // reparent out cancels
+        botChildWC.reparent(nonMemberParentWC, POSITION_TOP);
+        assertEquals(SYNC_STATE_NONE, botChildWC.mSyncState);
+
+        bse.onSurfacePlacement();
+        verify(listener, times(1)).onTransactionReady(eq(id), notNull());
+        assertEquals(SYNC_STATE_NONE, parentWC.mSyncState);
+        assertEquals(SYNC_STATE_NONE, topChildWC.mSyncState);
+    }
+
+    @Test
+    public void testReparentIn() {
+        TestWindowContainer nonMemberParentWC = new TestWindowContainer(mWm, true /* waiter */);
+        TestWindowContainer parentWC = new TestWindowContainer(mWm, true /* waiter */);
+        TestWindowContainer topChildWC = new TestWindowContainer(mWm, true /* waiter */);
+        TestWindowContainer botChildWC = new TestWindowContainer(mWm, true /* waiter */);
+        parentWC.addChild(topChildWC, POSITION_TOP);
+        nonMemberParentWC.addChild(botChildWC, POSITION_BOTTOM);
+
+        BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
+
+        BLASTSyncEngine.TransactionReadyListener listener = mock(
+                BLASTSyncEngine.TransactionReadyListener.class);
+
+        int id = bse.startSyncSet(listener);
+        bse.addToSyncSet(id, parentWC);
+        bse.setReady(id);
+        bse.onSurfacePlacement();
+        verify(listener, times(0)).onTransactionReady(anyInt(), any());
+
+        parentWC.onSyncFinishedDrawing();
+        topChildWC.onSyncFinishedDrawing();
+
+        // No-longer finished because new child
+        botChildWC.reparent(parentWC, POSITION_BOTTOM);
+        bse.onSurfacePlacement();
+        verify(listener, times(0)).onTransactionReady(anyInt(), any());
+
+        botChildWC.onSyncFinishedDrawing();
+        bse.onSurfacePlacement();
+        verify(listener, times(1)).onTransactionReady(eq(id), notNull());
+        assertEquals(SYNC_STATE_NONE, parentWC.mSyncState);
+        assertEquals(SYNC_STATE_NONE, topChildWC.mSyncState);
+        assertEquals(SYNC_STATE_NONE, botChildWC.mSyncState);
+    }
+
+    @Test
+    public void testRemoval() {
+        // Need different transactions to verify stuff
+        mWm.mTransactionFactory = () -> spy(new StubTransaction());
+        TestWindowContainer rootWC = new TestWindowContainer(mWm, false /* waiter */);
+        TestWindowContainer parentWC = new TestWindowContainer(mWm, true /* waiter */);
+        TestWindowContainer topChildWC = new TestWindowContainer(mWm, true /* waiter */);
+        TestWindowContainer botChildWC = new TestWindowContainer(mWm, true /* waiter */);
+        rootWC.addChild(parentWC, POSITION_TOP);
+        parentWC.addChild(topChildWC, POSITION_TOP);
+        parentWC.addChild(botChildWC, POSITION_BOTTOM);
+
+        BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
+
+        BLASTSyncEngine.TransactionReadyListener listener = mock(
+                BLASTSyncEngine.TransactionReadyListener.class);
+
+        int id = bse.startSyncSet(listener);
+        bse.addToSyncSet(id, parentWC);
+        final BLASTSyncEngine.SyncGroup syncGroup = parentWC.mSyncGroup;
+        bse.setReady(id);
+        bse.onSurfacePlacement();
+        verify(listener, times(0)).onTransactionReady(anyInt(), any());
+
+        parentWC.onSyncFinishedDrawing();
+        topChildWC.removeImmediately();
+        bse.onSurfacePlacement();
+        verify(listener, times(0)).onTransactionReady(anyInt(), any());
+
+        // Removal should merge transaction into parent
+        verify(parentWC.mSyncTransaction, times(1)).merge(eq(topChildWC.mSyncTransaction));
+        assertEquals(SYNC_STATE_NONE, topChildWC.mSyncState);
+
+        // Removal of a sync-root should merge transaction into orphan
+        parentWC.removeImmediately();
+        final SurfaceControl.Transaction orphan = syncGroup.getOrphanTransaction();
+        verify(orphan, times(1)).merge(eq(parentWC.mSyncTransaction));
+
+        // Then the orphan transaction should be merged into sync
+        bse.onSurfacePlacement();
+        final ArgumentCaptor<SurfaceControl.Transaction> merged =
+                ArgumentCaptor.forClass(SurfaceControl.Transaction.class);
+        verify(listener, times(1)).onTransactionReady(eq(id), merged.capture());
+        final SurfaceControl.Transaction mergedTransaction = merged.getValue();
+        verify(mergedTransaction, times(1)).merge(eq(orphan));
+
+        assertEquals(SYNC_STATE_NONE, parentWC.mSyncState);
+        assertEquals(SYNC_STATE_NONE, botChildWC.mSyncState);
+    }
+
+    static class TestWindowContainer extends WindowContainer {
+        final boolean mWaiter;
+        boolean mVisibleRequested = true;
+        boolean mFillsParent = false;
+
+        TestWindowContainer(WindowManagerService wms, boolean waiter) {
+            super(wms);
+            mWaiter = waiter;
+            mDisplayContent = wms.getDefaultDisplayContentLocked();
+        }
+
+        @Override
+        boolean prepareSync() {
+            if (!super.prepareSync()) {
+                return false;
+            }
+            if (mWaiter) {
+                mSyncState = SYNC_STATE_WAITING_FOR_DRAW;
+            }
+            return true;
+        }
+
+        @Override
+        void createSurfaceControl(boolean force) {
+            // nothing
+        }
+
+        @Override
+        boolean isVisibleRequested() {
+            return mVisibleRequested;
+        }
+
+        @Override
+        boolean fillsParent() {
+            return mFillsParent;
+        }
+    }
+}
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 d2b7ac4..2fa7589 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -420,8 +420,9 @@
 
         // Without limiting to be inside the parent bounds, the out screen size should keep relative
         // to the input bounds.
+        final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
         final ActivityRecord.CompatDisplayInsets compatIntsets =
-                new ActivityRecord.CompatDisplayInsets(display, task);
+                new ActivityRecord.CompatDisplayInsets(display, activity);
         task.computeConfigResourceOverrides(inOutConfig, parentConfig, compatIntsets);
 
         assertEquals(largerLandscapeBounds, inOutConfig.windowConfiguration.getAppBounds());
@@ -1002,14 +1003,18 @@
     public void testNotSpecifyOrientationByFloatingTask() {
         final Task task = getTestTask();
         final ActivityRecord activity = task.getTopMostActivity();
+        final WindowContainer<?> parentContainer = task.getParent();
         final TaskDisplayArea taskDisplayArea = task.getDisplayArea();
         activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
 
+        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, parentContainer.getOrientation());
         assertEquals(SCREEN_ORIENTATION_LANDSCAPE, taskDisplayArea.getOrientation());
 
         task.setWindowingMode(WINDOWING_MODE_PINNED);
 
-        assertEquals(SCREEN_ORIENTATION_UNSET, taskDisplayArea.getOrientation());
+        // TDA returns the last orientation when child returns UNSET
+        assertEquals(SCREEN_ORIENTATION_UNSET, parentContainer.getOrientation());
+        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, taskDisplayArea.getOrientation());
     }
 
     @Test
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 a54bbaf..788ef5a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
@@ -242,40 +242,6 @@
         assertTrue(activity.mOnDetachedFromWindowCalled);
     }
 
-    @Test
-    public void testTaskOnSingleTaskDisplayDrawn() throws Exception {
-        final Instrumentation instrumentation = getInstrumentation();
-
-        final CountDownLatch activityViewReadyLatch = new CountDownLatch(1);
-        final CountDownLatch singleTaskDisplayDrawnLatch = new CountDownLatch(1);
-        registerTaskStackChangedListener(new TaskStackListener() {
-            @Override
-            public void onSingleTaskDisplayDrawn(int displayId) throws RemoteException {
-                singleTaskDisplayDrawnLatch.countDown();
-            }
-        });
-        final ActivityViewTestActivity activity =
-                (ActivityViewTestActivity) startTestActivity(ActivityViewTestActivity.class);
-        final ActivityView activityView = activity.getActivityView();
-        activityView.setCallback(new ActivityView.StateCallback() {
-            @Override
-            public void onActivityViewReady(ActivityView view) {
-                activityViewReadyLatch.countDown();
-            }
-
-            @Override
-            public void onActivityViewDestroyed(ActivityView view) {
-            }
-        });
-        waitForCallback(activityViewReadyLatch);
-
-        final Context context = instrumentation.getContext();
-        Intent intent = new Intent(context, ActivityInActivityView.class);
-        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
-        SystemUtil.runWithShellPermissionIdentity(() -> activityView.startActivity(intent));
-        waitForCallback(singleTaskDisplayDrawnLatch);
-    }
-
     public static class ActivityLaunchesNewActivityInActivityView extends TestActivity {
         private boolean mActivityBLaunched = false;
 
@@ -291,61 +257,6 @@
     }
 
     @Test
-    public void testSingleTaskDisplayEmpty() throws Exception {
-        final Instrumentation instrumentation = getInstrumentation();
-
-        final CountDownLatch activityViewReadyLatch = new CountDownLatch(1);
-        final CountDownLatch activityViewDestroyedLatch = new CountDownLatch(1);
-        final CountDownLatch singleTaskDisplayDrawnLatch = new CountDownLatch(1);
-        final CountDownLatch singleTaskDisplayEmptyLatch = new CountDownLatch(1);
-
-        registerTaskStackChangedListener(new TaskStackListener() {
-            @Override
-            public void onSingleTaskDisplayDrawn(int displayId) throws RemoteException {
-                singleTaskDisplayDrawnLatch.countDown();
-            }
-            @Override
-            public void onSingleTaskDisplayEmpty(int displayId)
-                    throws RemoteException {
-                singleTaskDisplayEmptyLatch.countDown();
-            }
-        });
-        final ActivityViewTestActivity activity =
-                (ActivityViewTestActivity) startTestActivity(ActivityViewTestActivity.class);
-        final ActivityView activityView = activity.getActivityView();
-        activityView.setCallback(new ActivityView.StateCallback() {
-            @Override
-            public void onActivityViewReady(ActivityView view) {
-                activityViewReadyLatch.countDown();
-            }
-
-            @Override
-            public void onActivityViewDestroyed(ActivityView view) {
-                activityViewDestroyedLatch.countDown();
-            }
-        });
-        waitForCallback(activityViewReadyLatch);
-
-        // 1. start ActivityLaunchesNewActivityInActivityView in an ActivityView
-        // 2. ActivityLaunchesNewActivityInActivityView launches ActivityB
-        // 3. ActivityB finishes self.
-        // 4. Verify ITaskStackListener#onSingleTaskDisplayEmpty is not called yet.
-        final Context context = instrumentation.getContext();
-        Intent intent = new Intent(context, ActivityLaunchesNewActivityInActivityView.class);
-        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
-        SystemUtil.runWithShellPermissionIdentity(() -> activityView.startActivity(intent));
-        waitForCallback(singleTaskDisplayDrawnLatch);
-        UiDevice.getInstance(getInstrumentation()).waitForIdle();
-        assertEquals(1, singleTaskDisplayEmptyLatch.getCount());
-
-        // 5. Release the container, and ActivityLaunchesNewActivityInActivityView finishes.
-        // 6. Verify ITaskStackListener#onSingleTaskDisplayEmpty is called.
-        activityView.release();
-        waitForCallback(activityViewDestroyedLatch);
-        waitForCallback(singleTaskDisplayEmptyLatch);
-    }
-
-    @Test
     public void testTaskDisplayChanged() throws Exception {
         final CountDownLatch activityViewReadyLatch = new CountDownLatch(1);
         final ActivityViewTestActivity activity =
@@ -603,7 +514,7 @@
         public void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
 
-            mActivityView = new ActivityView.Builder(this).setSingleInstance(true).build();
+            mActivityView = new ActivityView.Builder(this).build();
             setContentView(mActivityView);
 
             ViewGroup.LayoutParams layoutParams = mActivityView.getLayoutParams();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index ee16a76..e95efe7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -150,7 +150,7 @@
             newDisplay.onRequestedOverrideConfigurationChanged(c);
             if (!mCanRotate) {
                 final DisplayRotation displayRotation = newDisplay.getDisplayRotation();
-                doReturn(false).when(displayRotation).respectAppRequestedOrientation();
+                doReturn(true).when(displayRotation).isFixedToUserRotation();
             }
             // Please add stubbing before this line. Services will start using this display in other
             // threads immediately after adding it to hierarchy. Calling doAnswer() type of stubbing
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
index ea12233..db1c12f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
@@ -22,7 +22,7 @@
 import android.os.RemoteException;
 import android.util.MergedConfiguration;
 import android.view.DragEvent;
-import android.view.IScrollCaptureController;
+import android.view.IScrollCaptureCallbacks;
 import android.view.IWindow;
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
@@ -107,7 +107,7 @@
     }
 
     @Override
-    public void requestScrollCapture(IScrollCaptureController controller) throws RemoteException {
+    public void requestScrollCapture(IScrollCaptureCallbacks callbacks) throws RemoteException {
     }
 
     @Override
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 dc85904..db5c796 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -197,19 +197,19 @@
     }
 
     @Override
-    public void screenTurningOn(ScreenOnListener screenOnListener) {
+    public void screenTurningOn(int displayId, ScreenOnListener screenOnListener) {
     }
 
     @Override
-    public void screenTurnedOn() {
+    public void screenTurnedOn(int displayId) {
     }
 
     @Override
-    public void screenTurningOff(ScreenOffListener screenOffListener) {
+    public void screenTurningOff(int displayId, ScreenOffListener screenOffListener) {
     }
 
     @Override
-    public void screenTurnedOff() {
+    public void screenTurnedOff(int displayId) {
     }
 
     @Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index 5b7cf5a..749f33e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -21,7 +21,9 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.os.Process.INVALID_UID;
+import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
 
@@ -32,8 +34,11 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
@@ -176,4 +181,31 @@
         mWm.dismissKeyguard(null, "test-dismiss-keyguard");
         verify(mWm.mAtmService.mStackSupervisor).wakeUp(anyString());
     }
+
+    @Test
+    public void testMoveWindowTokenToDisplay_NullToken_DoNothing() {
+        mWm.moveWindowTokenToDisplay(null, mDisplayContent.getDisplayId());
+
+        verify(mDisplayContent, never()).reParentWindowToken(any());
+    }
+
+    @Test
+    public void testMoveWindowTokenToDisplay_SameDisplay_DoNothing() {
+        final WindowToken windowToken = createTestWindowToken(TYPE_INPUT_METHOD_DIALOG,
+                mDisplayContent);
+
+        mWm.moveWindowTokenToDisplay(windowToken.token, mDisplayContent.getDisplayId());
+
+        verify(mDisplayContent, never()).reParentWindowToken(any());
+    }
+
+    @Test
+    public void testMoveWindowTokenToDisplay_DifferentDisplay_DoMoveDisplay() {
+        final WindowToken windowToken = createTestWindowToken(TYPE_INPUT_METHOD_DIALOG,
+                mDisplayContent);
+
+        mWm.moveWindowTokenToDisplay(windowToken.token, DEFAULT_DISPLAY);
+
+        assertThat(windowToken.getDisplayContent()).isEqualTo(mDefaultDisplay);
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 0152fc6..7a1f65a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -37,13 +37,13 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 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;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 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.wm.DisplayArea.Type.ABOVE_TASKS;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
+import static com.android.server.wm.WindowContainer.SYNC_STATE_READY;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -54,12 +54,14 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.atLeastOnce;
 
 import android.app.ActivityManager;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityTaskManager.RootTaskInfo;
 import android.app.PictureInPictureParams;
 import android.content.pm.ActivityInfo;
+import android.content.pm.ParceledListSlice;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Binder;
@@ -72,6 +74,7 @@
 import android.view.SurfaceControl;
 import android.window.ITaskOrganizer;
 import android.window.IWindowContainerTransactionCallback;
+import android.window.TaskAppearedInfo;
 import android.window.WindowContainerTransaction;
 
 import androidx.test.filters.SmallTest;
@@ -79,8 +82,10 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
 
 /**
@@ -93,14 +98,27 @@
 @Presubmit
 @RunWith(WindowTestRunner.class)
 public class WindowOrganizerTests extends WindowTestsBase {
-    private ITaskOrganizer registerMockOrganizer() {
+
+    private ITaskOrganizer createMockOrganizer() {
         final ITaskOrganizer organizer = mock(ITaskOrganizer.class);
         when(organizer.asBinder()).thenReturn(new Binder());
-
-        mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(organizer);
         return organizer;
     }
 
+    private ITaskOrganizer registerMockOrganizer(ArrayList<TaskAppearedInfo> existingTasks) {
+        final ITaskOrganizer organizer = createMockOrganizer();
+        ParceledListSlice<TaskAppearedInfo> tasks =
+                mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(organizer);
+        if (existingTasks != null) {
+            existingTasks.addAll(tasks.getList());
+        }
+        return organizer;
+    }
+
+    private ITaskOrganizer registerMockOrganizer() {
+        return registerMockOrganizer(null);
+    }
+
     Task createTask(Task stack, boolean fakeDraw) {
         final Task task = createTaskInStack(stack, 0);
 
@@ -128,27 +146,21 @@
 
     @Test
     public void testAppearVanish() throws RemoteException {
+        final ITaskOrganizer organizer = registerMockOrganizer();
         final Task stack = createStack();
         final Task task = createTask(stack);
-        final ITaskOrganizer organizer = registerMockOrganizer();
 
-        stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
-        stack.setTaskOrganizer(organizer);
         verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
 
-
         stack.removeImmediately();
         verify(organizer).onTaskVanished(any());
     }
 
     @Test
     public void testAppearWaitsForVisibility() throws RemoteException {
+        final ITaskOrganizer organizer = registerMockOrganizer();
         final Task stack = createStack();
         final Task task = createTask(stack, false);
-        final ITaskOrganizer organizer = registerMockOrganizer();
-
-        stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
-        stack.setTaskOrganizer(organizer);
 
         verify(organizer, never())
                 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
@@ -163,9 +175,9 @@
 
     @Test
     public void testNoVanishedIfNoAppear() throws RemoteException {
+        final ITaskOrganizer organizer = registerMockOrganizer();
         final Task stack = createStack();
         final Task task = createTask(stack, false /* hasBeenVisible */);
-        final ITaskOrganizer organizer = registerMockOrganizer();
 
         // In this test we skip making the Task visible, and verify
         // that even though a TaskOrganizer is set remove doesn't emit
@@ -179,28 +191,25 @@
 
     @Test
     public void testTaskNoDraw() throws RemoteException {
+        final ITaskOrganizer organizer = registerMockOrganizer();
         final Task stack = createStack();
         final Task task = createTask(stack, false /* fakeDraw */);
-        final ITaskOrganizer organizer = registerMockOrganizer();
 
-        stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
         verify(organizer, never())
                 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
         assertTrue(stack.isOrganized());
 
         mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer);
-        verify(organizer, never()).onTaskVanished(any());
+        assertTaskVanished(organizer, false /* expectVanished */, stack);
         assertFalse(stack.isOrganized());
     }
 
     @Test
     public void testClearOrganizer() throws RemoteException {
+        final ITaskOrganizer organizer = registerMockOrganizer();
         final Task stack = createStack();
         final Task task = createTask(stack);
-        final ITaskOrganizer organizer = registerMockOrganizer();
 
-        stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
-        stack.setTaskOrganizer(organizer);
         verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
         assertTrue(stack.isOrganized());
 
@@ -211,16 +220,15 @@
 
     @Test
     public void testUnregisterOrganizer() throws RemoteException {
+        final ITaskOrganizer organizer = registerMockOrganizer();
         final Task stack = createStack();
         final Task task = createTask(stack);
-        final ITaskOrganizer organizer = registerMockOrganizer();
 
-        stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
         verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
         assertTrue(stack.isOrganized());
 
         mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer);
-        verify(organizer).onTaskVanished(any());
+        assertTaskVanished(organizer, true /* expectVanished */, stack);
         assertFalse(stack.isOrganized());
     }
 
@@ -232,37 +240,47 @@
         final Task task2 = createTask(stack2);
         final Task stack3 = createStack();
         final Task task3 = createTask(stack3);
-        final ITaskOrganizer organizer = registerMockOrganizer();
+        final ArrayList<TaskAppearedInfo> existingTasks = new ArrayList<>();
+        final ITaskOrganizer organizer = registerMockOrganizer(existingTasks);
 
-        // verify that tasks are appeared on registration
-        verify(organizer, times(3))
-                .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
+        // verify that tasks are returned and taskAppeared is not called
+        assertContainsTasks(existingTasks, stack, stack2, stack3);
+        verify(organizer, times(0)).onTaskAppeared(any(RunningTaskInfo.class),
+                any(SurfaceControl.class));
+        verify(organizer, times(0)).onTaskVanished(any());
         assertTrue(stack.isOrganized());
 
-        // Now we replace the registration and1 verify the new organizer receives tasks
-        final ITaskOrganizer organizer2 = registerMockOrganizer();
-        verify(organizer2, times(3))
-                .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
+        // Now we replace the registration and verify the new organizer receives existing tasks
+        final ArrayList<TaskAppearedInfo> existingTasks2 = new ArrayList<>();
+        final ITaskOrganizer organizer2 = registerMockOrganizer(existingTasks2);
+        assertContainsTasks(existingTasks2, stack, stack2, stack3);
+        verify(organizer2, times(0)).onTaskAppeared(any(RunningTaskInfo.class),
+                any(SurfaceControl.class));
         verify(organizer2, times(0)).onTaskVanished(any());
-        // One for task
-        verify(organizer, times(3)).onTaskVanished(any());
+        // Removed tasks from the original organizer
+        assertTaskVanished(organizer, true /* expectVanished */, stack, stack2, stack3);
         assertTrue(stack2.isOrganized());
 
         // Now we unregister the second one, the first one should automatically be reregistered
         // so we verify that it's now seeing changes.
         mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer2);
-        verify(organizer, times(6))
+        verify(organizer, times(3))
                 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
-        verify(organizer2, times(3)).onTaskVanished(any());
+        assertTaskVanished(organizer2, true /* expectVanished */, stack, stack2, stack3);
     }
 
     @Test
     public void testRegisterTaskOrganizerWithExistingTasks() throws RemoteException {
         final Task stack = createStack();
         final Task task = createTask(stack);
+        final Task stack2 = createStack();
+        final Task task2 = createTask(stack2);
+        ArrayList<TaskAppearedInfo> existingTasks = new ArrayList<>();
+        final ITaskOrganizer organizer = registerMockOrganizer(existingTasks);
+        assertContainsTasks(existingTasks, stack, stack2);
 
-        final ITaskOrganizer organizer = registerMockOrganizer();
-        verify(organizer, times(1))
+        // Verify we don't get onTaskAppeared if we are returned the tasks
+        verify(organizer, never())
                 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
     }
 
@@ -366,7 +384,7 @@
     }
 
     @Test
-    public void testSetIgnoreOrientationRequest() {
+    public void testSetIgnoreOrientationRequest_taskDisplayArea() {
         removeGlobalMinSizeRestriction();
         final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
         final Task stack = taskDisplayArea.createStack(
@@ -378,7 +396,7 @@
         activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
 
         // TDA returns UNSET when ignoreOrientationRequest == true
-        // DC is UNSPECIFIED because it is using the previous (default) when TDA returns UNSET.
+        // DC is UNSPECIFIED when child returns UNSET
         assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSET);
         assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSPECIFIED);
 
@@ -399,8 +417,40 @@
         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
 
         // TDA returns UNSET when ignoreOrientationRequest == true
-        // DC is LANDSCAPE because it is using the previous when TDA returns UNSET.
+        // DC is UNSPECIFIED when child returns UNSET
         assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSET);
+        assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSPECIFIED);
+    }
+
+    @Test
+    public void testSetIgnoreOrientationRequest_displayContent() {
+        removeGlobalMinSizeRestriction();
+        final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
+        final Task stack = taskDisplayArea.createStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
+        final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true)
+                .setStack(stack).build();
+        mDisplayContent.setFocusedApp(activity);
+        activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+
+        // DC uses the orientation request from app
+        assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE);
+
+        WindowContainerTransaction t = new WindowContainerTransaction();
+        t.setIgnoreOrientationRequest(
+                mDisplayContent.mRemoteToken.toWindowContainerToken(),
+                true /* ignoreOrientationRequest */);
+        mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
+
+        // DC returns UNSPECIFIED when ignoreOrientationRequest == true
+        assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSPECIFIED);
+
+        t.setIgnoreOrientationRequest(
+                mDisplayContent.mRemoteToken.toWindowContainerToken(),
+                false /* ignoreOrientationRequest */);
+        mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
+
+        // DC uses the orientation request from app after mIgnoreOrientationRequest is set to false
         assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE);
     }
 
@@ -674,154 +724,35 @@
     }
 
     @Test
-    public void testTrivialBLASTCallback() throws RemoteException {
+    public void testBLASTCallbackWithActivityChildren() {
         final Task stackController1 = createStack();
         final Task task = createTask(stackController1);
-        final ITaskOrganizer organizer = registerMockOrganizer();
-
-        spyOn(task);
-        doReturn(true).when(task).isVisible();
-
-        BLASTSyncEngine bse = new BLASTSyncEngine();
-
-        BLASTSyncEngine.TransactionReadyListener transactionListener =
-                mock(BLASTSyncEngine.TransactionReadyListener.class);
-
-        int id = bse.startSyncSet(transactionListener);
-        bse.addToSyncSet(id, task);
-        bse.setReady(id);
-        // Since this task has no windows the sync is trivial and completes immediately.
-        verify(transactionListener)
-            .onTransactionReady(anyInt(), any());
-    }
-
-    @Test
-    public void testOverlappingBLASTCallback() throws RemoteException {
-        final Task stackController1 = createStack();
-        final Task task = createTask(stackController1);
-        final ITaskOrganizer organizer = registerMockOrganizer();
-
-        spyOn(task);
-        doReturn(true).when(task).isVisible();
-        final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window");
-        makeWindowVisible(w);
-
-        BLASTSyncEngine bse = new BLASTSyncEngine();
-
-        BLASTSyncEngine.TransactionReadyListener transactionListener =
-                mock(BLASTSyncEngine.TransactionReadyListener.class);
-
-        int id = bse.startSyncSet(transactionListener);
-        assertEquals(true, bse.addToSyncSet(id, task));
-        bse.setReady(id);
-
-        int id2 = bse.startSyncSet(transactionListener);
-        // We should be rejected from the second sync since we are already
-        // in one.
-        assertEquals(false, bse.addToSyncSet(id2, task));
-        w.immediatelyNotifyBlastSync();
-        assertEquals(true, bse.addToSyncSet(id2, task));
-        bse.setReady(id2);
-    }
-
-    @Test
-    public void testBLASTCallbackWithWindow() {
-        final Task stackController1 = createStack();
-        final Task task = createTask(stackController1);
-        final ITaskOrganizer organizer = registerMockOrganizer();
-        final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window");
-        makeWindowVisible(w);
-
-        BLASTSyncEngine bse = new BLASTSyncEngine();
-
-        BLASTSyncEngine.TransactionReadyListener transactionListener =
-                mock(BLASTSyncEngine.TransactionReadyListener.class);
-
-        int id = bse.startSyncSet(transactionListener);
-        bse.addToSyncSet(id, task);
-        bse.setReady(id);
-        // Since we have a window we have to wait for it to draw to finish sync.
-        verify(transactionListener, never())
-            .onTransactionReady(anyInt(), any());
-        w.immediatelyNotifyBlastSync();
-        verify(transactionListener)
-            .onTransactionReady(anyInt(), any());
-    }
-
-    @Test
-    public void testBLASTCallbackNoDoubleAdd() {
-        final Task stackController1 = createStack();
-        final Task task = createTask(stackController1);
-        final ITaskOrganizer organizer = registerMockOrganizer();
-        final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window");
-        makeWindowVisible(w);
-
-        BLASTSyncEngine bse = new BLASTSyncEngine();
-
-        BLASTSyncEngine.TransactionReadyListener transactionListener =
-                mock(BLASTSyncEngine.TransactionReadyListener.class);
-
-        int id = bse.startSyncSet(transactionListener);
-        assertTrue(bse.addToSyncSet(id, w));
-        assertFalse(bse.addToSyncSet(id, w));
-
-        // Clean-up
-        bse.setReady(id);
-    }
-
-
-    @Test
-    public void testBLASTCallbackWithInvisibleWindow() {
-        final Task stackController1 = createStack();
-        final Task task = createTask(stackController1);
-        final ITaskOrganizer organizer = registerMockOrganizer();
         final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window");
 
-        BLASTSyncEngine bse = new BLASTSyncEngine();
-
-        BLASTSyncEngine.TransactionReadyListener transactionListener =
-                mock(BLASTSyncEngine.TransactionReadyListener.class);
-
-        int id = bse.startSyncSet(transactionListener);
-        bse.addToSyncSet(id, task);
-        bse.setReady(id);
-
-        // Since the window was invisible, the Task had no visible leaves and the sync should
-        // complete as soon as we call setReady.
-        verify(transactionListener)
-            .onTransactionReady(anyInt(), any());
-    }
-
-    @Test
-    public void testBLASTCallbackWithChildWindow() {
-        final Task stackController1 = createStack();
-        final Task task = createTask(stackController1);
-        final ITaskOrganizer organizer = registerMockOrganizer();
-        final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window");
-        final WindowState child = createWindow(w, TYPE_APPLICATION, "Other Window");
-
+        w.mActivityRecord.mVisibleRequested = true;
         w.mActivityRecord.setVisible(true);
-        makeWindowVisible(w, child);
 
-        BLASTSyncEngine bse = new BLASTSyncEngine();
+        BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
 
         BLASTSyncEngine.TransactionReadyListener transactionListener =
                 mock(BLASTSyncEngine.TransactionReadyListener.class);
 
         int id = bse.startSyncSet(transactionListener);
-        assertEquals(true, bse.addToSyncSet(id, task));
+        bse.addToSyncSet(id, task);
         bse.setReady(id);
-        w.immediatelyNotifyBlastSync();
+        bse.onSurfacePlacement();
 
+        // Even though w is invisible (and thus activity isn't waiting on it), activity will
+        // continue to wait until it has at-least 1 visible window.
         // Since we have a child window we still shouldn't be done.
-        verify(transactionListener, never())
-            .onTransactionReady(anyInt(), any());
-        reset(transactionListener);
+        verify(transactionListener, never()).onTransactionReady(anyInt(), any());
 
-        child.immediatelyNotifyBlastSync();
-        // Ah finally! Done
-        verify(transactionListener)
-                .onTransactionReady(anyInt(), any());
+        makeWindowVisible(w);
+        bse.onSurfacePlacement();
+        w.immediatelyNotifyBlastSync();
+        bse.onSurfacePlacement();
+
+        verify(transactionListener).onTransactionReady(anyInt(), any());
     }
 
     class StubOrganizer extends ITaskOrganizer.Stub {
@@ -922,9 +853,9 @@
 
     @Test
     public void testPreventDuplicateAppear() throws RemoteException {
+        final ITaskOrganizer organizer = registerMockOrganizer();
         final Task stack = createStack();
         final Task task = createTask(stack, false /* fakeDraw */);
-        final ITaskOrganizer organizer = registerMockOrganizer();
 
         stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
         stack.setTaskOrganizer(organizer);
@@ -945,17 +876,14 @@
 
     @Test
     public void testInterceptBackPressedOnTaskRoot() throws RemoteException {
+        final ITaskOrganizer organizer = registerMockOrganizer();
         final Task stack = createStack();
         final Task task = createTask(stack);
         final ActivityRecord activity = createActivityRecordInTask(stack.mDisplayContent, task);
         final Task stack2 = createStack();
         final Task task2 = createTask(stack2);
         final ActivityRecord activity2 = createActivityRecordInTask(stack.mDisplayContent, task2);
-        final ITaskOrganizer organizer = registerMockOrganizer();
 
-        // Setup the task to be controlled by the MW mode organizer
-        stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
-        stack2.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
         assertTrue(stack.isOrganized());
         assertTrue(stack2.isOrganized());
 
@@ -981,10 +909,9 @@
     }
 
     @Test
-    public void testBLASTCallbackWithMultipleWindows() throws Exception {
+    public void testBLASTCallbackWithWindows() throws Exception {
         final Task stackController = createStack();
         final Task task = createTask(stackController);
-        final ITaskOrganizer organizer = registerMockOrganizer();
         final WindowState w1 = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window 1");
         final WindowState w2 = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window 2");
         makeWindowVisible(w1);
@@ -1001,13 +928,20 @@
         verify(mockCallback, never()).onTransactionReady(anyInt(), any());
         assertTrue(w1.useBLASTSync());
         assertTrue(w2.useBLASTSync());
-        w1.immediatelyNotifyBlastSync();
 
+        // Make second (bottom) ready. If we started with the top, since activities fillsParent
+        // by default, the sync would be considered finished.
+        w2.immediatelyNotifyBlastSync();
+        mWm.mSyncEngine.onSurfacePlacement();
+        verify(mockCallback, never()).onTransactionReady(anyInt(), any());
+
+        assertEquals(SYNC_STATE_READY, w2.mSyncState);
         // Even though one Window finished drawing, both windows should still be using blast sync
         assertTrue(w1.useBLASTSync());
         assertTrue(w2.useBLASTSync());
 
-        w2.immediatelyNotifyBlastSync();
+        w1.immediatelyNotifyBlastSync();
+        mWm.mSyncEngine.onSurfacePlacement();
         verify(mockCallback).onTransactionReady(anyInt(), any());
         assertFalse(w1.useBLASTSync());
         assertFalse(w2.useBLASTSync());
@@ -1035,4 +969,37 @@
             assertFalse(daTask.isForceHidden());
         });
     }
+
+    /**
+     * Verifies that task vanished is called for a specific task.
+     */
+    private void assertTaskVanished(ITaskOrganizer organizer, boolean expectVanished, Task... tasks)
+            throws RemoteException {
+        ArgumentCaptor<RunningTaskInfo> arg = ArgumentCaptor.forClass(RunningTaskInfo.class);
+        verify(organizer, atLeastOnce()).onTaskVanished(arg.capture());
+        List<RunningTaskInfo> taskInfos = arg.getAllValues();
+
+        HashSet<Integer> vanishedTaskIds = new HashSet<>();
+        for (int i = 0; i < taskInfos.size(); i++) {
+            vanishedTaskIds.add(taskInfos.get(i).taskId);
+        }
+        HashSet<Integer> taskIds = new HashSet<>();
+        for (int i = 0; i < tasks.length; i++) {
+            taskIds.add(tasks[i].mTaskId);
+        }
+
+        assertTrue(expectVanished
+                ? vanishedTaskIds.containsAll(taskIds)
+                : !vanishedTaskIds.removeAll(taskIds));
+    }
+
+    private void assertContainsTasks(List<TaskAppearedInfo> taskInfos, Task... expectedTasks) {
+        HashSet<Integer> taskIds = new HashSet<>();
+        for (int i = 0; i < taskInfos.size(); i++) {
+            taskIds.add(taskInfos.get(i).getTaskInfo().taskId);
+        }
+        for (int i = 0; i < expectedTasks.length; i++) {
+            assertTrue(taskIds.contains(expectedTasks[i].mTaskId));
+        }
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
index e50c009..94bbfca 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -28,7 +28,6 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.when;
 
 import android.Manifest;
 import android.app.IApplicationThread;
@@ -160,31 +159,6 @@
     }
 
     @Test
-    public void testDelayingConfigurationChange() {
-        when(mMockListener.isCached()).thenReturn(false);
-
-        Configuration tmpConfig = new Configuration(mWpc.getConfiguration());
-        invertOrientation(tmpConfig);
-        mWpc.onConfigurationChanged(tmpConfig);
-
-        // The last reported config should be the current config as the process is not cached.
-        Configuration originalConfig = new Configuration(mWpc.getConfiguration());
-        assertEquals(mWpc.getLastReportedConfiguration(), originalConfig);
-
-        when(mMockListener.isCached()).thenReturn(true);
-        invertOrientation(tmpConfig);
-        mWpc.onConfigurationChanged(tmpConfig);
-
-        Configuration newConfig = new Configuration(mWpc.getConfiguration());
-
-        // Last reported config hasn't changed because the process is in a cached state.
-        assertEquals(mWpc.getLastReportedConfiguration(), originalConfig);
-
-        mWpc.onProcCachedStateChanged(false);
-        assertEquals(mWpc.getLastReportedConfiguration(), newConfig);
-    }
-
-    @Test
     public void testActivityNotOverridingSystemUiProcessConfig() {
         final ComponentName systemUiServiceComponent = mAtm.getSysUiServiceComponentLocked();
         ApplicationInfo applicationInfo = mock(ApplicationInfo.class);
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 c18043f..2691ae8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -46,6 +46,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.WindowContainer.SYNC_STATE_WAITING_FOR_DRAW;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -71,7 +72,7 @@
 import android.platform.test.annotations.Presubmit;
 import android.util.Size;
 import android.view.DisplayCutout;
-import android.view.InsetsSource;
+import android.view.InsetsState;
 import android.view.SurfaceControl;
 import android.view.WindowManager;
 
@@ -426,10 +427,11 @@
                 .setWindow(statusBar, null /* frameProvider */, null /* imeFrameProvider */);
         mDisplayContent.getInsetsStateController().onBarControlTargetChanged(
                 app, null /* fakeTopControlling */, app, null /* fakeNavControlling */);
-        final InsetsSource source = new InsetsSource(ITYPE_STATUS_BAR);
-        source.setVisible(false);
+        final InsetsState state = new InsetsState();
+        state.getSource(ITYPE_STATUS_BAR).setVisible(false);
+        app.updateRequestedVisibility(state);
         mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_STATUS_BAR)
-                .onInsetsModified(app, source);
+                .updateClientVisibility(app);
         waitUntilHandlersIdle();
         assertFalse(statusBar.isVisible());
     }
@@ -608,7 +610,8 @@
 
         // Check that the window is in resizing if using blast sync.
         win.reportResized();
-        win.prepareForSync(mock(BLASTSyncEngine.TransactionReadyListener.class), 1);
+        win.prepareSync();
+        assertEquals(SYNC_STATE_WAITING_FOR_DRAW, win.mSyncState);
         win.updateResizingWindowIfNeeded();
         assertThat(mWm.mResizingWindows).contains(win);
 
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index f151d9c..8e56e5b 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -37,7 +37,6 @@
 import android.app.AppOpsManager;
 import android.app.IUidObserver;
 import android.app.PendingIntent;
-import android.app.admin.DeviceAdminInfo;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.app.usage.AppStandbyInfo;
 import android.app.usage.ConfigurationStats;
@@ -1431,10 +1430,11 @@
         private boolean hasObserverPermission() {
             final int callingUid = Binder.getCallingUid();
             DevicePolicyManagerInternal dpmInternal = getDpmInternal();
+            //TODO(b/169395065) Figure out if this flow makes sense in Device Owner mode.
             if (callingUid == Process.SYSTEM_UID
                     || (dpmInternal != null
-                        && dpmInternal.isActiveAdminWithPolicy(callingUid,
-                            DeviceAdminInfo.USES_POLICY_PROFILE_OWNER))) {
+                        && (dpmInternal.isActiveProfileOwner(callingUid)
+                        || dpmInternal.isActiveDeviceOwner(callingUid)))) {
                 // Caller is the system or the profile owner, so proceed.
                 return true;
             }
diff --git a/services/usb/java/com/android/server/usb/MtpNotificationManager.java b/services/usb/java/com/android/server/usb/MtpNotificationManager.java
index 462ee19..8845f11 100644
--- a/services/usb/java/com/android/server/usb/MtpNotificationManager.java
+++ b/services/usb/java/com/android/server/usb/MtpNotificationManager.java
@@ -64,12 +64,13 @@
 
     private final Context mContext;
     private final OnOpenInAppListener mListener;
+    private final Receiver mReceiver;
 
     MtpNotificationManager(Context context, OnOpenInAppListener listener) {
         mContext = context;
         mListener = listener;
-        final Receiver receiver = new Receiver();
-        context.registerReceiver(receiver, new IntentFilter(ACTION_OPEN_IN_APPS));
+        mReceiver = new Receiver();
+        context.registerReceiver(mReceiver, new IntentFilter(ACTION_OPEN_IN_APPS));
     }
 
     void showNotification(UsbDevice device) {
@@ -90,11 +91,12 @@
         intent.putExtra(UsbManager.EXTRA_DEVICE, device);
         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
 
+        // Simple notification clicks are immutable
         final PendingIntent openIntent = PendingIntent.getBroadcastAsUser(
                 mContext,
                 device.getDeviceId(),
                 intent,
-                PendingIntent.FLAG_UPDATE_CURRENT,
+                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE,
                 UserHandle.SYSTEM);
         builder.setContentIntent(openIntent);
 
@@ -154,4 +156,8 @@
     static interface OnOpenInAppListener {
         void onOpenInApp(UsbDevice device);
     }
+
+    public void unregister() {
+        mContext.unregisterReceiver(mReceiver);
+    }
 }
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 2269e1d..58859e0 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -1206,8 +1206,9 @@
                         Intent intent = Intent.makeRestartActivityTask(
                                 new ComponentName("com.android.settings",
                                         "com.android.settings.Settings$UsbDetailsActivity"));
+                        // Simple notification clicks are immutable
                         pi = PendingIntent.getActivityAsUser(mContext, 0,
-                                intent, 0, null, UserHandle.CURRENT);
+                                intent, PendingIntent.FLAG_IMMUTABLE, null, UserHandle.CURRENT);
                         channel = SystemNotificationChannels.USB;
                     } else {
                         final Intent intent = new Intent();
@@ -1217,7 +1218,9 @@
                                 "help_url_audio_accessory_not_supported");
 
                         if (mContext.getPackageManager().resolveActivity(intent, 0) != null) {
-                            pi = PendingIntent.getActivity(mContext, 0, intent, 0);
+                            // Simple notification clicks are immutable
+                            pi = PendingIntent.getActivity(mContext, 0, intent,
+                                    PendingIntent.FLAG_IMMUTABLE);
                         } else {
                             pi = null;
                         }
diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java
index ec7d4bd..ca18c57 100644
--- a/services/usb/java/com/android/server/usb/UsbPortManager.java
+++ b/services/usb/java/com/android/server/usb/UsbPortManager.java
@@ -230,8 +230,9 @@
                     com.android.internal.R.string.config_usbContaminantActivity)));
             intent.putExtra(UsbManager.EXTRA_PORT, ParcelableUsbPort.of(currentPortInfo.mUsbPort));
 
+            // Simple notification clicks are immutable
             PendingIntent pi = PendingIntent.getActivityAsUser(mContext, 0,
-                                intent, 0, null, UserHandle.CURRENT);
+                                intent, PendingIntent.FLAG_IMMUTABLE, null, UserHandle.CURRENT);
 
             Notification.Builder builder = new Notification.Builder(mContext, channel)
                     .setOngoing(true)
diff --git a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
index d7b6b5d..26ee03c 100644
--- a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
@@ -261,6 +261,15 @@
     }
 
     /**
+     * Unregister all broadcast receivers. Must be called explicitly before
+     * object deletion.
+     */
+    public void unregisterReceivers() {
+        mPackageMonitor.unregister();
+        mMtpNotificationManager.unregister();
+    }
+
+    /**
      * Remove all defaults and denied packages for a user.
      *
      * @param userToRemove The user
diff --git a/services/usb/java/com/android/server/usb/UsbSerialReader.java b/services/usb/java/com/android/server/usb/UsbSerialReader.java
index 095e8e9..f241e65 100644
--- a/services/usb/java/com/android/server/usb/UsbSerialReader.java
+++ b/services/usb/java/com/android/server/usb/UsbSerialReader.java
@@ -77,7 +77,7 @@
 
             UserHandle user = Binder.getCallingUserHandle();
             int packageTargetSdkVersion;
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 PackageInfo pkg;
                 try {
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index 14c7f04..444cb5c 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -281,7 +281,7 @@
                 int pid = Binder.getCallingPid();
                 int user = UserHandle.getUserId(uid);
 
-                long ident = clearCallingIdentity();
+                final long ident = clearCallingIdentity();
                 try {
                     synchronized (mLock) {
                         if (mUserManager.isSameProfileGroup(user, mCurrentUserId)) {
@@ -318,7 +318,7 @@
             int uid = Binder.getCallingUid();
             int user = UserHandle.getUserId(uid);
 
-            long ident = clearCallingIdentity();
+            final long ident = clearCallingIdentity();
             try {
                 synchronized (mLock) {
                     if (mUserManager.isSameProfileGroup(user, mCurrentUserId)) {
diff --git a/services/usb/java/com/android/server/usb/UsbSettingsManager.java b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
index 7b677ee..8e53ff4 100644
--- a/services/usb/java/com/android/server/usb/UsbSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
@@ -124,6 +124,7 @@
             if (mSettingsByProfileGroup.indexOfKey(userToRemove.getIdentifier()) >= 0) {
                 // The user to remove is the parent user of the group. The parent is the last user
                 // that gets removed. All state will be removed with the user
+                mSettingsByProfileGroup.get(userToRemove.getIdentifier()).unregisterReceivers();
                 mSettingsByProfileGroup.remove(userToRemove.getIdentifier());
             } else {
                 // We cannot find the parent user of the user that is removed, hence try to remove
diff --git a/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java b/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
index 333edfd9..e20b1a4d 100644
--- a/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
+++ b/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
@@ -505,22 +505,23 @@
             int uid,
             @NonNull Context userContext,
             @NonNull PendingIntent pi) {
-        long identity = Binder.clearCallingIdentity();
-        Intent intent = new Intent();
-        if (device != null) {
-            intent.putExtra(UsbManager.EXTRA_DEVICE, device);
-        } else {
-            intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
-        }
-        intent.putExtra(Intent.EXTRA_INTENT, pi);
-        intent.putExtra(Intent.EXTRA_UID, uid);
-        intent.putExtra(UsbManager.EXTRA_CAN_BE_DEFAULT, canBeDefault);
-        intent.putExtra(UsbManager.EXTRA_PACKAGE, packageName);
-        intent.setComponent(ComponentName.unflattenFromString(userContext.getResources().getString(
-                com.android.internal.R.string.config_usbPermissionActivity)));
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
+        final long identity = Binder.clearCallingIdentity();
         try {
+            Intent intent = new Intent();
+            if (device != null) {
+                intent.putExtra(UsbManager.EXTRA_DEVICE, device);
+            } else {
+                intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
+            }
+            intent.putExtra(Intent.EXTRA_INTENT, pi);
+            intent.putExtra(Intent.EXTRA_UID, uid);
+            intent.putExtra(UsbManager.EXTRA_CAN_BE_DEFAULT, canBeDefault);
+            intent.putExtra(UsbManager.EXTRA_PACKAGE, packageName);
+            intent.setComponent(
+                    ComponentName.unflattenFromString(userContext.getResources().getString(
+                            com.android.internal.R.string.config_usbPermissionActivity)));
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
             userContext.startActivityAsUser(intent, mUser);
         } catch (ActivityNotFoundException e) {
             Slog.e(TAG, "unable to start UsbPermissionActivity");
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index b6506b4..64f8c58 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
@@ -1027,7 +1027,7 @@
     // internalClearGlobalStateLocked() cleans up the telephony and power save listeners.
     private void internalClearGlobalStateLocked() {
         // Unregister from call state changes.
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
         } finally {
@@ -1100,7 +1100,7 @@
         if (mRecognitionRequested) {
             return;
         }
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             // Get the current call state synchronously for the first recognition.
             mCallActive = mTelephonyManager.getCallState() == TelephonyManager.CALL_STATE_OFFHOOK;
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index a73e73e..5999044 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -899,7 +899,7 @@
                 mClient = new ISoundTriggerDetectionServiceClient.Stub() {
                     @Override
                     public void onOpFinished(int opId) {
-                        long token = Binder.clearCallingIdentity();
+                        final long token = Binder.clearCallingIdentity();
                         try {
                             synchronized (mRemoteServiceLock) {
                                 mRunningOpIds.remove(opId);
@@ -1013,7 +1013,7 @@
              * Verify that the service has the expected properties and then bind to the service
              */
             private void bind() {
-                long token = Binder.clearCallingIdentity();
+                final long token = Binder.clearCallingIdentity();
                 try {
                     Intent i = new Intent();
                     i.setComponent(mServiceName);
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index f4fc185..547d253 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -1039,8 +1039,8 @@
             }
 
             final int callingUserId = UserHandle.getCallingUserId();
-            final long caller = Binder.clearCallingIdentity();
             boolean deleted = false;
+            final long caller = Binder.clearCallingIdentity();
             try {
                 SoundTriggerSession session = mLoadedKeyphraseIds.get(keyphraseId);
                 if (session != null) {
@@ -1048,9 +1048,9 @@
                     if (unloadStatus != SoundTriggerInternal.STATUS_OK) {
                         Slog.w(TAG, "Unable to unload keyphrase sound model:" + unloadStatus);
                     }
-                    deleted = mDbHelper.deleteKeyphraseSoundModel(keyphraseId, callingUserId,
-                            bcp47Locale);
                 }
+                deleted = mDbHelper.deleteKeyphraseSoundModel(keyphraseId, callingUserId,
+                        bcp47Locale);
                 return deleted ? SoundTriggerInternal.STATUS_OK : SoundTriggerInternal.STATUS_ERROR;
             } finally {
                 if (deleted) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index 0ff9273..cfdc568 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -309,7 +309,7 @@
         if (!"content".equals(uri.getScheme())) {
             return;
         }
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             // This will throw SecurityException for us.
             mUgmInternal.checkGrantUriPermission(srcUid, null,
diff --git a/startop/iorap/tests/src/com/google/android/startop/iorap/ParcelablesTest.kt b/startop/iorap/tests/src/com/google/android/startop/iorap/ParcelablesTest.kt
index 8fa0cde..150577a 100644
--- a/startop/iorap/tests/src/com/google/android/startop/iorap/ParcelablesTest.kt
+++ b/startop/iorap/tests/src/com/google/android/startop/iorap/ParcelablesTest.kt
@@ -124,7 +124,7 @@
     data class InputData<T : Parcelable>(val valid: T, val validCopy: T, val validOther: T) {
         val kls = valid.javaClass
         init {
-            assertThat(valid).isNotSameAs(validCopy)
+            assertThat(valid).isNotSameInstanceAs(validCopy)
             // Don't use isInstanceOf because of phantom warnings in intellij about Class!
             assertThat(validCopy.javaClass).isEqualTo(valid.javaClass)
             assertThat(validOther.javaClass).isEqualTo(valid.javaClass)
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 0469fa5..a85eb53 100755
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -966,6 +966,32 @@
         /**
          * Gets the verification status for the phone number of an incoming call as identified in
          * ATIS-1000082.
+         * <p>
+         * For incoming calls, the number verification status indicates whether the device was
+         * able to verify the authenticity of the calling number using the STIR process outlined
+         * in ATIS-1000082.  {@link Connection#VERIFICATION_STATUS_NOT_VERIFIED} indicates that
+         * the network was not able to use STIR to verify the caller's number (i.e. nothing is
+         * known regarding the authenticity of the number.
+         * {@link Connection#VERIFICATION_STATUS_PASSED} indicates that the network was able to
+         * use STIR to verify the caller's number.  This indicates that the network has a high
+         * degree of confidence that the incoming call actually originated from the indicated
+         * number.  {@link Connection#VERIFICATION_STATUS_FAILED} indicates that the network's
+         * STIR verification did not pass.  This indicates that the incoming call may not
+         * actually be from the indicated number.  This could occur if, for example, the caller
+         * is using an impersonated phone number.
+         * <p>
+         * A {@link CallScreeningService} can use this information to help determine if an
+         * incoming call is potentially an unwanted call.  A verification status of
+         * {@link Connection#VERIFICATION_STATUS_FAILED} indicates that an incoming call may not
+         * actually be from the number indicated on the call (i.e. impersonated number) and that it
+         * should potentially be blocked.  Likewise,
+         * {@link Connection#VERIFICATION_STATUS_PASSED} can be used as a positive signal to
+         * help clarify that the incoming call is originating from the indicated number and it
+         * is less likely to be an undesirable call.
+         * <p>
+         * An {@link InCallService} can use this information to provide a visual indicator to the
+         * user regarding the verification status of a call and to help identify calls from
+         * potentially impersonated numbers.
          * @return the verification status.
          */
         public @Connection.VerificationStatus int getCallerNumberVerificationStatus() {
@@ -1657,7 +1683,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public void enterBackgroundAudioProcessing() {
         if (mState != STATE_ACTIVE && mState != STATE_RINGING) {
             throw new IllegalStateException("Call must be active or ringing");
@@ -1678,7 +1703,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public void exitBackgroundAudioProcessing(boolean shouldRing) {
         if (mState != STATE_AUDIO_PROCESSING) {
             throw new IllegalStateException("Call must in the audio processing state");
@@ -2111,7 +2135,13 @@
 
     /**
      * Obtains a list of canned, pre-configured message responses to present to the user as
-     * ways of rejecting this {@code Call} using via a text message.
+     * ways of rejecting an incoming {@code Call} using via a text message.
+     * <p>
+     * <em>Note:</em> Since canned responses may be loaded from the file system, they are not
+     * guaranteed to be present when this {@link Call} is first added to the {@link InCallService}
+     * via {@link InCallService#onCallAdded(Call)}.  The callback
+     * {@link Call.Callback#onCannedTextResponsesLoaded(Call, List)} will be called when/if canned
+     * responses for the call become available.
      *
      * @see #reject(boolean, String)
      *
diff --git a/telecomm/java/android/telecom/CallScreeningService.java b/telecomm/java/android/telecom/CallScreeningService.java
index 4d9311c..7988b03 100644
--- a/telecomm/java/android/telecom/CallScreeningService.java
+++ b/telecomm/java/android/telecom/CallScreeningService.java
@@ -21,7 +21,6 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.app.Service;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -64,7 +63,7 @@
  *     </li>
  * </ol>
  * <p>
- * <h2>Becoming the {@link CallScreeningService}</h2>
+ * <h2>Becoming the CallScreeningService</h2>
  * Telecom will bind to a single app chosen by the user which implements the
  * {@link CallScreeningService} API when there are new incoming and outgoing calls.
  * <p>
@@ -90,7 +89,27 @@
  *         }
  *     }
  * }
+ * }
  * </pre>
+ *
+ * <h2>CallScreeningService Lifecycle</h2>
+ *
+ * The framework binds to the {@link CallScreeningService} implemented by the user-chosen app
+ * filling the {@link android.app.role.RoleManager#ROLE_CALL_SCREENING} role when incoming calls are
+ * received (prior to ringing) and when outgoing calls are placed.  The platform calls the
+ * {@link #onScreenCall(Call.Details)} method to provide your service with details about the call.
+ * <p>
+ * For incoming calls, the {@link CallScreeningService} must call
+ * {@link #respondToCall(Call.Details, CallResponse)} within 5 seconds of being bound to indicate to
+ * the platform whether the call should be blocked or not.  Your app must do this even if it is
+ * primarily performing caller ID operations and not screening calls.  It is important to perform
+ * screening operations in a timely matter as the user's device will not begin ringing until the
+ * response is received (or the timeout is hit).  A {@link CallScreeningService} may choose to
+ * perform local database lookups to help determine if a call should be screened or not; care should
+ * be taken to ensure the timeout is not repeatedly hit, causing delays in the incoming call flow.
+ * <p>
+ * If your app provides a caller ID experience, it should launch an activity to show the caller ID
+ * information from {@link #onScreenCall(Call.Details)}.
  */
 public abstract class CallScreeningService extends Service {
     /**
@@ -303,7 +322,6 @@
              * @hide
              */
             @SystemApi
-            @TestApi
             @RequiresPermission(Manifest.permission.CAPTURE_AUDIO_OUTPUT)
             public @NonNull Builder setShouldScreenCallViaAudioProcessing(
                     boolean shouldScreenCallViaAudioProcessing) {
@@ -339,7 +357,7 @@
     }
 
     /**
-     * Called when a new incoming or outgoing call is added which is not in the user's contact list.
+     * Called when a new incoming or outgoing call is added.
      * <p>
      * A {@link CallScreeningService} must indicate whether an incoming call is allowed or not by
      * calling
@@ -347,21 +365,32 @@
      * Your app can tell if a call is an incoming call by checking to see if
      * {@link Call.Details#getCallDirection()} is {@link Call.Details#DIRECTION_INCOMING}.
      * <p>
-     * Note: The {@link Call.Details} instance provided to a call screening service will only have
-     * the following properties set.  The rest of the {@link Call.Details} properties will be set to
-     * their default value or {@code null}.
+     * <em>Note:</em> A {@link CallScreeningService} must respond to a call within 5 seconds.  After
+     * this time, the framework will unbind from the {@link CallScreeningService} and ignore its
+     * response.
+     * <p>
+     * <em>Note:</em> The {@link Call.Details} instance provided to a call screening service will
+     * only have the following properties set.  The rest of the {@link Call.Details} properties will
+     * be set to their default value or {@code null}.
      * <ul>
      *     <li>{@link Call.Details#getCallDirection()}</li>
+     *     <li>{@link Call.Details#getCallerNumberVerificationStatus()}</li>
      *     <li>{@link Call.Details#getConnectTimeMillis()}</li>
      *     <li>{@link Call.Details#getCreationTimeMillis()}</li>
      *     <li>{@link Call.Details#getHandle()}</li>
-     *     <li>{@link Call.Details#getHandlePresentation()}</li>
      * </ul>
      * <p>
      * Only calls where the {@link Call.Details#getHandle() handle} {@link Uri#getScheme() scheme}
      * is {@link PhoneAccount#SCHEME_TEL} are passed for call
      * screening.  Further, only calls which are not in the user's contacts are passed for
-     * screening.  For outgoing calls, no post-dial digits are passed.
+     * screening, unless the {@link CallScreeningService} has been granted
+     * {@link Manifest.permission#READ_CONTACTS} permission by the user.  For outgoing calls, no
+     * post-dial digits are passed.
+     * <p>
+     * Calls with a {@link Call.Details#getHandlePresentation()} of
+     * {@link TelecomManager#PRESENTATION_RESTRICTED}, {@link TelecomManager#PRESENTATION_UNKNOWN}
+     * or {@link TelecomManager#PRESENTATION_PAYPHONE} presentation are not provided to the
+     * {@link CallScreeningService}.
      *
      * @param callDetails Information about a new call, see {@link Call.Details}.
      */
@@ -376,6 +405,13 @@
      * <p>
      * Calls to this method are ignored unless the {@link Call.Details#getCallDirection()} is
      * {@link Call.Details#DIRECTION_INCOMING}.
+     * <p>
+     * For incoming calls, a {@link CallScreeningService} MUST call this method within 5 seconds of
+     * {@link #onScreenCall(Call.Details)} being invoked by the platform.
+     * <p>
+     * Calls which are blocked/rejected will be logged to the system call log with a call type of
+     * {@link android.provider.CallLog.Calls#BLOCKED_TYPE} and
+     * {@link android.provider.CallLog.Calls#BLOCK_REASON_CALL_SCREENING_SERVICE} block reason.
      *
      * @param callDetails The call to allow.
      *                    <p>
diff --git a/telecomm/java/android/telecom/CallerInfo.java b/telecomm/java/android/telecom/CallerInfo.java
index fb6f994..aff2f01 100644
--- a/telecomm/java/android/telecom/CallerInfo.java
+++ b/telecomm/java/android/telecom/CallerInfo.java
@@ -405,7 +405,8 @@
         // Change the callerInfo number ONLY if it is an emergency number
         // or if it is the voicemail number.  If it is either, take a
         // shortcut and skip the query.
-        if (PhoneNumberUtils.isLocalEmergencyNumber(context, number)) {
+        TelephonyManager tm = context.getSystemService(TelephonyManager.class);
+        if (tm.isEmergencyNumber(number)) {
             return new CallerInfo().markAsEmergency(context);
         } else if (PhoneNumberUtils.isVoiceMailNumber(null, subId, number)) {
             return new CallerInfo().markAsVoiceMail(context, subId);
diff --git a/telecomm/java/android/telecom/CallerInfoAsyncQuery.java b/telecomm/java/android/telecom/CallerInfoAsyncQuery.java
index 4a81a8e..a9e1a8f 100644
--- a/telecomm/java/android/telecom/CallerInfoAsyncQuery.java
+++ b/telecomm/java/android/telecom/CallerInfoAsyncQuery.java
@@ -34,6 +34,7 @@
 import android.provider.ContactsContract.PhoneLookup;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 
 import java.util.ArrayList;
@@ -481,7 +482,8 @@
         cw.subId = subId;
 
         // check to see if these are recognized numbers, and use shortcuts if we can.
-        if (PhoneNumberUtils.isLocalEmergencyNumber(context, number)) {
+        TelephonyManager tm = context.getSystemService(TelephonyManager.class);
+        if (tm.isEmergencyNumber(number)) {
             cw.event = EVENT_EMERGENCY_NUMBER;
         } else if (PhoneNumberUtils.isVoiceMailNumber(context, subId, number)) {
             cw.event = EVENT_VOICEMAIL_NUMBER;
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index 39c3ff9..dc2fb94 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -24,7 +24,6 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.SystemClock;
@@ -137,7 +136,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public final @NonNull String getTelecomCallId() {
         return mTelecomCallId;
     }
@@ -609,7 +607,6 @@
      * @return The primary connection.
      * @hide
      */
-    @TestApi
     @SystemApi
     public Connection getPrimaryConnection() {
         if (mUnmodifiableChildConnections == null || mUnmodifiableChildConnections.isEmpty()) {
@@ -1012,7 +1009,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(MODIFY_PHONE_STATE)
     public void setConferenceState(boolean isConference) {
         mIsMultiparty = isConference;
@@ -1067,7 +1063,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(MODIFY_PHONE_STATE)
     public final void setAddress(@NonNull Uri address,
             @TelecomManager.Presentation int presentation) {
@@ -1155,7 +1150,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public final void setCallerDisplayName(@NonNull String callerDisplayName,
             @TelecomManager.Presentation int presentation) {
         Log.d(this, "setCallerDisplayName %s", callerDisplayName);
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
old mode 100755
new mode 100644
index 00b7116..bbf34df
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -25,7 +25,6 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.app.Notification;
 import android.bluetooth.BluetoothDevice;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -307,7 +306,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final int CAPABILITY_SPEED_UP_MT_AUDIO = 0x00040000;
 
     /**
@@ -345,7 +343,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final int CAPABILITY_CONFERENCE_HAS_NO_CHILDREN = 0x00200000;
 
     /**
@@ -417,7 +414,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 1<<0;
 
     /**
@@ -428,7 +424,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final int PROPERTY_GENERIC_CONFERENCE = 1<<1;
 
     /**
@@ -480,7 +475,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final int PROPERTY_IS_DOWNGRADED_CONFERENCE = 1<<6;
 
     /**
@@ -524,7 +518,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final int PROPERTY_REMOTELY_HOSTED = 1 << 11;
 
     /**
@@ -702,7 +695,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final String EXTRA_DISABLE_ADD_CALL =
             "android.telecom.extra.DISABLE_ADD_CALL";
 
@@ -2054,7 +2046,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public final @Nullable String getTelecomCallId() {
         return mTelecomCallId;
     }
@@ -2171,7 +2162,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public final @IntRange(from = 0) long getConnectTimeMillis() {
         return mConnectTimeMillis;
     }
@@ -2196,7 +2186,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public final @ElapsedRealtimeLong long getConnectionStartElapsedRealtimeMillis() {
         return mConnectElapsedTimeMillis;
     }
@@ -2279,7 +2268,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public void setTelecomCallId(@NonNull String callId) {
         mTelecomCallId = callId;
     }
@@ -2628,7 +2616,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(MODIFY_PHONE_STATE)
     public final void setConnectTimeMillis(@IntRange(from = 0) long connectTimeMillis) {
         mConnectTimeMillis = connectTimeMillis;
@@ -2651,7 +2638,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(MODIFY_PHONE_STATE)
     public final void setConnectionStartElapsedRealtimeMillis(
             @ElapsedRealtimeLong long connectElapsedTimeMillis) {
@@ -2722,7 +2708,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public final void resetConnectionTime() {
         for (Listener l : mListeners) {
             l.onConnectionTimeReset(this);
@@ -3505,7 +3490,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public void setPhoneAccountHandle(@NonNull PhoneAccountHandle phoneAccountHandle) {
         if (mPhoneAccountHandle != phoneAccountHandle) {
             mPhoneAccountHandle = phoneAccountHandle;
@@ -3524,7 +3508,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public @Nullable PhoneAccountHandle getPhoneAccountHandle() {
         return mPhoneAccountHandle;
     }
@@ -3590,7 +3573,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public void setCallDirection(@Call.Details.CallDirection int callDirection) {
         mCallDirection = callDirection;
     }
diff --git a/telecomm/java/android/telecom/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java
index 6d7ceca..b73ef9b 100644
--- a/telecomm/java/android/telecom/ConnectionRequest.java
+++ b/telecomm/java/android/telecom/ConnectionRequest.java
@@ -327,7 +327,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public @Nullable String getTelecomCallId() {
         return mTelecomCallId;
     }
diff --git a/telecomm/java/android/telecom/DefaultDialerManager.java b/telecomm/java/android/telecom/DefaultDialerManager.java
index 5b806a6..1c07074 100644
--- a/telecomm/java/android/telecom/DefaultDialerManager.java
+++ b/telecomm/java/android/telecom/DefaultDialerManager.java
@@ -74,7 +74,7 @@
      * */
     public static boolean setDefaultDialerApplication(Context context, String packageName,
             int user) {
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             CompletableFuture<Void> future = new CompletableFuture<>();
             Consumer<Boolean> callback = successful -> {
@@ -128,7 +128,7 @@
      * @hide
      * */
     public static String getDefaultDialerApplication(Context context, int user) {
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             return CollectionUtils.firstOrNull(context.getSystemService(RoleManager.class)
                     .getRoleHoldersAsUser(RoleManager.ROLE_DIALER, UserHandle.of(user)));
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index 866e171..5024ae2 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -21,7 +21,6 @@
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.content.Intent;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
@@ -635,7 +634,6 @@
          * @hide
          */
         @SystemApi
-        @TestApi
         @RequiresPermission(MODIFY_PHONE_STATE)
         public @NonNull Builder setGroupId(@NonNull String groupId) {
             if (groupId != null) {
diff --git a/telecomm/java/android/telecom/PhoneAccountSuggestionService.java b/telecomm/java/android/telecom/PhoneAccountSuggestionService.java
index ba3822c..8a91b9e 100644
--- a/telecomm/java/android/telecom/PhoneAccountSuggestionService.java
+++ b/telecomm/java/android/telecom/PhoneAccountSuggestionService.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.SdkConstant;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.app.Service;
 import android.content.Intent;
 import android.os.IBinder;
@@ -57,7 +56,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public class PhoneAccountSuggestionService extends Service {
     /**
      * The {@link Intent} that must be declared in the {@code intent-filter} element of the
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index f1deec6..82da447 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -25,7 +25,6 @@
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
-import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
@@ -693,7 +692,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     public static final int TTY_MODE_OFF = 0;
 
@@ -703,7 +701,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     public static final int TTY_MODE_FULL = 1;
 
@@ -714,7 +711,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     public static final int TTY_MODE_HCO = 2;
 
@@ -725,7 +721,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     public static final int TTY_MODE_VCO = 3;
 
@@ -736,7 +731,6 @@
      * TTY mode.
      * @hide
      */
-    @TestApi
     @SystemApi
     public static final String ACTION_CURRENT_TTY_MODE_CHANGED =
             "android.telecom.action.CURRENT_TTY_MODE_CHANGED";
@@ -759,7 +753,6 @@
      * plugged into the device.
      * @hide
      */
-    @TestApi
     @SystemApi
     public static final String EXTRA_CURRENT_TTY_MODE =
             "android.telecom.extra.CURRENT_TTY_MODE";
@@ -771,7 +764,6 @@
      * preferred TTY mode.
      * @hide
      */
-    @TestApi
     @SystemApi
     public static final String ACTION_TTY_PREFERRED_MODE_CHANGED =
             "android.telecom.action.TTY_PREFERRED_MODE_CHANGED";
@@ -790,7 +782,6 @@
      * </ul>
      * @hide
      */
-    @TestApi
     @SystemApi
     public static final String EXTRA_TTY_PREFERRED_MODE =
             "android.telecom.extra.TTY_PREFERRED_MODE";
@@ -1045,7 +1036,6 @@
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-    @TestApi
     @SystemApi
     public void setUserSelectedOutgoingPhoneAccount(@Nullable PhoneAccountHandle accountHandle) {
         try {
@@ -1219,7 +1209,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(READ_PRIVILEGED_PHONE_STATE)
     public @NonNull List<PhoneAccountHandle> getCallCapablePhoneAccounts(
             boolean includeDisabledAccounts) {
@@ -1453,7 +1442,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(READ_PRIVILEGED_PHONE_STATE)
     public @Nullable String getDefaultDialerPackage(@NonNull UserHandle userHandle) {
         try {
@@ -1677,7 +1665,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(anyOf = {
             READ_PRIVILEGED_PHONE_STATE,
             android.Manifest.permission.READ_PHONE_STATE
@@ -1835,7 +1822,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(READ_PRIVILEGED_PHONE_STATE)
     public @TtyMode int getCurrentTtyMode() {
         try {
@@ -2249,7 +2235,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @NonNull
     public Intent createLaunchEmergencyDialerIntent(@Nullable String number) {
         ITelecomService service = getTelecomService();
@@ -2402,7 +2387,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public boolean isInEmergencyCall() {
         try {
diff --git a/telephony/api/system-current.txt b/telephony/api/system-current.txt
index 3ff48ce..5eaa6c8 100644
--- a/telephony/api/system-current.txt
+++ b/telephony/api/system-current.txt
@@ -227,6 +227,25 @@
     field public static final String MBMS_STREAMING_SERVICE_ACTION = "android.telephony.action.EmbmsStreaming";
   }
 
+  public final class ModemActivityInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public android.telephony.ModemActivityInfo getDelta(@NonNull android.telephony.ModemActivityInfo);
+    method public long getIdleTimeMillis();
+    method public static int getNumTxPowerLevels();
+    method public long getReceiveTimeMillis();
+    method public long getSleepTimeMillis();
+    method public long getTimestampMillis();
+    method public long getTransmitDurationMillisAtPowerLevel(int);
+    method @NonNull public android.util.Range<java.lang.Integer> getTransmitPowerRange(int);
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ModemActivityInfo> CREATOR;
+    field public static final int TX_POWER_LEVEL_0 = 0; // 0x0
+    field public static final int TX_POWER_LEVEL_1 = 1; // 0x1
+    field public static final int TX_POWER_LEVEL_2 = 2; // 0x2
+    field public static final int TX_POWER_LEVEL_3 = 3; // 0x3
+    field public static final int TX_POWER_LEVEL_4 = 4; // 0x4
+  }
+
   public final class NetworkRegistrationInfo implements android.os.Parcelable {
     method @Nullable public android.telephony.DataSpecificRegistrationInfo getDataSpecificInfo();
     method public int getRegistrationState();
@@ -585,7 +604,6 @@
     method public boolean disableCellBroadcastRange(int, int, int);
     method public boolean enableCellBroadcastRange(int, int, int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getPremiumSmsConsent(@NonNull String);
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getSmsCapacityOnIcc();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void sendMultipartTextMessageWithoutPersisting(String, String, java.util.List<java.lang.String>, java.util.List<android.app.PendingIntent>, java.util.List<android.app.PendingIntent>);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setPremiumSmsConsent(@NonNull String, int);
     field public static final int PREMIUM_SMS_CONSENT_ALWAYS_ALLOW = 3; // 0x3
@@ -680,6 +698,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCarrierPrivilegeStatus(int);
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<java.lang.String> getCarrierPrivilegedPackagesForAllActiveSubscriptions();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.CarrierRestrictionRules getCarrierRestrictionRules();
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCdmaEnhancedRoamingIndicatorIconIndex();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn(int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin();
@@ -694,7 +713,6 @@
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDeviceSoftwareVersion(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean getEmergencyCallbackMode();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEmergencyNumberDbVersion();
-    method @NonNull @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<java.lang.String> getEquivalentHomePlmns();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimDomain();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimIst();
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.Map<java.lang.Integer,java.lang.Integer> getLogicalToPhysicalSlotMapping();
@@ -728,6 +746,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean isIccLockEnabled();
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isLteCdmaEvdoGsmWcdmaEnabled();
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isMobileDataPolicyEnabled(int);
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isOpportunisticNetworkEnabled();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isPotentialEmergencyNumber(@NonNull String);
@@ -758,6 +777,7 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataActivationState(int);
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMobileDataPolicyEnabledStatus(int, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setPreferredNetworkTypeBitmask(long);
@@ -808,6 +828,8 @@
     field public static final int INVALID_EMERGENCY_NUMBER_DB_VERSION = -1; // 0xffffffff
     field public static final int KEY_TYPE_EPDG = 1; // 0x1
     field public static final int KEY_TYPE_WLAN = 2; // 0x2
+    field public static final int MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL = 1; // 0x1
+    field public static final int MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED = 2; // 0x2
     field public static final long NETWORK_TYPE_BITMASK_1xRTT = 64L; // 0x40L
     field public static final long NETWORK_TYPE_BITMASK_CDMA = 8L; // 0x8L
     field public static final long NETWORK_TYPE_BITMASK_EDGE = 2L; // 0x2L
@@ -1410,6 +1432,10 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsExternalCallState> CREATOR;
   }
 
+  public class ImsManager {
+    method @NonNull public android.telephony.ims.SipDelegateManager getSipDelegateManager(int);
+  }
+
   public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
     method @Deprecated @NonNull @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getFeatureState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>) throws android.telephony.ims.ImsException;
@@ -1445,10 +1471,13 @@
     method public void disableIms(int);
     method public void enableIms(int);
     method public android.telephony.ims.stub.ImsConfigImplBase getConfig(int);
+    method public long getImsServiceCapabilities();
     method public android.telephony.ims.stub.ImsRegistrationImplBase getRegistration(int);
+    method @Nullable public android.telephony.ims.stub.SipTransportImplBase getSipTransport(int);
     method public final void onUpdateSupportedImsFeatures(android.telephony.ims.stub.ImsFeatureConfiguration) throws android.os.RemoteException;
     method public android.telephony.ims.stub.ImsFeatureConfiguration querySupportedImsFeatures();
     method public void readyForFeatureCreation();
+    field public static final long CAPABILITY_SIP_DELEGATE_CREATION = 2L; // 0x2L
   }
 
   public final class ImsSsData implements android.os.Parcelable {
@@ -1694,6 +1723,10 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
   }
 
+  public class SipDelegateManager {
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSupported() throws android.telephony.ims.ImsException;
+  }
+
 }
 
 package android.telephony.ims.feature {
@@ -1943,6 +1976,10 @@
     method public int updateColr(int);
   }
 
+  public class SipTransportImplBase {
+    ctor public SipTransportImplBase(@NonNull java.util.concurrent.Executor);
+  }
+
 }
 
 package android.telephony.mbms {
diff --git a/telephony/common/android/telephony/LocationAccessPolicy.java b/telephony/common/android/telephony/LocationAccessPolicy.java
index 502bfa3..85d59a2 100644
--- a/telephony/common/android/telephony/LocationAccessPolicy.java
+++ b/telephony/common/android/telephony/LocationAccessPolicy.java
@@ -377,7 +377,7 @@
     }
 
     private static boolean isCurrentProfile(@NonNull Context context, int uid) {
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             if (UserHandle.getUserHandleForUid(uid).getIdentifier()
                     == ActivityManager.getCurrentUser()) {
diff --git a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
index 7736473..b8ca326 100644
--- a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
+++ b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
@@ -96,7 +96,7 @@
      */
     public static void runWithCleanCallingIdentity(
             @NonNull Runnable action) {
-        long callingIdentity = Binder.clearCallingIdentity();
+        final long callingIdentity = Binder.clearCallingIdentity();
         try {
             action.run();
         } finally {
@@ -115,7 +115,7 @@
      */
     public static <T> T runWithCleanCallingIdentity(
             @NonNull Supplier<T> action) {
-        long callingIdentity = Binder.clearCallingIdentity();
+        final long callingIdentity = Binder.clearCallingIdentity();
         try {
             return action.get();
         } finally {
diff --git a/telephony/java/android/telephony/AccessNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java
index 39a7543..d012971 100644
--- a/telephony/java/android/telephony/AccessNetworkConstants.java
+++ b/telephony/java/android/telephony/AccessNetworkConstants.java
@@ -18,7 +18,6 @@
 
 import android.annotation.IntDef;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.hardware.radio.V1_1.GeranBands;
 import android.hardware.radio.V1_5.AccessNetwork;
 import android.hardware.radio.V1_5.EutranBands;
@@ -49,7 +48,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static final int TRANSPORT_TYPE_INVALID = -1;
 
     /**
@@ -438,7 +436,6 @@
          * @hide
          */
         @SystemApi
-        @TestApi
         public static final int FREQUENCY_RANGE_GROUP_UNKNOWN = 0;
 
         /**
@@ -447,7 +444,6 @@
          * @hide
          */
         @SystemApi
-        @TestApi
         public static final int FREQUENCY_RANGE_GROUP_1 = 1;
 
         /**
@@ -456,7 +452,6 @@
          * @hide
          */
         @SystemApi
-        @TestApi
         public static final int FREQUENCY_RANGE_GROUP_2 = 2;
 
         /**
@@ -481,7 +476,6 @@
          * @hide
          */
         @SystemApi
-        @TestApi
         public static @FrequencyRangeGroup int getFrequencyRangeGroup(@NgranBand int band) {
             switch (band) {
                 case BAND_1:
diff --git a/telephony/java/android/telephony/BarringInfo.java b/telephony/java/android/telephony/BarringInfo.java
index 92423a2..e9698ad 100644
--- a/telephony/java/android/telephony/BarringInfo.java
+++ b/telephony/java/android/telephony/BarringInfo.java
@@ -252,7 +252,6 @@
     private SparseArray<BarringServiceInfo> mBarringServiceInfos;
 
     /** @hide */
-    @TestApi
     @SystemApi
     public BarringInfo() {
         mBarringServiceInfos = new SparseArray<>();
diff --git a/telephony/java/android/telephony/BinderCacheManager.java b/telephony/java/android/telephony/BinderCacheManager.java
new file mode 100644
index 0000000..0d3e2fe
--- /dev/null
+++ b/telephony/java/android/telephony/BinderCacheManager.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.telephony;
+
+import android.annotation.NonNull;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.RemoteException;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.NoSuchElementException;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Keeps track of the connection to a Binder node, refreshes the cache if the node dies, and lets
+ * interested parties register listeners on the node to be notified when the node has died via the
+ * registered {@link Runnable}.
+ * @param <T> The IInterface representing the Binder type that this manager will be managing the
+ *           cache of.
+ * @hide
+ */
+public class BinderCacheManager<T extends IInterface> {
+
+    /**
+     * Factory class for creating new IInterfaces in the case that {@link #getBinder()} is
+     * called and there is no active binder available.
+     * @param <T> The IInterface that should be cached and returned to the caller when
+     * {@link #getBinder()} is called until the Binder node dies.
+     */
+    public interface BinderInterfaceFactory<T> {
+        /**
+         * @return A new instance of the Binder node, which will be cached until it dies.
+         */
+        T create();
+    }
+
+    /**
+     * Tracks the cached Binder node as well as the listeners that were associated with that
+     * Binder node during its lifetime. If the Binder node dies, the listeners will be called and
+     * then this tracker will be unlinked and cleaned up.
+     */
+    private class BinderDeathTracker implements IBinder.DeathRecipient {
+
+        private final T mConnection;
+        private final HashMap<Object, Runnable> mListeners = new HashMap<>();
+
+        /**
+         * Create a tracker to cache the Binder node and add the ability to listen for the cached
+         * interface's death.
+         */
+        BinderDeathTracker(@NonNull T connection) {
+            mConnection = connection;
+            try {
+                mConnection.asBinder().linkToDeath(this, 0 /*flags*/);
+            } catch (RemoteException e) {
+                // isAlive will return false.
+            }
+        }
+
+        public boolean addListener(Object key, Runnable r) {
+            synchronized (mListeners) {
+                if (!isAlive()) return false;
+                mListeners.put(key, r);
+                return true;
+            }
+        }
+
+        public void removeListener(Object runnableKey) {
+            synchronized (mListeners) {
+                mListeners.remove(runnableKey);
+            }
+        }
+
+        @Override
+        public void binderDied() {
+            ArrayList<Runnable> listeners;
+            synchronized (mListeners) {
+                listeners = new ArrayList<>(mListeners.values());
+                mListeners.clear();
+                try {
+                    mConnection.asBinder().unlinkToDeath(this, 0 /*flags*/);
+                } catch (NoSuchElementException e) {
+                    // No need to worry about this, this means the death recipient was never linked.
+                }
+            }
+            listeners.forEach(Runnable::run);
+        }
+
+        /**
+         * @return The cached Binder.
+         */
+        public T getConnection() {
+            return mConnection;
+        }
+
+        /**
+         * @return true if the cached Binder is alive at the time of calling, false otherwise.
+         */
+        public boolean isAlive() {
+            return mConnection.asBinder().isBinderAlive();
+        }
+    }
+
+    private final BinderInterfaceFactory<T> mBinderInterfaceFactory;
+    private final AtomicReference<BinderDeathTracker> mCachedConnection;
+
+    /**
+     * Create a new instance, which manages a cached IInterface and creates new ones using the
+     * provided factory when the cached IInterface dies.
+     * @param factory The factory used to create new Instances of the cached IInterface when it
+     *                dies.
+     */
+    public BinderCacheManager(BinderInterfaceFactory<T> factory) {
+        mBinderInterfaceFactory = factory;
+        mCachedConnection = new AtomicReference<>();
+    }
+
+    /**
+     * Get the binder node connection and add a Runnable to be run if this Binder dies. Once this
+     * Runnable is run, the Runnable itself is discarded and must be added again.
+     * <p>
+     * Note: There should be no assumptions here as to which Thread this Runnable is called on. If
+     * the Runnable should be called on a specific thread, it should be up to the caller to handle
+     * that in the runnable implementation.
+     * @param runnableKey The Key associated with this runnable so that it can be removed later
+     *                    using {@link #removeRunnable(Object)} if needed.
+     * @param deadRunnable The runnable that will be run if the cached Binder node dies.
+     * @return T if the runnable was added or {@code null} if the connection is not alive right now
+     * and the associated runnable was never added.
+     */
+    public T listenOnBinder(Object runnableKey, Runnable deadRunnable) {
+        if (runnableKey == null || deadRunnable == null) return null;
+        BinderDeathTracker tracker = getTracker();
+        if (tracker == null) return null;
+
+        boolean addSucceeded = tracker.addListener(runnableKey, deadRunnable);
+        return addSucceeded ? tracker.getConnection() : null;
+    }
+
+    /**
+     * @return The cached Binder node. May return null if the requested Binder node is not currently
+     * available.
+     */
+    public T getBinder() {
+        BinderDeathTracker tracker = getTracker();
+        return (tracker != null) ? tracker.getConnection() : null;
+    }
+
+    /**
+     * Removes a previously registered runnable associated with the returned  cached Binder node
+     * using the key it was registered with in {@link #listenOnBinder} if the runnable still exists.
+     * @param runnableKey The key that was used to register the Runnable earlier.
+     * @return The cached Binder node that the runnable used to registered to or null if the cached
+     * Binder node is not alive anymore.
+     */
+    public T removeRunnable(Object runnableKey) {
+        if (runnableKey == null) return null;
+        BinderDeathTracker tracker = getTracker();
+        if (tracker == null) return null;
+        tracker.removeListener(runnableKey);
+        return tracker.getConnection();
+    }
+
+    /**
+     * @return The BinderDeathTracker container, which contains the cached IInterface instance or
+     * null if it is not available right now.
+     */
+    private BinderDeathTracker getTracker() {
+        return mCachedConnection.updateAndGet((oldVal) -> {
+            BinderDeathTracker tracker = oldVal;
+            // Update cache if no longer alive. BinderDied will eventually be called on the tracker,
+            // which will call listeners & clean up.
+            if (tracker == null || !tracker.isAlive()) {
+                T binder = mBinderInterfaceFactory.create();
+                tracker = (binder != null) ? new BinderDeathTracker(binder) : null;
+
+            }
+            return (tracker != null && tracker.isAlive()) ? tracker : null;
+        });
+    }
+
+}
diff --git a/telephony/java/android/telephony/CallQuality.java b/telephony/java/android/telephony/CallQuality.java
index 1effeb7..fa70c33 100644
--- a/telephony/java/android/telephony/CallQuality.java
+++ b/telephony/java/android/telephony/CallQuality.java
@@ -20,7 +20,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -41,7 +40,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class CallQuality implements Parcelable {
 
     // Constants representing the call quality level (see #CallQuality);
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
old mode 100755
new mode 100644
index 8261b53..1576091
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -23,7 +23,6 @@
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
-import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
@@ -3833,11 +3832,26 @@
         public static final String KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT =
                 KEY_PREFIX + "wifi_off_deferring_time_millis_int";
 
+        /**
+         * A boolean flag specifying whether or not this carrier requires one IMS registration for
+         * all IMS services (MMTEL and RCS).
+         * <p>
+         * If set to {@code true}, the IMS Service must use one IMS registration for all IMS
+         * services. If set to {@code false}, IMS services may use separate IMS registrations for
+         * MMTEL and RCS.
+         * <p>
+         * The default value for this configuration is {@code false}.
+         * @see android.telephony.ims.SipDelegateManager
+         */
+        public static final String KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL =
+                KEY_PREFIX + "ims_single_registration_required_bool";
+
         private Ims() {}
 
         private static PersistableBundle getDefaults() {
             PersistableBundle defaults = new PersistableBundle();
             defaults.putInt(KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT, 4000);
+            defaults.putBoolean(KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL, false);
             return defaults;
         }
     }
@@ -4603,7 +4617,6 @@
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     @SystemApi
-    @TestApi
     public void overrideConfig(int subscriptionId, @Nullable PersistableBundle overrideValues) {
         overrideConfig(subscriptionId, overrideValues, false);
     }
diff --git a/telephony/java/android/telephony/CdmaEriInformation.java b/telephony/java/android/telephony/CdmaEriInformation.java
deleted file mode 100644
index fd0b905..0000000
--- a/telephony/java/android/telephony/CdmaEriInformation.java
+++ /dev/null
@@ -1,169 +0,0 @@
-/**
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.telephony;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.SystemApi;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * CDMA ERI (Enhanced Roaming Indicator) information.
- *
- * This contains the following ERI information
- *
- * 1. ERI (Enhanced Roaming Indicator) icon index. The number is assigned by
- *    3GPP2 C.R1001-H v1.0 Table 8.1-1. Additionally carriers define their own
- *    ERI icon index.
- * 2. CDMA ERI icon mode. This represents how the icon should be displayed.
- *    Its one of the following CDMA ERI icon mode
- *    {@link android.telephony.CdmaEriInformation#ERI_ICON_MODE_NORMAL}
- *    {@link android.telephony.CdmaEriInformation#ERI_ICON_MODE_FLASH}
- *
- * @hide
- */
-public final class CdmaEriInformation implements Parcelable {
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = {"ERI_"}, value = {
-                ERI_ON,
-                ERI_OFF,
-                ERI_FLASH
-            })
-    public @interface EriIconIndex {}
-
-    /**
-     * ERI (Enhanced Roaming Indicator) is ON i.e value 0 defined by
-     * 3GPP2 C.R1001-H v1.0 Table 8.1-1.
-     */
-    public static final int ERI_ON = 0;
-
-    /**
-     * ERI (Enhanced Roaming Indicator) is OFF i.e value 1 defined by
-     * 3GPP2 C.R1001-H v1.0 Table 8.1-1.
-     */
-    public static final int ERI_OFF = 1;
-
-    /**
-     * ERI (Enhanced Roaming Indicator) is FLASH i.e value 2 defined by
-     * 3GPP2 C.R1001-H v1.0 Table 8.1-1.
-     */
-    public static final int ERI_FLASH = 2;
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = {"ERI_ICON_MODE_"}, value = {
-                ERI_ICON_MODE_NORMAL,
-                ERI_ICON_MODE_FLASH
-            })
-    public @interface EriIconMode {}
-
-    /**
-     * ERI (Enhanced Roaming Indicator) icon mode is normal. This constant represents that
-     * the ERI icon should be displayed normally.
-     *
-     * Note: ERI is defined 3GPP2 C.R1001-H Table 8.1-1
-     */
-    public static final int ERI_ICON_MODE_NORMAL = 0;
-
-    /**
-     * ERI (Enhanced Roaming Indicator) icon mode flash. This constant represents that
-     * the ERI icon should be flashing.
-     *
-     * Note: ERI is defined 3GPP2 C.R1001-H Table 8.1-1
-     */
-    public static final int ERI_ICON_MODE_FLASH = 1;
-
-    private @EriIconIndex int mIconIndex;
-    private @EriIconMode int mIconMode;
-
-    /**
-     * Creates CdmaEriInformation from iconIndex and iconMode
-     *
-     * @hide
-     */
-    public CdmaEriInformation(@EriIconIndex int iconIndex, @EriIconMode int iconMode) {
-        mIconIndex = iconIndex;
-        mIconMode = iconMode;
-    }
-
-    /** Gets the ERI icon index */
-    public @EriIconIndex int getEriIconIndex() {
-        return mIconIndex;
-    }
-
-    /**
-     * Sets the ERI icon index
-     *
-     * @hide
-     */
-    public void setEriIconIndex(@EriIconIndex int iconIndex) {
-        mIconIndex = iconIndex;
-    }
-
-    /** Gets the ERI icon mode */
-    public @EriIconMode int getEriIconMode() {
-        return mIconMode;
-    }
-
-    /**
-     * Sets the ERI icon mode
-     *
-     * @hide
-     */
-    public void setEriIconMode(@EriIconMode int iconMode) {
-        mIconMode = iconMode;
-    }
-    /** Implement the Parcelable interface */
-    @Override
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        dest.writeInt(mIconIndex);
-        dest.writeInt(mIconMode);
-    }
-
-    /** Implement the Parcelable interface */
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /**
-     * Construct a CdmaEriInformation object from the given parcel
-     */
-    private CdmaEriInformation(Parcel in) {
-        mIconIndex = in.readInt();
-        mIconMode = in.readInt();
-    }
-
-    /** Implement the Parcelable interface */
-    public static final @android.annotation.NonNull Parcelable.Creator<CdmaEriInformation> CREATOR =
-            new Parcelable.Creator<CdmaEriInformation>() {
-        @Override
-        public CdmaEriInformation createFromParcel(Parcel in) {
-            return new CdmaEriInformation(in);
-        }
-
-        @Override
-        public CdmaEriInformation[] newArray(int size) {
-            return new CdmaEriInformation[size];
-        }
-    };
-}
diff --git a/telephony/java/android/telephony/CellSignalStrength.java b/telephony/java/android/telephony/CellSignalStrength.java
index 2e7bde3..e089657 100644
--- a/telephony/java/android/telephony/CellSignalStrength.java
+++ b/telephony/java/android/telephony/CellSignalStrength.java
@@ -17,6 +17,7 @@
 package android.telephony;
 
 import android.annotation.IntRange;
+import android.annotation.SystemApi;
 import android.os.PersistableBundle;
 
 /**
@@ -155,11 +156,12 @@
 
     /**
      * Returns the number of signal strength levels.
-     * @return Number of signal strength levels, enforced to be 5
+     * @return Number of signal strength levels, currently defined in the HAL as 5.
      *
      * @hide
      */
-    public static final int getNumSignalStrengthLevels() {
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static int getNumSignalStrengthLevels() {
         return NUM_SIGNAL_STRENGTH_BINS;
     }
 }
diff --git a/telephony/java/android/telephony/DataSpecificRegistrationInfo.java b/telephony/java/android/telephony/DataSpecificRegistrationInfo.java
index e91d6fc9..597fe8f 100644
--- a/telephony/java/android/telephony/DataSpecificRegistrationInfo.java
+++ b/telephony/java/android/telephony/DataSpecificRegistrationInfo.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -31,7 +30,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class DataSpecificRegistrationInfo implements Parcelable {
     /**
      * @hide
diff --git a/telephony/java/android/telephony/ImsManager.java b/telephony/java/android/telephony/ImsManager.java
index 3984bd7..28feab2 100644
--- a/telephony/java/android/telephony/ImsManager.java
+++ b/telephony/java/android/telephony/ImsManager.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.SdkConstant;
 import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.content.Context;
 import android.telephony.SubscriptionManager;
@@ -125,4 +126,24 @@
 
         return new ImsMmTelManager(subscriptionId);
     }
+
+    /**
+     * Create an instance of SipDelegateManager for the subscription id specified.
+     * <p>
+     * Used for RCS single registration cases, where an IMS application needs to forward SIP
+     * traffic through the device's IMS service.
+     * @param subscriptionId The ID of the subscription that this SipDelegateManager will use.
+     * @throws IllegalArgumentException if the subscription is invalid.
+     * @return a SipDelegateManager instance for the specified subscription ID.
+     * @hide
+     */
+    @SystemApi
+    @NonNull
+    public SipDelegateManager getSipDelegateManager(int subscriptionId) {
+        if (!SubscriptionManager.isValidSubscriptionId(subscriptionId)) {
+            throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId);
+        }
+
+        return new SipDelegateManager(mContext, subscriptionId);
+    }
 }
diff --git a/telephony/java/android/telephony/LteVopsSupportInfo.java b/telephony/java/android/telephony/LteVopsSupportInfo.java
index 7994c1b..83e41bf 100644
--- a/telephony/java/android/telephony/LteVopsSupportInfo.java
+++ b/telephony/java/android/telephony/LteVopsSupportInfo.java
@@ -20,7 +20,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -33,7 +32,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class LteVopsSupportInfo implements Parcelable {
 
     /**@hide*/
diff --git a/telephony/java/android/telephony/ModemActivityInfo.java b/telephony/java/android/telephony/ModemActivityInfo.java
index debb119..e164c4b 100644
--- a/telephony/java/android/telephony/ModemActivityInfo.java
+++ b/telephony/java/android/telephony/ModemActivityInfo.java
@@ -16,8 +16,12 @@
 
 package android.telephony;
 
+import android.annotation.DurationMillisLong;
+import android.annotation.ElapsedRealtimeLong;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
@@ -25,46 +29,49 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Arrays;
+import java.util.Objects;
 
 /**
- * Reports modem activity information.
+ * Contains information about the modem's activity. May be useful for power stats reporting.
  * @hide
  */
+@SystemApi
 public final class ModemActivityInfo implements Parcelable {
+    private static final int TX_POWER_LEVELS = 5;
+
     /**
-     * Tx(transmit) power level. see power index below
-     * <ul>
-     *   <li> index 0 = tx_power < 0dBm. </li>
-     *   <li> index 1 = 0dBm < tx_power < 5dBm. </li>
-     *   <li> index 2 = 5dBm < tx_power < 15dBm. </li>
-     *   <li> index 3 = 15dBm < tx_power < 20dBm. </li>
-     *   <li> index 4 = tx_power > 20dBm. </li>
-     * </ul>
-     */
-    public static final int TX_POWER_LEVELS = 5;
-    /**
-     * Tx(transmit) power level 0: tx_power < 0dBm
+     * Corresponds to transmit power of less than 0dBm.
      */
     public static final int TX_POWER_LEVEL_0 = 0;
+
     /**
-     * Tx(transmit) power level 1: 0dBm < tx_power < 5dBm
+     * Corresponds to transmit power between 0dBm and 5dBm.
      */
     public static final int TX_POWER_LEVEL_1 = 1;
+
     /**
-     * Tx(transmit) power level 2: 5dBm < tx_power < 15dBm
+     * Corresponds to transmit power between 5dBm and 15dBm.
      */
     public static final int TX_POWER_LEVEL_2 = 2;
+
     /**
-     * Tx(transmit) power level 3: 15dBm < tx_power < 20dBm.
+     * Corresponds to transmit power between 15dBm and 20dBm.
      */
     public static final int TX_POWER_LEVEL_3 = 3;
+
     /**
-     * Tx(transmit) power level 4: tx_power > 20dBm
+     * Corresponds to transmit power above 20dBm.
      */
     public static final int TX_POWER_LEVEL_4 = 4;
 
+    /**
+     * The number of transmit power levels. Fixed by HAL definition.
+     */
+    public static int getNumTxPowerLevels() {
+        return TX_POWER_LEVELS;
+    }
+
     /** @hide */
     @IntDef(prefix = {"TX_POWER_LEVEL_"}, value = {
             TX_POWER_LEVEL_0,
@@ -82,34 +89,39 @@
         new Range<>(5, 15),
         new Range<>(15, 20),
         new Range<>(20, Integer.MAX_VALUE)
-
     };
 
     private long mTimestamp;
     private int mSleepTimeMs;
     private int mIdleTimeMs;
-    private List<TransmitPower> mTransmitPowerInfo = new ArrayList<>(TX_POWER_LEVELS);
+    private int[] mTxTimeMs;
     private int mRxTimeMs;
 
+    /**
+     * @hide
+     */
+    @TestApi
     public ModemActivityInfo(long timestamp, int sleepTimeMs, int idleTimeMs,
                         @NonNull int[] txTimeMs, int rxTimeMs) {
+        Objects.requireNonNull(txTimeMs);
+        if (txTimeMs.length != TX_POWER_LEVELS) {
+            throw new IllegalArgumentException("txTimeMs must have length == TX_POWER_LEVELS");
+        }
         mTimestamp = timestamp;
         mSleepTimeMs = sleepTimeMs;
         mIdleTimeMs = idleTimeMs;
-        populateTransmitPowerRange(txTimeMs);
+        mTxTimeMs = txTimeMs;
         mRxTimeMs = rxTimeMs;
     }
 
-    /** helper API to populate tx power range for each bucket **/
-    private void populateTransmitPowerRange(@NonNull int[] transmitPowerMs) {
-        int i = 0;
-        for ( ; i < Math.min(transmitPowerMs.length, TX_POWER_LEVELS); i++) {
-            mTransmitPowerInfo.add(i, new TransmitPower(TX_POWER_RANGES[i], transmitPowerMs[i]));
-        }
-        // Make sure that mTransmitPowerInfo is fully initialized.
-        for ( ; i < TX_POWER_LEVELS; i++) {
-            mTransmitPowerInfo.add(i, new TransmitPower(TX_POWER_RANGES[i], 0));
-        }
+    /**
+     * Provided for convenience in manipulation since the API exposes long values but internal
+     * representations are ints.
+     * @hide
+     */
+    public ModemActivityInfo(long timestamp, long sleepTimeMs, long idleTimeMs,
+            @NonNull int[] txTimeMs, long rxTimeMs) {
+        this(timestamp, (int) sleepTimeMs, (int) idleTimeMs, txTimeMs, (int) rxTimeMs);
     }
 
     @Override
@@ -118,7 +130,7 @@
             + " mTimestamp=" + mTimestamp
             + " mSleepTimeMs=" + mSleepTimeMs
             + " mIdleTimeMs=" + mIdleTimeMs
-            + " mTransmitPowerInfo[]=" + mTransmitPowerInfo.toString()
+            + " mTxTimeMs[]=" + mTxTimeMs
             + " mRxTimeMs=" + mRxTimeMs
             + "}";
     }
@@ -129,14 +141,12 @@
 
     public static final @android.annotation.NonNull Parcelable.Creator<ModemActivityInfo> CREATOR =
             new Parcelable.Creator<ModemActivityInfo>() {
-        public ModemActivityInfo createFromParcel(Parcel in) {
+        public ModemActivityInfo createFromParcel(@NonNull Parcel in) {
             long timestamp = in.readLong();
             int sleepTimeMs = in.readInt();
             int idleTimeMs = in.readInt();
             int[] txTimeMs = new int[TX_POWER_LEVELS];
-            for (int i = 0; i < TX_POWER_LEVELS; i++) {
-                txTimeMs[i] = in.readInt();
-            }
+            in.readIntArray(txTimeMs);
             int rxTimeMs = in.readInt();
             return new ModemActivityInfo(timestamp, sleepTimeMs, idleTimeMs,
                                 txTimeMs, rxTimeMs);
@@ -147,21 +157,25 @@
         }
     };
 
-    public void writeToParcel(Parcel dest, int flags) {
+    /**
+     * @param dest The Parcel in which the object should be written.
+     * @param flags Additional flags about how the object should be written.
+     */
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeLong(mTimestamp);
         dest.writeInt(mSleepTimeMs);
         dest.writeInt(mIdleTimeMs);
-        for (int i = 0; i < TX_POWER_LEVELS; i++) {
-            dest.writeInt(mTransmitPowerInfo.get(i).getTimeInMillis());
-        }
+        dest.writeIntArray(mTxTimeMs);
         dest.writeInt(mRxTimeMs);
     }
 
     /**
-     * @return milliseconds since boot, including mTimeInMillis spent in sleep.
-     * @see SystemClock#elapsedRealtime()
+     * Gets the timestamp at which this modem activity info was recorded.
+     *
+     * @return The timestamp, as returned by {@link SystemClock#elapsedRealtime()}, when this
+     * {@link ModemActivityInfo} was recorded.
      */
-    public long getTimestamp() {
+    public @ElapsedRealtimeLong long getTimestampMillis() {
         return mTimestamp;
     }
 
@@ -171,35 +185,48 @@
     }
 
     /**
-     * @return an arrayList of {@link TransmitPower} with each element representing the total time where
-     * transmitter is awake time (in ms) for a given power range (in dbm).
+     * Gets the amount of time the modem spent transmitting at a certain power level.
      *
-     * @see #TX_POWER_LEVELS
+     * @param powerLevel The power level to query.
+     * @return The amount of time, in milliseconds, that the modem spent transmitting at the
+     * given power level.
      */
-    @NonNull
-    public List<TransmitPower> getTransmitPowerInfo() {
-        return mTransmitPowerInfo;
+    public @DurationMillisLong long getTransmitDurationMillisAtPowerLevel(
+            @TxPowerLevel int powerLevel) {
+        return mTxTimeMs[powerLevel];
+    }
+
+    /**
+     * Gets the range of transmit powers corresponding to a certain power level.
+     *
+     * @param powerLevel The power level to query
+     * @return A {@link Range} object representing the range of intensities (in dBm) to which this
+     * power level corresponds.
+     */
+    public @NonNull Range<Integer> getTransmitPowerRange(@TxPowerLevel int powerLevel) {
+        return TX_POWER_RANGES[powerLevel];
     }
 
     /** @hide */
     public void setTransmitTimeMillis(int[] txTimeMs) {
-        populateTransmitPowerRange(txTimeMs);
-    }
-
-    /** @hide */
-    @NonNull
-    public int[] getTransmitTimeMillis() {
-        int[] transmitTimeMillis = new int[TX_POWER_LEVELS];
-        for (int i = 0; i < transmitTimeMillis.length; i++) {
-            transmitTimeMillis[i] = mTransmitPowerInfo.get(i).getTimeInMillis();
-        }
-        return transmitTimeMillis;
+        mTxTimeMs = Arrays.copyOf(txTimeMs, TX_POWER_LEVELS);
     }
 
     /**
-     * @return total mTimeInMillis (in ms) when modem is in a low power or sleep state.
+     * @return The raw array of transmit power durations
+     * @hide
      */
-    public int getSleepTimeMillis() {
+    @NonNull
+    public int[] getTransmitTimeMillis() {
+        return mTxTimeMs;
+    }
+
+    /**
+     * Gets the amount of time (in milliseconds) when the modem is in a low power or sleep state.
+     *
+     * @return Time in milliseconds.
+     */
+    public @DurationMillisLong long getSleepTimeMillis() {
         return mSleepTimeMs;
     }
 
@@ -209,10 +236,44 @@
     }
 
     /**
-     * @return total mTimeInMillis (in ms) when modem is awake but neither the transmitter nor receiver are
-     * active.
+     * Provided for convenience, since the API surface needs to return longs but internal
+     * representations are ints.
+     * @hide
      */
-    public int getIdleTimeMillis() {
+    public void setSleepTimeMillis(long sleepTimeMillis) {
+        mSleepTimeMs = (int) sleepTimeMillis;
+    }
+
+    /**
+     * Computes the difference between this instance of {@link ModemActivityInfo} and another
+     * instance.
+     *
+     * This method should be used to compute the amount of activity that has happened between two
+     * samples of modem activity taken at separate times. The sample passed in as an argument to
+     * this method should be the one that's taken later in time (and therefore has more activity).
+     * @param other The other instance of {@link ModemActivityInfo} to diff against.
+     * @return An instance of {@link ModemActivityInfo} representing the difference in modem
+     * activity.
+     */
+    public @NonNull ModemActivityInfo getDelta(@NonNull ModemActivityInfo other) {
+        int[] txTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS];
+        for (int i = 0; i < ModemActivityInfo.TX_POWER_LEVELS; i++) {
+            txTimeMs[i] = other.mTxTimeMs[i] - mTxTimeMs[i];
+        }
+        return new ModemActivityInfo(other.getTimestampMillis(),
+                other.getSleepTimeMillis() - getSleepTimeMillis(),
+                other.getIdleTimeMillis() - getIdleTimeMillis(),
+                txTimeMs,
+                other.getReceiveTimeMillis() - getReceiveTimeMillis());
+    }
+
+    /**
+     * Gets the amount of time (in milliseconds) when the modem is awake but neither transmitting
+     * nor receiving.
+     *
+     * @return Time in milliseconds.
+     */
+    public @DurationMillisLong long getIdleTimeMillis() {
         return mIdleTimeMs;
     }
 
@@ -222,9 +283,20 @@
     }
 
     /**
-     * @return rx(receive) mTimeInMillis in ms.
+     * Provided for convenience, since the API surface needs to return longs but internal
+     * representations are ints.
+     * @hide
      */
-    public int getReceiveTimeMillis() {
+    public void setIdleTimeMillis(long idleTimeMillis) {
+        mIdleTimeMs = (int) idleTimeMillis;
+    }
+
+    /**
+     * Gets the amount of time (in milliseconds) when the modem is awake and receiving data.
+     *
+     * @return Time in milliseconds.
+     */
+    public @DurationMillisLong long getReceiveTimeMillis() {
         return mRxTimeMs;
     }
 
@@ -234,71 +306,56 @@
     }
 
     /**
+     * Provided for convenience, since the API surface needs to return longs but internal
+     * representations are ints.
+     * @hide
+     */
+    public void setReceiveTimeMillis(long receiveTimeMillis) {
+        mRxTimeMs = (int) receiveTimeMillis;
+    }
+
+    /**
      * Indicates if the modem has reported valid {@link ModemActivityInfo}.
      *
      * @return {@code true} if this {@link ModemActivityInfo} record is valid,
      * {@code false} otherwise.
+     *  TODO: remove usages of this outside Telephony by always returning a valid (or null) result
+     *  from telephony.
+     * @hide
      */
+    @TestApi
     public boolean isValid() {
-        for (TransmitPower powerInfo : getTransmitPowerInfo()) {
-            if(powerInfo.getTimeInMillis() < 0) {
-                return false;
-            }
-        }
+        boolean isTxPowerValid = Arrays.stream(mTxTimeMs).allMatch((i) -> i >= 0);
 
-        return ((getIdleTimeMillis() >= 0) && (getSleepTimeMillis() >= 0)
+        return isTxPowerValid && ((getIdleTimeMillis() >= 0) && (getSleepTimeMillis() >= 0)
                 && (getReceiveTimeMillis() >= 0) && !isEmpty());
     }
 
     private boolean isEmpty() {
-        for (TransmitPower txVal : getTransmitPowerInfo()) {
-            if(txVal.getTimeInMillis() != 0) {
-                return false;
-            }
-        }
+        boolean isTxPowerEmpty = mTxTimeMs == null || mTxTimeMs.length == 0
+                || Arrays.stream(mTxTimeMs).allMatch((i) -> i == 0);
 
-        return ((getIdleTimeMillis() == 0) && (getSleepTimeMillis() == 0)
+        return isTxPowerEmpty && ((getIdleTimeMillis() == 0) && (getSleepTimeMillis() == 0)
                 && (getReceiveTimeMillis() == 0));
     }
 
-    /**
-     * Transmit power Information, including the power range in dbm and the total time (in ms) where
-     * the transmitter is active/awake for this power range.
-     * e.g, range: 0dbm(lower) ~ 5dbm(upper)
-     *      time: 5ms
-     */
-    public class TransmitPower {
-        private int mTimeInMillis;
-        private Range<Integer> mPowerRangeInDbm;
-        /** @hide */
-        public TransmitPower(@NonNull Range<Integer> range, int time) {
-            this.mTimeInMillis = time;
-            this.mPowerRangeInDbm = range;
-        }
 
-        /**
-         * @return the total time in ms where the transmitter is active/wake for this power range
-         * {@link #getPowerRangeInDbm()}.
-         */
-        public int getTimeInMillis() {
-            return mTimeInMillis;
-        }
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        ModemActivityInfo that = (ModemActivityInfo) o;
+        return mTimestamp == that.mTimestamp
+                && mSleepTimeMs == that.mSleepTimeMs
+                && mIdleTimeMs == that.mIdleTimeMs
+                && mRxTimeMs == that.mRxTimeMs
+                && Arrays.equals(mTxTimeMs, that.mTxTimeMs);
+    }
 
-        /**
-         * @return the power range in dbm. e.g, range: 0dbm(lower) ~ 5dbm(upper)
-         */
-        @NonNull
-        public Range<Integer> getPowerRangeInDbm() {
-            return mPowerRangeInDbm;
-        }
-
-        @Override
-        public String toString() {
-            return "TransmitPower{"
-                + " mTimeInMillis=" + mTimeInMillis
-                + " mPowerRangeInDbm={" + mPowerRangeInDbm.getLower()
-                + "," + mPowerRangeInDbm.getUpper()
-                + "}}";
-        }
+    @Override
+    public int hashCode() {
+        int result = Objects.hash(mTimestamp, mSleepTimeMs, mIdleTimeMs, mRxTimeMs);
+        result = 31 * result + Arrays.hashCode(mTxTimeMs);
+        return result;
     }
 }
diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java
index aee1e84..9223842 100644
--- a/telephony/java/android/telephony/NetworkRegistrationInfo.java
+++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java
@@ -20,7 +20,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.AccessNetworkConstants.TransportType;
@@ -71,37 +70,37 @@
      * Not registered. The device is not currently searching a new operator to register.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public static final int REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING = 0;
     /**
      * Registered on home network.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public static final int REGISTRATION_STATE_HOME = 1;
     /**
      * Not registered. The device is currently searching a new operator to register.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public static final int REGISTRATION_STATE_NOT_REGISTERED_SEARCHING = 2;
     /**
      * Registration denied.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public static final int REGISTRATION_STATE_DENIED = 3;
     /**
      * Registration state is unknown.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public static final int REGISTRATION_STATE_UNKNOWN = 4;
     /**
      * Registered on roaming network.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public static final int REGISTRATION_STATE_ROAMING = 5;
 
     /** @hide */
@@ -386,7 +385,7 @@
      *
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public @RegistrationState int getRegistrationState() {
         return mRegistrationState;
     }
@@ -451,7 +450,7 @@
      * @return the current network roaming type.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public @ServiceState.RoamingType int getRoamingType() {
         return mRoamingType;
     }
@@ -460,7 +459,7 @@
      * @return Whether emergency is enabled.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public boolean isEmergencyEnabled() { return mEmergencyOnly; }
 
     /**
@@ -498,7 +497,7 @@
      * 10.5.3.6 for UMTS, 3GPP TS 24.301 9.9.3.9 for LTE, and 3GPP2 A.S0001 6.2.2.44 for CDMA
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public int getRejectCause() {
         return mRejectCause;
     }
@@ -545,7 +544,7 @@
      * @hide
      */
     @Nullable
-    @SystemApi @TestApi
+    @SystemApi
     public DataSpecificRegistrationInfo getDataSpecificInfo() {
         return mDataSpecificInfo;
     }
@@ -680,7 +679,7 @@
      * @hide
      */
     @Override
-    @SystemApi @TestApi
+    @SystemApi
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(mDomain);
         dest.writeInt(mTransportType);
@@ -772,7 +771,7 @@
      * </code></pre>
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public static final class Builder {
         @Domain
         private int mDomain;
@@ -877,7 +876,7 @@
          * @return The same instance of the builder.
          * @hide
          */
-        @SystemApi @TestApi
+        @SystemApi
         public @NonNull Builder setEmergencyOnly(boolean emergencyOnly) {
             mEmergencyOnly = emergencyOnly;
             return this;
@@ -891,7 +890,7 @@
          * @return The same instance of the builder.
          * @hide
          */
-        @SystemApi @TestApi
+        @SystemApi
         public @NonNull Builder setAvailableServices(
                 @NonNull @ServiceType List<Integer> availableServices) {
             mAvailableServices = availableServices;
@@ -906,7 +905,7 @@
          * @return The same instance of the builder.
          * @hide
          */
-        @SystemApi @TestApi
+        @SystemApi
         public @NonNull Builder setCellIdentity(@Nullable CellIdentity cellIdentity) {
             mCellIdentity = cellIdentity;
             return this;
@@ -929,7 +928,7 @@
          * @return the NetworkRegistrationInfo object.
          * @hide
          */
-        @SystemApi @TestApi
+        @SystemApi
         public @NonNull NetworkRegistrationInfo build() {
             return new NetworkRegistrationInfo(mDomain, mTransportType, mRegistrationState,
                     mAccessNetworkTechnology, mRejectCause, mEmergencyOnly, mAvailableServices,
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index ec99408..58e368b 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -2239,7 +2239,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static boolean isVoiceMailNumber(@NonNull Context context, int subId,
             @Nullable String number) {
         String vmNumber, mdn;
@@ -2728,7 +2727,6 @@
      * @return true if number contains @
      */
     @SystemApi
-    @TestApi
     public static boolean isUriNumber(@Nullable String number) {
         // Note we allow either "@" or "%40" to indicate a URI, in case
         // the passed-in string is URI-escaped.  (Neither "@" nor "%40"
@@ -2747,7 +2745,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public static @NonNull String getUsernameFromUriNumber(@NonNull String number) {
         // The delimiter between username and domain name can be
         // either "@" or "%40" (the URI-escaped equivalent.)
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index 1376cdd..7bd0bc0 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -287,11 +287,9 @@
     }
 
     /**
-     * Copy constructors
+     * This constructor is used to create a copy of an existing SignalStrength object.
      *
      * @param s Source SignalStrength
-     *
-     * @hide
      */
     public SignalStrength(@NonNull SignalStrength s) {
         copyFrom(s);
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 78dc377..2b17de6 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -1804,6 +1804,7 @@
      *
      * {@hide}
      */
+    @UnsupportedAppUsage
     @RequiresPermission(Manifest.permission.ACCESS_MESSAGES_ON_ICC)
     public boolean deleteMessageFromIcc(int messageIndex) {
         boolean success = false;
@@ -2181,17 +2182,25 @@
     }
 
     /**
-     * Gets the total capacity of SMS storage on RUIM and SIM cards
-     * <p>
-     * This is the number of 176 byte EF-SMS records which can be stored on the RUIM or SIM card.
-     * <p>
-     * See 3GPP TS 31.102 - 4.2.25 - EF-SMS for more information
+     * Gets the total capacity of SMS storage on the SIM card.
      *
-     * @return the total number of SMS records which can be stored on the RUIM or SIM cards.
-     * @hide
+     * <p>
+     * This is the number of 176 byte EF-SMS records which can be stored on the SIM card.
+     * See 3GPP TS 31.102 - 4.2.25 - EF-SMS for more information.
+     * </p>
+     *
+     * <p class="note"><strong>Note:</strong> This method will never trigger an SMS disambiguation
+     * dialog. If this method is called on a device that has multiple active subscriptions, this
+     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+     * default subscription is defined, the subscription ID associated with this method will be
+     * INVALID, which will result in the operation being completed on the subscription associated
+     * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the operation
+     * is performed on the correct subscription.
+     * </p>
+     *
+     * @return the total number of SMS records which can be stored on the SIM card.
      */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public int getSmsCapacityOnIcc() {
         int ret = 0;
         try {
@@ -2200,7 +2209,7 @@
                 ret = iccISms.getSmsCapacityOnIccForSubscriber(getSubscriptionId());
             }
         } catch (RemoteException ex) {
-            //ignore it
+            throw new RuntimeException(ex);
         }
         return ret;
     }
@@ -2596,13 +2605,12 @@
     /**
      * Send an MMS message
      *
-     * <p class="note"><strong>Note:</strong> This method will never trigger an SMS disambiguation
-     * dialog. If this method is called on a device that has multiple active subscriptions, this
-     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
-     * default subscription is defined, the subscription ID associated with this message will be
-     * INVALID, which will result in the operation being completed on the subscription associated
-     * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
-     * operation is performed on the correct subscription.
+     * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this
+     * manager on a multi-SIM device, this operation may fail sending the MMS message because no
+     * suitable default subscription could be found. In this case, if {@code sentIntent} is
+     * non-null, then the {@link PendingIntent} will be sent with an error code
+     * {@code RESULT_NO_DEFAULT_SMS_APP}. See {@link #getDefault()} for more information on the
+     * conditions where this operation may fail.
      * </p>
      *
      * @param context application context
@@ -2621,21 +2629,30 @@
         }
         MmsManager m = (MmsManager) context.getSystemService(Context.MMS_SERVICE);
         if (m != null) {
-            m.sendMultimediaMessage(getSubscriptionId(), contentUri, locationUrl, configOverrides,
-                    sentIntent, 0L /* messageId */);
+            resolveSubscriptionForOperation(new SubscriptionResolverResult() {
+                @Override
+                public void onSuccess(int subId) {
+                    m.sendMultimediaMessage(subId, contentUri, locationUrl, configOverrides,
+                            sentIntent, 0L /* messageId */);
+                }
+
+                @Override
+                public void onFailure() {
+                    notifySmsError(sentIntent, RESULT_NO_DEFAULT_SMS_APP);
+                }
+            });
         }
     }
 
     /**
      * Download an MMS message from carrier by a given location URL
      *
-     * <p class="note"><strong>Note:</strong> This method will never trigger an SMS disambiguation
-     * dialog. If this method is called on a device that has multiple active subscriptions, this
-     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
-     * default subscription is defined, the subscription ID associated with this message will be
-     * INVALID, which will result in the operation being completed on the subscription associated
-     * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
-     * operation is performed on the correct subscription.
+     * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this
+     * manager on a multi-SIM device, this operation may fail downloading the MMS message because no
+     * suitable default subscription could be found. In this case, if {@code downloadedIntent} is
+     * non-null, then the {@link PendingIntent} will be sent with an error code
+     * {@code RESULT_NO_DEFAULT_SMS_APP}. See {@link #getDefault()} for more information on the
+     * conditions where this operation may fail.
      * </p>
      *
      * @param context application context
@@ -2658,8 +2675,18 @@
         }
         MmsManager m = (MmsManager) context.getSystemService(Context.MMS_SERVICE);
         if (m != null) {
-            m.downloadMultimediaMessage(getSubscriptionId(), locationUrl, contentUri,
-                    configOverrides, downloadedIntent, 0L /* messageId */);
+            resolveSubscriptionForOperation(new SubscriptionResolverResult() {
+                @Override
+                public void onSuccess(int subId) {
+                    m.downloadMultimediaMessage(subId, locationUrl, contentUri, configOverrides,
+                            downloadedIntent, 0L /* messageId */);
+                }
+
+                @Override
+                public void onFailure() {
+                    notifySmsError(downloadedIntent, RESULT_NO_DEFAULT_SMS_APP);
+                }
+            });
         }
     }
 
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index a71a965..2e51ef1 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -32,7 +32,6 @@
 import android.annotation.SuppressAutoDoc;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
-import android.annotation.TestApi;
 import android.app.PendingIntent;
 import android.app.PropertyInvalidatedCache;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -279,7 +278,6 @@
      */
     @NonNull
     @SystemApi
-    @TestApi
     public static final Uri WFC_ENABLED_CONTENT_URI = Uri.withAppendedPath(CONTENT_URI, "wfc");
 
     /**
@@ -299,7 +297,6 @@
      */
     @NonNull
     @SystemApi
-    @TestApi
     public static final Uri ADVANCED_CALLING_ENABLED_CONTENT_URI = Uri.withAppendedPath(
             CONTENT_URI, "advanced_calling");
 
@@ -318,7 +315,6 @@
      */
     @NonNull
     @SystemApi
-    @TestApi
     public static final Uri WFC_MODE_CONTENT_URI = Uri.withAppendedPath(CONTENT_URI, "wfc_mode");
 
     /**
@@ -336,7 +332,6 @@
      */
     @NonNull
     @SystemApi
-    @TestApi
     public static final Uri WFC_ROAMING_MODE_CONTENT_URI = Uri.withAppendedPath(
             CONTENT_URI, "wfc_roaming_mode");
 
@@ -356,7 +351,6 @@
      */
     @NonNull
     @SystemApi
-    @TestApi
     public static final Uri VT_ENABLED_CONTENT_URI = Uri.withAppendedPath(
             CONTENT_URI, "vt_enabled");
 
@@ -375,7 +369,6 @@
      */
     @NonNull
     @SystemApi
-    @TestApi
     public static final Uri WFC_ROAMING_ENABLED_CONTENT_URI = Uri.withAppendedPath(
             CONTENT_URI, "wfc_roaming_enabled");
 
@@ -1966,7 +1959,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setDefaultVoiceSubscriptionId(int subscriptionId) {
         if (VDBG) logd("setDefaultVoiceSubId sub id = " + subscriptionId);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 12e56cc..9126387 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2835,13 +2835,15 @@
     };
 
     /**
-     * Return a collection of all network types
-     * @return network types
+     * Returns an array of all valid network types.
+     *
+     * @return An integer array containing all valid network types in no particular order.
      *
      * @hide
      */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public static @NonNull @NetworkType int[] getAllNetworkTypes() {
-        return NETWORK_TYPES;
+        return NETWORK_TYPES.clone();
     }
 
     /**
@@ -5642,27 +5644,78 @@
         }
     }
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"ERI_"}, value = {
+            ERI_ON,
+            ERI_OFF,
+            ERI_FLASH
+    })
+    public @interface EriIconIndex {}
+
     /**
-     * Get the CDMA ERI (Enhanced Roaming Indicator) information
+     * ERI (Enhanced Roaming Indicator) is ON i.e value 0 defined by
+     * 3GPP2 C.R1001-H v1.0 Table 8.1-1.
+     */
+    public static final int ERI_ON = 0;
+
+    /**
+     * ERI (Enhanced Roaming Indicator) is OFF i.e value 1 defined by
+     * 3GPP2 C.R1001-H v1.0 Table 8.1-1.
+     */
+    public static final int ERI_OFF = 1;
+
+    /**
+     * ERI (Enhanced Roaming Indicator) is FLASH i.e value 2 defined by
+     * 3GPP2 C.R1001-H v1.0 Table 8.1-1.
+     */
+    public static final int ERI_FLASH = 2;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"ERI_ICON_MODE_"}, value = {
+            ERI_ICON_MODE_NORMAL,
+            ERI_ICON_MODE_FLASH
+    })
+    public @interface EriIconMode {}
+
+    /**
+     * ERI (Enhanced Roaming Indicator) icon mode is normal. This constant represents that
+     * the ERI icon should be displayed normally.
      *
-     * Returns {@link android.telephony#CdmaEriInformation}
-     *
+     * Note: ERI is defined 3GPP2 C.R1001-H Table 8.1-1
      * @hide
      */
+    public static final int ERI_ICON_MODE_NORMAL = 0;
+
+    /**
+     * ERI (Enhanced Roaming Indicator) icon mode flash. This constant represents that
+     * the ERI icon should be flashing.
+     *
+     * Note: ERI is defined 3GPP2 C.R1001-H Table 8.1-1
+     * @hide
+     */
+    public static final int ERI_ICON_MODE_FLASH = 1;
+
+    /**
+     * Returns the CDMA ERI icon index to display. The number is assigned by
+     * 3GPP2 C.R1001-H v1.0 Table 8.1-1. Additionally carriers define their own ERI icon index.
+     * Defined values are {@link #ERI_ON}, {@link #ERI_OFF}, and {@link #ERI_FLASH}.
+     * @hide
+     */
+    @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    @NonNull
-    public CdmaEriInformation getCdmaEriInformation() {
-        return new CdmaEriInformation(
-               getCdmaEriIconIndex(getSubId()), getCdmaEriIconMode(getSubId()));
+    public @EriIconIndex int getCdmaEnhancedRoamingIndicatorIconIndex() {
+        return getCdmaEriIconIndex(getSubId());
     }
 
     /**
-     * Returns the CDMA ERI icon index to display for a subscription
+     * Returns the CDMA ERI icon index to display for a subscription.
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     @UnsupportedAppUsage
-    public int getCdmaEriIconIndex(int subId) {
+    public @EriIconIndex int getCdmaEriIconIndex(int subId) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony == null)
@@ -5686,7 +5739,7 @@
      */
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     @UnsupportedAppUsage
-    public int getCdmaEriIconMode(int subId) {
+    public @EriIconMode int getCdmaEriIconMode(int subId) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony == null)
@@ -8355,13 +8408,13 @@
     /**
      * Values used to return status for hasCarrierPrivileges call.
      */
-    /** @hide */ @SystemApi @TestApi
+    /** @hide */ @SystemApi
     public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1;
-    /** @hide */ @SystemApi @TestApi
+    /** @hide */ @SystemApi
     public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0;
-    /** @hide */ @SystemApi @TestApi
+    /** @hide */ @SystemApi
     public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1;
-    /** @hide */ @SystemApi @TestApi
+    /** @hide */ @SystemApi
     public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2;
 
     /**
@@ -8563,7 +8616,6 @@
 
     /** @hide */
     @SystemApi
-    @TestApi
     @SuppressLint("Doclava125")
     public int checkCarrierPrivilegesForPackage(String pkgName) {
         try {
@@ -8596,7 +8648,6 @@
 
     /** @hide */
     @SystemApi
-    @TestApi
     public List<String> getCarrierPackageNamesForIntent(Intent intent) {
         return getCarrierPackageNamesForIntentAndPhone(intent, getPhoneId());
     }
@@ -10098,7 +10149,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
     public @Nullable ComponentName getAndUpdateDefaultRespondViaMessageApplication() {
         return SmsApplication.getDefaultRespondViaMessageApplication(mContext, true);
@@ -10111,7 +10161,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
     public @Nullable ComponentName getDefaultRespondViaMessageApplication() {
         return SmsApplication.getDefaultRespondViaMessageApplication(mContext, false);
@@ -10397,19 +10446,25 @@
      * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
      * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges})
      * and {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
+     *
+     * May return {@code null} when the subscription is inactive or when there was an error
+     * communicating with the phone process.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(allOf = {
             Manifest.permission.READ_PHONE_STATE,
             Manifest.permission.ACCESS_COARSE_LOCATION
     })
-    public ServiceState getServiceState() {
+    public @Nullable ServiceState getServiceState() {
         return getServiceStateForSubscriber(getSubId());
     }
 
     /**
      * Returns the service state information on specified subscription. Callers require
      * either READ_PRIVILEGED_PHONE_STATE or READ_PHONE_STATE to retrieve the information.
+     *
+     * May return {@code null} when the subscription is inactive or when there was an error
+     * communicating with the phone process.
      * @hide
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
@@ -10436,9 +10491,9 @@
      * @param accountHandle The handle for the {@link PhoneAccount} for which to retrieve the
      * voicemail ringtone.
      * @return The URI for the ringtone to play when receiving a voicemail from a specific
-     * PhoneAccount.
+     * PhoneAccount. May be {@code null} if no ringtone is set.
      */
-    public Uri getVoicemailRingtoneUri(PhoneAccountHandle accountHandle) {
+    public @Nullable Uri getVoicemailRingtoneUri(PhoneAccountHandle accountHandle) {
         try {
             ITelephony service = getITelephony();
             if (service != null) {
@@ -11853,7 +11908,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     public static final int INVALID_EMERGENCY_NUMBER_DB_VERSION = -1;
 
@@ -11893,7 +11947,6 @@
      */
     @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
     @SystemApi
-    @TestApi
     public void updateOtaEmergencyNumberDbFilePath(
             @NonNull ParcelFileDescriptor otaParcelFileDescriptor) {
         try {
@@ -11919,7 +11972,6 @@
      */
     @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
     @SystemApi
-    @TestApi
     public void resetOtaEmergencyNumberDbFilePath() {
         try {
             ITelephony telephony = getITelephony();
@@ -12141,7 +12193,6 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public int getEmergencyNumberDbVersion() {
@@ -12808,7 +12859,7 @@
      *  1) User data is turned on, or
      *  2) APN is un-metered for this subscription, or
      *  3) APN type is whitelisted. E.g. MMS is whitelisted if
-     *  {@link #setAlwaysAllowMmsData(boolean)} is turned on.
+     *  {@link #MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED} is enabled.
      *
      * @param apnType Value indicating the apn type. Apn types are defined in {@link ApnSetting}.
      * @return whether data is enabled for a apn type.
@@ -12869,7 +12920,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public void setSystemSelectionChannels(@NonNull List<RadioAccessSpecifier> specifiers,
             @NonNull @CallbackExecutor Executor executor,
@@ -12887,7 +12937,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public void setSystemSelectionChannels(@NonNull List<RadioAccessSpecifier> specifiers) {
         Objects.requireNonNull(specifiers, "Specifiers must not be null.");
@@ -13285,26 +13334,63 @@
     }
 
     /**
-     * Set allowing mobile data during voice call. This is used for allowing data on the non-default
-     * data SIM. When a voice call is placed on the non-default data SIM on DSDS devices, users will
-     * not be able to use mobile data. By calling this API, data will be temporarily enabled on the
-     * non-default data SIM during the life cycle of the voice call.
+     * Controls whether mobile data  on the non-default SIM is allowed during a voice call.
      *
-     * @param allow {@code true} if allowing using data during voice call, {@code false} if
-     * disallowed.
+     * This is used for allowing data on the non-default data SIM when a voice call is placed on
+     * the non-default data SIM on DSDS devices. If this policy is disabled, users will not be able
+     * to use mobile data via the non-default data SIM during the call, which may mean no mobile
+     * data at all since some modem implementations disallow mobile data via the default data SIM
+     * during voice calls.
+     * If this policy is enabled, data will be temporarily enabled on the non-default data SIM
+     * during any voice calls.
      *
-     * @return {@code true} if operation is successful. otherwise {@code false}.
-     *
-     * @throws SecurityException if the caller doesn't have the permission.
-     *
+     * This policy can be enabled and disabled via {@link #setMobileDataPolicyEnabledStatus}.
      * @hide
      */
+    @SystemApi
+    public static final int MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL = 1;
+
+    /**
+     * Controls whether MMS messages bypass the user-specified "mobile data" toggle.
+     *
+     * When enabled, requests for connections to the MMS APN will be accepted by telephony even if
+     * the user has turned "mobile data" off on this specific sim card. {@link #isDataEnabledForApn}
+     * will also return true for {@link ApnSetting#TYPE_MMS}.
+     * When disabled, the MMS APN will be governed by the same rules as all other APNs.
+     *
+     * This policy can be enabled and disabled via {@link #setMobileDataPolicyEnabledStatus}.
+     * @hide
+     */
+    @SystemApi
+    public static final int MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED = 2;
+
+    /**
+     * @hide
+     */
+    @IntDef(prefix = { "MOBILE_DATA_POLICY_" }, value = {
+            MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL,
+            MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MobileDataPolicy { }
+
+    /**
+     * Enables or disables a piece of mobile data policy.
+     *
+     * Enables or disables the mobile data policy specified in {@code policy}. See the detailed
+     * description of each policy constant for what they do.
+     *
+     * @param policy The data policy to enable.
+     * @param enabled Whether to enable or disable the policy.
+     * @hide
+     */
+    @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-    public boolean setDataAllowedDuringVoiceCall(boolean allow) {
+    public void setMobileDataPolicyEnabledStatus(@MobileDataPolicy int policy, boolean enabled) {
         try {
             ITelephony service = getITelephony();
             if (service != null) {
-                return service.setDataAllowedDuringVoiceCall(getSubId(), allow);
+                service.setMobileDataPolicyEnabledStatus(getSubId(), policy, enabled);
             }
         } catch (RemoteException ex) {
             // This could happen if binder process crashes.
@@ -13312,27 +13398,22 @@
                 ex.rethrowAsRuntimeException();
             }
         }
-        return false;
     }
 
     /**
-     * Check whether data is allowed during voice call. This is used for allowing data on the
-     * non-default data SIM. When a voice call is placed on the non-default data SIM on DSDS
-     * devices, users will not be able to use mobile data. By calling this API, data will be
-     * temporarily enabled on the non-default data SIM during the life cycle of the voice call.
+     * Fetches the status of a piece of mobile data policy.
      *
-     * @return {@code true} if data is allowed during voice call.
-     *
-     * @throws SecurityException if the caller doesn't have the permission.
-     *
+     * @param policy The data policy that you want the status for.
+     * @return {@code true} if enabled, {@code false} otherwise.
      * @hide
      */
+    @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    public boolean isDataAllowedInVoiceCall() {
+    public boolean isMobileDataPolicyEnabled(@MobileDataPolicy int policy) {
         try {
             ITelephony service = getITelephony();
             if (service != null) {
-                return service.isDataAllowedInVoiceCall(getSubId());
+                return service.isMobileDataPolicyEnabled(getSubId(), policy);
             }
         } catch (RemoteException ex) {
             // This could happen if binder process crashes.
@@ -13344,31 +13425,6 @@
     }
 
     /**
-     * Set whether the specific sim card always allows MMS connection. If true, MMS network
-     * request will be accepted by telephony even if user turns "mobile data" off
-     * on this specific sim card.
-     *
-     * @param alwaysAllow whether Mms data is always allowed.
-     * @return whether operation is successful.
-     *
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-    public boolean setAlwaysAllowMmsData(boolean alwaysAllow) {
-        try {
-            ITelephony service = getITelephony();
-            if (service != null) {
-                return service.setAlwaysAllowMmsData(getSubId(), alwaysAllow);
-            }
-        } catch (RemoteException ex) {
-            if (!isSystemProcess()) {
-                ex.rethrowAsRuntimeException();
-            }
-        }
-        return false;
-    }
-
-    /**
      * The IccLock state or password was changed successfully.
      * @hide
      */
@@ -13671,9 +13727,7 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      * @throws SecurityException if the caller doesn't have the permission.
      *
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public @NonNull List<String> getEquivalentHomePlmns() {
         try {
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index 579200e..41381c5 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -411,6 +411,25 @@
             };
 
     /**
+     * Convert handover failure mode to string.
+     *
+     * @param handoverFailureMode Handover failure mode
+     * @return Handover failure mode in string
+     *
+     * @hide
+     */
+    public static String failureModeToString(@HandoverFailureMode int handoverFailureMode) {
+        switch (handoverFailureMode) {
+            case HANDOVER_FAILURE_MODE_UNKNOWN: return "unknown";
+            case HANDOVER_FAILURE_MODE_LEGACY: return "legacy";
+            case HANDOVER_FAILURE_MODE_DO_FALLBACK: return "fallback";
+            case HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER: return "retry handover";
+            case HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL: return "retry setup new one";
+            default: return Integer.toString(handoverFailureMode);
+        }
+    }
+
+    /**
      * Provides a convenient way to set the fields of a {@link DataCallResponse} when creating a new
      * instance.
      *
diff --git a/telephony/java/android/telephony/ims/ImsCallForwardInfo.java b/telephony/java/android/telephony/ims/ImsCallForwardInfo.java
index d53a2e6..3f9c8d2 100644
--- a/telephony/java/android/telephony/ims/ImsCallForwardInfo.java
+++ b/telephony/java/android/telephony/ims/ImsCallForwardInfo.java
@@ -19,7 +19,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -33,7 +32,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class ImsCallForwardInfo implements Parcelable {
 
     /**
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index f31fcf4..47a0ab6 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -48,7 +48,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class ImsCallProfile implements Parcelable {
     private static final String TAG = "ImsCallProfile";
 
diff --git a/telephony/java/android/telephony/ims/ImsCallSessionListener.java b/telephony/java/android/telephony/ims/ImsCallSessionListener.java
index d21a051..2fdd195 100644
--- a/telephony/java/android/telephony/ims/ImsCallSessionListener.java
+++ b/telephony/java/android/telephony/ims/ImsCallSessionListener.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.RemoteException;
 import android.telephony.Annotation;
 import android.telephony.CallQuality;
@@ -40,7 +39,6 @@
 // TODO: APIs in here do not conform to API guidelines yet. This can be changed if
 // ImsCallSessionListenerConverter is also changed.
 @SystemApi
-@TestApi
 public class ImsCallSessionListener {
 
     private final IImsCallSessionListener mListener;
diff --git a/telephony/java/android/telephony/ims/ImsConferenceState.java b/telephony/java/android/telephony/ims/ImsConferenceState.java
index 9bf2f44..1fa5f52 100644
--- a/telephony/java/android/telephony/ims/ImsConferenceState.java
+++ b/telephony/java/android/telephony/ims/ImsConferenceState.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -38,7 +37,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class ImsConferenceState implements Parcelable {
     private static final String TAG = "ImsConferenceState";
     /**
diff --git a/telephony/java/android/telephony/ims/ImsException.java b/telephony/java/android/telephony/ims/ImsException.java
index 1c3d58d..50fb828 100644
--- a/telephony/java/android/telephony/ims/ImsException.java
+++ b/telephony/java/android/telephony/ims/ImsException.java
@@ -19,7 +19,6 @@
 import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.content.pm.PackageManager;
 import android.telephony.SubscriptionManager;
 import android.text.TextUtils;
@@ -83,7 +82,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public ImsException(@Nullable String message) {
         super(getMessage(message, CODE_ERROR_UNSPECIFIED));
     }
@@ -94,7 +92,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public ImsException(@Nullable String message, @ImsErrorCode int code) {
         super(getMessage(message, code));
         mCode = code;
@@ -108,7 +105,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public ImsException(@Nullable String message, @ImsErrorCode  int code,
             @Nullable Throwable cause) {
         super(getMessage(message, code), cause);
diff --git a/telephony/java/android/telephony/ims/ImsExternalCallState.java b/telephony/java/android/telephony/ims/ImsExternalCallState.java
index 7d73165..fdf636c 100644
--- a/telephony/java/android/telephony/ims/ImsExternalCallState.java
+++ b/telephony/java/android/telephony/ims/ImsExternalCallState.java
@@ -20,7 +20,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -35,7 +34,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class ImsExternalCallState implements Parcelable {
 
     private static final String TAG = "ImsExternalCallState";
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index ee2fce7..76c1faf 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -25,7 +25,6 @@
 import android.annotation.SuppressAutoDoc;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Binder;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
@@ -97,7 +96,7 @@
      */
     // Do not add to this class, add to RegistrationManager.RegistrationCallback instead.
     @Deprecated
-    @SystemApi @TestApi
+    @SystemApi
     public static class RegistrationCallback extends RegistrationManager.RegistrationCallback {
 
         /**
@@ -161,7 +160,7 @@
             public void onCapabilitiesStatusChanged(int config) {
                 if (mLocalCallback == null) return;
 
-                long callingIdentity = Binder.clearCallingIdentity();
+                final long callingIdentity = Binder.clearCallingIdentity();
                 try {
                     mExecutor.execute(() -> mLocalCallback.onCapabilitiesStatusChanged(
                             new MmTelFeature.MmTelCapabilities(config)));
@@ -231,7 +230,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @Deprecated
     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
     @RequiresPermission(anyOf = {
@@ -280,7 +278,7 @@
      * @hide
      */
     @Deprecated
-    @SystemApi @TestApi
+    @SystemApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void registerImsRegistrationCallback(@NonNull @CallbackExecutor Executor executor,
             @NonNull RegistrationCallback c) throws ImsException {
@@ -366,7 +364,7 @@
      * @hide
      */
     @Deprecated
-    @SystemApi @TestApi
+    @SystemApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void unregisterImsRegistrationCallback(@NonNull RegistrationCallback c) {
         if (c == null) {
@@ -422,7 +420,7 @@
      * @hide
      */
     @Override
-    @SystemApi @TestApi
+    @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void getRegistrationState(@NonNull @CallbackExecutor Executor executor,
             @NonNull @ImsRegistrationState Consumer<Integer> stateCallback) {
@@ -681,7 +679,7 @@
      * @hide
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
-    @SystemApi @TestApi
+    @SystemApi
     public void setAdvancedCallingSettingEnabled(boolean isEnabled) {
         ITelephony iTelephony = getITelephony();
         if (iTelephony == null) {
@@ -725,7 +723,7 @@
      * @hide
      */
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    @SystemApi @TestApi
+    @SystemApi
     public boolean isCapable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
             @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) {
         ITelephony iTelephony = getITelephony();
@@ -758,7 +756,7 @@
      *         otherwise.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isAvailable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
             @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) {
@@ -790,7 +788,7 @@
      * available.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void isSupported(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
             @AccessNetworkConstants.TransportType int transportType,
@@ -879,7 +877,7 @@
      * @see #isVtSettingEnabled()
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVtSettingEnabled(boolean isEnabled) {
         ITelephony iTelephony = getITelephony();
@@ -954,7 +952,7 @@
      * @see #isVoWiFiSettingEnabled()
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVoWiFiSettingEnabled(boolean isEnabled) {
         ITelephony iTelephony = getITelephony();
@@ -1032,7 +1030,7 @@
      * @see #isVoWiFiRoamingSettingEnabled()
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVoWiFiRoamingSettingEnabled(boolean isEnabled) {
         ITelephony iTelephony = getITelephony();
@@ -1069,7 +1067,7 @@
      * @see #setVoWiFiSettingEnabled(boolean)
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVoWiFiNonPersistent(boolean isCapable, int mode) {
         ITelephony iTelephony = getITelephony();
@@ -1152,7 +1150,7 @@
      * @see #getVoWiFiModeSetting()
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVoWiFiModeSetting(@WiFiCallingMode int mode) {
         ITelephony iTelephony = getITelephony();
@@ -1188,7 +1186,7 @@
      * @see #setVoWiFiRoamingSettingEnabled(boolean)
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public @WiFiCallingMode int getVoWiFiRoamingModeSetting() {
         ITelephony iTelephony = getITelephony();
@@ -1224,7 +1222,7 @@
      * @see #getVoWiFiRoamingModeSetting()
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVoWiFiRoamingModeSetting(@WiFiCallingMode int mode) {
         ITelephony iTelephony = getITelephony();
@@ -1258,7 +1256,7 @@
      * @param isEnabled if true RTT should be enabled during calls made on this subscription.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setRttCapabilitySetting(boolean isEnabled) {
         ITelephony iTelephony = getITelephony();
@@ -1338,7 +1336,7 @@
      * the IMS service is not available.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void getFeatureState(@NonNull @CallbackExecutor Executor executor,
             @NonNull @ImsFeature.ImsState Consumer<Integer> callback) throws ImsException {
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
index 94407f1..a7586ec 100644
--- a/telephony/java/android/telephony/ims/ImsRcsManager.java
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -99,7 +99,7 @@
             public void onCapabilitiesStatusChanged(int config) {
                 if (mLocalCallback == null) return;
 
-                long callingIdentity = Binder.clearCallingIdentity();
+                final long callingIdentity = Binder.clearCallingIdentity();
                 try {
                     mExecutor.execute(() -> mLocalCallback.onAvailabilityChanged(
                             new RcsFeature.RcsImsCapabilities(config)));
diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java
index 8a05bdf..9ab5aeb 100644
--- a/telephony/java/android/telephony/ims/ImsService.java
+++ b/telephony/java/android/telephony/ims/ImsService.java
@@ -17,8 +17,9 @@
 package android.telephony.ims;
 
 import android.annotation.LongDef;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.app.Service;
 import android.content.Intent;
 import android.os.IBinder;
@@ -30,12 +31,14 @@
 import android.telephony.ims.aidl.IImsRegistration;
 import android.telephony.ims.aidl.IImsServiceController;
 import android.telephony.ims.aidl.IImsServiceControllerListener;
+import android.telephony.ims.aidl.ISipTransport;
 import android.telephony.ims.feature.ImsFeature;
 import android.telephony.ims.feature.MmTelFeature;
 import android.telephony.ims.feature.RcsFeature;
 import android.telephony.ims.stub.ImsConfigImplBase;
 import android.telephony.ims.stub.ImsFeatureConfiguration;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.telephony.ims.stub.SipTransportImplBase;
 import android.util.Log;
 import android.util.SparseArray;
 
@@ -98,25 +101,52 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public class ImsService extends Service {
 
     private static final String LOG_TAG = "ImsService";
 
     /**
      * This ImsService supports the capability to place emergency calls over MMTEL.
+     * <p>
+     * Note: This should never be set by {@link #getImsServiceCapabilities()}, as whether it is
+     * there or not depends on whether or not {@link ImsFeature#FEATURE_EMERGENCY_MMTEL} is defined
+     * for this ImsService. If it is set, it will be removed during sanitization before the final
+     * capabilities bitfield is sent back to the framework.
      * @hide This is encoded into the {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, but we will be
      * adding other capabilities in a central location, so track this capability here as well.
      */
     public static final long CAPABILITY_EMERGENCY_OVER_MMTEL = 1 << 0;
 
     /**
+     * This ImsService supports the capability to create SIP delegates for other IMS applications
+     * to use to proxy SIP messaging traffic through it.
+     * <p>
+     * In order for the framework to report SipDelegate creation as being available for this
+     * ImsService implementation, this ImsService must report this capability flag in
+     * {@link #getImsServiceCapabilities()}, {@link #getSipTransport(int)} must not return null, and
+     * this ImsService MUST report the ability to create both {@link ImsFeature#FEATURE_MMTEL} and
+     * {@link ImsFeature#FEATURE_RCS} features.
+     */
+    public static final long CAPABILITY_SIP_DELEGATE_CREATION = 1 << 1;
+
+    /**
+     * Used for internal correctness checks of capabilities set by the ImsService implementation and
+     * tracks the index of the largest defined flag in the capabilities long.
+     * @hide
+     */
+    public static final long CAPABILITY_MAX_INDEX =
+            Long.numberOfTrailingZeros(CAPABILITY_SIP_DELEGATE_CREATION);
+
+    /**
      * @hide
      */
     @LongDef(flag = true,
             prefix = "CAPABILITY_",
             value = {
-                    CAPABILITY_EMERGENCY_OVER_MMTEL
+                    // CAPABILITY_EMERGENCY_OVER_MMTEL is not included here because it is managed by
+                    // whether or not ImsFeature.FEATURE_EMERGENCY_MMTEL feature is set and should
+                    // not be set by users of ImsService.
+                    CAPABILITY_SIP_DELEGATE_CREATION
             })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ImsServiceCapability {}
@@ -127,6 +157,7 @@
      */
     private static final Map<Long, String> CAPABILITIES_LOG_MAP = new HashMap<Long, String>() {{
             put(CAPABILITY_EMERGENCY_OVER_MMTEL, "EMERGENCY_OVER_MMTEL");
+            put(CAPABILITY_SIP_DELEGATE_CREATION, "SIP_DELEGATE_CREATION");
         }};
 
     /**
@@ -201,6 +232,17 @@
         }
 
         @Override
+        public long getImsServiceCapabilities() {
+            long caps = ImsService.this.getImsServiceCapabilities();
+            long sanitizedCaps = sanitizeCapabilities(caps);
+            if (caps != sanitizedCaps) {
+                Log.w(LOG_TAG, "removing invalid bits from field: 0x"
+                        + Long.toHexString(caps ^ sanitizedCaps));
+            }
+            return sanitizedCaps;
+        }
+
+        @Override
         public void notifyImsServiceReadyForFeatureCreation() {
             ImsService.this.readyForFeatureCreation();
         }
@@ -218,6 +260,12 @@
         }
 
         @Override
+        public ISipTransport getSipTransport(int slotId) {
+            SipTransportImplBase s = ImsService.this.getSipTransport(slotId);
+            return s != null ? s.getBinder() : null;
+        }
+
+        @Override
         public void enableIms(int slotId) {
             ImsService.this.enableIms(slotId);
         }
@@ -371,6 +419,20 @@
     }
 
     /**
+     * The optional capabilities that this ImsService supports.
+     * <p>
+     * This should be a static configuration and should not change at runtime.
+     * @return The optional static capabilities of this ImsService implementation.
+     */
+    // ImsService follows a different convention, since it is a stub class. The on* methods are
+    // final and call back into the framework with a state update.
+    @SuppressLint("OnNameExpected")
+    public @ImsServiceCapability long getImsServiceCapabilities() {
+        // Stub implementation to be implemented by ImsService.
+        return 0L;
+    }
+
+    /**
      * The ImsService has been bound and is ready for ImsFeature creation based on the Features that
      * the ImsService has registered for with the framework, either in the manifest or via
      * {@link #querySupportedImsFeatures()}.
@@ -443,7 +505,40 @@
     }
 
     /**
-     * @return A string representation of the ImsService capabilties for logging.
+     * Return the {@link SipTransportImplBase} implementation associated with the provided slot.
+     * <p>
+     * This is an optional interface used for devices that must support IMS single registration and
+     * proxy SIP traffic to remote IMS applications. If this is not supported for this IMS service,
+     * this method should return {@code null}. If this feature is supported, then this method must
+     * never be {@code null} and the optional ImsService capability flag
+     * {@link #CAPABILITY_SIP_DELEGATE_CREATION} must be set in
+     * {@link #getImsServiceCapabilities()}. Otherwise the framework will assume this feature is not
+     * supported for this ImsService.
+     * @param slotId The slot that is associated with the SipTransport implementation.
+     * @return the SipTransport implementation for the specified slot.
+     */
+    // ImsService follows a different convention, since it is a stub class. The on* methods are
+    // final and call back into the framework with a state update.
+    @SuppressLint("OnNameExpected")
+    public @Nullable SipTransportImplBase getSipTransport(int slotId) {
+        // Stub implementation for ImsServices that do not support SipTransport.
+        return null;
+    }
+
+    private static long sanitizeCapabilities(@ImsServiceCapability long caps) {
+        long filter = 0xFFFFFFFFFFFFFFFFL;
+        // pad the "allowed" set with zeros
+        filter <<= CAPABILITY_MAX_INDEX + 1;
+        // remove values above the allowed set.
+        caps &= ~filter;
+        // CAPABILITY_EMERGENCY_OVER_MMTEL should also not be set here, will be set by telephony
+        // internally.
+        caps &= ~CAPABILITY_EMERGENCY_OVER_MMTEL;
+        return caps;
+    }
+
+    /**
+     * @return A string representation of the ImsService capabilities for logging.
      * @hide
      */
     public static String getCapabilitiesString(@ImsServiceCapability long caps) {
@@ -467,4 +562,4 @@
         result.append("}");
         return result.toString();
     }
-}
\ No newline at end of file
+}
diff --git a/telephony/java/android/telephony/ims/ImsSsData.java b/telephony/java/android/telephony/ims/ImsSsData.java
index 70bf0c5..fb8e5d3 100644
--- a/telephony/java/android/telephony/ims/ImsSsData.java
+++ b/telephony/java/android/telephony/ims/ImsSsData.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -37,7 +36,6 @@
  * {@hide}
  */
 @SystemApi
-@TestApi
 public final class ImsSsData implements Parcelable {
 
     private static final String TAG = ImsSsData.class.getCanonicalName();
diff --git a/telephony/java/android/telephony/ims/ImsSsInfo.java b/telephony/java/android/telephony/ims/ImsSsInfo.java
index 9cce95f..27b56b8 100644
--- a/telephony/java/android/telephony/ims/ImsSsInfo.java
+++ b/telephony/java/android/telephony/ims/ImsSsInfo.java
@@ -20,7 +20,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -38,7 +37,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class ImsSsInfo implements Parcelable {
 
     /**@hide*/
diff --git a/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java b/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java
index b70fd64..131cb1a 100644
--- a/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java
+++ b/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -30,7 +29,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class ImsStreamMediaProfile implements Parcelable {
     private static final String TAG = "ImsStreamMediaProfile";
 
diff --git a/telephony/java/android/telephony/ims/ImsSuppServiceNotification.java b/telephony/java/android/telephony/ims/ImsSuppServiceNotification.java
index f67f68e..1630368 100644
--- a/telephony/java/android/telephony/ims/ImsSuppServiceNotification.java
+++ b/telephony/java/android/telephony/ims/ImsSuppServiceNotification.java
@@ -19,7 +19,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -32,7 +31,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class ImsSuppServiceNotification implements Parcelable {
     private static final String TAG = "ImsSuppServiceNotification";
 
diff --git a/telephony/java/android/telephony/ims/ImsUtListener.java b/telephony/java/android/telephony/ims/ImsUtListener.java
index 460a032..baa0576 100644
--- a/telephony/java/android/telephony/ims/ImsUtListener.java
+++ b/telephony/java/android/telephony/ims/ImsUtListener.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.telephony.ims.stub.ImsUtImplBase;
@@ -34,7 +33,6 @@
 // DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
 // will break other implementations of ImsUt maintained by other ImsServices.
 @SystemApi
-@TestApi
 public class ImsUtListener {
 
     /**
diff --git a/telephony/java/android/telephony/ims/ImsVideoCallProvider.java b/telephony/java/android/telephony/ims/ImsVideoCallProvider.java
index 569c6d5..2fca409 100644
--- a/telephony/java/android/telephony/ims/ImsVideoCallProvider.java
+++ b/telephony/java/android/telephony/ims/ImsVideoCallProvider.java
@@ -17,14 +17,12 @@
 package android.telephony.ims;
 
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
-import android.telecom.Connection;
 import android.telecom.VideoProfile;
 import android.telecom.VideoProfile.CameraCapabilities;
 import android.view.Surface;
@@ -37,7 +35,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public abstract class ImsVideoCallProvider {
     private static final int MSG_SET_CALLBACK = 1;
     private static final int MSG_SET_CAMERA = 2;
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index 2a073a1..24ae979 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -23,7 +23,6 @@
 import android.annotation.RequiresPermission;
 import android.annotation.StringDef;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.annotation.WorkerThread;
 import android.os.Binder;
 import android.os.RemoteException;
@@ -59,7 +58,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public class ProvisioningManager {
 
     /**@hide*/
@@ -879,7 +877,7 @@
 
             @Override
             public final void onIntConfigChanged(int item, int value) {
-                long callingIdentity = Binder.clearCallingIdentity();
+                final long callingIdentity = Binder.clearCallingIdentity();
                 try {
                     mExecutor.execute(() ->
                             mLocalConfigurationCallback.onProvisioningIntChanged(item, value));
@@ -890,7 +888,7 @@
 
             @Override
             public final void onStringConfigChanged(int item, String value) {
-                long callingIdentity = Binder.clearCallingIdentity();
+                final long callingIdentity = Binder.clearCallingIdentity();
                 try {
                     mExecutor.execute(() ->
                             mLocalConfigurationCallback.onProvisioningStringChanged(item, value));
diff --git a/media/java/android/media/tv/TvChannelInfo.aidl b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.aidl
similarity index 81%
copy from media/java/android/media/tv/TvChannelInfo.aidl
copy to telephony/java/android/telephony/ims/RcsContactPresenceTuple.aidl
index 71cd0a7..a704702 100644
--- a/media/java/android/media/tv/TvChannelInfo.aidl
+++ b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * Copyright (c) 2020 The Android Open 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,6 +14,7 @@
  * limitations under the License.
  */
 
-package android.media.tv;
 
-parcelable TvChannelInfo;
+package android.telephony.ims;
+
+parcelable RcsContactPresenceTuple;
diff --git a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
new file mode 100644
index 0000000..b0aaa92
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.telephony.ims;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringDef;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Represents a PIDF tuple element that is part of the presence element returned from the carrier
+ * network during a SUBSCRIBE request. See RFC3863 for more information.
+ * @hide
+ */
+public class RcsContactPresenceTuple implements Parcelable {
+
+    /** The service id of the MMTEL */
+    public static final String SERVICE_ID_MMTEL = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.mmtel";
+
+    /** The service capabilities is available. */
+    public static final String TUPLE_BASIC_STATUS_OPEN = "open";
+
+    /** The service capabilities is unavailable. */
+    public static final String TUPLE_BASIC_STATUS_CLOSED = "closed";
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @StringDef(prefix = "TUPLE_BASIC_STATUS_", value = {
+        TUPLE_BASIC_STATUS_OPEN,
+        TUPLE_BASIC_STATUS_CLOSED
+    })
+    public @interface BasicStatus {}
+
+    /**
+     * An optional addition to the PIDF Presence Tuple containing service capabilities, which is
+     * defined in the servcaps element. See RFC5196, section 3.2.1.
+     */
+    public static class ServiceCapabilities implements Parcelable {
+
+        /** The service can simultaneously send and receive data. */
+        public static final String DUPLEX_MODE_FULL = "full";
+
+        /** The service can alternate between sending and receiving data.*/
+        public static final String DUPLEX_MODE_HALF = "half";
+
+        /** The service can only receive data. */
+        public static final String DUPLEX_MODE_RECEIVE_ONLY = "receive-only";
+
+        /** The service can only send data. */
+        public static final String DUPLEX_MODE_SEND_ONLY = "send-only";
+
+        /** @hide */
+        @Retention(RetentionPolicy.SOURCE)
+        @StringDef(prefix = "DUPLEX_MODE_", value = {
+            DUPLEX_MODE_FULL,
+            DUPLEX_MODE_HALF,
+            DUPLEX_MODE_RECEIVE_ONLY,
+            DUPLEX_MODE_SEND_ONLY
+        })
+        public @interface DuplexMode {}
+
+        /**
+         * Builder to help construct {@link ServiceCapabilities} instances.
+         */
+        public static class Builder {
+
+            private ServiceCapabilities mCapabilities;
+
+            /**
+             * Create the ServiceCapabilities builder, which can be used to set service capabilities
+             * as well as custom capability extensions.
+             * @param isAudioCapable Whether the audio is capable or not.
+             * @param isVideoCapable Whether the video is capable or not.
+             */
+            public Builder(boolean isAudioCapable, boolean isVideoCapable) {
+                mCapabilities = new ServiceCapabilities(isAudioCapable, isVideoCapable);
+            }
+
+            /**
+             * Add the supported duplex mode.
+             * @param mode The supported duplex mode
+             */
+            public Builder addSupportedDuplexMode(@NonNull @DuplexMode String mode) {
+                mCapabilities.mSupportedDuplexModeList.add(mode);
+                return this;
+            }
+
+            /**
+             * Add the unsupported duplex mode.
+             * @param mode The unsupported duplex mode
+             */
+            public Builder addUnsupportedDuplexMode(@NonNull @DuplexMode String mode) {
+                mCapabilities.mUnsupportedDuplexModeList.add(mode);
+                return this;
+            }
+
+            /**
+             * @return the ServiceCapabilities instance.
+             */
+            public ServiceCapabilities build() {
+                return mCapabilities;
+            }
+        }
+
+        private final boolean mIsAudioCapable;
+        private final boolean mIsVideoCapable;
+        private final @DuplexMode List<String> mSupportedDuplexModeList = new ArrayList<>();
+        private final @DuplexMode List<String> mUnsupportedDuplexModeList = new ArrayList<>();
+
+        /**
+         * Use {@link Builder} to build an instance of this interface.
+         * @param isAudioCapable Whether the audio is capable.
+         * @param isVideoCapable Whether the video is capable.
+         */
+        ServiceCapabilities(boolean isAudioCapable, boolean isVideoCapable) {
+            mIsAudioCapable = isAudioCapable;
+            mIsVideoCapable = isVideoCapable;
+        }
+
+        private ServiceCapabilities(Parcel in) {
+            mIsAudioCapable = in.readBoolean();
+            mIsVideoCapable = in.readBoolean();
+            in.readStringList(mSupportedDuplexModeList);
+            in.readStringList(mUnsupportedDuplexModeList);
+        }
+        @Override
+        public void writeToParcel(@NonNull Parcel out, int flags) {
+            out.writeBoolean(mIsAudioCapable);
+            out.writeBoolean(mIsVideoCapable);
+            out.writeStringList(mSupportedDuplexModeList);
+            out.writeStringList(mUnsupportedDuplexModeList);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        public static final @NonNull Creator<ServiceCapabilities> CREATOR =
+                new Creator<ServiceCapabilities>() {
+                    @Override
+                    public ServiceCapabilities createFromParcel(Parcel in) {
+                        return new ServiceCapabilities(in);
+                    }
+
+                    @Override
+                    public ServiceCapabilities[] newArray(int size) {
+                        return new ServiceCapabilities[size];
+                    }
+                };
+
+        /**
+         * Query the audio capable.
+         * @return true if the audio is capable, false otherwise.
+         */
+        public boolean isAudioCapable() {
+            return mIsAudioCapable;
+        }
+
+        /**
+         * Query the video capable.
+         * @return true if the video is capable, false otherwise.
+         */
+        public boolean isVideoCapable() {
+            return mIsVideoCapable;
+        }
+
+        /**
+         * Get the supported duplex mode list.
+         * @return The list of supported duplex mode
+         */
+        public @NonNull @DuplexMode List<String> getSupportedDuplexModes() {
+            return Collections.unmodifiableList(mSupportedDuplexModeList);
+        }
+
+        /**
+         * Get the unsupported duplex mode list.
+         * @return The list of unsupported duplex mode
+         */
+        public @NonNull @DuplexMode List<String> getUnsupportedDuplexModes() {
+            return Collections.unmodifiableList(mUnsupportedDuplexModeList);
+        }
+    }
+
+    /**
+     * Builder to help construct {@link RcsContactPresenceTuple} instances.
+     */
+    public static class Builder {
+
+        private RcsContactPresenceTuple mPresenceTuple;
+
+        /**
+         * Builds a RcsContactPresenceTuple instance.
+         * @param serviceId The OMA Presence service-id associated with this capability. See the
+         * OMA Presence SIMPLE specification v1.1, section 10.5.1.
+         * @param serviceVersion The OMA Presence version associated with the service capability.
+         * See the OMA Presence SIMPLE specification v1.1, section 10.5.1.
+         */
+        public Builder(@NonNull @BasicStatus String status, @NonNull String serviceId,
+                @NonNull String serviceVersion) {
+            mPresenceTuple = new RcsContactPresenceTuple(status, serviceId, serviceVersion);
+        }
+
+        /**
+         * The optional SIP Contact URI associated with the PIDF tuple element.
+         */
+        public Builder addContactUri(@NonNull Uri contactUri) {
+            mPresenceTuple.mContactUri = contactUri;
+            return this;
+        }
+
+        /**
+         * The optional timestamp indicating the data and time of the status change of this tuple.
+         * See RFC3863, section 4.1.7 for more information on the expected format.
+         */
+        public Builder addTimeStamp(@NonNull String timestamp) {
+            mPresenceTuple.mTimestamp = timestamp;
+            return this;
+        }
+
+        /**
+         * An optional parameter containing the description element of the service-description. See
+         * OMA Presence SIMPLE specification v1.1
+         */
+        public Builder addDescription(@NonNull String description) {
+            mPresenceTuple.mServiceDescription = description;
+            return this;
+        }
+
+        /**
+         * An optional parameter containing the service capabilities of the presence tuple if they
+         * are present in the servcaps element.
+         */
+        public Builder addServiceCapabilities(@NonNull ServiceCapabilities caps) {
+            mPresenceTuple.mServiceCapabilities = caps;
+            return this;
+        }
+
+        /**
+         * @return the constructed instance.
+         */
+        public RcsContactPresenceTuple build() {
+            return mPresenceTuple;
+        }
+    }
+
+    private Uri mContactUri;
+    private String mTimestamp;
+    private @BasicStatus String mStatus;
+
+    // The service information in the service-description element.
+    private String mServiceId;
+    private String mServiceVersion;
+    private String mServiceDescription;
+
+    private ServiceCapabilities mServiceCapabilities;
+
+    private RcsContactPresenceTuple(@NonNull @BasicStatus String status, @NonNull String serviceId,
+            @NonNull String serviceVersion) {
+        mStatus = status;
+        mServiceId = serviceId;
+        mServiceVersion = serviceVersion;
+    }
+
+    private RcsContactPresenceTuple(Parcel in) {
+        mContactUri = in.readParcelable(Uri.class.getClassLoader());
+        mTimestamp = in.readString();
+        mStatus = in.readString();
+        mServiceId = in.readString();
+        mServiceVersion = in.readString();
+        mServiceDescription = in.readString();
+        mServiceCapabilities = in.readParcelable(ServiceCapabilities.class.getClassLoader());
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeParcelable(mContactUri, flags);
+        out.writeString(mTimestamp);
+        out.writeString(mStatus);
+        out.writeString(mServiceId);
+        out.writeString(mServiceVersion);
+        out.writeString(mServiceDescription);
+        out.writeParcelable(mServiceCapabilities, flags);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final @NonNull Creator<RcsContactPresenceTuple> CREATOR =
+            new Creator<RcsContactPresenceTuple>() {
+                @Override
+                public RcsContactPresenceTuple createFromParcel(Parcel in) {
+                    return new RcsContactPresenceTuple(in);
+                }
+
+                @Override
+                public RcsContactPresenceTuple[] newArray(int size) {
+                    return new RcsContactPresenceTuple[size];
+                }
+            };
+
+    /** @return the status of the tuple element. */
+    public @NonNull @BasicStatus String getStatus() {
+        return mStatus;
+    }
+
+    /** @return the service-id element of the service-description */
+    public @NonNull String getServiceId() {
+        return mServiceId;
+    }
+
+    /** @return the version element of the service-description */
+    public @NonNull String getServiceVersion() {
+        return mServiceVersion;
+    }
+
+    /** @return the SIP URI contained in the contact element of the tuple if it exists. */
+    public @Nullable Uri getContactUri() {
+        return mContactUri;
+    }
+
+    /** @return the timestamp element contained in the tuple if it exists */
+    public @Nullable String getTimestamp() {
+        return mTimestamp;
+    }
+
+    /** @return the description element contained in the service-description if it exists */
+    public @Nullable String getServiceDescription() {
+        return mServiceDescription;
+    }
+
+    /** @return the {@link ServiceCapabilities} of the tuple if it exists. */
+    public @Nullable ServiceCapabilities getServiceCapabilities() {
+        return mServiceCapabilities;
+    }
+}
diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
index dc36edf..d12a6ae 100644
--- a/telephony/java/android/telephony/ims/RcsContactUceCapability.java
+++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
@@ -16,7 +16,7 @@
 
 package android.telephony.ims;
 
-import android.annotation.LongDef;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.net.Uri;
@@ -27,9 +27,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 /**
  * Contains the User Capability Exchange capabilities corresponding to a contact's URI.
@@ -37,114 +35,80 @@
  */
 public final class RcsContactUceCapability implements Parcelable {
 
-    /** Supports 1-to-1 chat */
-    public static final int CAPABILITY_CHAT_STANDALONE = (1 << 0);
-    /** Supports group chat */
-    public static final int CAPABILITY_CHAT_SESSION = (1 << 1);
-    /** Supports full store and forward group chat information. */
-    public static final int CAPABILITY_CHAT_SESSION_STORE_FORWARD = (1 << 2);
-    /**
-     * Supports file transfer via Message Session Relay Protocol (MSRP) without Store and Forward.
-     */
-    public static final int CAPABILITY_FILE_TRANSFER = (1 << 3);
-    /** Supports File Transfer Thumbnail */
-    public static final int CAPABILITY_FILE_TRANSFER_THUMBNAIL = (1 << 4);
-    /** Supports File Transfer with Store and Forward */
-    public static final int CAPABILITY_FILE_TRANSFER_STORE_FORWARD = (1 << 5);
-    /** Supports File Transfer via HTTP */
-    public static final int CAPABILITY_FILE_TRANSFER_HTTP = (1 << 6);
-    /** Supports file transfer via SMS */
-    public static final int CAPABILITY_FILE_TRANSFER_SMS = (1 << 7);
-    /** Supports image sharing */
-    public static final int CAPABILITY_IMAGE_SHARE = (1 << 8);
-    /** Supports video sharing during a circuit-switch call (IR.74)*/
-    public static final int CAPABILITY_VIDEO_SHARE_DURING_CS_CALL = (1 << 9);
-    /** Supports video share outside of voice call (IR.84) */
-    public static final int CAPABILITY_VIDEO_SHARE = (1 << 10);
-    /** Supports social presence information */
-    public static final int CAPABILITY_SOCIAL_PRESENCE = (1 << 11);
-    /** Supports capability discovery via presence */
-    public static final int CAPABILITY_DISCOVERY_VIA_PRESENCE = (1 << 12);
-    /** Supports IP Voice calling over LTE or IWLAN (IR.92/IR.51) */
-    public static final int CAPABILITY_IP_VOICE_CALL = (1 << 13);
-    /** Supports IP video calling (IR.94) */
-    public static final int CAPABILITY_IP_VIDEO_CALL = (1 << 14);
-    /** Supports Geolocation PUSH during 1-to-1 or multiparty chat */
-    public static final int CAPABILITY_GEOLOCATION_PUSH = (1 << 15);
-    /** Supports Geolocation PUSH via SMS for fallback.  */
-    public static final int CAPABILITY_GEOLOCATION_PUSH_SMS = (1 << 16);
-    /** Supports Geolocation pull. */
-    public static final int CAPABILITY_GEOLOCATION_PULL = (1 << 17);
-    /** Supports Geolocation pull using file transfer support. */
-    public static final int CAPABILITY_GEOLOCATION_PULL_FILE_TRANSFER = (1 << 18);
-    /** Supports RCS voice calling */
-    public static final int CAPABILITY_RCS_VOICE_CALL = (1 << 19);
-    /** Supports RCS video calling */
-    public static final int CAPABILITY_RCS_VIDEO_CALL = (1 << 20);
-    /** Supports RCS video calling, where video media can not be dropped. */
-    public static final int CAPABILITY_RCS_VIDEO_ONLY_CALL = (1 << 21);
-    /** Supports call composer, where outgoing calls can be enriched with pre-call content.*/
-    public static final int CAPABILITY_CALL_COMPOSER = (1 << 22);
-    /** Supports post call information that is included in the call if the call is missed.*/
-    public static final int CAPABILITY_POST_CALL = (1 << 23);
-    /** Supports sharing a map where the user can draw, share markers, and share their position. */
-    public static final int CAPABILITY_SHARED_MAP = (1 << 24);
-    /** Supports sharing a canvas, where users can draw, add images, and change background colors.*/
-    public static final int CAPABILITY_SHARED_SKETCH = (1 << 25);
-    /** Supports communication with Chatbots. */
-    public static final int CAPABILITY_CHAT_BOT = (1 << 26);
-    /** Supports Chatbot roles. */
-    public static final int CAPABILITY_CHAT_BOT_ROLE = (1 << 27);
-    /** Supports the unidirectional plug-ins framework. */
-    public static final int CAPABILITY_PLUG_IN = (1 << 28);
-    /** Supports standalone Chatbot communication. */
-    public static final int CAPABILITY_STANDALONE_CHAT_BOT = (1 << 29);
-    /** Supports MMTEL based call composer. */
-    public static final int CAPABILITY_MMTEL_CALL_COMPOSER = (1 << 30);
+    /** Contains presence information associated with the contact */
+    public static final int CAPABILITY_MECHANISM_PRESENCE = 1;
 
+    /** Contains OPTIONS information associated with the contact */
+    public static final int CAPABILITY_MECHANISM_OPTIONS = 2;
 
-
-    /** @hide*/
+    /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @LongDef(prefix = "CAPABILITY_", flag = true, value = {
-            CAPABILITY_CHAT_STANDALONE,
-            CAPABILITY_CHAT_SESSION,
-            CAPABILITY_CHAT_SESSION_STORE_FORWARD,
-            CAPABILITY_FILE_TRANSFER,
-            CAPABILITY_FILE_TRANSFER_THUMBNAIL,
-            CAPABILITY_FILE_TRANSFER_STORE_FORWARD,
-            CAPABILITY_FILE_TRANSFER_HTTP,
-            CAPABILITY_FILE_TRANSFER_SMS,
-            CAPABILITY_IMAGE_SHARE,
-            CAPABILITY_VIDEO_SHARE_DURING_CS_CALL,
-            CAPABILITY_VIDEO_SHARE,
-            CAPABILITY_SOCIAL_PRESENCE,
-            CAPABILITY_DISCOVERY_VIA_PRESENCE,
-            CAPABILITY_IP_VOICE_CALL,
-            CAPABILITY_IP_VIDEO_CALL,
-            CAPABILITY_GEOLOCATION_PUSH,
-            CAPABILITY_GEOLOCATION_PUSH_SMS,
-            CAPABILITY_GEOLOCATION_PULL,
-            CAPABILITY_GEOLOCATION_PULL_FILE_TRANSFER,
-            CAPABILITY_RCS_VOICE_CALL,
-            CAPABILITY_RCS_VIDEO_CALL,
-            CAPABILITY_RCS_VIDEO_ONLY_CALL,
-            CAPABILITY_CALL_COMPOSER,
-            CAPABILITY_POST_CALL,
-            CAPABILITY_SHARED_MAP,
-            CAPABILITY_SHARED_SKETCH,
-            CAPABILITY_CHAT_BOT,
-            CAPABILITY_CHAT_BOT_ROLE,
-            CAPABILITY_PLUG_IN,
-            CAPABILITY_STANDALONE_CHAT_BOT,
-            CAPABILITY_MMTEL_CALL_COMPOSER
+    @IntDef(prefix = "CAPABILITY_MECHANISM_", value = {
+        CAPABILITY_MECHANISM_PRESENCE,
+        CAPABILITY_MECHANISM_OPTIONS
     })
-    public @interface CapabilityFlag {}
+    public @interface CapabilityMechanism {}
 
     /**
-     * Builder to help construct {@link RcsContactUceCapability} instances.
+     * The capabilities of this contact were requested recently enough to still be considered in
+     * the availability window.
      */
-    public static class Builder {
+    public static final int SOURCE_TYPE_NETWORK = 0;
+
+    /**
+     * The capabilities of this contact were retrieved from the cached information in the Enhanced
+     * Address Book.
+     */
+    public static final int SOURCE_TYPE_CACHED = 1;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "SOURCE_TYPE_", value = {
+        SOURCE_TYPE_NETWORK,
+        SOURCE_TYPE_CACHED
+    })
+    public @interface SourceType {}
+
+    /**
+     * The requested contact was found to be offline when queried. This is only applicable to
+     * contact capabilities that were queried via OPTIONS requests and the network returned a
+     * 408/480 response.
+     */
+    public static final int REQUEST_RESULT_NOT_ONLINE = 0;
+
+    /**
+     * Capability information for the requested contact was not found. The contact should not be
+     * considered an RCS user.
+     */
+    public static final int REQUEST_RESULT_NOT_FOUND = 1;
+
+    /**
+     * Capability information for the requested contact was found successfully.
+     */
+    public static final int REQUEST_RESULT_FOUND = 2;
+
+    /**
+     * Capability information for the requested contact has expired and can not be refreshed due to
+     * a temporary network error. This is a temporary error and the capabilities of the contact
+     * should be queried again at a later time.
+     */
+    public static final int REQUEST_RESULT_UNKNOWN = 3;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "REQUEST_RESULT_", value = {
+        REQUEST_RESULT_NOT_ONLINE,
+        REQUEST_RESULT_NOT_FOUND,
+        REQUEST_RESULT_FOUND,
+        REQUEST_RESULT_UNKNOWN
+    })
+    public @interface RequestResult {}
+
+    /**
+     * Builder to help construct {@link RcsContactUceCapability} instances when capabilities were
+     * queried through SIP OPTIONS.
+     */
+    public static class OptionsBuilder {
 
         private final RcsContactUceCapability mCapabilities;
 
@@ -153,51 +117,38 @@
          * capability extensions.
          * @param contact The contact URI that the capabilities are attached to.
          */
-        public Builder(@NonNull Uri contact) {
-            mCapabilities = new RcsContactUceCapability(contact);
+        public OptionsBuilder(@NonNull Uri contact) {
+            mCapabilities = new RcsContactUceCapability(contact, CAPABILITY_MECHANISM_OPTIONS,
+                    SOURCE_TYPE_NETWORK);
         }
 
         /**
-         * Add a UCE capability bit-field as well as the associated URI that the framework should
-         * use for those services. This is mainly used for capabilities that may use a URI separate
-         * from the contact's URI, for example the URI to use for VT calls.
-         * @param type The capability to map to a service URI that is different from the contact's
-         *         URI.
+         * Set the result of the capabilities request.
+         * @param requestResult the request result
+         * @return this OptionBuilder
          */
-        public @NonNull Builder add(@CapabilityFlag long type, @NonNull Uri serviceUri) {
-            mCapabilities.mCapabilities |= type;
-            // Put each of these capabilities into the map separately.
-            for (long shift = 0; shift < Integer.SIZE; shift++) {
-                long cap = type & (1 << shift);
-                if (cap != 0) {
-                    mCapabilities.mServiceMap.put(cap, serviceUri);
-                    // remove that capability from the field.
-                    type &= ~cap;
-                }
-                if (type == 0) {
-                    // no need to keep going, end early.
-                    break;
-                }
-            }
+        public @NonNull OptionsBuilder setRequestResult(@RequestResult int requestResult) {
+            mCapabilities.mRequestResult = requestResult;
             return this;
         }
 
         /**
-         * Add a UCE capability flag that this contact supports.
-         * @param type the capability that the contact supports.
+         * Add the feature tag into the capabilities instance.
+         * @param tag the supported feature tag
+         * @return this OptionBuilder
          */
-        public @NonNull Builder add(@CapabilityFlag long type) {
-            mCapabilities.mCapabilities |= type;
+        public @NonNull OptionsBuilder addFeatureTag(String tag) {
+            mCapabilities.mFeatureTags.add(tag);
             return this;
         }
 
         /**
-         * Add a carrier specific service tag.
-         * @param extension A string containing a carrier specific service tag that is an extension
-         *         of the {@link CapabilityFlag}s that are defined here.
+         * Add the list of feature tag into the capabilities instance.
+         * @param tags the list of the supported feature tags
+         * @return this OptionBuilder
          */
-        public @NonNull Builder add(@NonNull String extension) {
-            mCapabilities.mExtensionTags.add(extension);
+        public @NonNull OptionsBuilder addFeatureTags(List<String> tags) {
+            mCapabilities.mFeatureTags.addAll(tags);
             return this;
         }
 
@@ -209,56 +160,88 @@
         }
     }
 
-    private final Uri mContactUri;
-    private long mCapabilities;
-    private List<String> mExtensionTags = new ArrayList<>();
-    private Map<Long, Uri> mServiceMap = new HashMap<>();
-
     /**
-     * Use {@link Builder} to build an instance of this interface.
-     * @param contact The URI associated with this capability information.
-     * @hide
+     * Builder to help construct {@link RcsContactUceCapability} instances when capabilities were
+     * queried through a presence server.
      */
-    RcsContactUceCapability(@NonNull Uri contact) {
-        mContactUri = contact;
+    public static class PresenceBuilder {
+
+        private final RcsContactUceCapability mCapabilities;
+
+        /**
+         * Create the builder, which can be used to set UCE capabilities as well as custom
+         * capability extensions.
+         * @param contact The contact URI that the capabilities are attached to.
+         * @param sourceType The type where the capabilities of this contact were retrieved from.
+         * @param requestResult the request result
+         */
+        public PresenceBuilder(@NonNull Uri contact, @SourceType int sourceType,
+                @RequestResult int requestResult) {
+            mCapabilities = new RcsContactUceCapability(contact, CAPABILITY_MECHANISM_PRESENCE,
+                sourceType);
+            mCapabilities.mRequestResult = requestResult;
+        }
+
+        /**
+         * Add the {@link RcsContactPresenceTuple} into the capabilities instance.
+         * @param tuple The {@link RcsContactPresenceTuple} to be added into.
+         * @return this PresenceBuilder
+         */
+        public @NonNull PresenceBuilder addCapabilityTuple(RcsContactPresenceTuple tuple) {
+            mCapabilities.mPresenceTuples.add(tuple);
+            return this;
+        }
+
+        /**
+         * Add the list of {@link RcsContactPresenceTuple} into the capabilities instance.
+         * @param tuples The list of the {@link RcsContactPresenceTuple} to be added into.
+         * @return this PresenceBuilder
+         */
+        public @NonNull PresenceBuilder addCapabilityTuples(List<RcsContactPresenceTuple> tuples) {
+            mCapabilities.mPresenceTuples.addAll(tuples);
+            return this;
+        }
+
+        /**
+         * @return the RcsContactUceCapability instance.
+         */
+        public @NonNull RcsContactUceCapability build() {
+            return mCapabilities;
+        }
+    }
+
+    private final Uri mContactUri;
+    private @SourceType int mSourceType;
+    private @CapabilityMechanism int mCapabilityMechanism;
+    private @RequestResult int mRequestResult;
+
+    private final List<String> mFeatureTags = new ArrayList<>();
+    private final List<RcsContactPresenceTuple> mPresenceTuples = new ArrayList<>();
+
+    private RcsContactUceCapability(@NonNull Uri contactUri, @CapabilityMechanism int mechanism,
+            @SourceType int sourceType) {
+        mContactUri = contactUri;
+        mCapabilityMechanism = mechanism;
+        mSourceType = sourceType;
     }
 
     private RcsContactUceCapability(Parcel in) {
         mContactUri = in.readParcelable(Uri.class.getClassLoader());
-        mCapabilities = in.readLong();
-        in.readStringList(mExtensionTags);
-        // read mServiceMap as key,value pair
-        int mapSize = in.readInt();
-        for (int i = 0; i < mapSize; i++) {
-            mServiceMap.put(in.readLong(), in.readParcelable(Uri.class.getClassLoader()));
-        }
+        mCapabilityMechanism = in.readInt();
+        mSourceType = in.readInt();
+        mRequestResult = in.readInt();
+        in.readStringList(mFeatureTags);
+        in.readParcelableList(mPresenceTuples, RcsContactPresenceTuple.class.getClassLoader());
     }
 
-    public static final @NonNull Creator<RcsContactUceCapability> CREATOR =
-            new Creator<RcsContactUceCapability>() {
-        @Override
-        public RcsContactUceCapability createFromParcel(Parcel in) {
-            return new RcsContactUceCapability(in);
-        }
-
-        @Override
-        public RcsContactUceCapability[] newArray(int size) {
-            return new RcsContactUceCapability[size];
-        }
-    };
-
     @Override
     public void writeToParcel(@NonNull Parcel out, int flags) {
-        out.writeParcelable(mContactUri, 0);
-        out.writeLong(mCapabilities);
-        out.writeStringList(mExtensionTags);
-        // write mServiceMap as key,value pairs
-        int mapSize = mServiceMap.keySet().size();
-        out.writeInt(mapSize);
-        for (long key : mServiceMap.keySet()) {
-            out.writeLong(key);
-            out.writeParcelable(mServiceMap.get(key), 0);
-        }
+        out.writeParcelable(mContactUri, flags);
+        out.writeInt(mCapabilityMechanism);
+        out.writeInt(mSourceType);
+        out.writeInt(mRequestResult);
+        out.writeStringList(mFeatureTags);
+        out.writeParcelableList(mPresenceTuples, flags);
     }
 
     @Override
@@ -266,49 +249,87 @@
         return 0;
     }
 
+    public static final @NonNull Creator<RcsContactUceCapability> CREATOR =
+            new Creator<RcsContactUceCapability>() {
+                @Override
+                public RcsContactUceCapability createFromParcel(Parcel in) {
+                    return new RcsContactUceCapability(in);
+                }
+
+                @Override
+                public RcsContactUceCapability[] newArray(int size) {
+                    return new RcsContactUceCapability[size];
+                }
+            };
+
     /**
-     * Query for a capability
-     * @param type The capability flag to query.
-     * @return true if the capability flag specified is set, false otherwise.
+     * @return The mechanism used to get the capabilities.
      */
-    public boolean isCapable(@CapabilityFlag long type) {
-        return (mCapabilities & type) > 0;
+    public @CapabilityMechanism int getCapabilityMechanism() {
+        return mCapabilityMechanism;
     }
 
     /**
-     * @return true if the extension service tag is set, false otherwise.
-     */
-    public boolean isCapable(@NonNull String extensionTag) {
-        return mExtensionTags.contains(extensionTag);
-    }
-
-    /**
-     * @return An immutable list containing all of the extension tags that have been set as capable.
-     * @throws UnsupportedOperationException if this list is modified.
-     */
-    public @NonNull List<String> getCapableExtensionTags() {
-        return Collections.unmodifiableList(mExtensionTags);
-    }
-
-    /**
-     * Retrieves the {@link Uri} associated with the capability being queried.
+     * @return The feature tags present in the OPTIONS response from the network.
      * <p>
-     * This will typically be the contact {@link Uri} available via {@link #getContactUri()} unless
-     * a different service {@link Uri} was associated with this capability using
-     * {@link Builder#add(long, Uri)}.
-     *
-     * @return a String containing the {@link Uri} associated with the service tag or
-     * {@code null} if this capability is not set as capable.
-     * @see #isCapable(long)
+     * Note: this is only populated if {@link #getCapabilityMechanism} is
+     * {@link CAPABILITY_MECHANISM_OPTIONS}
      */
-    public @Nullable Uri getServiceUri(@CapabilityFlag long type) {
-        Uri result = mServiceMap.getOrDefault(type, null);
-        // If the capability is capable, but does not have a service URI associated, use the default
-        // contact URI.
-        if (result == null) {
-            return isCapable(type) ? getContactUri() : null;
+    public @NonNull List<String> getOptionsFeatureTags() {
+        if (mCapabilityMechanism != CAPABILITY_MECHANISM_OPTIONS) {
+            return Collections.emptyList();
         }
-        return result;
+        return Collections.unmodifiableList(mFeatureTags);
+    }
+
+    /**
+     * @return The tuple elements associated with the presence element portion of the PIDF document
+     * contained in the NOTIFY response from the network.
+     * <p>
+     * Note: this is only populated if {@link #getCapabilityMechanism} is
+     * {@link CAPABILITY_MECHANISM_PRESENCE}
+     */
+    public @NonNull List<RcsContactPresenceTuple> getPresenceTuples() {
+        if (mCapabilityMechanism != CAPABILITY_MECHANISM_PRESENCE) {
+            return Collections.emptyList();
+        }
+        return Collections.unmodifiableList(mPresenceTuples);
+    }
+
+    /**
+     * Get the RcsContactPresenceTuple associated with the given service id.
+     * @param serviceId The service id to get the presence tuple.
+     * @return The RcsContactPresenceTuple which has the given service id.
+     *
+     * <p>
+     * Note: this is only populated if {@link #getCapabilityMechanism} is
+     * {@link CAPABILITY_MECHANISM_PRESENCE}
+     */
+    public @Nullable RcsContactPresenceTuple getPresenceTuple(String serviceId) {
+        if (mCapabilityMechanism != CAPABILITY_MECHANISM_PRESENCE) {
+            return null;
+        }
+        for (RcsContactPresenceTuple tuple : mPresenceTuples) {
+            if (tuple.getServiceId().equals(serviceId)) {
+                return tuple;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * @return the source of the data that was used to populate the capabilities of the requested
+     * contact.
+     */
+    public @SourceType int getSourceType() {
+        return mSourceType;
+    }
+
+    /**
+     * @return the result of querying the capabilities of the requested contact.
+     */
+    public @RequestResult int getRequestResult() {
+        return mRequestResult;
     }
 
     /**
diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java
index a427d05..c2ddcea 100644
--- a/telephony/java/android/telephony/ims/RcsUceAdapter.java
+++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java
@@ -22,7 +22,6 @@
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.content.Context;
 import android.net.Uri;
 import android.os.Binder;
@@ -206,7 +205,7 @@
             public void onPublishStateChanged(int publishState) {
                 if (mLocalCallback == null) return;
 
-                long callingIdentity = Binder.clearCallingIdentity();
+                final long callingIdentity = Binder.clearCallingIdentity();
                 try {
                     mExecutor.execute(() -> mLocalCallback.onChanged(publishState));
                 } finally {
@@ -322,7 +321,7 @@
         IRcsUceControllerCallback internalCallback = new IRcsUceControllerCallback.Stub() {
             @Override
             public void onCapabilitiesReceived(List<RcsContactUceCapability> contactCapabilities) {
-                long callingIdentity = Binder.clearCallingIdentity();
+                final long callingIdentity = Binder.clearCallingIdentity();
                 try {
                     executor.execute(() ->
                             c.onCapabilitiesReceived(contactCapabilities));
@@ -332,7 +331,7 @@
             }
             @Override
             public void onError(int errorCode) {
-                long callingIdentity = Binder.clearCallingIdentity();
+                final long callingIdentity = Binder.clearCallingIdentity();
                 try {
                     executor.execute(() -> c.onError(errorCode));
                 } finally {
@@ -519,7 +518,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setUceSettingEnabled(boolean isEnabled) throws ImsException {
         IImsRcsController imsRcsController = getIImsRcsController();
diff --git a/telephony/java/android/telephony/ims/RegistrationManager.java b/telephony/java/android/telephony/ims/RegistrationManager.java
index e085dec..1a78e16 100644
--- a/telephony/java/android/telephony/ims/RegistrationManager.java
+++ b/telephony/java/android/telephony/ims/RegistrationManager.java
@@ -105,7 +105,7 @@
             public void onRegistered(int imsRadioTech) {
                 if (mLocalCallback == null) return;
 
-                long callingIdentity = Binder.clearCallingIdentity();
+                final long callingIdentity = Binder.clearCallingIdentity();
                 try {
                     mExecutor.execute(() ->
                             mLocalCallback.onRegistered(getAccessType(imsRadioTech)));
@@ -118,7 +118,7 @@
             public void onRegistering(int imsRadioTech) {
                 if (mLocalCallback == null) return;
 
-                long callingIdentity = Binder.clearCallingIdentity();
+                final long callingIdentity = Binder.clearCallingIdentity();
                 try {
                     mExecutor.execute(() ->
                             mLocalCallback.onRegistering(getAccessType(imsRadioTech)));
@@ -131,7 +131,7 @@
             public void onDeregistered(ImsReasonInfo info) {
                 if (mLocalCallback == null) return;
 
-                long callingIdentity = Binder.clearCallingIdentity();
+                final long callingIdentity = Binder.clearCallingIdentity();
                 try {
                     mExecutor.execute(() -> mLocalCallback.onUnregistered(info));
                 } finally {
@@ -143,7 +143,7 @@
             public void onTechnologyChangeFailed(int imsRadioTech, ImsReasonInfo info) {
                 if (mLocalCallback == null) return;
 
-                long callingIdentity = Binder.clearCallingIdentity();
+                final long callingIdentity = Binder.clearCallingIdentity();
                 try {
                     mExecutor.execute(() -> mLocalCallback.onTechnologyChangeFailed(
                             getAccessType(imsRadioTech), info));
@@ -155,7 +155,7 @@
             public void onSubscriberAssociatedUriChanged(Uri[] uris) {
                 if (mLocalCallback == null) return;
 
-                long callingIdentity = Binder.clearCallingIdentity();
+                final long callingIdentity = Binder.clearCallingIdentity();
                 try {
                     mExecutor.execute(() -> mLocalCallback.onSubscriberAssociatedUriChanged(uris));
                 } finally {
diff --git a/telephony/java/android/telephony/ims/SipDelegateManager.java b/telephony/java/android/telephony/ims/SipDelegateManager.java
new file mode 100644
index 0000000..82c8a9c
--- /dev/null
+++ b/telephony/java/android/telephony/ims/SipDelegateManager.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.telephony.ims;
+
+import android.Manifest;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.telephony.CarrierConfigManager;
+import android.telephony.TelephonyFrameworkInitializer;
+import android.telephony.ims.aidl.IImsRcsController;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Manages the creation and destruction of SipDelegates, which allow an IMS application to forward
+ * SIP messages for the purposes of providing a single IMS registration to the carrier's IMS network
+ * from multiple sources.
+ * @hide
+ */
+@SystemApi
+public class SipDelegateManager {
+
+    private final Context mContext;
+    private final int mSubId;
+
+    /**
+     * Only visible for testing. To instantiate an instance of this class, please use
+     * {@link ImsManager#getSipDelegateManager(int)}.
+     * @hide
+     */
+    @VisibleForTesting
+    public SipDelegateManager(Context context, int subId) {
+        mContext = context;
+        mSubId = subId;
+    }
+
+    /**
+     * Determines if creating SIP delegates are supported for the subscription specified.
+     * <p>
+     * If SIP delegates are not supported on this device or the carrier associated with this
+     * subscription, creating a SIP delegate will always fail, as this feature is not supported.
+     * @return true if this device supports creating a SIP delegate and the carrier associated with
+     * this subscription supports single registration, false if creating SIP delegates is not
+     * supported.
+     * @throws ImsException If the remote ImsService is not available for any reason or the
+     * subscription associated with this instance is no longer active. See
+     * {@link ImsException#getCode()} for more information.
+     *
+     * @see CarrierConfigManager.Ims#KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL
+     */
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public boolean isSupported() throws ImsException {
+        try {
+            IImsRcsController controller = getIImsRcsController();
+            if (controller == null) {
+                throw new ImsException("Telephony server is down",
+                        ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+            }
+            return controller.isSipDelegateSupported(mSubId);
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
+        } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(),
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    private IImsRcsController getIImsRcsController() {
+        IBinder binder = TelephonyFrameworkInitializer
+                .getTelephonyServiceManager()
+                .getTelephonyImsServiceRegisterer()
+                .get();
+        return IImsRcsController.Stub.asInterface(binder);
+    }
+}
diff --git a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
index e01ea91..6d25a09 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
@@ -53,6 +53,9 @@
     void registerUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c);
     void unregisterUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c);
 
+    // SipDelegateManager
+    boolean isSipDelegateSupported(int subId);
+
     // Internal commands that should not be made public
     void registerRcsFeatureCallback(int slotId, in IImsServiceFeatureCallback callback);
     void unregisterImsFeatureCallback(in IImsServiceFeatureCallback callback);
diff --git a/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl b/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl
index c956cbc..c6966b3 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl
@@ -21,6 +21,7 @@
 import android.telephony.ims.aidl.IImsConfig;
 import android.telephony.ims.aidl.IImsRegistration;
 import android.telephony.ims.aidl.IImsServiceControllerListener;
+import android.telephony.ims.aidl.ISipTransport;
 import android.telephony.ims.stub.ImsFeatureConfiguration;
 
 import com.android.ims.internal.IImsFeatureStatusCallback;
@@ -34,6 +35,7 @@
     IImsMmTelFeature createMmTelFeature(int slotId);
     IImsRcsFeature createRcsFeature(int slotId);
     ImsFeatureConfiguration querySupportedImsFeatures();
+    long getImsServiceCapabilities();
     void addFeatureStatusCallback(int slotId, int featureType, in IImsFeatureStatusCallback c);
     void removeFeatureStatusCallback(int slotId, int featureType, in IImsFeatureStatusCallback c);
     // Synchronous call to ensure the ImsService is ready before continuing with feature creation.
@@ -41,6 +43,7 @@
     void removeImsFeature(int slotId, int featureType);
     IImsConfig getConfig(int slotId);
     IImsRegistration getRegistration(int slotId);
+    ISipTransport getSipTransport(int slotId);
     oneway void enableIms(int slotId);
     oneway void disableIms(int slotId);
 }
diff --git a/media/java/android/media/tv/TvChannelInfo.aidl b/telephony/java/android/telephony/ims/aidl/ISipTransport.aidl
similarity index 73%
copy from media/java/android/media/tv/TvChannelInfo.aidl
copy to telephony/java/android/telephony/ims/aidl/ISipTransport.aidl
index 71cd0a7..fe23343 100644
--- a/media/java/android/media/tv/TvChannelInfo.aidl
+++ b/telephony/java/android/telephony/ims/aidl/ISipTransport.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * Copyright (c) 2020 The Android Open 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,6 +14,11 @@
  * limitations under the License.
  */
 
-package android.media.tv;
+package android.telephony.ims.aidl;
 
-parcelable TvChannelInfo;
+/**
+ * Interface for commands to the SIP Transport implementation.
+ * {@hide}
+ */
+interface ISipTransport {
+}
diff --git a/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java b/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java
index 1918bcb..87a5094 100644
--- a/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java
+++ b/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
@@ -34,7 +33,6 @@
  * {@hide}
  */
 @SystemApi
-@TestApi
 public final class CapabilityChangeRequest implements Parcelable {
 
     /**
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index e5779b3..b0a7b62 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -44,7 +44,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public abstract class ImsFeature {
 
     private static final String LOG_TAG = "ImsFeature";
@@ -62,19 +61,19 @@
      * CSFB for emergency calling.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public static final int FEATURE_EMERGENCY_MMTEL = 0;
     /**
      * This feature supports the MMTEL feature.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public static final int FEATURE_MMTEL = 1;
     /**
      * This feature supports the RCS feature.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public static final int FEATURE_RCS = 2;
     /**
      * Total number of features defined
@@ -124,7 +123,7 @@
      * during this time will result in an {@link IllegalStateException}.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public static final int STATE_UNAVAILABLE = 0;
     /**
      * This {@link ImsFeature} state is initializing and should not be communicated with. This will
@@ -132,14 +131,14 @@
      * during this time will result in an {@link IllegalStateException}.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public static final int STATE_INITIALIZING = 1;
     /**
      * This {@link ImsFeature} is ready for communication. Do not attempt to call framework methods
      * until {@see #onFeatureReady()} is called.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public static final int STATE_READY = 2;
 
     /**
@@ -169,13 +168,13 @@
      * The capability was unable to be changed.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public static final int CAPABILITY_ERROR_GENERIC = -1;
     /**
      * The capability was able to be changed.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public static final int CAPABILITY_SUCCESS = 0;
 
     /**
@@ -349,7 +348,7 @@
      * subscription IDs associated with this slot.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public final int getSlotIndex() {
         return mSlotId;
     }
@@ -359,7 +358,7 @@
      * or {@link #STATE_UNAVAILABLE} if it has not been updated  yet.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public @ImsState int getFeatureState() {
         synchronized (mLock) {
             return mState;
@@ -373,7 +372,7 @@
      * {@link #STATE_INITIALIZING}, or {@link #STATE_READY}.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public final void setFeatureState(@ImsState int state) {
         synchronized (mLock) {
             if (mState != state) {
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index d8a10eb..508d1a7 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -20,7 +20,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Bundle;
 import android.os.Message;
 import android.os.RemoteException;
@@ -60,7 +59,7 @@
     /**
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public MmTelFeature() {
     }
 
@@ -228,7 +227,7 @@
          * @see #removeCapabilities(int)
          * @hide
          */
-        @SystemApi @TestApi
+        @SystemApi
         public MmTelCapabilities() {
             super();
         }
@@ -237,7 +236,7 @@
          * @hide
          */
         @Deprecated
-        @SystemApi @TestApi
+        @SystemApi
         public MmTelCapabilities(Capabilities c) {
             mCapabilities = c.mCapabilities;
         }
@@ -248,7 +247,7 @@
          *                     bitfield.
          * @hide
          */
-        @SystemApi @TestApi
+        @SystemApi
         public MmTelCapabilities(@MmTelCapability int capabilities) {
             super(capabilities);
         }
@@ -288,7 +287,7 @@
          * @hide
          */
         @Override
-        @SystemApi @TestApi
+        @SystemApi
         public final void addCapabilities(@MmTelCapability int capabilities) {
             super.addCapabilities(capabilities);
         }
@@ -297,7 +296,7 @@
          * @hide
          */
         @Override
-        @SystemApi @TestApi
+        @SystemApi
         public final void removeCapabilities(@MmTelCapability int capability) {
             super.removeCapabilities(capability);
         }
@@ -375,14 +374,14 @@
      * outgoing call as IMS.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public static final int PROCESS_CALL_IMS = 0;
     /**
      * To be returned by {@link #shouldProcessCall(String[])} when the telephony framework should
      * not process the outgoing call as IMS and should instead use circuit switch.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public static final int PROCESS_CALL_CSFB = 1;
 
     /** @hide */
@@ -400,7 +399,7 @@
      * This is an optional boolean flag.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String EXTRA_IS_USSD = "android.telephony.ims.feature.extra.IS_USSD";
 
     /**
@@ -413,7 +412,7 @@
      * This is an optional boolean flag.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public static final String EXTRA_IS_UNKNOWN_CALL =
             "android.telephony.ims.feature.extra.IS_UNKNOWN_CALL";
 
@@ -453,7 +452,7 @@
      * @hide
      */
     @Override
-    @SystemApi @TestApi
+    @SystemApi
     public @NonNull final MmTelCapabilities queryCapabilityStatus() {
         return new MmTelCapabilities(super.queryCapabilityStatus());
     }
@@ -468,7 +467,7 @@
      * {@link #changeEnabledCapabilities}) should also show the status as disabled.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public final void notifyCapabilitiesStatusChanged(@NonNull MmTelCapabilities c) {
         if (c == null) {
             throw new IllegalArgumentException("MmTelCapabilities must be non-null!");
@@ -483,7 +482,7 @@
      * {@link #EXTRA_IS_UNKNOWN_CALL} and {@link #EXTRA_IS_USSD} above.
       * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public final void notifyIncomingCall(@NonNull ImsCallSessionImplBase c,
             @NonNull Bundle extras) {
         if (c == null || extras == null) {
@@ -509,7 +508,7 @@
      * @param reason The {@link ImsReasonInfo} call rejection reason.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public final void notifyRejectedCall(@NonNull ImsCallProfile callProfile,
             @NonNull ImsReasonInfo reason) {
         if (callProfile == null || reason == null) {
@@ -548,7 +547,7 @@
      * @link count the new Voice Message count.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public final void notifyVoiceMessageCountUpdate(int count) {
         IImsMmTelListener listener = getListener();
         if (listener == null) {
@@ -571,7 +570,7 @@
      * @hide
      */
     @Override
-    @SystemApi @TestApi
+    @SystemApi
     public boolean queryCapabilityConfiguration(@MmTelCapabilities.MmTelCapability int capability,
             @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
         // Base implementation - Override to provide functionality
@@ -592,7 +591,7 @@
      * * @hide
      */
     @Override
-    @SystemApi @TestApi
+    @SystemApi
     public void changeEnabledCapabilities(@NonNull CapabilityChangeRequest request,
             @NonNull CapabilityCallbackProxy c) {
         // Base implementation, no-op
@@ -617,7 +616,7 @@
      * @return a {@link ImsCallProfile} object
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public @Nullable ImsCallProfile createCallProfile(int callSessionType, int callType) {
         // Base Implementation - Should be overridden
         return null;
@@ -640,7 +639,7 @@
      * @param profile a call profile to make the call
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public @Nullable ImsCallSessionImplBase createCallSession(@NonNull ImsCallProfile profile) {
         // Base Implementation - Should be overridden
         return null;
@@ -659,7 +658,7 @@
      *        call will be placed over IMS or via CSFB.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public @ProcessCallResult int shouldProcessCall(@NonNull String[] numbers) {
         return PROCESS_CALL_IMS;
     }
@@ -694,7 +693,7 @@
      * configuration.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public @NonNull ImsUtImplBase getUt() {
         // Base Implementation - Should be overridden
         return new ImsUtImplBase();
@@ -705,7 +704,7 @@
      * calls that support it.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public @NonNull ImsEcbmImplBase getEcbm() {
         // Base Implementation - Should be overridden
         return new ImsEcbmImplBase();
@@ -716,7 +715,7 @@
      * package processing for multi-endpoint.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public @NonNull ImsMultiEndpointImplBase getMultiEndpoint() {
         // Base Implementation - Should be overridden
         return new ImsMultiEndpointImplBase();
@@ -744,7 +743,7 @@
      * }
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public void setUiTtyMode(int mode, @Nullable Message onCompleteMessage) {
         // Base Implementation - Should be overridden
     }
@@ -780,7 +779,7 @@
      * Provider.
      * @hide
      */
-    @SystemApi @TestApi
+    @SystemApi
     public @NonNull ImsSmsImplBase getSmsImplementation() {
         return new ImsSmsImplBase();
     }
@@ -794,7 +793,7 @@
      * @hide
      */
     @Override
-    @SystemApi @TestApi
+    @SystemApi
     public void onFeatureRemoved() {
         // Base Implementation - Should be overridden
     }
@@ -804,7 +803,7 @@
      * @hide
      */
     @Override
-    @SystemApi @TestApi
+    @SystemApi
     public void onFeatureReady() {
         // Base Implementation - Should be overridden
     }
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
index 98b0bcf..b8ae146 100644
--- a/telephony/java/android/telephony/ims/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -20,7 +20,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.net.Uri;
 import android.os.RemoteException;
 import android.telephony.ims.RcsContactUceCapability;
@@ -50,7 +49,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public class RcsFeature extends ImsFeature {
 
     private static final String LOG_TAG = "RcsFeature";
diff --git a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
index 8f738d2..1cebdd5 100644
--- a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Message;
 import android.os.RemoteException;
 import android.telephony.ims.ImsCallProfile;
@@ -39,7 +38,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 // DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
 // will break other implementations of ImsCallSession maintained by other ImsServices.
 public class ImsCallSessionImplBase implements AutoCloseable {
diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
index 4ef44d3..e0290a5 100644
--- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
@@ -19,7 +19,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.content.Context;
 import android.os.PersistableBundle;
 import android.os.RemoteException;
@@ -51,7 +50,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public class ImsConfigImplBase {
 
     private static final String TAG = "ImsConfigImplBase";
diff --git a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
index 4a3a2ea..06c35ea 100644
--- a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
@@ -17,7 +17,6 @@
 package android.telephony.ims.stub;
 
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.RemoteException;
 import android.util.Log;
 
@@ -34,7 +33,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public class ImsEcbmImplBase {
     private static final String TAG = "ImsEcbmImplBase";
 
diff --git a/telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.java b/telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.java
index 4e7307e..cd9ebbf 100644
--- a/telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.java
+++ b/telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.ims.feature.ImsFeature;
@@ -36,7 +35,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class ImsFeatureConfiguration implements Parcelable {
 
     public static final class FeatureSlotPair {
diff --git a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
index 0ae5bba..d002903 100644
--- a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
@@ -17,7 +17,6 @@
 package android.telephony.ims.stub;
 
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.RemoteException;
 import android.telephony.ims.ImsExternalCallState;
 import android.util.Log;
@@ -38,7 +37,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public class ImsMultiEndpointImplBase {
     private static final String TAG = "MultiEndpointImplBase";
 
diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index 2cdf70e..12abdd1 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -18,7 +18,6 @@
 
 import android.annotation.IntDef;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.net.Uri;
 import android.os.RemoteException;
 import android.telephony.ims.ImsReasonInfo;
@@ -40,7 +39,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public class ImsRegistrationImplBase {
 
     private static final String LOG_TAG = "ImsRegistrationImplBase";
diff --git a/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java b/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
index a9a33c0..2783e29 100644
--- a/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
@@ -19,7 +19,6 @@
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.RemoteException;
 import android.telephony.SmsManager;
 import android.telephony.SmsMessage;
@@ -38,7 +37,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public class ImsSmsImplBase {
     private static final String LOG_TAG = "SmsImplBase";
 
diff --git a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
index 8564f7a..f5219d5 100644
--- a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
@@ -20,7 +20,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.telephony.ims.ImsUtListener;
@@ -40,7 +39,6 @@
 // DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
 // will break other implementations of ImsUt maintained by other ImsServices.
 @SystemApi
-@TestApi
 public class ImsUtImplBase {
     /**
      * Bar all incoming calls. (See 3GPP TS 24.611)
diff --git a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
new file mode 100644
index 0000000..b2b2914
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.telephony.ims.stub;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.telephony.ims.aidl.ISipTransport;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Manages the creation and destruction of SipDelegates in order to proxy SIP traffic to other
+ * IMS applications in order to support IMS single registration.
+ * @hide
+ */
+@SystemApi
+public class SipTransportImplBase {
+
+    private final Executor mBinderExecutor;
+    private final ISipTransport mSipTransportImpl = new ISipTransport.Stub() {
+
+    };
+
+    /**
+     * Create an implementation of SipTransportImplBase.
+     *
+     * @param executor The executor that remote calls from the framework should be called on.
+     */
+    public SipTransportImplBase(@NonNull Executor executor) {
+        if (executor == null) {
+            throw new IllegalArgumentException("executor must not be null");
+        }
+
+        mBinderExecutor = executor;
+    }
+
+    /**
+     * @return The IInterface used by the framework.
+     * @hide
+     */
+    public ISipTransport getBinder() {
+        return mSipTransportImpl;
+    }
+}
diff --git a/telephony/java/android/telephony/mbms/DownloadRequest.java b/telephony/java/android/telephony/mbms/DownloadRequest.java
index ac258cd..eb59f87 100644
--- a/telephony/java/android/telephony/mbms/DownloadRequest.java
+++ b/telephony/java/android/telephony/mbms/DownloadRequest.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Parcel;
@@ -186,7 +185,6 @@
          * @hide
          */
         @SystemApi
-        @TestApi
         public Builder setServiceId(String serviceId) {
             fileServiceId = serviceId;
             return this;
diff --git a/telephony/java/android/telephony/mbms/FileInfo.java b/telephony/java/android/telephony/mbms/FileInfo.java
index ada2872..e52b2ce 100644
--- a/telephony/java/android/telephony/mbms/FileInfo.java
+++ b/telephony/java/android/telephony/mbms/FileInfo.java
@@ -17,7 +17,6 @@
 package android.telephony.mbms;
 
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -50,7 +49,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public FileInfo(Uri uri, String mimeType) {
         this.uri = uri;
         this.mimeType = mimeType;
diff --git a/telephony/java/android/telephony/mbms/FileServiceInfo.java b/telephony/java/android/telephony/mbms/FileServiceInfo.java
index 8c79ab6..8777e7f 100644
--- a/telephony/java/android/telephony/mbms/FileServiceInfo.java
+++ b/telephony/java/android/telephony/mbms/FileServiceInfo.java
@@ -17,7 +17,6 @@
 package android.telephony.mbms;
 
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -36,7 +35,6 @@
 
     /** @hide */
     @SystemApi
-    @TestApi
     public FileServiceInfo(Map<Locale, String> newNames, String newClassName,
             List<Locale> newLocales, String newServiceId, Date start, Date end,
             List<FileInfo> newFiles) {
diff --git a/telephony/java/android/telephony/mbms/InternalDownloadProgressListener.java b/telephony/java/android/telephony/mbms/InternalDownloadProgressListener.java
index 6a13569..a413ef8 100644
--- a/telephony/java/android/telephony/mbms/InternalDownloadProgressListener.java
+++ b/telephony/java/android/telephony/mbms/InternalDownloadProgressListener.java
@@ -43,7 +43,7 @@
             return;
         }
 
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             mExecutor.execute(new Runnable() {
                 @Override
diff --git a/telephony/java/android/telephony/mbms/InternalDownloadSessionCallback.java b/telephony/java/android/telephony/mbms/InternalDownloadSessionCallback.java
index ce32477..67539a0 100644
--- a/telephony/java/android/telephony/mbms/InternalDownloadSessionCallback.java
+++ b/telephony/java/android/telephony/mbms/InternalDownloadSessionCallback.java
@@ -40,7 +40,7 @@
             return;
         }
 
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             mExecutor.execute(new Runnable() {
                 @Override
@@ -59,7 +59,7 @@
             return;
         }
 
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             mExecutor.execute(new Runnable() {
                 @Override
@@ -78,7 +78,7 @@
             return;
         }
 
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             mExecutor.execute(new Runnable() {
                 @Override
diff --git a/telephony/java/android/telephony/mbms/InternalDownloadStatusListener.java b/telephony/java/android/telephony/mbms/InternalDownloadStatusListener.java
index 87163ff..ce96a8f 100644
--- a/telephony/java/android/telephony/mbms/InternalDownloadStatusListener.java
+++ b/telephony/java/android/telephony/mbms/InternalDownloadStatusListener.java
@@ -42,7 +42,7 @@
             return;
         }
 
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             mExecutor.execute(new Runnable() {
                 @Override
diff --git a/telephony/java/android/telephony/mbms/InternalGroupCallCallback.java b/telephony/java/android/telephony/mbms/InternalGroupCallCallback.java
index c7600b6..5e1f1f1 100644
--- a/telephony/java/android/telephony/mbms/InternalGroupCallCallback.java
+++ b/telephony/java/android/telephony/mbms/InternalGroupCallCallback.java
@@ -38,7 +38,7 @@
             return;
         }
 
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             mExecutor.execute(new Runnable() {
                 @Override
@@ -57,7 +57,7 @@
             return;
         }
 
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             mExecutor.execute(new Runnable() {
                 @Override
@@ -76,7 +76,7 @@
             return;
         }
 
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             mExecutor.execute(new Runnable() {
                 @Override
diff --git a/telephony/java/android/telephony/mbms/InternalGroupCallSessionCallback.java b/telephony/java/android/telephony/mbms/InternalGroupCallSessionCallback.java
index 0b7667e..ca4190c 100644
--- a/telephony/java/android/telephony/mbms/InternalGroupCallSessionCallback.java
+++ b/telephony/java/android/telephony/mbms/InternalGroupCallSessionCallback.java
@@ -39,7 +39,7 @@
             return;
         }
 
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             mExecutor.execute(new Runnable() {
                 @Override
@@ -58,7 +58,7 @@
             return;
         }
 
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             mExecutor.execute(new Runnable() {
                 @Override
@@ -77,7 +77,7 @@
             return;
         }
 
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             mExecutor.execute(new Runnable() {
                 @Override
@@ -96,7 +96,7 @@
             return;
         }
 
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             mExecutor.execute(new Runnable() {
                 @Override
diff --git a/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java b/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java
index 3a4ed08..d62add1 100644
--- a/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java
+++ b/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java
@@ -39,7 +39,7 @@
             return;
         }
 
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             mExecutor.execute(new Runnable() {
                 @Override
@@ -58,7 +58,7 @@
             return;
         }
 
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             mExecutor.execute(new Runnable() {
                 @Override
@@ -77,7 +77,7 @@
             return;
         }
 
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             mExecutor.execute(new Runnable() {
                 @Override
@@ -96,7 +96,7 @@
             return;
         }
 
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             mExecutor.execute(new Runnable() {
                 @Override
@@ -115,7 +115,7 @@
             return;
         }
 
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             mExecutor.execute(new Runnable() {
                 @Override
diff --git a/telephony/java/android/telephony/mbms/InternalStreamingSessionCallback.java b/telephony/java/android/telephony/mbms/InternalStreamingSessionCallback.java
index 2eb280e..f4ee4dc 100644
--- a/telephony/java/android/telephony/mbms/InternalStreamingSessionCallback.java
+++ b/telephony/java/android/telephony/mbms/InternalStreamingSessionCallback.java
@@ -40,7 +40,7 @@
             return;
         }
 
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             mExecutor.execute(new Runnable() {
                 @Override
@@ -60,7 +60,7 @@
             return;
         }
 
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             mExecutor.execute(new Runnable() {
                 @Override
@@ -79,7 +79,7 @@
             return;
         }
 
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
         try {
             mExecutor.execute(new Runnable() {
                 @Override
diff --git a/telephony/java/android/telephony/mbms/StreamingServiceInfo.java b/telephony/java/android/telephony/mbms/StreamingServiceInfo.java
index 8ad1d89..316e8656 100644
--- a/telephony/java/android/telephony/mbms/StreamingServiceInfo.java
+++ b/telephony/java/android/telephony/mbms/StreamingServiceInfo.java
@@ -17,7 +17,6 @@
 package android.telephony.mbms;
 
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -42,7 +41,6 @@
      * @hide
      */
     @SystemApi
-    @TestApi
     public StreamingServiceInfo(Map<Locale, String> names, String className,
             List<Locale> locales, String serviceId, Date start, Date end) {
         super(names, className, locales, serviceId, start, end);
diff --git a/telephony/java/android/telephony/mbms/UriPathPair.java b/telephony/java/android/telephony/mbms/UriPathPair.java
index f53d7e0..9258919 100644
--- a/telephony/java/android/telephony/mbms/UriPathPair.java
+++ b/telephony/java/android/telephony/mbms/UriPathPair.java
@@ -17,7 +17,6 @@
 package android.telephony.mbms;
 
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.content.ContentResolver;
 import android.net.Uri;
 import android.os.Parcel;
@@ -30,7 +29,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public final class UriPathPair implements Parcelable {
     private final Uri mFilePathUri;
     private final Uri mContentUri;
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
index 3053ea0..ffc1d2e 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.content.Intent;
 import android.os.Binder;
 import android.os.IBinder;
@@ -45,7 +44,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
     private final Map<IBinder, DownloadStatusListener> mDownloadStatusListenerBinderMap =
             new HashMap<>();
@@ -576,7 +574,6 @@
     // Following two methods exist to workaround b/124210145
     /** @hide */
     @SystemApi
-    @TestApi
     @Override
     public android.os.IBinder asBinder() {
         return super.asBinder();
@@ -584,7 +581,6 @@
 
     /** @hide */
     @SystemApi
-    @TestApi
     @Override
     public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply,
             int flags) throws RemoteException {
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java
index 1335b52..e5b18bb 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.app.Service;
 import android.content.Intent;
 import android.os.Binder;
@@ -41,7 +40,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public class MbmsGroupCallServiceBase extends Service {
     private final IBinder mInterface = new Stub() {
         @Override
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
index cced447..e169b16 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
@@ -18,7 +18,6 @@
 
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Binder;
@@ -39,7 +38,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
     /**
      * Initialize streaming service for this app and subId, registering the listener.
@@ -299,7 +297,6 @@
     // Following two methods exist to workaround b/124210145
     /** @hide */
     @SystemApi
-    @TestApi
     @Override
     public android.os.IBinder asBinder() {
         return super.asBinder();
@@ -307,7 +304,6 @@
 
     /** @hide */
     @SystemApi
-    @TestApi
     @Override
     public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply,
             int flags) throws RemoteException {
diff --git a/telephony/java/android/telephony/mbms/vendor/VendorUtils.java b/telephony/java/android/telephony/mbms/vendor/VendorUtils.java
index f1cac8c..a43f122 100644
--- a/telephony/java/android/telephony/mbms/vendor/VendorUtils.java
+++ b/telephony/java/android/telephony/mbms/vendor/VendorUtils.java
@@ -17,7 +17,6 @@
 package android.telephony.mbms.vendor;
 
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -35,7 +34,6 @@
  * @hide
  */
 @SystemApi
-@TestApi
 public class VendorUtils {
 
     /**
diff --git a/telephony/java/com/android/ims/ImsFeatureContainer.java b/telephony/java/com/android/ims/ImsFeatureContainer.java
index b259679..80c1d43 100644
--- a/telephony/java/com/android/ims/ImsFeatureContainer.java
+++ b/telephony/java/com/android/ims/ImsFeatureContainer.java
@@ -17,12 +17,14 @@
 package com.android.ims;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.ims.ImsService;
 import android.telephony.ims.aidl.IImsConfig;
 import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.aidl.ISipTransport;
 import android.telephony.ims.feature.ImsFeature;
 
 import java.util.Objects;
@@ -49,6 +51,11 @@
     public final IImsRegistration imsRegistration;
 
     /**
+     * An optional interface containing the SIP transport implementation from the ImsService.
+     */
+    public final ISipTransport sipTransport;
+
+    /**
      * State of the feature that is being tracked.
      */
     private @ImsFeature.ImsState int mState = ImsFeature.STATE_UNAVAILABLE;
@@ -66,10 +73,11 @@
      * @param initialCaps The initial capabilities that the ImsService supports.
      */
     public ImsFeatureContainer(@NonNull IBinder iFace, @NonNull IImsConfig iConfig,
-            @NonNull IImsRegistration iReg, long initialCaps) {
+            @NonNull IImsRegistration iReg, @Nullable ISipTransport transport, long initialCaps) {
         imsFeature = iFace;
         imsConfig = iConfig;
         imsRegistration = iReg;
+        sipTransport = transport;
         mCapabilities = initialCaps;
     }
 
@@ -80,6 +88,7 @@
         imsFeature = in.readStrongBinder();
         imsConfig = IImsConfig.Stub.asInterface(in.readStrongBinder());
         imsRegistration = IImsRegistration.Stub.asInterface(in.readStrongBinder());
+        sipTransport = ISipTransport.Stub.asInterface(in.readStrongBinder());
         mState = in.readInt();
         mCapabilities = in.readLong();
     }
@@ -123,13 +132,15 @@
         return imsFeature.equals(that.imsFeature) &&
                 imsConfig.equals(that.imsConfig) &&
                 imsRegistration.equals(that.imsRegistration) &&
+                sipTransport.equals(that.sipTransport) &&
                 mState == that.getState() &&
                 mCapabilities == that.getCapabilities();
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(imsFeature, imsConfig, imsRegistration, mState, mCapabilities);
+        return Objects.hash(imsFeature, imsConfig, imsRegistration, sipTransport, mState,
+                mCapabilities);
     }
 
     @Override
@@ -138,6 +149,7 @@
                 "imsFeature=" + imsFeature +
                 ", imsConfig=" + imsConfig +
                 ", imsRegistration=" + imsRegistration +
+                ", sipTransport=" + sipTransport +
                 ", state=" + ImsFeature.STATE_LOG_MAP.get(mState) +
                 ", capabilities = " + ImsService.getCapabilitiesString(mCapabilities) +
                 '}';
@@ -153,6 +165,7 @@
         dest.writeStrongBinder(imsFeature);
         dest.writeStrongInterface(imsConfig);
         dest.writeStrongInterface(imsRegistration);
+        dest.writeStrongInterface(sipTransport);
         dest.writeInt(mState);
         dest.writeLong(mCapabilities);
     }
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 53069a1..0d8351d 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2164,21 +2164,9 @@
      */
     String getMmsUAProfUrl(int subId);
 
-    /**
-     * Set allowing mobile data during voice call.
-     */
-    boolean setDataAllowedDuringVoiceCall(int subId, boolean allow);
+    void setMobileDataPolicyEnabledStatus(int subscriptionId, int policy, boolean enabled);
 
-    /**
-     * Check whether data is allowed during voice call. Note this is for dual sim device that
-     * data might be disabled on non-default data subscription but explicitly turned on by settings.
-     */
-    boolean isDataAllowedInVoiceCall(int subId);
-
-    /**
-     * Set whether a subscription always allows MMS connection.
-     */
-    boolean setAlwaysAllowMmsData(int subId, boolean allow);
+    boolean isMobileDataPolicyEnabled(int subscriptionId, int policy);
 
     /**
      * Command line command to enable or disable handling of CEP data for test purposes.
diff --git a/test-mock/api/test-current.txt b/test-mock/api/test-current.txt
index 32ca250..79d746a 100644
--- a/test-mock/api/test-current.txt
+++ b/test-mock/api/test-current.txt
@@ -6,21 +6,12 @@
   }
 
   @Deprecated public class MockPackageManager extends android.content.pm.PackageManager {
-    method public void addOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
-    method public boolean arePermissionsIndividuallyControlled();
-    method public String getDefaultBrowserPackageNameAsUser(int);
     method public int getInstallReason(String, android.os.UserHandle);
     method public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplicationsAsUser(int, int);
-    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 removeOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
-    method public void revokeRuntimePermission(String, String, android.os.UserHandle);
-    method public void updatePermissionFlags(String, String, int, int, android.os.UserHandle);
   }
 
 }
diff --git a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java
index 475305e..66e44e1 100644
--- a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java
+++ b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java
@@ -32,7 +32,7 @@
 
 
 
-    // Code below generated by codegen v1.0.15.
+    // Code below generated by codegen v1.0.16.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
@@ -98,8 +98,8 @@
     };
 
     @DataClass.Generated(
-            time = 1582685650576L,
-            codegenVersion = "1.0.15",
+            time = 1601950882280L,
+            codegenVersion = "1.0.16",
             sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java",
             inputSignatures = "private  int mBaseData\nclass HierrarchicalDataClassBase extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genSetters=true)")
     @Deprecated
diff --git a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java
index 150b324..643abd8 100644
--- a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java
+++ b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java
@@ -46,7 +46,7 @@
 
 
 
-    // Code below generated by codegen v1.0.15.
+    // Code below generated by codegen v1.0.16.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
@@ -120,8 +120,8 @@
     };
 
     @DataClass.Generated(
-            time = 1582685651560L,
-            codegenVersion = "1.0.15",
+            time = 1601950883222L,
+            codegenVersion = "1.0.16",
             sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java",
             inputSignatures = "private @android.annotation.NonNull java.lang.String mChildData\nclass HierrarchicalDataClassChild extends com.android.codegentest.HierrarchicalDataClassBase implements []\n@com.android.internal.util.DataClass(genParcelable=true, genConstructor=false, genSetters=true)")
     @Deprecated
diff --git a/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java b/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java
index 3087156..5e33a33 100644
--- a/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java
+++ b/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java
@@ -54,7 +54,7 @@
 
 
 
-    // Code below generated by codegen v1.0.15.
+    // Code below generated by codegen v1.0.16.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
@@ -412,8 +412,8 @@
     }
 
     @DataClass.Generated(
-            time = 1582685649678L,
-            codegenVersion = "1.0.15",
+            time = 1601950881327L,
+            codegenVersion = "1.0.16",
             sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java",
             inputSignatures = " @android.annotation.NonNull java.lang.String[] mStringArray\n @android.annotation.NonNull int[] mIntArray\n @android.annotation.NonNull java.util.List<java.lang.String> mStringList\n @android.annotation.NonNull java.util.Map<java.lang.String,com.android.codegentest.SampleWithCustomBuilder> mMap\n @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.String> mStringMap\n @android.annotation.NonNull android.util.SparseArray<com.android.codegentest.SampleWithCustomBuilder> mSparseArray\n @android.annotation.NonNull android.util.SparseIntArray mSparseIntArray\n @java.lang.SuppressWarnings({\"WeakerAccess\"}) @android.annotation.Nullable java.lang.Boolean mNullableBoolean\nclass ParcelAllTheThingsDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)")
     @Deprecated
diff --git a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java
index 8d421bf..9e5b1a9 100644
--- a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java
+++ b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java
@@ -342,7 +342,7 @@
 
 
 
-    // Code below generated by codegen v1.0.15.
+    // Code below generated by codegen v1.0.16.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
@@ -1872,8 +1872,8 @@
     }
 
     @DataClass.Generated(
-            time = 1582685647656L,
-            codegenVersion = "1.0.15",
+            time = 1601950879293L,
+            codegenVersion = "1.0.16",
             sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java",
             inputSignatures = "public static final  java.lang.String STATE_NAME_UNDEFINED\npublic static final  java.lang.String STATE_NAME_ON\npublic static final  java.lang.String STATE_NAME_OFF\npublic static final  int STATE_UNDEFINED\npublic static final  int STATE_ON\npublic static final  int STATE_OFF\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_AUGMENTED_REQUEST\nprivate  int mNum\nprivate  int mNum2\nprivate  int mNum4\nprivate @android.annotation.Nullable java.lang.String mName\nprivate @android.annotation.NonNull java.lang.String mName2\nprivate @android.annotation.NonNull java.lang.String mName4\nprivate @android.annotation.Nullable android.view.accessibility.AccessibilityNodeInfo mOtherParcelable\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.codegentest.MyDateParcelling.class) @android.annotation.NonNull java.util.Date mDate\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForPattern.class) @android.annotation.NonNull java.util.regex.Pattern mPattern\nprivate @android.annotation.NonNull java.util.List<android.net.LinkAddress> mLinkAddresses2\nprivate @com.android.internal.util.DataClass.PluralOf(\"linkAddress\") @android.annotation.NonNull java.util.ArrayList<android.net.LinkAddress> mLinkAddresses\nprivate @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses4\nprivate @com.android.codegentest.SampleDataClass.StateName @android.annotation.NonNull java.lang.String mStateName\nprivate @com.android.codegentest.SampleDataClass.RequestFlags int mFlags\nprivate @com.android.codegentest.SampleDataClass.State int mState\npublic @android.annotation.NonNull java.lang.CharSequence charSeq\nprivate final @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses5\nprivate transient  android.net.LinkAddress[] mLinkAddresses6\ntransient  int[] mTmpStorage\nprivate @android.annotation.StringRes int mStringRes\nprivate @android.annotation.IntRange(from=0L, to=6L) int mDayOfWeek\nprivate @android.annotation.Size(2L) @android.annotation.NonNull @com.android.internal.util.DataClass.Each @android.annotation.FloatRange(from=0.0) float[] mCoords\nprivate static  java.lang.String defaultName4()\nprivate  int[] lazyInitTmpStorage()\npublic  android.net.LinkAddress[] getLinkAddresses4()\nprivate  boolean patternEquals(java.util.regex.Pattern)\nprivate  int patternHashCode()\nprivate  void onConstructed()\npublic  void dump(java.io.PrintWriter)\nclass SampleDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=true, genEqualsHashCode=true, genToString=true, genForEachField=true, genSetters=true)")
     @Deprecated
diff --git a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java
index d9fe1fd..735f704 100644
--- a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java
+++ b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java
@@ -85,7 +85,7 @@
 
 
 
-    // Code below generated by codegen v1.0.15.
+    // Code below generated by codegen v1.0.16.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
@@ -253,10 +253,10 @@
     }
 
     @DataClass.Generated(
-            time = 1582685648622L,
-            codegenVersion = "1.0.15",
+            time = 1601950880290L,
+            codegenVersion = "1.0.16",
             sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java",
-            inputSignatures = "  long delayAmount\n @android.annotation.NonNull java.util.concurrent.TimeUnit delayUnit\n  long creationTimestamp\nprivate static  java.util.concurrent.TimeUnit unparcelDelayUnit(android.os.Parcel)\nprivate  void parcelDelayUnit(android.os.Parcel,int)\nclass SampleWithCustomBuilder extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)\nabstract  com.android.codegentest.SampleWithCustomBuilder.Builder setDelayAmount(long)\npublic abstract  com.android.codegentest.SampleWithCustomBuilder.Builder setDelayUnit(java.util.concurrent.TimeUnit)\npublic  com.android.codegentest.SampleWithCustomBuilder.Builder setDelay(long,java.util.concurrent.TimeUnit)\nclass BaseBuilder extends java.lang.Object implements []")
+            inputSignatures = "  long delayAmount\n @android.annotation.NonNull java.util.concurrent.TimeUnit delayUnit\n  long creationTimestamp\nprivate static  java.util.concurrent.TimeUnit unparcelDelayUnit(android.os.Parcel)\nprivate  void parcelDelayUnit(android.os.Parcel,int)\nclass SampleWithCustomBuilder extends java.lang.Object implements [android.os.Parcelable]\nabstract  com.android.codegentest.SampleWithCustomBuilder.Builder setDelayAmount(long)\npublic abstract  com.android.codegentest.SampleWithCustomBuilder.Builder setDelayUnit(java.util.concurrent.TimeUnit)\npublic  com.android.codegentest.SampleWithCustomBuilder.Builder setDelay(long,java.util.concurrent.TimeUnit)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)\nabstract  com.android.codegentest.SampleWithCustomBuilder.Builder setDelayAmount(long)\npublic abstract  com.android.codegentest.SampleWithCustomBuilder.Builder setDelayUnit(java.util.concurrent.TimeUnit)\npublic  com.android.codegentest.SampleWithCustomBuilder.Builder setDelay(long,java.util.concurrent.TimeUnit)\nclass BaseBuilder extends java.lang.Object implements []")
     @Deprecated
     private void __metadata() {}
 
diff --git a/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java b/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java
index f98d7b0..5160121 100644
--- a/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java
+++ b/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java
@@ -36,7 +36,7 @@
 
 
 
-        // Code below generated by codegen v1.0.15.
+        // Code below generated by codegen v1.0.16.
         //
         // DO NOT MODIFY!
         // CHECKSTYLE:OFF Generated code
@@ -135,8 +135,8 @@
         };
 
         @DataClass.Generated(
-                time = 1582685653406L,
-                codegenVersion = "1.0.15",
+                time = 1601950885147L,
+                codegenVersion = "1.0.16",
                 sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java",
                 inputSignatures = " @android.annotation.NonNull java.lang.String mBar\nclass NestedDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true)")
         @Deprecated
@@ -160,7 +160,7 @@
 
 
 
-            // Code below generated by codegen v1.0.15.
+            // Code below generated by codegen v1.0.16.
             //
             // DO NOT MODIFY!
             // CHECKSTYLE:OFF Generated code
@@ -259,8 +259,8 @@
             };
 
             @DataClass.Generated(
-                    time = 1582685653415L,
-                    codegenVersion = "1.0.15",
+                    time = 1601950885156L,
+                    codegenVersion = "1.0.16",
                     sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java",
                     inputSignatures = " @android.annotation.NonNull long mBaz2\nclass NestedDataClass3 extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true)")
             @Deprecated
@@ -274,7 +274,7 @@
 
 
 
-        // Code below generated by codegen v1.0.15.
+        // Code below generated by codegen v1.0.16.
         //
         // DO NOT MODIFY!
         // CHECKSTYLE:OFF Generated code
@@ -373,8 +373,8 @@
         };
 
         @DataClass.Generated(
-                time = 1582685653420L,
-                codegenVersion = "1.0.15",
+                time = 1601950885161L,
+                codegenVersion = "1.0.16",
                 sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java",
                 inputSignatures = " @android.annotation.NonNull java.lang.String mBaz\nclass NestedDataClass2 extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true)")
         @Deprecated
diff --git a/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java b/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java
index 6b4fc2f..5417c11 100644
--- a/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java
+++ b/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java
@@ -16,9 +16,13 @@
 package com.android.codegentest;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 
 import com.android.internal.util.DataClass;
 
+import java.util.List;
+import java.util.Set;
+
 /**
  * Test for some false positive pitfalls for
  * {@link android.processor.staledataclass.StaleDataclassProcessor}
@@ -46,12 +50,16 @@
     /* Initializers should be ignored */
     {}
 
+    /* Wildcard type argument should work correctly */
+    @Nullable
+    private List<Set<?>> mUsesWildcards = null;
+
     /** Unrelated methods should be noted, without triggering staleness false positives */
     public @NonNull String someMethod(int param) { return null; }
 
 
 
-    // Code below generated by codegen v1.0.15.
+    // Code below generated by codegen v1.0.16.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
@@ -64,11 +72,22 @@
     //@formatter:off
 
 
+    @DataClass.Generated.Member
+    public @Nullable List<Set<?>> getUsesWildcards() {
+        return mUsesWildcards;
+    }
+
+    @DataClass.Generated.Member
+    public StaleDataclassDetectorFalsePositivesTest setUsesWildcards(@NonNull List<Set<?>> value) {
+        mUsesWildcards = value;
+        return this;
+    }
+
     @DataClass.Generated(
-            time = 1582685652436L,
-            codegenVersion = "1.0.15",
+            time = 1601950884160L,
+            codegenVersion = "1.0.16",
             sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java",
-            inputSignatures = "public @android.annotation.NonNull java.lang.String someMethod(int)\nclass StaleDataclassDetectorFalsePositivesTest extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false)")
+            inputSignatures = "private @android.annotation.Nullable java.util.List<java.util.Set<?>> mUsesWildcards\npublic @android.annotation.NonNull java.lang.String someMethod(int)\nclass StaleDataclassDetectorFalsePositivesTest extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp
index d430db5..3b9bec9 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -54,4 +54,18 @@
         "launcher-aosp-tapl",
         "platform-test-annotations",
     ],
+}
+
+java_library {
+    name: "wm-flicker-common-assertions",
+    platform_apis: true,
+    srcs: ["src/**/*Assertions.java", "src/**/*Assertions.kt"],
+    exclude_srcs: [
+        "**/helpers/*",
+    ],
+    static_libs: [
+        "flickerlib",
+        "truth-prebuilt",
+        "app-helpers-core"
+    ],
 }
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index 69b1187..8457039 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -21,13 +21,17 @@
 import com.android.server.wm.flicker.dsl.WmAssertion
 import com.android.server.wm.flicker.helpers.WindowUtils
 
+const val NAVIGATION_BAR_WINDOW_TITLE = "NavigationBar"
+const val STATUS_BAR_WINDOW_TITLE = "StatusBar"
+const val DOCKED_STACK_DIVIDER = "DockedStackDivider"
+
 @JvmOverloads
 fun WmAssertion.statusBarWindowIsAlwaysVisible(
     bugId: Int = 0,
     enabled: Boolean = bugId == 0
 ) {
     all("statusBarWindowIsAlwaysVisible", enabled, bugId) {
-        this.showsAboveAppWindow(FlickerTestBase.STATUS_BAR_WINDOW_TITLE)
+        this.showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE)
     }
 }
 
@@ -37,7 +41,7 @@
     enabled: Boolean = bugId == 0
 ) {
     all("navBarWindowIsAlwaysVisible", enabled, bugId) {
-        this.showsAboveAppWindow(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE)
+        this.showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE)
     }
 }
 
@@ -77,7 +81,7 @@
     enabled: Boolean = bugId == 0
 ) {
     all("navBarLayerIsAlwaysVisible", enabled, bugId) {
-        this.showsLayer(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE)
+        this.showsLayer(NAVIGATION_BAR_WINDOW_TITLE)
     }
 }
 
@@ -87,7 +91,7 @@
     enabled: Boolean = bugId == 0
 ) {
     all("statusBarLayerIsAlwaysVisible", enabled, bugId) {
-        this.showsLayer(FlickerTestBase.STATUS_BAR_WINDOW_TITLE)
+        this.showsLayer(STATUS_BAR_WINDOW_TITLE)
     }
 }
 
@@ -102,15 +106,15 @@
     val endingPos = WindowUtils.getNavigationBarPosition(endRotation)
 
     start("navBarLayerRotatesAndScales_StartingPos", enabled, bugId) {
-        this.hasVisibleRegion(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE, startingPos)
+        this.hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos)
     }
     end("navBarLayerRotatesAndScales_EndingPost", enabled, bugId) {
-        this.hasVisibleRegion(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE, endingPos)
+        this.hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, endingPos)
     }
 
     if (startingPos == endingPos) {
         all("navBarLayerRotatesAndScales", enabled = false, bugId = 167747321) {
-            this.hasVisibleRegion(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE, startingPos)
+            this.hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos)
         }
     }
 }
@@ -126,10 +130,10 @@
     val endingPos = WindowUtils.getStatusBarPosition(endRotation)
 
     start("statusBarLayerRotatesScales_StartingPos", enabled, bugId) {
-        this.hasVisibleRegion(FlickerTestBase.STATUS_BAR_WINDOW_TITLE, startingPos)
+        this.hasVisibleRegion(STATUS_BAR_WINDOW_TITLE, startingPos)
     }
     end("statusBarLayerRotatesScales_EndingPos", enabled, bugId) {
-        this.hasVisibleRegion(FlickerTestBase.STATUS_BAR_WINDOW_TITLE, endingPos)
+        this.hasVisibleRegion(STATUS_BAR_WINDOW_TITLE, endingPos)
     }
 }
 
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.kt
deleted file mode 100644
index abe7dbc..0000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.kt
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.flicker
-
-import android.os.RemoteException
-import android.os.SystemClock
-import android.platform.helpers.IAppHelper
-import android.view.Surface
-import androidx.test.platform.app.InstrumentationRegistry
-import androidx.test.uiautomator.UiDevice
-
-/**
- * Base class of all Flicker test that performs common functions for all flicker tests:
- *
- *
- * - Caches transitions so that a transition is run once and the transition results are used by
- * tests multiple times. This is needed for parameterized tests which call the BeforeClass methods
- * multiple times.
- * - Keeps track of all test artifacts and deletes ones which do not need to be reviewed.
- * - Fails tests if results are not available for any test due to jank.
- */
-abstract class FlickerTestBase {
-    val instrumentation by lazy {
-        InstrumentationRegistry.getInstrumentation()
-    }
-    val uiDevice by lazy {
-        UiDevice.getInstance(instrumentation)
-    }
-
-    /**
-     * Build a test tag for the test
-     * @param testName Name of the transition(s) being tested
-     * @param app App being launcher
-     * @param rotation Initial screen rotation
-     *
-     * @return test tag with pattern <NAME>__<APP>__<ROTATION>
-    </ROTATION></APP></NAME> */
-    protected fun buildTestTag(testName: String, app: IAppHelper, rotation: Int): String {
-        return buildTestTag(
-                testName, app, rotation, rotation, app2 = null, extraInfo = "")
-    }
-
-    /**
-     * Build a test tag for the test
-     * @param testName Name of the transition(s) being tested
-     * @param app App being launcher
-     * @param beginRotation Initial screen rotation
-     * @param endRotation End screen rotation (if any, otherwise use same as initial)
-     *
-     * @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION>
-    </END_ROTATION></BEGIN_ROTATION></APP></NAME> */
-    protected fun buildTestTag(
-        testName: String,
-        app: IAppHelper,
-        beginRotation: Int,
-        endRotation: Int
-    ): String {
-        return buildTestTag(
-                testName, app, beginRotation, endRotation, app2 = null, extraInfo = "")
-    }
-
-    /**
-     * Build a test tag for the test
-     * @param testName Name of the transition(s) being tested
-     * @param app App being launcher
-     * @param app2 Second app being launched (if any)
-     * @param beginRotation Initial screen rotation
-     * @param endRotation End screen rotation (if any, otherwise use same as initial)
-     * @param extraInfo Additional information to append to the tag
-     *
-     * @return test tag with pattern <NAME>__<APP></APP>(S)>__<ROTATION></ROTATION>(S)>[__<EXTRA>]
-    </EXTRA></NAME> */
-    protected fun buildTestTag(
-        testName: String,
-        app: IAppHelper,
-        beginRotation: Int,
-        endRotation: Int,
-        app2: IAppHelper?,
-        extraInfo: String
-    ): String {
-        var testTag = "${testName}__${app.launcherName}"
-        if (app2 != null) {
-            testTag += "-${app2.launcherName}"
-        }
-        testTag += "__${Surface.rotationToString(beginRotation)}"
-        if (endRotation != beginRotation) {
-            testTag += "-${Surface.rotationToString(endRotation)}"
-        }
-        if (extraInfo.isNotEmpty()) {
-            testTag += "__$extraInfo"
-        }
-        return testTag
-    }
-
-    protected fun Flicker.setRotation(rotation: Int) {
-        try {
-            when (rotation) {
-                Surface.ROTATION_270 -> device.setOrientationLeft()
-                Surface.ROTATION_90 -> device.setOrientationRight()
-                Surface.ROTATION_0 -> device.setOrientationNatural()
-                else -> device.setOrientationNatural()
-            }
-            // Wait for animation to complete
-            SystemClock.sleep(1000)
-        } catch (e: RemoteException) {
-            throw RuntimeException(e)
-        }
-    }
-
-    companion object {
-        const val NAVIGATION_BAR_WINDOW_TITLE = "NavigationBar"
-        const val STATUS_BAR_WINDOW_TITLE = "StatusBar"
-        const val DOCKED_STACK_DIVIDER = "DockedStackDivider"
-    }
-}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/NonRotationTestBase.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/NonRotationTestBase.kt
deleted file mode 100644
index e7d1f8e..0000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/NonRotationTestBase.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.flicker
-
-import android.view.Surface
-import org.junit.runners.Parameterized
-
-abstract class NonRotationTestBase(
-    protected val rotationName: String,
-    protected val rotation: Int
-) : FlickerTestBase() {
-    companion object {
-        const val SCREENSHOT_LAYER = "RotationLayer"
-
-        @Parameterized.Parameters(name = "{0}")
-        @JvmStatic
-        fun getParams(): Collection<Array<Any>> {
-            val supportedRotations = intArrayOf(Surface.ROTATION_0, Surface.ROTATION_90)
-            return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
-        }
-    }
-}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/RotationTestBase.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/RotationTestBase.kt
deleted file mode 100644
index 3b67727..0000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/RotationTestBase.kt
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.flicker
-
-import android.view.Surface
-import org.junit.runners.Parameterized
-
-abstract class RotationTestBase(
-    beginRotationName: String,
-    endRotationName: String,
-    protected val beginRotation: Int,
-    protected val endRotation: Int
-) : FlickerTestBase() {
-    companion object {
-        @Parameterized.Parameters(name = "{0}-{1}")
-        @JvmStatic
-        fun getParams(): Collection<Array<Any>> {
-            val supportedRotations = intArrayOf(Surface.ROTATION_0, Surface.ROTATION_90)
-            val params: MutableCollection<Array<Any>> = mutableListOf()
-            for (begin in supportedRotations) {
-                for (end in supportedRotations) {
-                    if (begin != end) {
-                        params.add(arrayOf(
-                                Surface.rotationToString(begin),
-                                Surface.rotationToString(end),
-                                begin,
-                                end
-                        ))
-                    }
-                }
-            }
-            return params
-        }
-    }
-}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
new file mode 100644
index 0000000..742003a
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.flicker.helpers
+
+import android.os.Bundle
+import android.os.RemoteException
+import android.os.SystemClock
+import android.platform.helpers.IAppHelper
+import android.view.Surface
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.endRotation
+import com.android.server.wm.flicker.startRotation
+
+fun Flicker.setRotation(rotation: Int) {
+    try {
+        when (rotation) {
+            Surface.ROTATION_270 -> device.setOrientationLeft()
+            Surface.ROTATION_90 -> device.setOrientationRight()
+            Surface.ROTATION_0 -> device.setOrientationNatural()
+            else -> device.setOrientationNatural()
+        }
+        // Wait for animation to complete
+        SystemClock.sleep(1000)
+    } catch (e: RemoteException) {
+        throw RuntimeException(e)
+    }
+}
+
+/**
+ * Build a test tag for the test
+ * @param testName Name of the transition(s) being tested
+ * @param app App being launcher
+ * @param beginRotation Initial screen rotation
+ * @param endRotation End screen rotation (if any, otherwise use same as initial)
+ *
+ * @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION>
+</END_ROTATION></BEGIN_ROTATION></APP></NAME> */
+fun buildTestTag(
+    testName: String,
+    app: IAppHelper,
+    beginRotation: Int,
+    endRotation: Int
+): String {
+    return buildTestTag(
+        testName, app.launcherName, beginRotation, endRotation, app2 = null, extraInfo = "")
+}
+
+/**
+ * Build a test tag for the test
+ * @param testName Name of the transition(s) being tested
+ * @param app App being launcher
+ * @param rotation Screen rotation configuration for the test
+ *
+ * @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION>
+</END_ROTATION></BEGIN_ROTATION></APP></NAME> */
+fun buildTestTag(
+    testName: String,
+    app: IAppHelper?,
+    configuration: Bundle
+): String {
+    return buildTestTag(testName, app?.launcherName ?: "", configuration.startRotation,
+        configuration.endRotation, app2 = null, extraInfo = "")
+}
+
+/**
+ * Build a test tag for the test
+ * @param testName Name of the transition(s) being tested
+ * @param app App being launcher
+ * @param app2 Second app being launched (if any)
+ * @param beginRotation Initial screen rotation
+ * @param endRotation End screen rotation (if any, otherwise use same as initial)
+ * @param extraInfo Additional information to append to the tag
+ *
+ * @return test tag with pattern <NAME>__<APP></APP>(S)>__<ROTATION></ROTATION>(S)>[__<EXTRA>]
+</EXTRA></NAME> */
+fun buildTestTag(
+    testName: String,
+    app: String,
+    beginRotation: Int,
+    endRotation: Int,
+    app2: String?,
+    extraInfo: String
+): String {
+    var testTag = "${testName}__$app"
+    if (app2 != null) {
+        testTag += "-$app2"
+    }
+    testTag += "__${Surface.rotationToString(beginRotation)}"
+    if (endRotation != beginRotation) {
+        testTag += "-${Surface.rotationToString(endRotation)}"
+    }
+    if (extraInfo.isNotEmpty()) {
+        testTag += "__$extraInfo"
+    }
+    return testTag
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
index 404c789..a73264d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
@@ -16,21 +16,27 @@
 
 package com.android.server.wm.flicker.ime
 
+import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.dsl.flicker
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
 import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
-import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.buildTestTag
+import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
 import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
 import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import org.junit.FixMethodOrder
-import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -39,56 +45,62 @@
  * Test IME window closing back to app window transitions.
  * To run this test: `atest FlickerTests:CloseImeWindowToAppTest`
  */
+@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class CloseImeAutoOpenWindowToAppTest(
-    rotationName: String,
-    rotation: Int
-) : CloseImeWindowToAppTest(rotationName, rotation) {
-    override val testApp: ImeAppHelper
-        get() = ImeAppAutoFocusHelper(instrumentation)
+    testName: String,
+    flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
 
-    @Test
-    override fun test() {
-        flicker(instrumentation) {
-            withTag { buildTestTag("imeToAppAutoOpen", testApp, rotation) }
-            repeat { 1 }
-            setup {
-                eachRun {
-                    device.wakeUpAndGoToHomeScreen()
-                    this.setRotation(rotation)
-                    testApp.open()
-                    testApp.openIME(device)
-                }
-            }
-            teardown {
-                eachRun {
-                    testApp.exit()
-                    this.setRotation(Surface.ROTATION_0)
-                }
-            }
-            transitions {
-                device.pressBack()
-                device.waitForIdle()
-            }
-            assertions {
-                windowManagerTrace {
-                    navBarWindowIsAlwaysVisible()
-                    statusBarWindowIsAlwaysVisible()
-                    imeAppWindowIsAlwaysVisible(testApp, bugId = 141458352)
-                }
+    companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): List<Array<Any>> {
+            val instrumentation = InstrumentationRegistry.getInstrumentation()
+            val testApp = ImeAppAutoFocusHelper(instrumentation)
 
-                layersTrace {
-                    navBarLayerIsAlwaysVisible()
-                    statusBarLayerIsAlwaysVisible()
-                    noUncoveredRegions(rotation)
-                    navBarLayerRotatesAndScales(rotation)
-                    statusBarLayerRotatesScales(rotation)
-                    imeLayerBecomesInvisible(bugId = 141458352)
-                    imeAppLayerIsAlwaysVisible(testApp, bugId = 141458352)
+            return FlickerTestRunnerFactory(instrumentation)
+                .buildTest { configuration ->
+                    withTag { buildTestTag("imeToAppAutoOpen", testApp, configuration) }
+                    repeat { configuration.repetitions }
+                    setup {
+                        eachRun {
+                            device.wakeUpAndGoToHomeScreen()
+                            this.setRotation(configuration.startRotation)
+                            testApp.open()
+                            testApp.openIME(device)
+                        }
+                    }
+                    teardown {
+                        eachRun {
+                            testApp.exit()
+                            this.setRotation(Surface.ROTATION_0)
+                        }
+                    }
+                    transitions {
+                        device.pressBack()
+                        device.waitForIdle()
+                    }
+                    assertions {
+                        windowManagerTrace {
+                            navBarWindowIsAlwaysVisible()
+                            statusBarWindowIsAlwaysVisible()
+                            imeAppWindowIsAlwaysVisible(testApp, bugId = 141458352)
+                        }
+
+                        layersTrace {
+                            navBarLayerIsAlwaysVisible()
+                            statusBarLayerIsAlwaysVisible()
+                            noUncoveredRegions(configuration.startRotation)
+                            navBarLayerRotatesAndScales(configuration.startRotation)
+                            statusBarLayerRotatesScales(configuration.startRotation)
+                            imeLayerBecomesInvisible(bugId = 141458352)
+                            imeAppLayerIsAlwaysVisible(testApp, bugId = 141458352)
+                        }
+                    }
                 }
-            }
         }
     }
-}
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
index c1ba21a..7647802 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
@@ -19,19 +19,24 @@
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.dsl.flicker
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
 import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
-import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.buildTestTag
+import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
 import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
 import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import org.junit.FixMethodOrder
-import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -45,53 +50,63 @@
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class CloseImeAutoOpenWindowToHomeTest(
-    rotationName: String,
-    rotation: Int
-) : CloseImeWindowToHomeTest(rotationName, rotation) {
-    override val testApp: ImeAppHelper
-        get() = ImeAppAutoFocusHelper(instrumentation)
+    testName: String,
+    flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
 
-    @Test
-    override fun test() {
-        flicker(instrumentation) {
-            withTag { buildTestTag("imeToHomeAutoOpen", testApp, rotation) }
-            repeat { 1 }
-            setup {
-                eachRun {
-                    device.wakeUpAndGoToHomeScreen()
-                    this.setRotation(rotation)
-                    testApp.open()
-                    testApp.openIME(device)
-                }
-            }
-            teardown {
-                eachRun {
-                    testApp.exit()
-                    this.setRotation(Surface.ROTATION_0)
-                }
-            }
-            transitions {
-                device.pressHome()
-                device.waitForIdle()
-            }
-            assertions {
-                windowManagerTrace {
-                    navBarWindowIsAlwaysVisible()
-                    statusBarWindowIsAlwaysVisible()
-                    imeWindowBecomesInvisible(bugId = 141458352)
-                    imeAppWindowBecomesInvisible(testApp, bugId = 157449248)
-                }
+    companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): List<Array<Any>> {
+            val instrumentation = InstrumentationRegistry.getInstrumentation()
+            val testApp = ImeAppAutoFocusHelper(instrumentation)
 
-                layersTrace {
-                    navBarLayerIsAlwaysVisible(bugId = 140855415)
-                    statusBarLayerIsAlwaysVisible(bugId = 140855415)
-                    noUncoveredRegions(rotation, Surface.ROTATION_0, allStates = false)
-                    navBarLayerRotatesAndScales(rotation, Surface.ROTATION_0, bugId = 140855415)
-                    statusBarLayerRotatesScales(rotation, Surface.ROTATION_0)
-                    imeLayerBecomesInvisible(bugId = 141458352)
-                    imeAppLayerBecomesInvisible(testApp, bugId = 153739621)
+            return FlickerTestRunnerFactory(instrumentation)
+                .buildTest { configuration ->
+                    withTestName {
+                        buildTestTag("imeToHomeAutoOpen", testApp, configuration)
+                    }
+                    repeat { configuration.repetitions }
+                    setup {
+                        eachRun {
+                            device.wakeUpAndGoToHomeScreen()
+                            this.setRotation(configuration.startRotation)
+                            testApp.open()
+                            testApp.openIME(device)
+                        }
+                    }
+                    teardown {
+                        eachRun {
+                            testApp.exit()
+                            this.setRotation(Surface.ROTATION_0)
+                        }
+                    }
+                    transitions {
+                        device.pressHome()
+                        device.waitForIdle()
+                    }
+                    assertions {
+                        windowManagerTrace {
+                            navBarWindowIsAlwaysVisible()
+                            statusBarWindowIsAlwaysVisible()
+                            imeWindowBecomesInvisible(bugId = 141458352)
+                            imeAppWindowBecomesInvisible(testApp, bugId = 157449248)
+                        }
+
+                        layersTrace {
+                            navBarLayerIsAlwaysVisible(bugId = 140855415)
+                            statusBarLayerIsAlwaysVisible(bugId = 140855415)
+                            noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0,
+                                allStates = false)
+                            navBarLayerRotatesAndScales(configuration.startRotation,
+                                Surface.ROTATION_0, bugId = 140855415)
+                            statusBarLayerRotatesScales(configuration.startRotation,
+                                Surface.ROTATION_0)
+                            imeLayerBecomesInvisible(bugId = 141458352)
+                            imeAppLayerBecomesInvisible(testApp, bugId = 153739621)
+                        }
+                    }
                 }
-            }
         }
     }
 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
index 2c00722..136cf86 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
@@ -19,19 +19,24 @@
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.NonRotationTestBase
-import com.android.server.wm.flicker.dsl.flicker
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
 import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.buildTestTag
+import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
 import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
 import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import org.junit.FixMethodOrder
-import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -44,52 +49,57 @@
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class CloseImeWindowToAppTest(
-    rotationName: String,
-    rotation: Int
-) : NonRotationTestBase(rotationName, rotation) {
-    open val testApp = ImeAppHelper(instrumentation)
+class CloseImeWindowToAppTest(
+    testName: String,
+    flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
 
-    @Test
-    open fun test() {
-        flicker(instrumentation) {
-            withTag { buildTestTag("imeToApp", testApp, rotation) }
-            repeat { 1 }
-            setup {
-                eachRun {
-                    device.wakeUpAndGoToHomeScreen()
-                    this.setRotation(rotation)
-                    testApp.open()
-                    testApp.openIME(device)
-                }
-            }
-            teardown {
-                eachRun {
-                    testApp.exit()
-                    this.setRotation(Surface.ROTATION_0)
-                }
-            }
-            transitions {
-                device.pressBack()
-                device.waitForIdle()
-            }
-            assertions {
-                windowManagerTrace {
-                    navBarWindowIsAlwaysVisible()
-                    statusBarWindowIsAlwaysVisible()
-                    imeAppWindowIsAlwaysVisible(testApp)
-                }
+    companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): List<Array<Any>> {
+            val instrumentation = InstrumentationRegistry.getInstrumentation()
+            val testApp = ImeAppHelper(instrumentation)
+            return FlickerTestRunnerFactory(instrumentation)
+                .buildTest { configuration ->
+                    withTestName { buildTestTag("imeToApp", testApp, configuration) }
+                    repeat { configuration.repetitions }
+                    setup {
+                        eachRun {
+                            device.wakeUpAndGoToHomeScreen()
+                            this.setRotation(configuration.startRotation)
+                            testApp.open()
+                            testApp.openIME(device)
+                        }
+                    }
+                    teardown {
+                        eachRun {
+                            testApp.exit()
+                            this.setRotation(Surface.ROTATION_0)
+                        }
+                    }
+                    transitions {
+                        device.pressBack()
+                        device.waitForIdle()
+                    }
+                    assertions {
+                        windowManagerTrace {
+                            navBarWindowIsAlwaysVisible()
+                            statusBarWindowIsAlwaysVisible()
+                            imeAppWindowIsAlwaysVisible(testApp)
+                        }
 
-                layersTrace {
-                    navBarLayerIsAlwaysVisible()
-                    statusBarLayerIsAlwaysVisible()
-                    noUncoveredRegions(rotation)
-                    navBarLayerRotatesAndScales(rotation)
-                    statusBarLayerRotatesScales(rotation)
-                    imeLayerBecomesInvisible(enabled = false)
-                    imeAppLayerIsAlwaysVisible(testApp)
+                        layersTrace {
+                            navBarLayerIsAlwaysVisible()
+                            statusBarLayerIsAlwaysVisible()
+                            noUncoveredRegions(configuration.startRotation)
+                            navBarLayerRotatesAndScales(configuration.startRotation)
+                            statusBarLayerRotatesScales(configuration.startRotation)
+                            imeLayerBecomesInvisible(enabled = false)
+                            imeAppLayerIsAlwaysVisible(testApp)
+                        }
+                    }
                 }
-            }
         }
     }
 }
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
index 4697adc..6cfb282 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
@@ -19,21 +19,26 @@
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.NonRotationTestBase
-import com.android.server.wm.flicker.dsl.flicker
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
 import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.buildTestTag
 import com.android.server.wm.flicker.helpers.openQuickstep
 import com.android.server.wm.flicker.helpers.reopenAppFromOverview
+import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
 import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
 import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import org.junit.FixMethodOrder
-import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -45,61 +50,69 @@
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class CloseImeWindowToHomeTest(
-    rotationName: String,
-    rotation: Int
-) : NonRotationTestBase(rotationName, rotation) {
-    open val testApp = ImeAppHelper(instrumentation)
+class CloseImeWindowToHomeTest(
+    testName: String,
+    flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
 
-    @Test
-    open fun test() {
-        flicker(instrumentation) {
-            withTag { buildTestTag("imeToHome", testApp, rotation) }
-            repeat { 1 }
-            setup {
-                test {
-                    device.wakeUpAndGoToHomeScreen()
-                    this.setRotation(rotation)
-                    testApp.open()
-                }
-                eachRun {
-                    device.openQuickstep()
-                    device.reopenAppFromOverview()
-                    this.setRotation(rotation)
-                    testApp.openIME(device)
-                }
-            }
-            transitions {
-                device.pressHome()
-                device.waitForIdle()
-            }
-            teardown {
-                eachRun {
-                    device.pressHome()
-                }
-                test {
-                    testApp.exit()
-                    this.setRotation(Surface.ROTATION_0)
-                }
-            }
-            assertions {
-                windowManagerTrace {
-                    navBarWindowIsAlwaysVisible()
-                    statusBarWindowIsAlwaysVisible()
-                    imeWindowBecomesInvisible()
-                    imeAppWindowBecomesInvisible(testApp)
-                }
+    companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): List<Array<Any>> {
+            val instrumentation = InstrumentationRegistry.getInstrumentation()
+            val testApp = ImeAppHelper(instrumentation)
+            return FlickerTestRunnerFactory(instrumentation)
+                .buildTest { configuration ->
+                    withTestName { buildTestTag("imeToHome", testApp, configuration) }
+                    repeat { configuration.repetitions }
+                    setup {
+                        test {
+                            device.wakeUpAndGoToHomeScreen()
+                            this.setRotation(configuration.startRotation)
+                            testApp.open()
+                        }
+                        eachRun {
+                            device.openQuickstep()
+                            device.reopenAppFromOverview()
+                            this.setRotation(configuration.startRotation)
+                            testApp.openIME(device)
+                        }
+                    }
+                    transitions {
+                        device.pressHome()
+                        device.waitForIdle()
+                    }
+                    teardown {
+                        eachRun {
+                            device.pressHome()
+                        }
+                        test {
+                            testApp.exit()
+                            this.setRotation(Surface.ROTATION_0)
+                        }
+                    }
+                    assertions {
+                        windowManagerTrace {
+                            navBarWindowIsAlwaysVisible()
+                            statusBarWindowIsAlwaysVisible()
+                            imeWindowBecomesInvisible()
+                            imeAppWindowBecomesInvisible(testApp)
+                        }
 
-                layersTrace {
-                    navBarLayerIsAlwaysVisible(bugId = 140855415)
-                    statusBarLayerIsAlwaysVisible(bugId = 140855415)
-                    noUncoveredRegions(rotation, Surface.ROTATION_0, allStates = false)
-                    navBarLayerRotatesAndScales(rotation, Surface.ROTATION_0, bugId = 140855415)
-                    statusBarLayerRotatesScales(rotation, Surface.ROTATION_0)
-                    imeLayerBecomesInvisible(bugId = 153739621)
-                    imeAppLayerBecomesInvisible(testApp, bugId = 153739621)
+                        layersTrace {
+                            navBarLayerIsAlwaysVisible(bugId = 140855415)
+                            statusBarLayerIsAlwaysVisible(bugId = 140855415)
+                            noUncoveredRegions(configuration.startRotation,
+                                Surface.ROTATION_0, allStates = false)
+                            navBarLayerRotatesAndScales(configuration.startRotation,
+                                Surface.ROTATION_0, bugId = 140855415)
+                            statusBarLayerRotatesScales(configuration.startRotation,
+                                Surface.ROTATION_0)
+                            imeLayerBecomesInvisible(bugId = 153739621)
+                            imeAppLayerBecomesInvisible(testApp, bugId = 153739621)
+                        }
+                    }
                 }
-            }
         }
     }
 }
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ImeAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/ime/ImeAssertions.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
index 2caa8f3..5767a94 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
@@ -19,19 +19,24 @@
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.NonRotationTestBase
-import com.android.server.wm.flicker.dsl.flicker
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
 import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.buildTestTag
+import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
 import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
 import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import org.junit.FixMethodOrder
-import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -45,62 +50,65 @@
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class OpenImeWindowTest(
-    rotationName: String,
-    rotation: Int
-) : NonRotationTestBase(rotationName, rotation) {
-    @Test
-    fun test() {
-        val testApp = ImeAppHelper(instrumentation)
-
-        flicker(instrumentation) {
-            withTag { buildTestTag("openIme", testApp, rotation) }
-            repeat { 1 }
-            setup {
-                test {
-                    device.wakeUpAndGoToHomeScreen()
-                    this.setRotation(rotation)
-                    testApp.open()
-                }
-            }
-            transitions {
-                testApp.openIME(device)
-            }
-            teardown {
-                eachRun {
-                    testApp.closeIME(device)
-                }
-                test {
-                    testApp.exit()
-                    this.setRotation(Surface.ROTATION_0)
-                }
-            }
-            assertions {
-                windowManagerTrace {
-                    navBarWindowIsAlwaysVisible()
-                    statusBarWindowIsAlwaysVisible()
-
-                    all("imeWindowBecomesVisible") {
-                        this.skipUntilFirstAssertion()
-                            .hidesNonAppWindow(IME_WINDOW_TITLE)
-                            .then()
-                            .showsNonAppWindow(IME_WINDOW_TITLE)
-                    }
-                }
-
-                layersTrace {
-                    navBarLayerIsAlwaysVisible()
-                    statusBarLayerIsAlwaysVisible()
-                    noUncoveredRegions(rotation)
-                    navBarLayerRotatesAndScales(rotation)
-                    statusBarLayerRotatesScales(rotation)
-
-                    imeLayerBecomesVisible()
-                }
-            }
-        }
-    }
-
+    testName: String,
+    flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
     companion object {
         private const val IME_WINDOW_TITLE = "InputMethod"
+
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): List<Array<Any>> {
+            val instrumentation = InstrumentationRegistry.getInstrumentation()
+            val testApp = ImeAppHelper(instrumentation)
+
+            return FlickerTestRunnerFactory(instrumentation)
+                .buildTest { configuration ->
+                    withTestName { buildTestTag("openIme", testApp, configuration) }
+                    repeat { configuration.repetitions }
+                    setup {
+                        test {
+                            device.wakeUpAndGoToHomeScreen()
+                            this.setRotation(configuration.startRotation)
+                            testApp.open()
+                        }
+                    }
+                    transitions {
+                        testApp.openIME(device)
+                    }
+                    teardown {
+                        eachRun {
+                            testApp.closeIME(device)
+                        }
+                        test {
+                            testApp.exit()
+                            this.setRotation(Surface.ROTATION_0)
+                        }
+                    }
+                    assertions {
+                        windowManagerTrace {
+                            navBarWindowIsAlwaysVisible()
+                            statusBarWindowIsAlwaysVisible()
+
+                            all("imeWindowBecomesVisible") {
+                                this.skipUntilFirstAssertion()
+                                    .hidesNonAppWindow(IME_WINDOW_TITLE)
+                                    .then()
+                                    .showsNonAppWindow(IME_WINDOW_TITLE)
+                            }
+                        }
+
+                        layersTrace {
+                            navBarLayerIsAlwaysVisible()
+                            statusBarLayerIsAlwaysVisible()
+                            noUncoveredRegions(configuration.startRotation)
+                            navBarLayerRotatesAndScales(configuration.startRotation)
+                            statusBarLayerRotatesScales(configuration.startRotation)
+
+                            imeLayerBecomesVisible()
+                        }
+                    }
+                }
+        }
     }
 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
new file mode 100644
index 0000000..7e857f3
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.flicker.launch
+
+import android.platform.helpers.IAppHelper
+import com.android.server.wm.flicker.dsl.LayersAssertion
+import com.android.server.wm.flicker.dsl.WmAssertion
+
+fun WmAssertion.wallpaperWindowBecomesInvisible(
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all("wallpaperWindowBecomesInvisible", enabled, bugId) {
+        this.showsBelowAppWindow("Wallpaper")
+                .then()
+                .hidesBelowAppWindow("Wallpaper")
+    }
+}
+
+fun WmAssertion.appWindowReplacesLauncherAsTopWindow(
+    testApp: IAppHelper,
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all("appWindowReplacesLauncherAsTopWindow", enabled, bugId) {
+        this.showsAppWindowOnTop("Launcher")
+                .then()
+                .showsAppWindowOnTop("Snapshot", testApp.getPackage())
+    }
+}
+
+fun LayersAssertion.wallpaperLayerBecomesInvisible(
+    testApp: IAppHelper,
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all("wallpaperLayerBecomesInvisible", enabled, bugId) {
+        this.showsLayer("Wallpaper")
+                .then()
+                .replaceVisibleLayer("Wallpaper", testApp.getPackage())
+    }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index 2c9c8ba..1081414 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -19,18 +19,26 @@
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.dsl.flicker
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.focusChanges
+import com.android.server.wm.flicker.helpers.StandardAppHelper
+import com.android.server.wm.flicker.helpers.buildTestTag
+import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
 import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
 import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import org.junit.FixMethodOrder
-import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -44,53 +52,64 @@
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class OpenAppColdTest(
-    rotationName: String,
-    rotation: Int
-) : OpenAppTestBase(rotationName, rotation) {
-    @Test
-    fun test() {
-        flicker(instrumentation) {
-            withTag { buildTestTag("openAppCold", testApp, rotation) }
-            repeat { 1 }
-            setup {
-                test {
-                    device.wakeUpAndGoToHomeScreen()
-                }
-                eachRun {
-                    this.setRotation(rotation)
-                }
-            }
-            transitions {
-                testApp.open()
-            }
-            teardown {
-                eachRun {
-                    testApp.exit()
-                    this.setRotation(Surface.ROTATION_0)
-                }
-            }
-            assertions {
-                windowManagerTrace {
-                    navBarWindowIsAlwaysVisible()
-                    statusBarWindowIsAlwaysVisible()
-                    appWindowReplacesLauncherAsTopWindow()
-                    wallpaperWindowBecomesInvisible()
-                }
+    testName: String,
+    flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
+    companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): List<Array<Any>> {
+            val instrumentation = InstrumentationRegistry.getInstrumentation()
+            val testApp = StandardAppHelper(instrumentation,
+                "com.android.server.wm.flicker.testapp", "SimpleApp")
+            return FlickerTestRunnerFactory(instrumentation)
+                .buildTest { configuration ->
+                    withTag { buildTestTag("openAppCold", testApp, configuration) }
+                    repeat { configuration.repetitions }
+                    setup {
+                        test {
+                            device.wakeUpAndGoToHomeScreen()
+                        }
+                        eachRun {
+                            this.setRotation(configuration.startRotation)
+                        }
+                    }
+                    transitions {
+                        testApp.open()
+                    }
+                    teardown {
+                        eachRun {
+                            testApp.exit()
+                            this.setRotation(Surface.ROTATION_0)
+                        }
+                    }
+                    assertions {
+                        windowManagerTrace {
+                            navBarWindowIsAlwaysVisible()
+                            statusBarWindowIsAlwaysVisible()
+                            appWindowReplacesLauncherAsTopWindow(testApp)
+                            wallpaperWindowBecomesInvisible()
+                        }
 
-                layersTrace {
-                    // During testing the launcher is always in portrait mode
-                    noUncoveredRegions(Surface.ROTATION_0, rotation, bugId = 141361128)
-                    navBarLayerRotatesAndScales(Surface.ROTATION_0, rotation)
-                    statusBarLayerRotatesScales(Surface.ROTATION_0, rotation)
-                    navBarLayerIsAlwaysVisible(enabled = rotation == Surface.ROTATION_0)
-                    statusBarLayerIsAlwaysVisible(enabled = false)
-                    wallpaperLayerBecomesInvisible()
-                }
+                        layersTrace {
+                            // During testing the launcher is always in portrait mode
+                            noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation,
+                                bugId = 141361128)
+                            navBarLayerRotatesAndScales(Surface.ROTATION_0,
+                                configuration.endRotation)
+                            statusBarLayerRotatesScales(Surface.ROTATION_0,
+                                configuration.endRotation)
+                            navBarLayerIsAlwaysVisible(
+                                enabled = configuration.endRotation == Surface.ROTATION_0)
+                            statusBarLayerIsAlwaysVisible(enabled = false)
+                            wallpaperLayerBecomesInvisible(testApp)
+                        }
 
-                eventLog {
-                    focusChanges("NexusLauncherActivity", testApp.`package`)
+                        eventLog {
+                            focusChanges("NexusLauncherActivity", testApp.`package`)
+                        }
+                    }
                 }
-            }
         }
     }
 }
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTestBase.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTestBase.kt
deleted file mode 100644
index 98e05d5..0000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTestBase.kt
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.flicker.launch
-
-import com.android.server.wm.flicker.NonRotationTestBase
-import com.android.server.wm.flicker.helpers.StandardAppHelper
-import com.android.server.wm.flicker.dsl.LayersAssertion
-import com.android.server.wm.flicker.dsl.WmAssertion
-
-abstract class OpenAppTestBase(
-    rotationName: String,
-    rotation: Int
-) : NonRotationTestBase(rotationName, rotation) {
-    protected val testApp = StandardAppHelper(instrumentation,
-            "com.android.server.wm.flicker.testapp", "SimpleApp")
-
-    protected fun WmAssertion.wallpaperWindowBecomesInvisible(
-        bugId: Int = 0,
-        enabled: Boolean = bugId == 0
-    ) {
-        all("wallpaperWindowBecomesInvisible", enabled, bugId) {
-            this.showsBelowAppWindow("Wallpaper")
-                    .then()
-                    .hidesBelowAppWindow("Wallpaper")
-        }
-    }
-
-    protected fun WmAssertion.appWindowReplacesLauncherAsTopWindow(
-        bugId: Int = 0,
-        enabled: Boolean = bugId == 0
-    ) {
-        all("appWindowReplacesLauncherAsTopWindow", enabled, bugId) {
-            this.showsAppWindowOnTop("Launcher")
-                    .then()
-                    .showsAppWindowOnTop("Snapshot", testApp.getPackage())
-        }
-    }
-
-    protected fun LayersAssertion.wallpaperLayerBecomesInvisible(
-        bugId: Int = 0,
-        enabled: Boolean = bugId == 0
-    ) {
-        all("appWindowReplacesLauncherAsTopWindow", enabled, bugId) {
-            this.showsLayer("Wallpaper")
-                    .then()
-                    .replaceVisibleLayer("Wallpaper", testApp.getPackage())
-        }
-    }
-}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
index acd141a..2061994 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
@@ -16,21 +16,29 @@
 
 package com.android.server.wm.flicker.launch
 
+import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.helpers.StandardAppHelper
-import com.android.server.wm.flicker.dsl.flicker
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.focusChanges
+import com.android.server.wm.flicker.helpers.StandardAppHelper
+import com.android.server.wm.flicker.helpers.buildTestTag
+import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
 import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
 import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import org.junit.FixMethodOrder
-import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -39,64 +47,73 @@
  * Test warm launch app.
  * To run this test: `atest FlickerTests:OpenAppWarmTest`
  */
+@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class OpenAppWarmTest(
-    rotationName: String,
-    rotation: Int
-) : OpenAppTestBase(rotationName, rotation) {
-    @Test
-    fun test() {
-        val testApp = StandardAppHelper(instrumentation,
+    testName: String,
+    flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
+    companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): List<Array<Any>> {
+            val instrumentation = InstrumentationRegistry.getInstrumentation()
+            val testApp = StandardAppHelper(instrumentation,
                 "com.android.server.wm.flicker.testapp", "SimpleApp")
+            return FlickerTestRunnerFactory(instrumentation)
+                .buildTest { configuration ->
+                    withTag { buildTestTag("openAppWarm", testApp, configuration) }
+                    repeat { configuration.repetitions }
+                    setup {
+                        test {
+                            device.wakeUpAndGoToHomeScreen()
+                            testApp.open()
+                        }
+                        eachRun {
+                            device.pressHome()
+                            this.setRotation(configuration.startRotation)
+                        }
+                    }
+                    transitions {
+                        testApp.open()
+                    }
+                    teardown {
+                        eachRun {
+                            this.setRotation(Surface.ROTATION_0)
+                        }
+                        test {
+                            testApp.exit()
+                        }
+                    }
+                    assertions {
+                        windowManagerTrace {
+                            navBarWindowIsAlwaysVisible()
+                            statusBarWindowIsAlwaysVisible()
+                            appWindowReplacesLauncherAsTopWindow(testApp)
+                            wallpaperWindowBecomesInvisible(enabled = false)
+                        }
 
-        flicker(instrumentation) {
-            withTag { buildTestTag("openAppWarm", testApp, rotation) }
-            repeat { 1 }
-            setup {
-                test {
-                    device.wakeUpAndGoToHomeScreen()
-                    testApp.open()
-                }
-                eachRun {
-                    device.pressHome()
-                    this.setRotation(rotation)
-                }
-            }
-            transitions {
-                testApp.open()
-            }
-            teardown {
-                eachRun {
-                    this.setRotation(Surface.ROTATION_0)
-                }
-                test {
-                    testApp.exit()
-                }
-            }
-            assertions {
-                windowManagerTrace {
-                    navBarWindowIsAlwaysVisible()
-                    statusBarWindowIsAlwaysVisible()
-                    appWindowReplacesLauncherAsTopWindow()
-                    wallpaperWindowBecomesInvisible(enabled = false)
-                }
+                        layersTrace {
+                            // During testing the launcher is always in portrait mode
+                            noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation,
+                                bugId = 141361128)
+                            navBarLayerRotatesAndScales(Surface.ROTATION_0,
+                                configuration.endRotation)
+                            statusBarLayerRotatesScales(Surface.ROTATION_0,
+                                configuration.endRotation)
+                            navBarLayerIsAlwaysVisible(
+                                enabled = configuration.endRotation == Surface.ROTATION_0)
+                            statusBarLayerIsAlwaysVisible(enabled = false)
+                            wallpaperLayerBecomesInvisible(testApp)
+                        }
 
-                layersTrace {
-                    // During testing the launcher is always in portrait mode
-                    noUncoveredRegions(Surface.ROTATION_0, rotation, bugId = 141361128)
-                    navBarLayerRotatesAndScales(Surface.ROTATION_0, rotation)
-                    statusBarLayerRotatesScales(Surface.ROTATION_0, rotation)
-                    navBarLayerIsAlwaysVisible(enabled = rotation == Surface.ROTATION_0)
-                    statusBarLayerIsAlwaysVisible(enabled = false)
-                    wallpaperLayerBecomesInvisible()
+                        eventLog {
+                            focusChanges("NexusLauncherActivity", testApp.`package`)
+                        }
+                    }
                 }
-
-                eventLog {
-                    focusChanges("NexusLauncherActivity", testApp.`package`)
-                }
-            }
         }
     }
 }
\ No newline at end of file
diff --git a/media/java/android/media/tv/TvChannelInfo.aidl b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/CommonAssertions.kt
similarity index 78%
copy from media/java/android/media/tv/TvChannelInfo.aidl
copy to tests/FlickerTests/src/com/android/server/wm/flicker/pip/CommonAssertions.kt
index 71cd0a7..6bc9dcb 100644
--- a/media/java/android/media/tv/TvChannelInfo.aidl
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/CommonAssertions.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open 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,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.media.tv;
+package com.android.server.wm.flicker.pip
 
-parcelable TvChannelInfo;
+internal const val PIP_WINDOW_TITLE = "PipMenuActivity"
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/EnterPipTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/EnterPipTest.kt
index 9cfc033..89539fd 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/EnterPipTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/EnterPipTest.kt
@@ -16,23 +16,31 @@
 
 package com.android.server.wm.flicker.pip
 
+import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.dsl.flicker
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.helpers.PipAppHelper
+import com.android.server.wm.flicker.helpers.buildTestTag
 import com.android.server.wm.flicker.helpers.closePipWindow
 import com.android.server.wm.flicker.helpers.expandPipWindow
 import com.android.server.wm.flicker.helpers.hasPipWindow
+import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
 import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
 import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import org.junit.FixMethodOrder
-import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -41,81 +49,85 @@
  * Test Pip launch.
  * To run this test: `atest FlickerTests:PipToAppTest`
  */
+@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @FlakyTest(bugId = 152738416)
 class EnterPipTest(
-    rotationName: String,
-    rotation: Int
-) : PipTestBase(rotationName, rotation) {
-    @Test
-    fun test() {
-        flicker(instrumentation) {
-            withTag { buildTestTag("enterPip", testApp, rotation) }
-            repeat { 1 }
-            setup {
-                test {
-                    device.wakeUpAndGoToHomeScreen()
-                }
-                eachRun {
-                    device.pressHome()
-                    testApp.open()
-                    this.setRotation(rotation)
-                }
-            }
-            teardown {
-                eachRun {
-                    if (device.hasPipWindow()) {
-                        device.closePipWindow()
-                    }
-                    testApp.exit()
-                    this.setRotation(Surface.ROTATION_0)
-                }
-                test {
-                    if (device.hasPipWindow()) {
-                        device.closePipWindow()
-                    }
-                }
-            }
-            transitions {
-                testApp.clickEnterPipButton(device)
-                device.expandPipWindow()
-            }
-            assertions {
-                windowManagerTrace {
-                    navBarWindowIsAlwaysVisible()
-                    statusBarWindowIsAlwaysVisible()
-                    all("pipWindowBecomesVisible") {
-                        this.showsAppWindow(testApp.`package`)
-                                .then()
-                                .showsAppWindow(sPipWindowTitle)
-                    }
-                }
-
-                layersTrace {
-                    navBarLayerIsAlwaysVisible()
-                    statusBarLayerIsAlwaysVisible()
-                    noUncoveredRegions(rotation, Surface.ROTATION_0, allStates = false)
-                    navBarLayerRotatesAndScales(rotation, Surface.ROTATION_0)
-                    statusBarLayerRotatesScales(rotation, Surface.ROTATION_0)
-
-                    all("pipLayerBecomesVisible") {
-                        this.showsLayer(testApp.launcherName)
-                                .then()
-                                .showsLayer(sPipWindowTitle)
-                    }
-                }
-            }
-        }
-    }
-
+    testName: String,
+    flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
-        fun getParams(): Collection<Array<Any>> {
-            val supportedRotations = intArrayOf(Surface.ROTATION_0)
-            return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
+        fun getParams(): List<Array<Any>> {
+            val instrumentation = InstrumentationRegistry.getInstrumentation()
+            val testApp = PipAppHelper(instrumentation)
+            return FlickerTestRunnerFactory(instrumentation, listOf(Surface.ROTATION_0))
+                .buildTest { configuration ->
+                    withTestName { buildTestTag("enterPip", testApp, configuration) }
+                    repeat { configuration.repetitions }
+                    setup {
+                        test {
+                            device.wakeUpAndGoToHomeScreen()
+                        }
+                        eachRun {
+                            device.pressHome()
+                            testApp.open()
+                            this.setRotation(configuration.startRotation)
+                        }
+                    }
+                    teardown {
+                        eachRun {
+                            if (device.hasPipWindow()) {
+                                device.closePipWindow()
+                            }
+                            testApp.exit()
+                            this.setRotation(Surface.ROTATION_0)
+                        }
+                        test {
+                            if (device.hasPipWindow()) {
+                                device.closePipWindow()
+                            }
+                        }
+                    }
+                    transitions {
+                        testApp.clickEnterPipButton(device)
+                        device.expandPipWindow()
+                    }
+                    assertions {
+                        windowManagerTrace {
+                            navBarWindowIsAlwaysVisible()
+                            statusBarWindowIsAlwaysVisible()
+
+                            all("pipWindowBecomesVisible") {
+                                this.showsAppWindow(testApp.`package`)
+                                    .then()
+                                    .showsAppWindow(PIP_WINDOW_TITLE)
+                            }
+                        }
+
+                        layersTrace {
+                            navBarLayerIsAlwaysVisible(bugId = 140855415)
+                            statusBarLayerIsAlwaysVisible()
+                            noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0,
+                                enabled = false)
+                            navBarLayerRotatesAndScales(configuration.startRotation,
+                                Surface.ROTATION_0, bugId = 140855415)
+                            statusBarLayerRotatesScales(configuration.startRotation,
+                                Surface.ROTATION_0)
+                        }
+
+                        layersTrace {
+                            all("pipLayerBecomesVisible") {
+                                this.showsLayer(testApp.launcherName)
+                                    .then()
+                                    .showsLayer(PIP_WINDOW_TITLE)
+                            }
+                        }
+                    }
+                }
         }
     }
 }
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipTestBase.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipTestBase.kt
deleted file mode 100644
index 691db7fb..0000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipTestBase.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.flicker.pip
-
-import com.android.server.wm.flicker.NonRotationTestBase
-import com.android.server.wm.flicker.helpers.PipAppHelper
-
-abstract class PipTestBase(
-    rotationName: String,
-    rotation: Int
-) : NonRotationTestBase(rotationName, rotation) {
-    protected val testApp = PipAppHelper(instrumentation)
-
-    companion object {
-        const val sPipWindowTitle = "PipMenuActivity"
-    }
-}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToAppTest.kt
index deccc90..ac54a0a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToAppTest.kt
@@ -19,21 +19,28 @@
 import android.view.Surface
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.dsl.flicker
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
 import com.android.server.wm.flicker.focusChanges
+import com.android.server.wm.flicker.helpers.PipAppHelper
+import com.android.server.wm.flicker.helpers.buildTestTag
 import com.android.server.wm.flicker.helpers.closePipWindow
 import com.android.server.wm.flicker.helpers.expandPipWindow
 import com.android.server.wm.flicker.helpers.hasPipWindow
+import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
 import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
 import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import org.junit.FixMethodOrder
-import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -47,73 +54,82 @@
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @FlakyTest(bugId = 152738416)
 class PipToAppTest(
-    rotationName: String,
-    rotation: Int
-) : PipTestBase(rotationName, rotation) {
-    @Test
-    fun test() {
-        flicker(instrumentation) {
-            withTag { buildTestTag("exitPipModeToApp", testApp, rotation) }
-            repeat { 1 }
-            setup {
-                test {
-                    device.wakeUpAndGoToHomeScreen()
-                    device.pressHome()
-                    testApp.open()
-                }
-                eachRun {
-                    this.setRotation(rotation)
-                    testApp.clickEnterPipButton(device)
-                    device.hasPipWindow()
-                }
-            }
-            teardown {
-                eachRun {
-                    this.setRotation(Surface.ROTATION_0)
-                }
-                test {
-                    if (device.hasPipWindow()) {
-                        device.closePipWindow()
+    testName: String,
+    flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
+    companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): List<Array<Any>> {
+            val instrumentation = InstrumentationRegistry.getInstrumentation()
+            val testApp = PipAppHelper(instrumentation)
+            return FlickerTestRunnerFactory(instrumentation, listOf(Surface.ROTATION_0))
+                .buildTest { configuration ->
+                    withTestName { buildTestTag("exitPipModeToApp", testApp, configuration) }
+                    repeat { configuration.repetitions }
+                    setup {
+                        test {
+                            device.wakeUpAndGoToHomeScreen()
+                            device.pressHome()
+                            testApp.open()
+                        }
+                        eachRun {
+                            this.setRotation(configuration.startRotation)
+                            testApp.clickEnterPipButton(device)
+                            device.hasPipWindow()
+                        }
                     }
-                    testApp.exit()
-                }
-            }
-            transitions {
-                device.expandPipWindow()
-                device.waitForIdle()
-            }
-            assertions {
-                windowManagerTrace {
-                    navBarWindowIsAlwaysVisible()
-                    statusBarWindowIsAlwaysVisible()
+                    teardown {
+                        eachRun {
+                            this.setRotation(Surface.ROTATION_0)
+                        }
+                        test {
+                            if (device.hasPipWindow()) {
+                                device.closePipWindow()
+                            }
+                            testApp.exit()
+                        }
+                    }
+                    transitions {
+                        device.expandPipWindow()
+                        device.waitForIdle()
+                    }
+                    assertions {
+                        windowManagerTrace {
+                            navBarWindowIsAlwaysVisible()
+                            statusBarWindowIsAlwaysVisible()
 
-                    all("appReplacesPipWindow") {
-                        this.showsAppWindow(sPipWindowTitle)
-                                .then()
-                                .showsAppWindowOnTop(testApp.launcherName)
+                            all("appReplacesPipWindow") {
+                                this.showsAppWindow(PIP_WINDOW_TITLE)
+                                    .then()
+                                    .showsAppWindowOnTop(testApp.launcherName)
+                            }
+                        }
+
+                        layersTrace {
+                            navBarLayerIsAlwaysVisible(bugId = 140855415)
+                            statusBarLayerIsAlwaysVisible()
+                            noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0,
+                                enabled = false)
+                            navBarLayerRotatesAndScales(configuration.startRotation,
+                                Surface.ROTATION_0, bugId = 140855415)
+                            statusBarLayerRotatesScales(configuration.startRotation,
+                                Surface.ROTATION_0)
+
+                            all("appReplacesPipLayer") {
+                                this.showsLayer(PIP_WINDOW_TITLE)
+                                    .then()
+                                    .showsLayer(testApp.launcherName)
+                            }
+                        }
+
+                        eventLog {
+                            focusChanges(
+                                "NexusLauncherActivity", testApp.launcherName,
+                                "NexusLauncherActivity", bugId = 151179149)
+                        }
                     }
                 }
-
-                layersTrace {
-                    navBarLayerIsAlwaysVisible()
-                    statusBarLayerIsAlwaysVisible()
-                    noUncoveredRegions(rotation)
-                    navBarLayerRotatesAndScales(rotation)
-                    statusBarLayerRotatesScales(rotation)
-
-                    all("appReplacesPipLayer") {
-                        this.showsLayer(sPipWindowTitle)
-                                .then()
-                                .showsLayer(testApp.launcherName)
-                    }
-                }
-
-                eventLog {
-                    focusChanges(
-                            "NexusLauncherActivity", testApp.launcherName, "NexusLauncherActivity",
-                            bugId = 151179149)
-                }
-            }
         }
     }
 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToHomeTest.kt
index f40869c..f14a27d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToHomeTest.kt
@@ -19,20 +19,27 @@
 import android.view.Surface
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.dsl.flicker
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
 import com.android.server.wm.flicker.focusChanges
+import com.android.server.wm.flicker.helpers.PipAppHelper
+import com.android.server.wm.flicker.helpers.buildTestTag
 import com.android.server.wm.flicker.helpers.closePipWindow
 import com.android.server.wm.flicker.helpers.hasPipWindow
+import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
 import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
 import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import org.junit.FixMethodOrder
-import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -46,83 +53,83 @@
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @FlakyTest(bugId = 152738416)
 class PipToHomeTest(
-    rotationName: String,
-    rotation: Int
-) : PipTestBase(rotationName, rotation) {
-    @Test
-    fun test() {
-        flicker(instrumentation) {
-            withTag { buildTestTag("exitPipModeToApp", testApp, rotation) }
-            repeat { 1 }
-            setup {
-                test {
-                    device.wakeUpAndGoToHomeScreen()
-                    device.pressHome()
-                }
-                eachRun {
-                    testApp.open()
-                    this.setRotation(rotation)
-                    testApp.clickEnterPipButton(device)
-                    device.hasPipWindow()
-                }
-            }
-            teardown {
-                eachRun {
-                    this.setRotation(Surface.ROTATION_0)
-                    if (device.hasPipWindow()) {
-                        device.closePipWindow()
-                    }
-                }
-                test {
-                    if (device.hasPipWindow()) {
-                        device.closePipWindow()
-                    }
-                    testApp.exit()
-                }
-            }
-            transitions {
-                testApp.closePipWindow(device)
-            }
-            assertions {
-                windowManagerTrace {
-                    navBarWindowIsAlwaysVisible()
-                    statusBarWindowIsAlwaysVisible()
-
-                    all("pipWindowBecomesInvisible") {
-                        this.showsAppWindow(sPipWindowTitle)
-                                .then()
-                                .hidesAppWindow(sPipWindowTitle)
-                    }
-                }
-
-                layersTrace {
-                    navBarLayerIsAlwaysVisible()
-                    statusBarLayerIsAlwaysVisible()
-                    // The final state is the launcher, so always in portrait mode
-                    noUncoveredRegions(rotation, Surface.ROTATION_0, allStates = false)
-                    navBarLayerRotatesAndScales(rotation)
-                    statusBarLayerRotatesScales(rotation)
-
-                    all("pipLayerBecomesInvisible") {
-                        this.showsLayer(sPipWindowTitle)
-                                .then()
-                                .hidesLayer(sPipWindowTitle)
-                    }
-                }
-
-                eventLog {
-                    focusChanges(testApp.launcherName, "NexusLauncherActivity", bugId = 151179149)
-                }
-            }
-        }
-    }
-
+    testName: String,
+    flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
-        fun getParams(): Collection<Array<Any>> {
-            val supportedRotations = intArrayOf(Surface.ROTATION_0)
-            return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
+        fun getParams(): List<Array<Any>> {
+            val instrumentation = InstrumentationRegistry.getInstrumentation()
+            val testApp = PipAppHelper(instrumentation)
+            return FlickerTestRunnerFactory(instrumentation, listOf(Surface.ROTATION_0))
+                .buildTest { configuration ->
+                    withTestName { buildTestTag("exitPipModeToApp", testApp, configuration) }
+                    repeat { configuration.repetitions }
+                    setup {
+                        test {
+                            device.wakeUpAndGoToHomeScreen()
+                            device.pressHome()
+                        }
+                        eachRun {
+                            testApp.open()
+                            this.setRotation(configuration.startRotation)
+                            testApp.clickEnterPipButton(device)
+                            device.hasPipWindow()
+                        }
+                    }
+                    teardown {
+                        eachRun {
+                            this.setRotation(Surface.ROTATION_0)
+                            if (device.hasPipWindow()) {
+                                device.closePipWindow()
+                            }
+                        }
+                        test {
+                            if (device.hasPipWindow()) {
+                                device.closePipWindow()
+                            }
+                            testApp.exit()
+                        }
+                    }
+                    transitions {
+                        testApp.closePipWindow(device)
+                    }
+                    assertions {
+                        windowManagerTrace {
+                            navBarWindowIsAlwaysVisible()
+                            statusBarWindowIsAlwaysVisible()
+
+                            all("pipWindowBecomesInvisible") {
+                                this.showsAppWindow(PIP_WINDOW_TITLE)
+                                    .then()
+                                    .hidesAppWindow(PIP_WINDOW_TITLE)
+                            }
+                        }
+
+                        layersTrace {
+                            navBarLayerIsAlwaysVisible(bugId = 140855415)
+                            statusBarLayerIsAlwaysVisible()
+                            noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0,
+                                enabled = false)
+                            navBarLayerRotatesAndScales(configuration.startRotation,
+                                Surface.ROTATION_0, bugId = 140855415)
+                            statusBarLayerRotatesScales(configuration.startRotation,
+                                Surface.ROTATION_0)
+
+                            all("pipLayerBecomesInvisible") {
+                                this.showsLayer(PIP_WINDOW_TITLE)
+                                    .then()
+                                    .hidesLayer(PIP_WINDOW_TITLE)
+                            }
+                        }
+
+                        eventLog {
+                            focusChanges(testApp.launcherName, "NexusLauncherActivity",
+                                bugId = 151179149)
+                        }
+                    }
+                }
         }
     }
 }
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index 99218c2..24ca311 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -16,24 +16,30 @@
 
 package com.android.server.wm.flicker.rotation
 
+import android.platform.test.annotations.Presubmit
 import androidx.test.filters.RequiresDevice
 import android.view.Surface
-import com.android.server.wm.flicker.NonRotationTestBase.Companion.SCREENSHOT_LAYER
-import com.android.server.wm.flicker.RotationTestBase
-import com.android.server.wm.flicker.helpers.StandardAppHelper
-import com.android.server.wm.flicker.dsl.flicker
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.focusDoesNotChange
+import com.android.server.wm.flicker.helpers.StandardAppHelper
 import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.helpers.buildTestTag
 import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
 import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
 import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import org.junit.FixMethodOrder
-import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -42,84 +48,95 @@
  * Cycle through supported app rotations.
  * To run this test: `atest FlickerTest:ChangeAppRotationTest`
  */
+@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class ChangeAppRotationTest(
-    beginRotationName: String,
-    endRotationName: String,
-    beginRotation: Int,
-    endRotation: Int
-) : RotationTestBase(beginRotationName, endRotationName, beginRotation, endRotation) {
-    @Test
-    fun test() {
-        val testApp = StandardAppHelper(instrumentation,
+    testName: String,
+    flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
+    companion object {
+        private const val SCREENSHOT_LAYER = "RotationLayer"
+
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): Collection<Array<Any>> {
+            val instrumentation = InstrumentationRegistry.getInstrumentation()
+            val testApp = StandardAppHelper(instrumentation,
                 "com.android.server.wm.flicker.testapp", "SimpleApp")
-
-        flicker(instrumentation) {
-            withTag {
-                buildTestTag("changeAppRotation", testApp, beginRotation, endRotation)
-            }
-            repeat { 1 }
-            setup {
-                test {
-                    device.wakeUpAndGoToHomeScreen()
-                    testApp.open()
-                }
-                eachRun {
-                    this.setRotation(beginRotation)
-                }
-            }
-            teardown {
-                eachRun {
-                    this.setRotation(Surface.ROTATION_0)
-                }
-                test {
-                    testApp.exit()
-                }
-            }
-            transitions {
-                this.setRotation(endRotation)
-            }
-            assertions {
-                windowManagerTrace {
-                    navBarWindowIsAlwaysVisible()
-                    statusBarWindowIsAlwaysVisible()
-                }
-
-                layersTrace {
-                    navBarLayerIsAlwaysVisible(bugId = 140855415)
-                    statusBarLayerIsAlwaysVisible(bugId = 140855415)
-                    noUncoveredRegions(beginRotation, endRotation, allStates = false)
-                    navBarLayerRotatesAndScales(beginRotation, endRotation)
-                    statusBarLayerRotatesScales(beginRotation, endRotation)
-                }
-
-                layersTrace {
-                    val startingPos = WindowUtils.getDisplayBounds(beginRotation)
-                    val endingPos = WindowUtils.getDisplayBounds(endRotation)
-
-                    start("appLayerRotates_StartingPos") {
-                        this.hasVisibleRegion(testApp.getPackage(), startingPos)
+            return FlickerTestRunnerFactory(instrumentation)
+                .buildRotationTest { configuration ->
+                    withTestName {
+                        buildTestTag(
+                            "changeAppRotation", testApp, configuration)
                     }
-
-                    end("appLayerRotates_EndingPos") {
-                        this.hasVisibleRegion(testApp.getPackage(), endingPos)
+                    repeat { configuration.repetitions }
+                    setup {
+                        test {
+                            device.wakeUpAndGoToHomeScreen()
+                            testApp.open()
+                        }
+                        eachRun {
+                            this.setRotation(configuration.startRotation)
+                        }
                     }
+                    teardown {
+                        eachRun {
+                            this.setRotation(Surface.ROTATION_0)
+                        }
+                        test {
+                            testApp.exit()
+                        }
+                    }
+                    transitions {
+                        this.setRotation(configuration.endRotation)
+                    }
+                    assertions {
+                        windowManagerTrace {
+                            navBarWindowIsAlwaysVisible()
+                            statusBarWindowIsAlwaysVisible()
+                        }
 
-                    all("screenshotLayerBecomesInvisible") {
-                        this.showsLayer(testApp.getPackage())
-                                .then()
-                                .showsLayer(SCREENSHOT_LAYER)
-                                .then()
+                        layersTrace {
+                            navBarLayerIsAlwaysVisible(bugId = 140855415)
+                            statusBarLayerIsAlwaysVisible(bugId = 140855415)
+                            noUncoveredRegions(configuration.startRotation,
+                                configuration.endRotation, allStates = false)
+                            navBarLayerRotatesAndScales(configuration.startRotation,
+                                configuration.endRotation)
+                            statusBarLayerRotatesScales(configuration.startRotation,
+                                configuration.endRotation)
+                        }
+
+                        layersTrace {
+                            val startingPos = WindowUtils.getDisplayBounds(
+                                configuration.startRotation)
+                            val endingPos = WindowUtils.getDisplayBounds(
+                                configuration.endRotation)
+
+                            start("appLayerRotates_StartingPos") {
+                                this.hasVisibleRegion(testApp.getPackage(), startingPos)
+                            }
+
+                            end("appLayerRotates_EndingPos") {
+                                this.hasVisibleRegion(testApp.getPackage(), endingPos)
+                            }
+
+                            all("screenshotLayerBecomesInvisible") {
+                                this.showsLayer(testApp.getPackage())
+                                        .then()
+                                        .showsLayer(SCREENSHOT_LAYER)
+                                        .then()
                                 showsLayer(testApp.getPackage())
+                            }
+                        }
+
+                        eventLog {
+                            focusDoesNotChange(bugId = 151179149)
+                        }
                     }
                 }
-
-                eventLog {
-                    focusDoesNotChange(bugId = 151179149)
-                }
-            }
         }
     }
 }
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index 33a823d..b29fae3 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -16,29 +16,36 @@
 
 package com.android.server.wm.flicker.rotation
 
+import android.content.ComponentName
 import android.content.Intent
-import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
+import android.os.Bundle
+import android.platform.test.annotations.Presubmit
 import android.view.Surface
-import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
+import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.uiautomator.By
 import androidx.test.uiautomator.Until
-import com.android.server.wm.flicker.RotationTestBase
-import com.android.server.wm.flicker.dsl.flicker
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.focusDoesNotChange
 import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.helpers.buildTestTag
+import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.helpers.stopPackage
 import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
 import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
 import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import com.android.server.wm.flicker.testapp.ActivityOptions
 import org.junit.FixMethodOrder
-import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -47,150 +54,141 @@
  * Cycle through supported app rotations using seamless rotations.
  * To run this test: `atest FlickerTests:SeamlessAppRotationTest`
  */
+@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 147659548)
 class SeamlessAppRotationTest(
-    testId: String,
-    private val intent: Intent,
-    beginRotationName: String,
-    endRotationName: String,
-    beginRotation: Int,
-    endRotation: Int
-) : RotationTestBase(beginRotationName, endRotationName, beginRotation, endRotation) {
-    @Test
-    fun test() {
-        var intentId = ""
-        if (intent.extras?.getBoolean(ActivityOptions.EXTRA_STARVE_UI_THREAD) == true) {
-            intentId = "BUSY_UI_THREAD"
-        }
-
-        flicker(instrumentation) {
-            withTag {
-                "changeAppRotation_" + intentId + "_" +
-                        Surface.rotationToString(beginRotation) + "_" +
-                        Surface.rotationToString(endRotation)
-            }
-            repeat { 1 }
-            setup {
-                eachRun {
-                    device.wakeUpAndGoToHomeScreen()
-                    instrumentation.targetContext.startActivity(intent)
-                    device.wait(Until.hasObject(By.pkg(intent.component?.packageName)
-                            .depth(0)), APP_LAUNCH_TIMEOUT)
-                    this.setRotation(beginRotation)
-                }
-            }
-            teardown {
-                eachRun {
-                    stopPackage(
-                            instrumentation.targetContext,
-                            intent.component?.packageName
-                                    ?: error("Unable to determine package name for intent"))
-                    this.setRotation(Surface.ROTATION_0)
-                }
-            }
-            transitions {
-                this.setRotation(endRotation)
-            }
-            assertions {
-                windowManagerTrace {
-                    navBarWindowIsAlwaysVisible(bugId = 140855415)
-                    statusBarWindowIsAlwaysVisible(bugId = 140855415)
-                }
-
-                layersTrace {
-                    navBarLayerIsAlwaysVisible(bugId = 140855415)
-                    statusBarLayerIsAlwaysVisible(bugId = 140855415)
-                    noUncoveredRegions(beginRotation, endRotation, allStates = true)
-                    navBarLayerRotatesAndScales(beginRotation, endRotation)
-                    statusBarLayerRotatesScales(beginRotation, endRotation, enabled = false)
-                }
-
-                layersTrace {
-                    all("appLayerRotates"/*, bugId = 147659548*/) {
-                        val startingPos = WindowUtils.getDisplayBounds(beginRotation)
-                        val endingPos = WindowUtils.getDisplayBounds(endRotation)
-
-                        if (startingPos == endingPos) {
-                            this.hasVisibleRegion(
-                                    intent.component?.packageName ?: "",
-                                    startingPos)
-                        } else {
-                            this.hasVisibleRegion(intent.component?.packageName ?: "", startingPos)
-                                    .then()
-                                    .hasVisibleRegion(intent.component?.packageName
-                                            ?: "", endingPos)
-                        }
-                    }
-
-                    all("noUncoveredRegions"/*, bugId = 147659548*/) {
-                        val startingBounds = WindowUtils.getDisplayBounds(beginRotation)
-                        val endingBounds = WindowUtils.getDisplayBounds(endRotation)
-                        if (startingBounds == endingBounds) {
-                            this.coversAtLeastRegion(startingBounds)
-                        } else {
-                            this.coversAtLeastRegion(startingBounds)
-                                    .then()
-                                    .coversAtLeastRegion(endingBounds)
-                        }
-                    }
-                }
-
-                eventLog {
-                    focusDoesNotChange(bugId = 151179149)
-                }
-            }
-        }
-    }
-
+    testName: String,
+    flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
     companion object {
         private const val APP_LAUNCH_TIMEOUT: Long = 10000
 
-        // launch test activity that supports seamless rotation with a busy UI thread to miss frames
-        // when the app is asked to redraw
+        private val Bundle.intent: Intent?
+            get() = this.getParcelable(Intent::class.java.simpleName)
+
+        private val Bundle.intentPackageName: String
+            get() = this.intent?.component?.packageName ?: ""
+
+        private val Bundle.intentId get() = if (this.intent?.getBooleanExtra(
+                ActivityOptions.EXTRA_STARVE_UI_THREAD, false) == true) {
+            "BUSY_UI_THREAD"
+        } else {
+            ""
+        }
+
+        private fun Bundle.createConfig(starveUiThread: Boolean): Bundle {
+            val config = this.deepCopy()
+            val intent = Intent()
+            intent.addCategory(Intent.CATEGORY_LAUNCHER)
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+            intent.component = ComponentName("com.android.server.wm.flicker.testapp",
+                "com.android.server.wm.flicker.testapp.SeamlessRotationActivity")
+
+            intent.putExtra(ActivityOptions.EXTRA_STARVE_UI_THREAD, starveUiThread)
+
+            config.putParcelable(Intent::class.java.simpleName, intent)
+            return config
+        }
+
+        @JvmStatic
+        private fun FlickerTestRunnerFactory.getConfigurations(): List<Bundle> {
+            return this.getConfigRotationTests().flatMap {
+                val defaultRun = it.createConfig(starveUiThread = false)
+                val busyUiRun = it.createConfig(starveUiThread = true)
+                listOf(defaultRun, busyUiRun)
+            }
+        }
+
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
         fun getParams(): Collection<Array<Any>> {
-            val supportedRotations = intArrayOf(Surface.ROTATION_0, Surface.ROTATION_90)
-            val params = mutableListOf<Array<Any>>()
-            val testIntents = mutableListOf<Intent>()
+            val instrumentation = InstrumentationRegistry.getInstrumentation()
+            val factory = FlickerTestRunnerFactory(instrumentation)
+            val configurations = factory.getConfigurations()
+            return factory.buildRotationTest(configurations) { configuration ->
+                withTestName {
+                    buildTestTag("seamlessRotation_" + configuration.intentId,
+                        app = null, configuration = configuration)
+                }
+                repeat { configuration.repetitions }
+                setup {
+                    test {
+                        device.wakeUpAndGoToHomeScreen()
+                        instrumentation.targetContext.startActivity(configuration.intent)
+                        val searchQuery = By.pkg(configuration.intent?.component?.packageName)
+                            .depth(0)
+                        device.wait(Until.hasObject(searchQuery), APP_LAUNCH_TIMEOUT)
+                    }
+                    eachRun {
+                        this.setRotation(configuration.startRotation)
+                    }
+                }
+                teardown {
+                    test {
+                        this.setRotation(Surface.ROTATION_0)
+                        stopPackage(
+                            instrumentation.targetContext,
+                            configuration.intent?.component?.packageName
+                                ?: error("Unable to determine package name for intent"))
+                    }
+                }
+                transitions {
+                    this.setRotation(configuration.endRotation)
+                }
+                assertions {
+                    windowManagerTrace {
+                        navBarWindowIsAlwaysVisible(bugId = 140855415)
+                        statusBarWindowIsAlwaysVisible(bugId = 140855415)
+                    }
 
-            // launch test activity that supports seamless rotation
-            var intent = Intent(Intent.ACTION_MAIN)
-            intent.component = ActivityOptions.SEAMLESS_ACTIVITY_COMPONENT_NAME
-            intent.flags = FLAG_ACTIVITY_NEW_TASK
-            testIntents.add(intent)
+                    layersTrace {
+                        navBarLayerIsAlwaysVisible(bugId = 140855415)
+                        statusBarLayerIsAlwaysVisible(bugId = 140855415)
+                        noUncoveredRegions(configuration.startRotation,
+                            configuration.endRotation, allStates = false, bugId = 147659548)
+                        navBarLayerRotatesAndScales(configuration.startRotation,
+                            configuration.endRotation)
+                        statusBarLayerRotatesScales(configuration.startRotation,
+                            configuration.endRotation, enabled = false)
+                    }
 
-            // launch test activity that supports seamless rotation with a busy UI thread to miss frames
-            // when the app is asked to redraw
-            intent = Intent(intent)
-            intent.putExtra(ActivityOptions.EXTRA_STARVE_UI_THREAD, true)
-            intent.flags = FLAG_ACTIVITY_NEW_TASK
-            testIntents.add(intent)
-            for (testIntent in testIntents) {
-                for (begin in supportedRotations) {
-                    for (end in supportedRotations) {
-                        if (begin != end) {
-                            var testId: String = Surface.rotationToString(begin) +
-                                    "_" + Surface.rotationToString(end)
-                            if (testIntent.extras?.getBoolean(
-                                            ActivityOptions.EXTRA_STARVE_UI_THREAD) == true) {
-                                testId += "_" + "BUSY_UI_THREAD"
+                    layersTrace {
+                        val startingBounds = WindowUtils
+                            .getDisplayBounds(configuration.startRotation)
+                        val endingBounds = WindowUtils
+                            .getDisplayBounds(configuration.endRotation)
+
+                        all("appLayerRotates", bugId = 147659548) {
+                            if (startingBounds == endingBounds) {
+                                this.hasVisibleRegion(
+                                    configuration.intentPackageName, startingBounds)
+                            } else {
+                                this.hasVisibleRegion(configuration.intentPackageName,
+                                    startingBounds)
+                                    .then()
+                                    .hasVisibleRegion(configuration.intentPackageName,
+                                        endingBounds)
                             }
-                            params.add(arrayOf(
-                                    testId,
-                                    testIntent,
-                                    Surface.rotationToString(begin),
-                                    Surface.rotationToString(end),
-                                    begin,
-                                    end))
                         }
+
+                        all("noUncoveredRegions"/*, bugId = 147659548*/) {
+                            if (startingBounds == endingBounds) {
+                                this.coversAtLeastRegion(startingBounds)
+                            } else {
+                                this.coversAtLeastRegion(startingBounds)
+                                    .then()
+                                    .coversAtLeastRegion(endingBounds)
+                            }
+                        }
+                    }
+
+                    eventLog {
+                        focusDoesNotChange(bugId = 151179149)
                     }
                 }
             }
-            return params
         }
     }
 }
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt
index 3b5e669..ae9fcf9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt
@@ -16,25 +16,32 @@
 
 package com.android.server.wm.flicker.splitscreen
 
-import android.view.Surface
+import android.platform.test.annotations.Presubmit
+import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.NonRotationTestBase
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.helpers.StandardAppHelper
-import com.android.server.wm.flicker.dsl.flicker
 import com.android.server.wm.flicker.focusChanges
+import com.android.server.wm.flicker.helpers.buildTestTag
 import com.android.server.wm.flicker.helpers.exitSplitScreen
 import com.android.server.wm.flicker.helpers.isInSplitScreen
 import com.android.server.wm.flicker.helpers.launchSplitScreen
+import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
 import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
 import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.repetitions
 import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import org.junit.FixMethodOrder
-import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -43,78 +50,79 @@
  * Test open app to split screen.
  * To run this test: `atest FlickerTests:OpenAppToSplitScreenTest`
  */
+@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@FlakyTest(bugId = 161435597)
 class OpenAppToSplitScreenTest(
-    rotationName: String,
-    rotation: Int
-) : NonRotationTestBase(rotationName, rotation) {
-    @Test
-    fun test() {
-        val testApp = StandardAppHelper(instrumentation,
-        "com.android.server.wm.flicker.testapp", "SimpleApp")
-
-        flicker(instrumentation) {
-            withTag { buildTestTag("appToSplitScreen", testApp, rotation) }
-            repeat { 1 }
-            setup {
-                test {
-                    device.wakeUpAndGoToHomeScreen()
-                }
-                eachRun {
-                    testApp.open()
-                    this.setRotation(rotation)
-                }
-            }
-            teardown {
-                eachRun {
-                    if (device.isInSplitScreen()) {
-                        device.exitSplitScreen()
-                    }
-                }
-                test {
-                    testApp.exit()
-                }
-            }
-            transitions {
-                device.launchSplitScreen()
-            }
-            assertions {
-                windowManagerTrace {
-                    navBarWindowIsAlwaysVisible()
-                    statusBarWindowIsAlwaysVisible()
-                }
-
-                layersTrace {
-                    navBarLayerIsAlwaysVisible(bugId = 140855415)
-                    statusBarLayerIsAlwaysVisible()
-                    noUncoveredRegions(rotation, enabled = false)
-                    navBarLayerRotatesAndScales(rotation, bugId = 140855415)
-                    statusBarLayerRotatesScales(rotation)
-
-                    all("dividerLayerBecomesVisible") {
-                        this.hidesLayer(DOCKED_STACK_DIVIDER)
-                                .then()
-                                .showsLayer(DOCKED_STACK_DIVIDER)
-                    }
-                }
-
-                eventLog {
-                    focusChanges(testApp.`package`,
-                            "recents_animation_input_consumer", "NexusLauncherActivity",
-                            bugId = 151179149)
-                }
-            }
-        }
-    }
-
+    testName: String,
+    flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
         fun getParams(): Collection<Array<Any>> {
-            val supportedRotations = intArrayOf(Surface.ROTATION_0)
-            return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
+            val instrumentation = InstrumentationRegistry.getInstrumentation()
+            val testApp = StandardAppHelper(instrumentation,
+                "com.android.server.wm.flicker.testapp", "SimpleApp")
+
+            return FlickerTestRunnerFactory(instrumentation)
+                .buildTest { configuration ->
+                    withTestName {
+                        buildTestTag("appToSplitScreen", testApp, configuration)
+                    }
+                    repeat { configuration.repetitions }
+                    setup {
+                        test {
+                            device.wakeUpAndGoToHomeScreen()
+                        }
+                        eachRun {
+                            testApp.open()
+                            this.setRotation(configuration.endRotation)
+                        }
+                    }
+                    teardown {
+                        eachRun {
+                            if (device.isInSplitScreen()) {
+                                device.exitSplitScreen()
+                            }
+                        }
+                        test {
+                            testApp.exit()
+                        }
+                    }
+                    transitions {
+                        device.launchSplitScreen()
+                    }
+                    assertions {
+                        windowManagerTrace {
+                            navBarWindowIsAlwaysVisible()
+                            statusBarWindowIsAlwaysVisible()
+                        }
+
+                        layersTrace {
+                            navBarLayerIsAlwaysVisible(bugId = 140855415)
+                            statusBarLayerIsAlwaysVisible()
+                            noUncoveredRegions(configuration.endRotation, enabled = false)
+                            navBarLayerRotatesAndScales(configuration.endRotation,
+                                bugId = 140855415)
+                            statusBarLayerRotatesScales(configuration.endRotation)
+
+                            all("dividerLayerBecomesVisible") {
+                                this.hidesLayer(DOCKED_STACK_DIVIDER)
+                                    .then()
+                                    .showsLayer(DOCKED_STACK_DIVIDER)
+                            }
+                        }
+
+                        eventLog {
+                            focusChanges(testApp.`package`,
+                                "recents_animation_input_consumer", "NexusLauncherActivity",
+                                bugId = 151179149)
+                        }
+                    }
+                }
         }
     }
 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/ResizeSplitScreenTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/ResizeSplitScreenTest.kt
index abf41a1..4b9f024 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/ResizeSplitScreenTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/ResizeSplitScreenTest.kt
@@ -19,32 +19,39 @@
 import android.graphics.Region
 import android.util.Rational
 import android.view.Surface
-import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
+import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.uiautomator.By
-import com.android.server.wm.flicker.FlickerTestBase
+import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.helpers.StandardAppHelper
-import com.android.server.wm.flicker.dsl.flicker
 import com.android.server.wm.flicker.focusDoesNotChange
 import com.android.server.wm.flicker.helpers.ImeAppHelper
 import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.helpers.buildTestTag
 import com.android.server.wm.flicker.helpers.exitSplitScreen
 import com.android.server.wm.flicker.helpers.isInSplitScreen
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.resizeSplitScreen
+import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
 import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
 import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import org.junit.FixMethodOrder
-import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
 
 /**
  * Test split screen resizing window transitions.
@@ -53,138 +60,149 @@
  * Currently it runs only in 0 degrees because of b/156100803
  */
 @RequiresDevice
-@RunWith(AndroidJUnit4::class)
+@RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @FlakyTest(bugId = 159096424)
-class ResizeSplitScreenTest : FlickerTestBase() {
-    @Test
-    fun test() {
-        val testAppTop = StandardAppHelper(instrumentation,
-                "com.android.server.wm.flicker.testapp", "SimpleApp")
-        val testAppBottom = ImeAppHelper(instrumentation)
-
-        flicker(instrumentation) {
-            withTag {
-                val description = (startRatio.toString().replace("/", "-") + "_to_" +
-                        stopRatio.toString().replace("/", "-"))
-                buildTestTag("resizeSplitScreen", testAppTop, rotation,
-                        rotation, testAppBottom, description)
-            }
-            repeat { 1 }
-            setup {
-                eachRun {
-                    device.wakeUpAndGoToHomeScreen()
-                    this.setRotation(rotation)
-                    this.launcherStrategy.clearRecentAppsFromOverview()
-                    testAppBottom.open()
-                    device.pressHome()
-                    testAppTop.open()
-                    device.waitForIdle()
-                    device.launchSplitScreen()
-                    val snapshot = device.findObject(By.res(device.launcherPackageName, "snapshot"))
-                    snapshot.click()
-                    testAppBottom.openIME(device)
-                    device.pressBack()
-                    device.resizeSplitScreen(startRatio)
-                }
-            }
-            teardown {
-                eachRun {
-                    device.exitSplitScreen()
-                    device.pressHome()
-                    testAppTop.exit()
-                    testAppBottom.exit()
-                }
-                test {
-                    if (device.isInSplitScreen()) {
-                        device.exitSplitScreen()
-                    }
-                }
-            }
-            transitions {
-                device.resizeSplitScreen(stopRatio)
-            }
-            assertions {
-                windowManagerTrace {
-                    navBarWindowIsAlwaysVisible()
-                    statusBarWindowIsAlwaysVisible()
-
-                    all("topAppWindowIsAlwaysVisible", bugId = 156223549) {
-                        this.showsAppWindow(sSimpleActivity)
-                    }
-
-                    all("bottomAppWindowIsAlwaysVisible", bugId = 156223549) {
-                        this.showsAppWindow(sImeActivity)
-                    }
-                }
-
-                layersTrace {
-                    navBarLayerIsAlwaysVisible()
-                    statusBarLayerIsAlwaysVisible()
-                    noUncoveredRegions(rotation)
-                    navBarLayerRotatesAndScales(rotation)
-                    statusBarLayerRotatesScales(rotation)
-
-                    all("topAppLayerIsAlwaysVisible") {
-                        this.showsLayer(sSimpleActivity)
-                    }
-
-                    all("bottomAppLayerIsAlwaysVisible") {
-                        this.showsLayer(sImeActivity)
-                    }
-
-                    all("dividerLayerIsAlwaysVisible") {
-                        this.showsLayer(DOCKED_STACK_DIVIDER)
-                    }
-
-                    start("appsStartingBounds", enabled = false) {
-                        val displayBounds = WindowUtils.displayBounds
-                        val entry = this.trace.entries.firstOrNull()
-                                ?: throw IllegalStateException("Trace is empty")
-                        val dividerBounds = entry.getVisibleBounds(DOCKED_STACK_DIVIDER).bounds
-
-                        val topAppBounds = Region(0, 0, dividerBounds.right,
-                                dividerBounds.top + WindowUtils.dockedStackDividerInset)
-                        val bottomAppBounds = Region(0,
-                                dividerBounds.bottom - WindowUtils.dockedStackDividerInset,
-                                displayBounds.right,
-                                displayBounds.bottom - WindowUtils.navigationBarHeight)
-                        this.hasVisibleRegion("SimpleActivity", topAppBounds)
-                                .and()
-                                .hasVisibleRegion("ImeActivity", bottomAppBounds)
-                    }
-
-                    end("appsEndingBounds", enabled = false) {
-                        val displayBounds = WindowUtils.displayBounds
-                        val entry = this.trace.entries.lastOrNull()
-                                ?: throw IllegalStateException("Trace is empty")
-                        val dividerBounds = entry.getVisibleBounds(DOCKED_STACK_DIVIDER).bounds
-
-                        val topAppBounds = Region(0, 0, dividerBounds.right,
-                                dividerBounds.top + WindowUtils.dockedStackDividerInset)
-                        val bottomAppBounds = Region(0,
-                                dividerBounds.bottom - WindowUtils.dockedStackDividerInset,
-                                displayBounds.right,
-                                displayBounds.bottom - WindowUtils.navigationBarHeight)
-
-                        this.hasVisibleRegion(sSimpleActivity, topAppBounds)
-                                .and()
-                                .hasVisibleRegion(sImeActivity, bottomAppBounds)
-                    }
-                }
-
-                eventLog {
-                    focusDoesNotChange()
-                }
-            }
-        }
-    }
-
+class ResizeSplitScreenTest(
+    testName: String,
+    flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
     companion object {
         private const val sSimpleActivity = "SimpleActivity"
         private const val sImeActivity = "ImeActivity"
-        private val rotation = Surface.ROTATION_0
         private val startRatio = Rational(1, 3)
         private val stopRatio = Rational(2, 3)
+
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): Collection<Array<Any>> {
+            val instrumentation = InstrumentationRegistry.getInstrumentation()
+            val testAppTop = StandardAppHelper(instrumentation,
+                "com.android.server.wm.flicker.testapp", "SimpleApp")
+            val testAppBottom = ImeAppHelper(instrumentation)
+
+            return FlickerTestRunnerFactory(instrumentation, listOf(Surface.ROTATION_0))
+                .buildTest { configuration ->
+                    withTestName {
+                        val description = (startRatio.toString().replace("/", "-") + "_to_" +
+                            stopRatio.toString().replace("/", "-"))
+                        buildTestTag("resizeSplitScreen", testAppTop.launcherName,
+                            configuration.startRotation, configuration.endRotation,
+                            testAppBottom.launcherName, description)
+                    }
+                    repeat { configuration.repetitions }
+                    setup {
+                        eachRun {
+                            device.wakeUpAndGoToHomeScreen()
+                            this.setRotation(configuration.startRotation)
+                            this.launcherStrategy.clearRecentAppsFromOverview()
+                            testAppBottom.open()
+                            device.pressHome()
+                            testAppTop.open()
+                            device.waitForIdle()
+                            device.launchSplitScreen()
+                            val snapshot =
+                                device.findObject(By.res(device.launcherPackageName, "snapshot"))
+                            snapshot.click()
+                            testAppBottom.openIME(device)
+                            device.pressBack()
+                            device.resizeSplitScreen(startRatio)
+                        }
+                    }
+                    teardown {
+                        eachRun {
+                            if (device.isInSplitScreen()) {
+                                device.exitSplitScreen()
+                            }
+                            device.pressHome()
+                            testAppTop.exit()
+                            testAppBottom.exit()
+                        }
+                        test {
+                            if (device.isInSplitScreen()) {
+                                device.exitSplitScreen()
+                            }
+                        }
+                    }
+                    transitions {
+                        device.resizeSplitScreen(stopRatio)
+                    }
+                    assertions {
+                        windowManagerTrace {
+                            navBarWindowIsAlwaysVisible()
+                            statusBarWindowIsAlwaysVisible()
+
+                            all("topAppWindowIsAlwaysVisible", bugId = 156223549) {
+                                this.showsAppWindow(sSimpleActivity)
+                            }
+
+                            all("bottomAppWindowIsAlwaysVisible", bugId = 156223549) {
+                                this.showsAppWindow(sImeActivity)
+                            }
+                        }
+
+                        layersTrace {
+                            navBarLayerIsAlwaysVisible()
+                            statusBarLayerIsAlwaysVisible()
+                            noUncoveredRegions(configuration.endRotation)
+                            navBarLayerRotatesAndScales(configuration.endRotation)
+                            statusBarLayerRotatesScales(configuration.endRotation)
+
+                            all("topAppLayerIsAlwaysVisible") {
+                                this.showsLayer(sSimpleActivity)
+                            }
+
+                            all("bottomAppLayerIsAlwaysVisible") {
+                                this.showsLayer(sImeActivity)
+                            }
+
+                            all("dividerLayerIsAlwaysVisible") {
+                                this.showsLayer(DOCKED_STACK_DIVIDER)
+                            }
+
+                            start("appsStartingBounds", enabled = false) {
+                                val displayBounds = WindowUtils.displayBounds
+                                val entry = this.trace.entries.firstOrNull()
+                                    ?: throw IllegalStateException("Trace is empty")
+                                val dividerBounds =
+                                    entry.getVisibleBounds(DOCKED_STACK_DIVIDER).bounds
+
+                                val topAppBounds = Region(0, 0, dividerBounds.right,
+                                    dividerBounds.top + WindowUtils.dockedStackDividerInset)
+                                val bottomAppBounds = Region(0,
+                                    dividerBounds.bottom - WindowUtils.dockedStackDividerInset,
+                                    displayBounds.right,
+                                    displayBounds.bottom - WindowUtils.navigationBarHeight)
+                                this.hasVisibleRegion("SimpleActivity", topAppBounds)
+                                    .and()
+                                    .hasVisibleRegion("ImeActivity", bottomAppBounds)
+                            }
+
+                            end("appsEndingBounds", enabled = false) {
+                                val displayBounds = WindowUtils.displayBounds
+                                val entry = this.trace.entries.lastOrNull()
+                                    ?: throw IllegalStateException("Trace is empty")
+                                val dividerBounds =
+                                    entry.getVisibleBounds(DOCKED_STACK_DIVIDER).bounds
+
+                                val topAppBounds = Region(0, 0, dividerBounds.right,
+                                    dividerBounds.top + WindowUtils.dockedStackDividerInset)
+                                val bottomAppBounds = Region(0,
+                                    dividerBounds.bottom - WindowUtils.dockedStackDividerInset,
+                                    displayBounds.right,
+                                    displayBounds.bottom - WindowUtils.navigationBarHeight)
+
+                                this.hasVisibleRegion(sSimpleActivity, topAppBounds)
+                                    .and()
+                                    .hasVisibleRegion(sImeActivity, bottomAppBounds)
+                            }
+                        }
+
+                        eventLog {
+                            focusDoesNotChange()
+                        }
+                    }
+                }
+        }
     }
 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt
index 7447bda..f966a66 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt
@@ -19,23 +19,29 @@
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.NonRotationTestBase
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
 import com.android.server.wm.flicker.helpers.StandardAppHelper
-import com.android.server.wm.flicker.dsl.flicker
+import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.focusDoesNotChange
+import com.android.server.wm.flicker.helpers.buildTestTag
 import com.android.server.wm.flicker.helpers.exitSplitScreen
 import com.android.server.wm.flicker.helpers.isInSplitScreen
 import com.android.server.wm.flicker.helpers.launchSplitScreen
+import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
 import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
 import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.repetitions
 import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import org.junit.FixMethodOrder
-import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -49,82 +55,80 @@
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class SplitScreenToLauncherTest(
-    rotationName: String,
-    rotation: Int
-) : NonRotationTestBase(rotationName, rotation) {
-    @Test
-    fun test() {
-        val testApp = StandardAppHelper(instrumentation,
-                "com.android.server.wm.flicker.testapp", "SimpleApp")
-
-        flicker(instrumentation) {
-            withTag { buildTestTag("splitScreenToLauncher", testApp, rotation) }
-            repeat { 1 }
-            setup {
-                test {
-                    device.wakeUpAndGoToHomeScreen()
-                }
-                eachRun {
-                    testApp.open()
-                    this.setRotation(rotation)
-                    device.launchSplitScreen()
-                    device.waitForIdle()
-                }
-            }
-            teardown {
-                eachRun {
-                    testApp.exit()
-                }
-                test {
-                    if (device.isInSplitScreen()) {
-                        device.exitSplitScreen()
-                    }
-                }
-            }
-            transitions {
-                device.exitSplitScreen()
-            }
-            assertions {
-                windowManagerTrace {
-                    navBarWindowIsAlwaysVisible()
-                    statusBarWindowIsAlwaysVisible()
-                }
-
-                layersTrace {
-                    navBarLayerIsAlwaysVisible()
-                    statusBarLayerIsAlwaysVisible()
-                    noUncoveredRegions(rotation)
-                    navBarLayerRotatesAndScales(rotation)
-                    statusBarLayerRotatesScales(rotation)
-
-                    // b/161435597 causes the test not to work on 90 degrees
-                    all("dividerLayerBecomesInvisible") {
-                        this.showsLayer(DOCKED_STACK_DIVIDER)
-                                .then()
-                                .hidesLayer(DOCKED_STACK_DIVIDER)
-                    }
-
-                    all("appLayerBecomesInvisible") {
-                        this.showsLayer(testApp.getPackage())
-                            .then()
-                            .hidesLayer(testApp.getPackage())
-                    }
-                }
-
-                eventLog {
-                    focusDoesNotChange(bugId = 151179149)
-                }
-            }
-        }
-    }
-
+    testName: String,
+    flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
         fun getParams(): Collection<Array<Any>> {
+            val instrumentation = InstrumentationRegistry.getInstrumentation()
+            val testApp = StandardAppHelper(instrumentation,
+                "com.android.server.wm.flicker.testapp", "SimpleApp")
+
             // b/161435597 causes the test not to work on 90 degrees
-            val supportedRotations = intArrayOf(Surface.ROTATION_0)
-            return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
+            return FlickerTestRunnerFactory(instrumentation, listOf(Surface.ROTATION_0))
+                .buildTest { configuration ->
+                    withTestName {
+                        buildTestTag("splitScreenToLauncher", testApp, configuration)
+                    }
+                    repeat { configuration.repetitions }
+                    setup {
+                        test {
+                            device.wakeUpAndGoToHomeScreen()
+                        }
+                        eachRun {
+                            testApp.open()
+                            this.setRotation(configuration.endRotation)
+                            device.launchSplitScreen()
+                            device.waitForIdle()
+                        }
+                    }
+                    teardown {
+                        eachRun {
+                            testApp.exit()
+                        }
+                        test {
+                            if (device.isInSplitScreen()) {
+                                device.exitSplitScreen()
+                            }
+                        }
+                    }
+                    transitions {
+                        device.exitSplitScreen()
+                    }
+                    assertions {
+                        windowManagerTrace {
+                            navBarWindowIsAlwaysVisible()
+                            statusBarWindowIsAlwaysVisible()
+                        }
+
+                        layersTrace {
+                            navBarLayerIsAlwaysVisible()
+                            statusBarLayerIsAlwaysVisible()
+                            noUncoveredRegions(configuration.endRotation)
+                            navBarLayerRotatesAndScales(configuration.endRotation)
+                            statusBarLayerRotatesScales(configuration.endRotation)
+
+                            // b/161435597 causes the test not to work on 90 degrees
+                            all("dividerLayerBecomesInvisible") {
+                                this.showsLayer(DOCKED_STACK_DIVIDER)
+                                    .then()
+                                    .hidesLayer(DOCKED_STACK_DIVIDER)
+                            }
+
+                            all("appLayerBecomesInvisible") {
+                                this.showsLayer(testApp.getPackage())
+                                    .then()
+                                    .hidesLayer(testApp.getPackage())
+                            }
+                        }
+
+                        eventLog {
+                            focusDoesNotChange(bugId = 151179149)
+                        }
+                    }
+                }
         }
     }
 }
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java
index 08144c8..b53b78a 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.graphics.BitmapShader;
 import android.graphics.Canvas;
 import android.graphics.ColorMatrix;
 import android.graphics.ColorMatrixColorFilter;
@@ -30,6 +31,7 @@
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.RuntimeShader;
+import android.graphics.Shader;
 import android.os.Bundle;
 import android.view.View;
 
@@ -59,9 +61,10 @@
         private int mPorterDuffColor = 0;
 
         static final String sSkSL =
-                "uniform float param1;\n"
-                + "void main(float2 xy, inout half4 color) {\n"
-                + "color = half4(color.r, half(param1), color.b, 1.0);\n"
+                "in shader bitmapShader;\n"
+                + "uniform float param1;\n"
+                + "half4 main(float2 xy) {\n"
+                + "  return half4(sample(bitmapShader, xy).rgb, param1);\n"
                 + "}\n";
 
         private byte[] mUniforms = new byte[4];
@@ -84,7 +87,9 @@
             mBlendPaint.setColorFilter(new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_OVER));
 
             mShaderPaint = new Paint();
-            mShaderPaint.setShader(new RuntimeShader(sSkSL, mUniforms, true));
+            Shader[] inputShaders = { new BitmapShader(mBitmap1, Shader.TileMode.CLAMP,
+                                                       Shader.TileMode.CLAMP) };
+            mShaderPaint.setShader(new RuntimeShader(sSkSL, mUniforms, inputShaders, true));
             setShaderParam1(0.0f);
 
             ObjectAnimator sat = ObjectAnimator.ofFloat(this, "saturation", 1.0f);
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index 9d35cbc..0e9f723 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -9,4 +9,5 @@
             "android-support-test",
             "ub-uiautomator",
         ],
+    test_suites: ["device-tests"],
 }
diff --git a/tests/Input/TEST_MAPPING b/tests/Input/TEST_MAPPING
new file mode 100644
index 0000000..15b2bfa
--- /dev/null
+++ b/tests/Input/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "InputTests"
+    }
+  ]
+}
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
index ab2e492..52718be 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
@@ -482,6 +482,26 @@
     }
 
     @Test
+    public void testRollbackApkDataDirectories_Phase1() throws Exception {
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
+        Install.single(TestApp.A1).commit();
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+    }
+
+    @Test
+    public void testRollbackApkDataDirectories_Phase2() throws Exception {
+        Install.single(TestApp.A2).setStaged().setEnableRollback().commit();
+    }
+
+    @Test
+    public void testRollbackApkDataDirectories_Phase3() throws Exception {
+        RollbackInfo available = RollbackUtils.getAvailableRollback(TestApp.A);
+        RollbackUtils.rollback(available.getRollbackId(), TestApp.A2);
+        RollbackInfo committed = RollbackUtils.getCommittedRollbackById(available.getRollbackId());
+        InstallUtils.waitForSessionReady(committed.getCommittedSessionId());
+    }
+
+    @Test
     public void isCheckpointSupported() {
         Context context = InstrumentationRegistry.getInstrumentation().getContext();
         StorageManager sm = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
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
index c2fd0c3..725bfa9 100644
--- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
+++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
@@ -18,6 +18,8 @@
 
 import static com.android.tests.rollback.host.WatchdogEventLogger.watchdogEventOccurred;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
@@ -25,6 +27,7 @@
 import static org.junit.Assume.assumeTrue;
 
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.ddmlib.Log;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.IFileEntry;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
@@ -51,6 +54,7 @@
  */
 @RunWith(DeviceJUnit4ClassRunner.class)
 public class StagedRollbackTest extends BaseHostJUnit4Test {
+    private static final String TAG = "StagedRollbackTest";
     private static final int NATIVE_CRASHES_THRESHOLD = 5;
 
     /**
@@ -124,12 +128,14 @@
         }
 
         if (found) {
-            if (!getDevice().isAdbRoot()) {
+            try {
                 getDevice().enableAdbRoot();
-            }
-            getDevice().remountSystemWritable();
-            for (String file : files) {
-                getDevice().executeShellCommand("rm -rf " + file);
+                getDevice().remountSystemWritable();
+                for (String file : files) {
+                    getDevice().executeShellCommand("rm -rf " + file);
+                }
+            } finally {
+                getDevice().disableAdbRoot();
             }
             getDevice().reboot();
         }
@@ -272,7 +278,9 @@
         List<String> after = getSnapshotDirectories("/data/misc_ce/0/rollback");
         // Only check directories newly created during the test
         after.removeAll(before);
-        after.forEach(dir -> assertDirectoryIsEmpty(dir));
+        // There should be only one /data/misc_ce/0/rollback/<rollbackId> created during test
+        assertThat(after).hasSize(1);
+        assertDirectoryIsEmpty(after.get(0));
     }
 
     /**
@@ -328,37 +336,45 @@
         String oldFilePath1 = apexDataDirDeSys(APK_IN_APEX_TESTAPEX_NAME) + "/" + TEST_FILENAME_1;
         String oldFilePath2 =
                 apexDataDirDeSys(APK_IN_APEX_TESTAPEX_NAME) + TEST_SUBDIR + TEST_FILENAME_2;
-        assertTrue(getDevice().pushString(TEST_STRING_1, oldFilePath1));
-        assertTrue(getDevice().pushString(TEST_STRING_2, oldFilePath2));
+        runAsRoot(() -> {
+            assertThat(getDevice().pushString(TEST_STRING_1, oldFilePath1)).isTrue();
+            assertThat(getDevice().pushString(TEST_STRING_2, oldFilePath2)).isTrue();
+        });
 
         // Install new version of the APEX with rollback enabled
         runPhase("testRollbackApexDataDirectories_Phase1");
         getDevice().reboot();
 
         // Replace files in data directory
-        getDevice().deleteFile(oldFilePath1);
-        getDevice().deleteFile(oldFilePath2);
         String newFilePath3 = apexDataDirDeSys(APK_IN_APEX_TESTAPEX_NAME) + "/" + TEST_FILENAME_3;
         String newFilePath4 =
                 apexDataDirDeSys(APK_IN_APEX_TESTAPEX_NAME) + TEST_SUBDIR + TEST_FILENAME_4;
-        assertTrue(getDevice().pushString(TEST_STRING_3, newFilePath3));
-        assertTrue(getDevice().pushString(TEST_STRING_4, newFilePath4));
+        runAsRoot(() -> {
+            getDevice().deleteFile(oldFilePath1);
+            getDevice().deleteFile(oldFilePath2);
+            assertThat(getDevice().pushString(TEST_STRING_3, newFilePath3)).isTrue();
+            assertThat(getDevice().pushString(TEST_STRING_4, newFilePath4)).isTrue();
+        });
 
         // Roll back the APEX
         runPhase("testRollbackApexDataDirectories_Phase2");
         getDevice().reboot();
 
         // Verify that old files have been restored and new files are gone
-        assertEquals(TEST_STRING_1, getDevice().pullFileContents(oldFilePath1));
-        assertEquals(TEST_STRING_2, getDevice().pullFileContents(oldFilePath2));
-        assertNull(getDevice().pullFile(newFilePath3));
-        assertNull(getDevice().pullFile(newFilePath4));
+        runAsRoot(() -> {
+            assertEquals(TEST_STRING_1, getDevice().pullFileContents(oldFilePath1));
+            assertEquals(TEST_STRING_2, getDevice().pullFileContents(oldFilePath2));
+            assertNull(getDevice().pullFile(newFilePath3));
+            assertNull(getDevice().pullFile(newFilePath4));
+        });
 
         // Verify snapshots are deleted after restoration
         List<String> after = getSnapshotDirectories("/data/misc/apexrollback");
         // Only check directories newly created during the test
         after.removeAll(before);
-        after.forEach(dir -> assertDirectoryIsEmpty(dir));
+        // There should be only one /data/misc/apexrollback/<rollbackId> created during test
+        assertThat(after).hasSize(1);
+        assertDirectoryIsEmpty(after.get(0));
     }
 
     /**
@@ -374,38 +390,46 @@
                 APK_IN_APEX_TESTAPEX_NAME, 0) + "/" + TEST_FILENAME_1;
         String oldFilePath2 =
                 apexDataDirDeUser(APK_IN_APEX_TESTAPEX_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_2;
-        assertTrue(getDevice().pushString(TEST_STRING_1, oldFilePath1));
-        assertTrue(getDevice().pushString(TEST_STRING_2, oldFilePath2));
+        runAsRoot(() -> {
+            assertThat(getDevice().pushString(TEST_STRING_1, oldFilePath1)).isTrue();
+            assertThat(getDevice().pushString(TEST_STRING_2, oldFilePath2)).isTrue();
+        });
 
         // Install new version of the APEX with rollback enabled
         runPhase("testRollbackApexDataDirectories_Phase1");
         getDevice().reboot();
 
         // Replace files in data directory
-        getDevice().deleteFile(oldFilePath1);
-        getDevice().deleteFile(oldFilePath2);
         String newFilePath3 =
                 apexDataDirDeUser(APK_IN_APEX_TESTAPEX_NAME, 0) + "/" + TEST_FILENAME_3;
         String newFilePath4 =
                 apexDataDirDeUser(APK_IN_APEX_TESTAPEX_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_4;
-        assertTrue(getDevice().pushString(TEST_STRING_3, newFilePath3));
-        assertTrue(getDevice().pushString(TEST_STRING_4, newFilePath4));
+        runAsRoot(() -> {
+            getDevice().deleteFile(oldFilePath1);
+            getDevice().deleteFile(oldFilePath2);
+            assertThat(getDevice().pushString(TEST_STRING_3, newFilePath3)).isTrue();
+            assertThat(getDevice().pushString(TEST_STRING_4, newFilePath4)).isTrue();
+        });
 
         // Roll back the APEX
         runPhase("testRollbackApexDataDirectories_Phase2");
         getDevice().reboot();
 
         // Verify that old files have been restored and new files are gone
-        assertEquals(TEST_STRING_1, getDevice().pullFileContents(oldFilePath1));
-        assertEquals(TEST_STRING_2, getDevice().pullFileContents(oldFilePath2));
-        assertNull(getDevice().pullFile(newFilePath3));
-        assertNull(getDevice().pullFile(newFilePath4));
+        runAsRoot(() -> {
+            assertEquals(TEST_STRING_1, getDevice().pullFileContents(oldFilePath1));
+            assertEquals(TEST_STRING_2, getDevice().pullFileContents(oldFilePath2));
+            assertNull(getDevice().pullFile(newFilePath3));
+            assertNull(getDevice().pullFile(newFilePath4));
+        });
 
         // Verify snapshots are deleted after restoration
         List<String> after = getSnapshotDirectories("/data/misc_de/0/apexrollback");
         // Only check directories newly created during the test
         after.removeAll(before);
-        after.forEach(dir -> assertDirectoryIsEmpty(dir));
+        // There should be only one /data/misc_de/0/apexrollback/<rollbackId> created during test
+        assertThat(after).hasSize(1);
+        assertDirectoryIsEmpty(after.get(0));
     }
 
     /**
@@ -420,37 +444,88 @@
         String oldFilePath1 = apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + "/" + TEST_FILENAME_1;
         String oldFilePath2 =
                 apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_2;
-        assertTrue(getDevice().pushString(TEST_STRING_1, oldFilePath1));
-        assertTrue(getDevice().pushString(TEST_STRING_2, oldFilePath2));
+        runAsRoot(() -> {
+            assertThat(getDevice().pushString(TEST_STRING_1, oldFilePath1)).isTrue();
+            assertThat(getDevice().pushString(TEST_STRING_2, oldFilePath2)).isTrue();
+        });
 
         // Install new version of the APEX with rollback enabled
         runPhase("testRollbackApexDataDirectories_Phase1");
         getDevice().reboot();
 
         // Replace files in data directory
-        getDevice().deleteFile(oldFilePath1);
-        getDevice().deleteFile(oldFilePath2);
         String newFilePath3 = apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + "/" + TEST_FILENAME_3;
         String newFilePath4 =
                 apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_4;
-        assertTrue(getDevice().pushString(TEST_STRING_3, newFilePath3));
-        assertTrue(getDevice().pushString(TEST_STRING_4, newFilePath4));
+        runAsRoot(() -> {
+            getDevice().deleteFile(oldFilePath1);
+            getDevice().deleteFile(oldFilePath2);
+            assertThat(getDevice().pushString(TEST_STRING_3, newFilePath3)).isTrue();
+            assertThat(getDevice().pushString(TEST_STRING_4, newFilePath4)).isTrue();
+        });
 
         // Roll back the APEX
         runPhase("testRollbackApexDataDirectories_Phase2");
         getDevice().reboot();
 
         // Verify that old files have been restored and new files are gone
-        assertEquals(TEST_STRING_1, getDevice().pullFileContents(oldFilePath1));
-        assertEquals(TEST_STRING_2, getDevice().pullFileContents(oldFilePath2));
-        assertNull(getDevice().pullFile(newFilePath3));
-        assertNull(getDevice().pullFile(newFilePath4));
+        runAsRoot(() -> {
+            assertEquals(TEST_STRING_1, getDevice().pullFileContents(oldFilePath1));
+            assertEquals(TEST_STRING_2, getDevice().pullFileContents(oldFilePath2));
+            assertNull(getDevice().pullFile(newFilePath3));
+            assertNull(getDevice().pullFile(newFilePath4));
+        });
 
         // Verify snapshots are deleted after restoration
         List<String> after = getSnapshotDirectories("/data/misc_ce/0/apexrollback");
         // Only check directories newly created during the test
         after.removeAll(before);
-        after.forEach(dir -> assertDirectoryIsEmpty(dir));
+        // There should be only one /data/misc_ce/0/apexrollback/<rollbackId> created during test
+        assertThat(after).hasSize(1);
+        assertDirectoryIsEmpty(after.get(0));
+    }
+
+    /**
+     * Tests that data in DE apk data directory is restored when apk is rolled back.
+     */
+    @Test
+    public void testRollbackApkDataDirectories_De() throws Exception {
+        // Install version 1 of TESTAPP_A
+        runPhase("testRollbackApkDataDirectories_Phase1");
+
+        // Push files to apk data directory
+        String oldFilePath1 = apkDataDirDe(TESTAPP_A, 0) + "/" + TEST_FILENAME_1;
+        String oldFilePath2 = apkDataDirDe(TESTAPP_A, 0) + TEST_SUBDIR + TEST_FILENAME_2;
+        runAsRoot(() -> {
+            assertThat(getDevice().pushString(TEST_STRING_1, oldFilePath1)).isTrue();
+            assertThat(getDevice().pushString(TEST_STRING_2, oldFilePath2)).isTrue();
+        });
+
+        // Install version 2 of TESTAPP_A with rollback enabled
+        runPhase("testRollbackApkDataDirectories_Phase2");
+        getDevice().reboot();
+
+        // Replace files in data directory
+        String newFilePath3 = apkDataDirDe(TESTAPP_A, 0) + "/" + TEST_FILENAME_3;
+        String newFilePath4 = apkDataDirDe(TESTAPP_A, 0) + TEST_SUBDIR + TEST_FILENAME_4;
+        runAsRoot(() -> {
+            getDevice().deleteFile(oldFilePath1);
+            getDevice().deleteFile(oldFilePath2);
+            assertThat(getDevice().pushString(TEST_STRING_3, newFilePath3)).isTrue();
+            assertThat(getDevice().pushString(TEST_STRING_4, newFilePath4)).isTrue();
+        });
+
+        // Roll back the APK
+        runPhase("testRollbackApkDataDirectories_Phase3");
+        getDevice().reboot();
+
+        // Verify that old files have been restored and new files are gone
+        runAsRoot(() -> {
+            assertEquals(TEST_STRING_1, getDevice().pullFileContents(oldFilePath1));
+            assertEquals(TEST_STRING_2, getDevice().pullFileContents(oldFilePath2));
+            assertNull(getDevice().pullFile(newFilePath3));
+            assertNull(getDevice().pullFile(newFilePath4));
+        });
     }
 
     @Test
@@ -462,8 +537,10 @@
         String oldFilePath1 = apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + "/" + TEST_FILENAME_1;
         String oldFilePath2 =
                 apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_2;
-        assertTrue(getDevice().pushString(TEST_STRING_1, oldFilePath1));
-        assertTrue(getDevice().pushString(TEST_STRING_2, oldFilePath2));
+        runAsRoot(() -> {
+            assertThat(getDevice().pushString(TEST_STRING_1, oldFilePath1)).isTrue();
+            assertThat(getDevice().pushString(TEST_STRING_2, oldFilePath2)).isTrue();
+        });
 
         // Install new version of the APEX with rollback enabled
         runPhase("testRollbackApexDataDirectories_Phase1");
@@ -472,22 +549,28 @@
         List<String> after = getSnapshotDirectories("/data/misc_ce/0/apexrollback");
         // Only check directories newly created during the test
         after.removeAll(before);
+        // There should be only one /data/misc_ce/0/apexrollback/<rollbackId> created during test
+        assertThat(after).hasSize(1);
         // Expire all rollbacks and check CE snapshot directories are deleted
         runPhase("testCleanUp");
-        for (String dir : after) {
-            assertNull(getDevice().getFileEntry(dir));
-        }
+        runAsRoot(() -> {
+            for (String dir : after) {
+                assertNull(getDevice().getFileEntry(dir));
+            }
+        });
     }
 
     private void pushTestApex() throws Exception {
         CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
         final String fileName = APK_IN_APEX_TESTAPEX_NAME + "_v1.apex";
         final File apex = buildHelper.getTestFile(fileName);
-        if (!getDevice().isAdbRoot()) {
+        try {
             getDevice().enableAdbRoot();
+            getDevice().remountSystemWritable();
+            assertTrue(getDevice().pushFile(apex, "/system/apex/" + fileName));
+        } finally {
+            getDevice().disableAdbRoot();
         }
-        getDevice().remountSystemWritable();
-        assertTrue(getDevice().pushFile(apex, "/system/apex/" + fileName));
         getDevice().reboot();
     }
 
@@ -503,25 +586,39 @@
         return String.format("/data/misc_ce/%d/apexdata/%s", userId, apexName);
     }
 
-    private List<String> getSnapshotDirectories(String baseDir) {
+    private static String apkDataDirDe(String apexName, int userId) {
+        return String.format("/data/user_de/%d/%s", userId, apexName);
+    }
+
+    private List<String> getSnapshotDirectories(String baseDir) throws Exception {
         try {
-            return getDevice().getFileEntry(baseDir).getChildren(false)
+            getDevice().enableAdbRoot();
+            IFileEntry f = getDevice().getFileEntry(baseDir);
+            if (f == null) {
+                Log.d(TAG, "baseDir doesn't exist: " + baseDir);
+                return Collections.EMPTY_LIST;
+            }
+            List<String> list = f.getChildren(false)
                     .stream().filter(entry -> entry.getName().matches("\\d+(-prerestore)?"))
                     .map(entry -> entry.getFullPath())
                     .collect(Collectors.toList());
-        } catch (Exception e) {
-            // Return an empty list if any error
-            return Collections.EMPTY_LIST;
+            Log.d(TAG, "getSnapshotDirectories=" + list);
+            return list;
+        } finally {
+            getDevice().disableAdbRoot();
         }
     }
 
-    private void assertDirectoryIsEmpty(String path) {
+    private void assertDirectoryIsEmpty(String path) throws Exception {
         try {
+            getDevice().enableAdbRoot();
             IFileEntry file = getDevice().getFileEntry(path);
             assertTrue("Not a directory: " + path, file.isDirectory());
             assertTrue("Directory not empty: " + path, file.getChildren(false).isEmpty());
         } catch (DeviceNotAvailableException e) {
             fail("Can't access directory: " + path);
+        } finally {
+            getDevice().disableAdbRoot();
         }
     }
 
@@ -559,4 +656,18 @@
             return false;
         }
     }
+
+    @FunctionalInterface
+    private interface ExceptionalRunnable {
+        void run() throws Exception;
+    }
+
+    private void runAsRoot(ExceptionalRunnable runnable) throws Exception {
+        try {
+            getDevice().enableAdbRoot();
+            runnable.run();
+        } finally {
+            getDevice().disableAdbRoot();
+        }
+    }
 }
diff --git a/tests/SilkFX/AndroidManifest.xml b/tests/SilkFX/AndroidManifest.xml
index 050e9c3..c30d761 100644
--- a/tests/SilkFX/AndroidManifest.xml
+++ b/tests/SilkFX/AndroidManifest.xml
@@ -4,9 +4,9 @@
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
      You may obtain a copy of the License at
-  
+
           http://www.apache.org/licenses/LICENSE-2.0
-  
+
      Unless required by applicable law or agreed to in writing, software
      distributed under the License is distributed on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -40,7 +40,14 @@
             android:label="Glow Examples"/>
 
         <activity android:name=".materials.GlassActivity"
-            android:label="Glass Examples"/>
+            android:label="Glass Examples"
+            android:banner="@drawable/background1"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
+            </intent-filter>
+        </activity>
 
     </application>
 </manifest>
diff --git a/tests/SilkFX/res/layout-television/activity_glass.xml b/tests/SilkFX/res/layout-television/activity_glass.xml
new file mode 100644
index 0000000..1f566860
--- /dev/null
+++ b/tests/SilkFX/res/layout-television/activity_glass.xml
@@ -0,0 +1,302 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+-->
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".MainActivity">
+
+    <ImageView
+        android:id="@+id/background"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:scaleType="matrix"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        tools:srcCompat="@drawable/background1" />
+
+    <com.android.test.silkfx.materials.GlassView
+        android:id="@+id/materialView"
+        android:layout_width="400dp"
+        android:layout_height="100dp"
+        android:layout_marginEnd="64dp"
+        android:layout_marginStart="64dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="@id/background"
+        app:layout_constraintStart_toStartOf="@id/background"
+        app:layout_constraintTop_toTopOf="parent">
+        <TextView
+            android:id="@+id/textOverlay"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="18dp"
+            android:layout_gravity="center"
+            android:textColor="#ffffff"
+            android:text="Lorem Ipsum dolor sit amet." />
+    </com.android.test.silkfx.materials.GlassView>
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/bottomPanel"
+        android:layout_width="400dp"
+        android:layout_height="wrap_content"
+        android:background="?android:attr/colorBackground"
+        android:paddingTop="24dp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent">
+
+    <SeekBar
+        android:id="@+id/materialOpacity"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="12dp"
+        android:layout_marginEnd="12dp"
+        android:layout_marginBottom="16dp"
+        android:max="100"
+        android:progress="12"
+        app:layout_constraintBottom_toTopOf="@+id/scrimOpacityTitle"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHorizontal_bias="1.0"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <SeekBar
+        android:id="@+id/zoom"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="16dp"
+        android:layout_marginEnd="12dp"
+        android:layout_marginStart="12dp"
+        android:min="-100"
+        android:max="100"
+        android:progress="-15"
+        app:layout_constraintBottom_toTopOf="@+id/blurRadiusTitle"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHorizontal_bias="1.0"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <SeekBar
+        android:id="@+id/blurRadius"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="16dp"
+        android:layout_marginEnd="12dp"
+        android:layout_marginStart="12dp"
+        android:max="150"
+        android:progress="40"
+        app:layout_constraintBottom_toTopOf="@+id/materialOpacityTitle"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHorizontal_bias="1.0"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <SeekBar
+        android:id="@+id/scrimOpacity"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="12dp"
+        android:layout_marginEnd="12dp"
+        android:layout_marginBottom="16dp"
+        android:max="100"
+        android:progress="50"
+        app:layout_constraintBottom_toTopOf="@+id/noiseOpacityTitle"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHorizontal_bias="1.0"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <SeekBar
+        android:id="@+id/noiseOpacity"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="12dp"
+        android:layout_marginEnd="12dp"
+        android:layout_marginBottom="24dp"
+        android:max="100"
+        android:progress="15"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHorizontal_bias="0.0"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <TextView
+        android:id="@+id/scrimOpacityTitle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="24dp"
+        android:layout_marginBottom="8dp"
+        android:text="Scrim Opacity"
+        android:textColor="@android:color/white"
+        app:layout_constraintBottom_toTopOf="@+id/scrimOpacity"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <TextView
+        android:id="@+id/materialOpacityTitle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="24dp"
+        android:layout_marginBottom="8dp"
+        android:text="Soft light Opacity"
+        android:textColor="@android:color/white"
+        app:layout_constraintBottom_toTopOf="@+id/materialOpacity"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <TextView
+        android:id="@+id/zoomTitle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="24dp"
+        android:layout_marginBottom="8dp"
+        android:text="Zoom"
+        android:textColor="@android:color/white"
+        app:layout_constraintBottom_toTopOf="@+id/zoom"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <TextView
+        android:id="@+id/blurRadiusTitle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="24dp"
+        android:layout_marginBottom="8dp"
+        android:text="Blur Radius"
+        android:textColor="@android:color/white"
+        app:layout_constraintBottom_toTopOf="@+id/blurRadius"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <TextView
+        android:id="@+id/noiseOpacityTitle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="24dp"
+        android:layout_marginBottom="8dp"
+        android:textColor="@android:color/white"
+        android:text="Noise Opacity"
+        app:layout_constraintBottom_toTopOf="@+id/noiseOpacity"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <ImageView
+        android:id="@+id/background1"
+        android:layout_width="64dp"
+        android:layout_height="64dp"
+        android:layout_marginStart="24dp"
+        android:layout_marginBottom="16dp"
+        android:foreground="?android:attr/selectableItemBackgroundBorderless"
+        android:clickable="true"
+        android:onClick="onBackgroundClick"
+        android:scaleType="centerCrop"
+        app:layout_constraintBottom_toTopOf="@+id/lightMaterialSwitch"
+        app:layout_constraintStart_toStartOf="parent"
+        android:src="@drawable/background1" />
+
+    <ImageView
+        android:id="@+id/background2"
+        android:layout_width="64dp"
+        android:layout_height="64dp"
+        android:layout_marginStart="8dp"
+        android:foreground="?android:attr/selectableItemBackgroundBorderless"
+        android:clickable="true"
+        android:onClick="onBackgroundClick"
+        android:scaleType="centerCrop"
+        app:layout_constraintBottom_toBottomOf="@+id/background1"
+        app:layout_constraintStart_toEndOf="@+id/background1"
+        android:src="@drawable/background2" />
+
+    <ImageView
+        android:id="@+id/background3"
+        android:layout_width="64dp"
+        android:layout_height="64dp"
+        android:layout_marginStart="8dp"
+        android:scaleType="centerCrop"
+        android:foreground="?android:attr/selectableItemBackgroundBorderless"
+        android:clickable="true"
+        android:onClick="onBackgroundClick"
+        app:layout_constraintBottom_toBottomOf="@+id/background1"
+        app:layout_constraintStart_toEndOf="@+id/background2"
+        android:src="@drawable/background3" />
+
+    <Button
+        android:id="@+id/pickImage"
+        android:layout_width="64dp"
+        android:layout_height="64dp"
+        android:layout_marginStart="8dp"
+        android:scaleType="centerCrop"
+        android:foreground="?android:attr/selectableItemBackgroundBorderless"
+        android:clickable="true"
+        android:onClick="onPickImageClick"
+        app:layout_constraintBottom_toBottomOf="@+id/background1"
+        app:layout_constraintStart_toEndOf="@+id/background3"
+        android:text="Pick file" />
+
+    <Switch
+        android:id="@+id/lightMaterialSwitch"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="24dp"
+        android:layout_marginBottom="8dp"
+        android:text="Light Material"
+        app:layout_constraintBottom_toTopOf="@+id/zoomTitle"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <TextView
+        android:id="@+id/blurRadiusValue"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="TextView"
+        android:layout_marginLeft="8dp"
+        app:layout_constraintBottom_toBottomOf="@+id/blurRadiusTitle"
+        app:layout_constraintStart_toEndOf="@+id/blurRadiusTitle" />
+
+    <TextView
+        android:id="@+id/zoomValue"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="TextView"
+        android:layout_marginLeft="8dp"
+        app:layout_constraintBottom_toBottomOf="@+id/zoomTitle"
+        app:layout_constraintStart_toEndOf="@+id/zoomTitle" />
+
+    <TextView
+        android:id="@+id/materialOpacityValue"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="TextView"
+        android:layout_marginLeft="8dp"
+        app:layout_constraintBottom_toBottomOf="@+id/materialOpacityTitle"
+        app:layout_constraintStart_toEndOf="@+id/materialOpacityTitle" />
+
+    <TextView
+        android:id="@+id/noiseOpacityValue"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="TextView"
+        android:layout_marginLeft="8dp"
+        app:layout_constraintBottom_toBottomOf="@+id/noiseOpacityTitle"
+        app:layout_constraintStart_toEndOf="@+id/noiseOpacityTitle" />
+
+
+    <TextView
+        android:id="@+id/scrimOpacityValue"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="TextView"
+        android:layout_marginLeft="8dp"
+        app:layout_constraintBottom_toBottomOf="@+id/scrimOpacityTitle"
+        app:layout_constraintStart_toEndOf="@+id/scrimOpacityTitle" />
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/tests/net/Android.bp b/tests/net/Android.bp
index 0fe84ab..a762219 100644
--- a/tests/net/Android.bp
+++ b/tests/net/Android.bp
@@ -50,6 +50,7 @@
     platform_apis: true,
     test_suites: ["device-tests"],
     certificate: "platform",
+    jarjar_rules: "jarjar-rules.txt",
     static_libs: [
         "androidx.test.rules",
         "FrameworksNetCommonTests",
@@ -59,6 +60,7 @@
         "mockito-target-minus-junit4",
         "net-tests-utils",
         "platform-test-annotations",
+        "service-connectivity",
         "services.core",
         "services.net",
     ],
diff --git a/tests/net/TEST_MAPPING b/tests/net/TEST_MAPPING
index 005cbe9..89fc6ea 100644
--- a/tests/net/TEST_MAPPING
+++ b/tests/net/TEST_MAPPING
@@ -8,5 +8,10 @@
     {
       "name": "FrameworksNetDeflakeTest"
     }
+  ],
+  "imports": [
+    {
+      "path": "cts/tests/tests/net"
+    }
   ]
 }
\ No newline at end of file
diff --git a/tests/net/common/Android.bp b/tests/net/common/Android.bp
index 46d680f..373aac6 100644
--- a/tests/net/common/Android.bp
+++ b/tests/net/common/Android.bp
@@ -25,6 +25,7 @@
         "junit",
         "mockito-target-minus-junit4",
         "net-tests-utils",
+        "net-utils-framework-common",
         "platform-test-annotations",
     ],
     libs: [
diff --git a/tests/net/common/java/android/net/LinkPropertiesTest.java b/tests/net/common/java/android/net/LinkPropertiesTest.java
index 3c3076f..550953d 100644
--- a/tests/net/common/java/android/net/LinkPropertiesTest.java
+++ b/tests/net/common/java/android/net/LinkPropertiesTest.java
@@ -32,7 +32,6 @@
 import static org.junit.Assert.fail;
 
 import android.net.LinkProperties.ProvisioningChange;
-import android.net.util.LinkPropertiesUtils.CompareResult;
 import android.os.Build;
 import android.system.OsConstants;
 import android.util.ArraySet;
@@ -41,6 +40,7 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
 import com.android.testutils.DevSdkIgnoreRule;
 import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
@@ -52,7 +52,6 @@
 import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.net.InetAddress;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
@@ -447,23 +446,21 @@
         assertEquals(3, lp.getRoutes().size());
         assertAllRoutesHaveInterface("wlan0", lp);
 
-        // Check comparisons work.
+        // Check routes are updated correctly when calling setInterfaceName.
         LinkProperties lp2 = new LinkProperties(lp);
         assertAllRoutesHaveInterface("wlan0", lp2);
-        // LinkProperties#compareAllRoutes exists both in R and before R, but the return type
-        // changed in R, so a test compiled with the R version of LinkProperties cannot run on Q.
-        if (isAtLeastR()) {
-            assertEquals(0, lp.compareAllRoutes(lp2).added.size());
-            assertEquals(0, lp.compareAllRoutes(lp2).removed.size());
-        }
+        final CompareResult<RouteInfo> cr1 =
+                new CompareResult<>(lp.getAllRoutes(), lp2.getAllRoutes());
+        assertEquals(0, cr1.added.size());
+        assertEquals(0, cr1.removed.size());
 
         lp2.setInterfaceName("p2p0");
         assertAllRoutesHaveInterface("p2p0", lp2);
         assertAllRoutesNotHaveInterface("wlan0", lp2);
-        if (isAtLeastR()) {
-            assertEquals(3, lp.compareAllRoutes(lp2).added.size());
-            assertEquals(3, lp.compareAllRoutes(lp2).removed.size());
-        }
+        final CompareResult<RouteInfo> cr2 =
+                new CompareResult<>(lp.getAllRoutes(), lp2.getAllRoutes());
+        assertEquals(3, cr2.added.size());
+        assertEquals(3, cr2.removed.size());
 
         // Remove route with incorrect interface, no route removed.
         lp.removeRoute(new RouteInfo(prefix2, null, null));
@@ -954,28 +951,6 @@
         assertTrue(rmnet3.getAllRoutes().isEmpty());
         rmnet3.ensureDirectlyConnectedRoutes();
         assertEqualRoutes(Collections.singletonList(directRoute3), rmnet3.getAllRoutes());
-
-    }
-
-    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
-    public void testCompareResult() {
-        // Either adding or removing items
-        compareResult(Arrays.asList(1, 2, 3, 4), Arrays.asList(1),
-                Arrays.asList(2, 3, 4), new ArrayList<>());
-        compareResult(Arrays.asList(1, 2), Arrays.asList(3, 2, 1, 4),
-                new ArrayList<>(), Arrays.asList(3, 4));
-
-
-        // adding and removing items at the same time
-        compareResult(Arrays.asList(1, 2, 3, 4), Arrays.asList(2, 3, 4, 5),
-                Arrays.asList(1), Arrays.asList(5));
-        compareResult(Arrays.asList(1, 2, 3), Arrays.asList(4, 5, 6),
-                Arrays.asList(1, 2, 3), Arrays.asList(4, 5, 6));
-
-        // null cases
-        compareResult(Arrays.asList(1, 2, 3), null, Arrays.asList(1, 2, 3), new ArrayList<>());
-        compareResult(null, Arrays.asList(3, 2, 1), new ArrayList<>(), Arrays.asList(1, 2, 3));
-        compareResult(null, null, new ArrayList<>(), new ArrayList<>());
     }
 
     private void assertEqualRoutes(Collection<RouteInfo> expected, Collection<RouteInfo> actual) {
@@ -987,13 +962,6 @@
         assertEquals(expectedSet, actualSet);
     }
 
-    private <T> void compareResult(List<T> oldItems, List<T> newItems, List<T> expectRemoved,
-            List<T> expectAdded) {
-        CompareResult<T> result = new CompareResult<>(oldItems, newItems);
-        assertEquals(new ArraySet<>(expectAdded), new ArraySet<>(result.added));
-        assertEquals(new ArraySet<>(expectRemoved), (new ArraySet<>(result.removed)));
-    }
-
     private static LinkProperties makeLinkPropertiesForParceling() {
         LinkProperties source = new LinkProperties();
         source.setInterfaceName(NAME);
diff --git a/tests/net/integration/Android.bp b/tests/net/integration/Android.bp
index 874bd4b..69742b9 100644
--- a/tests/net/integration/Android.bp
+++ b/tests/net/integration/Android.bp
@@ -32,6 +32,7 @@
         "kotlin-reflect",
         "mockito-target-extended-minus-junit4",
         "net-tests-utils",
+        "service-connectivity",
         "services.core",
         "services.net",
         "testables",
@@ -59,6 +60,7 @@
         "net-tests-utils",
     ],
     libs: [
+        "service-connectivity",
         "services.core",
         "services.net",
     ],
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
index bc069e1..dba1856e 100644
--- a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
@@ -167,7 +167,7 @@
         cm = ConnectivityManager(context, service)
         context.addMockSystemService(Context.CONNECTIVITY_SERVICE, cm)
 
-        service.systemReady()
+        service.systemReadyInternal()
     }
 
     private inner class TestConnectivityService(deps: Dependencies) : ConnectivityService(
diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
index c895420..85704d0 100644
--- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -213,7 +213,7 @@
 
     public void connect() {
         assertNotEquals("MockNetworkAgents can only be connected once",
-                getNetworkInfo().getDetailedState(), NetworkInfo.DetailedState.CONNECTED);
+                mNetworkInfo.getDetailedState(), NetworkInfo.DetailedState.CONNECTED);
         mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
         mNetworkAgent.sendNetworkInfo(mNetworkInfo);
     }
@@ -268,10 +268,6 @@
         return mNetworkAgent;
     }
 
-    public NetworkInfo getNetworkInfo() {
-        return mNetworkInfo;
-    }
-
     public NetworkCapabilities getNetworkCapabilities() {
         return mNetworkCapabilities;
     }
diff --git a/tests/net/jarjar-rules.txt b/tests/net/jarjar-rules.txt
new file mode 100644
index 0000000..ca88672
--- /dev/null
+++ b/tests/net/jarjar-rules.txt
@@ -0,0 +1,2 @@
+# Module library in frameworks/libs/net
+rule com.android.net.module.util.** android.net.frameworktests.util.@1
diff --git a/tests/net/java/android/net/IpSecAlgorithmTest.java b/tests/net/java/android/net/IpSecAlgorithmTest.java
index 8e9d08c..2e1c29a 100644
--- a/tests/net/java/android/net/IpSecAlgorithmTest.java
+++ b/tests/net/java/android/net/IpSecAlgorithmTest.java
@@ -16,34 +16,50 @@
 
 package android.net;
 
+import static android.net.IpSecAlgorithm.ALGO_TO_REQUIRED_FIRST_SDK;
+
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 
+import android.content.res.Resources;
+import android.os.Build;
 import android.os.Parcel;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.util.CollectionUtils;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.util.AbstractMap.SimpleEntry;
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.Map.Entry;
 import java.util.Random;
+import java.util.Set;
 
 /** Unit tests for {@link IpSecAlgorithm}. */
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class IpSecAlgorithmTest {
-
     private static final byte[] KEY_MATERIAL;
 
+    private final Resources mMockResources = mock(Resources.class);
+
     static {
         KEY_MATERIAL = new byte[128];
         new Random().nextBytes(KEY_MATERIAL);
     };
 
+    private static byte[] generateKey(int keyLenInBits) {
+        return Arrays.copyOf(KEY_MATERIAL, keyLenInBits / 8);
+    }
+
     @Test
     public void testNoTruncLen() throws Exception {
         Entry<String, Integer>[] authAndAeadList =
@@ -53,7 +69,7 @@
                     new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA256, 256),
                     new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA384, 384),
                     new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA512, 512),
-                    new SimpleEntry<>(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, 224)
+                    new SimpleEntry<>(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, 224),
                 };
 
         // Expect auth and aead algorithms to throw errors if trunclen is omitted.
@@ -70,6 +86,52 @@
         new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, Arrays.copyOf(KEY_MATERIAL, 256 / 8));
     }
 
+    private void checkAuthKeyAndTruncLenValidation(String algoName, int keyLen, int truncLen)
+            throws Exception {
+        new IpSecAlgorithm(algoName, generateKey(keyLen), truncLen);
+
+        try {
+            new IpSecAlgorithm(algoName, generateKey(keyLen));
+            fail("Expected exception on unprovided auth trunclen");
+        } catch (IllegalArgumentException pass) {
+        }
+
+        try {
+            new IpSecAlgorithm(algoName, generateKey(keyLen + 8), truncLen);
+            fail("Invalid key length not validated");
+        } catch (IllegalArgumentException pass) {
+        }
+
+        try {
+            new IpSecAlgorithm(algoName, generateKey(keyLen), truncLen + 1);
+            fail("Invalid truncation length not validated");
+        } catch (IllegalArgumentException pass) {
+        }
+    }
+
+    private void checkCryptKeyLenValidation(String algoName, int keyLen) throws Exception {
+        new IpSecAlgorithm(algoName, generateKey(keyLen));
+
+        try {
+            new IpSecAlgorithm(algoName, generateKey(keyLen + 8));
+            fail("Invalid key length not validated");
+        } catch (IllegalArgumentException pass) {
+        }
+    }
+
+    @Test
+    public void testValidationForAlgosAddedInS() throws Exception {
+        if (Build.VERSION.FIRST_SDK_INT <= Build.VERSION_CODES.R) {
+            return;
+        }
+
+        for (int len : new int[] {160, 224, 288}) {
+            checkCryptKeyLenValidation(IpSecAlgorithm.CRYPT_AES_CTR, len);
+        }
+        checkAuthKeyAndTruncLenValidation(IpSecAlgorithm.AUTH_AES_XCBC, 128, 96);
+        checkAuthKeyAndTruncLenValidation(IpSecAlgorithm.AUTH_CRYPT_CHACHA20_POLY1305, 288, 128);
+    }
+
     @Test
     public void testTruncLenValidation() throws Exception {
         for (int truncLen : new int[] {256, 512}) {
@@ -127,4 +189,37 @@
         assertTrue("Parcel/Unparcel failed!", IpSecAlgorithm.equals(init, fin));
         p.recycle();
     }
+
+    private static Set<String> getMandatoryAlgos() {
+        return CollectionUtils.filter(
+                ALGO_TO_REQUIRED_FIRST_SDK.keySet(),
+                i -> Build.VERSION.FIRST_SDK_INT >= ALGO_TO_REQUIRED_FIRST_SDK.get(i));
+    }
+
+    private static Set<String> getOptionalAlgos() {
+        return CollectionUtils.filter(
+                ALGO_TO_REQUIRED_FIRST_SDK.keySet(),
+                i -> Build.VERSION.FIRST_SDK_INT < ALGO_TO_REQUIRED_FIRST_SDK.get(i));
+    }
+
+    @Test
+    public void testGetSupportedAlgorithms() throws Exception {
+        assertTrue(IpSecAlgorithm.getSupportedAlgorithms().containsAll(getMandatoryAlgos()));
+        assertTrue(ALGO_TO_REQUIRED_FIRST_SDK.keySet().containsAll(
+                IpSecAlgorithm.getSupportedAlgorithms()));
+    }
+
+    @Test
+    public void testLoadAlgos() throws Exception {
+        final Set<String> optionalAlgoSet = getOptionalAlgos();
+        final String[] optionalAlgos = optionalAlgoSet.toArray(new String[0]);
+
+        doReturn(optionalAlgos).when(mMockResources)
+                .getStringArray(com.android.internal.R.array.config_optionalIpSecAlgorithms);
+
+        final Set<String> enabledAlgos = new HashSet<>(IpSecAlgorithm.loadAlgos(mMockResources));
+        final Set<String> expectedAlgos = ALGO_TO_REQUIRED_FIRST_SDK.keySet();
+
+        assertEquals(expectedAlgos, enabledAlgos);
+    }
 }
diff --git a/tests/net/java/android/net/util/IpUtilsTest.java b/tests/net/java/android/net/util/IpUtilsTest.java
deleted file mode 100644
index 193d85d..0000000
--- a/tests/net/java/android/net/util/IpUtilsTest.java
+++ /dev/null
@@ -1,166 +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.
- */
-
-package android.net.util;
-
-import static org.junit.Assert.assertEquals;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.nio.ByteBuffer;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class IpUtilsTest {
-
-    private static final int IPV4_HEADER_LENGTH = 20;
-    private static final int IPV6_HEADER_LENGTH = 40;
-    private static final int TCP_HEADER_LENGTH = 20;
-    private static final int UDP_HEADER_LENGTH = 8;
-    private static final int IP_CHECKSUM_OFFSET = 10;
-    private static final int TCP_CHECKSUM_OFFSET = 16;
-    private static final int UDP_CHECKSUM_OFFSET = 6;
-
-    private int getUnsignedByte(ByteBuffer buf, int offset) {
-        return buf.get(offset) & 0xff;
-    }
-
-    private int getChecksum(ByteBuffer buf, int offset) {
-        return getUnsignedByte(buf, offset) * 256 + getUnsignedByte(buf, offset + 1);
-    }
-
-    private void assertChecksumEquals(int expected, short actual) {
-        assertEquals(Integer.toHexString(expected), Integer.toHexString(actual & 0xffff));
-    }
-
-    // Generate test packets using Python code like this::
-    //
-    // from scapy import all as scapy
-    //
-    // def JavaPacketDefinition(bytes):
-    //   out = "        ByteBuffer packet = ByteBuffer.wrap(new byte[] {\n            "
-    //   for i in xrange(len(bytes)):
-    //     out += "(byte) 0x%02x" % ord(bytes[i])
-    //     if i < len(bytes) - 1:
-    //       if i % 4 == 3:
-    //         out += ",\n            "
-    //       else:
-    //         out += ", "
-    //   out += "\n        });"
-    //   return out
-    //
-    // packet = (scapy.IPv6(src="2001:db8::1", dst="2001:db8::2") /
-    //           scapy.UDP(sport=12345, dport=7) /
-    //           "hello")
-    // print JavaPacketDefinition(str(packet))
-
-    @Test
-    public void testIpv6TcpChecksum() throws Exception {
-        // packet = (scapy.IPv6(src="2001:db8::1", dst="2001:db8::2", tc=0x80) /
-        //           scapy.TCP(sport=12345, dport=7,
-        //                     seq=1692871236, ack=128376451, flags=16,
-        //                     window=32768) /
-        //           "hello, world")
-        ByteBuffer packet = ByteBuffer.wrap(new byte[] {
-            (byte) 0x68, (byte) 0x00, (byte) 0x00, (byte) 0x00,
-            (byte) 0x00, (byte) 0x20, (byte) 0x06, (byte) 0x40,
-            (byte) 0x20, (byte) 0x01, (byte) 0x0d, (byte) 0xb8,
-            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
-            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
-            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01,
-            (byte) 0x20, (byte) 0x01, (byte) 0x0d, (byte) 0xb8,
-            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
-            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
-            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x02,
-            (byte) 0x30, (byte) 0x39, (byte) 0x00, (byte) 0x07,
-            (byte) 0x64, (byte) 0xe7, (byte) 0x2a, (byte) 0x44,
-            (byte) 0x07, (byte) 0xa6, (byte) 0xde, (byte) 0x83,
-            (byte) 0x50, (byte) 0x10, (byte) 0x80, (byte) 0x00,
-            (byte) 0xee, (byte) 0x71, (byte) 0x00, (byte) 0x00,
-            (byte) 0x68, (byte) 0x65, (byte) 0x6c, (byte) 0x6c,
-            (byte) 0x6f, (byte) 0x2c, (byte) 0x20, (byte) 0x77,
-            (byte) 0x6f, (byte) 0x72, (byte) 0x6c, (byte) 0x64
-        });
-
-        // Check that a valid packet has checksum 0.
-        int transportLen = packet.limit() - IPV6_HEADER_LENGTH;
-        assertEquals(0, IpUtils.tcpChecksum(packet, 0, IPV6_HEADER_LENGTH, transportLen));
-
-        // Check that we can calculate the checksum from scratch.
-        int sumOffset = IPV6_HEADER_LENGTH + TCP_CHECKSUM_OFFSET;
-        int sum = getUnsignedByte(packet, sumOffset) * 256 + getUnsignedByte(packet, sumOffset + 1);
-        assertEquals(0xee71, sum);
-
-        packet.put(sumOffset, (byte) 0);
-        packet.put(sumOffset + 1, (byte) 0);
-        assertChecksumEquals(sum, IpUtils.tcpChecksum(packet, 0, IPV6_HEADER_LENGTH, transportLen));
-
-        // Check that writing the checksum back into the packet results in a valid packet.
-        packet.putShort(
-            sumOffset,
-            IpUtils.tcpChecksum(packet, 0, IPV6_HEADER_LENGTH, transportLen));
-        assertEquals(0, IpUtils.tcpChecksum(packet, 0, IPV6_HEADER_LENGTH, transportLen));
-    }
-
-    @Test
-    public void testIpv4UdpChecksum() {
-        // packet = (scapy.IP(src="192.0.2.1", dst="192.0.2.2", tos=0x40) /
-        //           scapy.UDP(sport=32012, dport=4500) /
-        //           "\xff")
-        ByteBuffer packet = ByteBuffer.wrap(new byte[] {
-            (byte) 0x45, (byte) 0x40, (byte) 0x00, (byte) 0x1d,
-            (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00,
-            (byte) 0x40, (byte) 0x11, (byte) 0xf6, (byte) 0x8b,
-            (byte) 0xc0, (byte) 0x00, (byte) 0x02, (byte) 0x01,
-            (byte) 0xc0, (byte) 0x00, (byte) 0x02, (byte) 0x02,
-            (byte) 0x7d, (byte) 0x0c, (byte) 0x11, (byte) 0x94,
-            (byte) 0x00, (byte) 0x09, (byte) 0xee, (byte) 0x36,
-            (byte) 0xff
-        });
-
-        // Check that a valid packet has IP checksum 0 and UDP checksum 0xffff (0 is not a valid
-        // UDP checksum, so the udpChecksum rewrites 0 to 0xffff).
-        assertEquals(0, IpUtils.ipChecksum(packet, 0));
-        assertEquals((short) 0xffff, IpUtils.udpChecksum(packet, 0, IPV4_HEADER_LENGTH));
-
-        // Check that we can calculate the checksums from scratch.
-        final int ipSumOffset = IP_CHECKSUM_OFFSET;
-        final int ipSum = getChecksum(packet, ipSumOffset);
-        assertEquals(0xf68b, ipSum);
-
-        packet.put(ipSumOffset, (byte) 0);
-        packet.put(ipSumOffset + 1, (byte) 0);
-        assertChecksumEquals(ipSum, IpUtils.ipChecksum(packet, 0));
-
-        final int udpSumOffset = IPV4_HEADER_LENGTH + UDP_CHECKSUM_OFFSET;
-        final int udpSum = getChecksum(packet, udpSumOffset);
-        assertEquals(0xee36, udpSum);
-
-        packet.put(udpSumOffset, (byte) 0);
-        packet.put(udpSumOffset + 1, (byte) 0);
-        assertChecksumEquals(udpSum, IpUtils.udpChecksum(packet, 0, IPV4_HEADER_LENGTH));
-
-        // Check that writing the checksums back into the packet results in a valid packet.
-        packet.putShort(ipSumOffset, IpUtils.ipChecksum(packet, 0));
-        packet.putShort(udpSumOffset, IpUtils.udpChecksum(packet, 0, IPV4_HEADER_LENGTH));
-        assertEquals(0, IpUtils.ipChecksum(packet, 0));
-        assertEquals((short) 0xffff, IpUtils.udpChecksum(packet, 0, IPV4_HEADER_LENGTH));
-    }
-}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 7dfac9c..4081346 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -147,6 +147,7 @@
 import android.net.ConnectivityManager.TooManyRequestsException;
 import android.net.ConnectivityThread;
 import android.net.DataStallReportParcelable;
+import android.net.EthernetManager;
 import android.net.IConnectivityDiagnosticsCallback;
 import android.net.IDnsResolver;
 import android.net.IIpConnectivityMetrics;
@@ -313,6 +314,8 @@
     private static final long TIMESTAMP = 1234L;
 
     private static final int NET_ID = 110;
+    // Set a non-zero value to verify the flow to set tcp init rwnd value.
+    private static final int TEST_TCP_INIT_RWND = 60;
 
     private static final String CLAT_PREFIX = "v4-";
     private static final String MOBILE_IFNAME = "test_rmnet_data0";
@@ -355,6 +358,8 @@
     @Mock LocationManager mLocationManager;
     @Mock AppOpsManager mAppOpsManager;
     @Mock TelephonyManager mTelephonyManager;
+    @Mock MockableSystemProperties mSystemProperties;
+    @Mock EthernetManager mEthernetManager;
 
     private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor =
             ArgumentCaptor.forClass(ResolverParamsParcel.class);
@@ -442,6 +447,7 @@
             if (Context.LOCATION_SERVICE.equals(name)) return mLocationManager;
             if (Context.APP_OPS_SERVICE.equals(name)) return mAppOpsManager;
             if (Context.TELEPHONY_SERVICE.equals(name)) return mTelephonyManager;
+            if (Context.ETHERNET_SERVICE.equals(name)) return mEthernetManager;
             return super.getSystemService(name);
         }
 
@@ -1246,7 +1252,7 @@
         // Create local CM before sending system ready so that we can answer
         // getSystemService() correctly.
         mCm = new WrappedConnectivityManager(InstrumentationRegistry.getContext(), mService);
-        mService.systemReady();
+        mService.systemReadyInternal();
         mockVpn(Process.myUid());
         mCm.bindProcessToNetwork(null);
 
@@ -1257,21 +1263,20 @@
     }
 
     private ConnectivityService.Dependencies makeDependencies() {
-        final MockableSystemProperties systemProperties = spy(new MockableSystemProperties());
-        when(systemProperties.getInt("net.tcp.default_init_rwnd", 0)).thenReturn(0);
-        when(systemProperties.getBoolean("ro.radio.noril", false)).thenReturn(false);
-
+        doReturn(TEST_TCP_INIT_RWND).when(mSystemProperties)
+                .getInt("net.tcp.default_init_rwnd", 0);
+        doReturn(false).when(mSystemProperties).getBoolean("ro.radio.noril", false);
+        doNothing().when(mSystemProperties).setTcpInitRwnd(anyInt());
         final ConnectivityService.Dependencies deps = mock(ConnectivityService.Dependencies.class);
         doReturn(mCsHandlerThread).when(deps).makeHandlerThread();
         doReturn(new TestNetIdManager()).when(deps).makeNetIdManager();
         doReturn(mNetworkStack).when(deps).getNetworkStack();
-        doReturn(systemProperties).when(deps).getSystemProperties();
+        doReturn(mSystemProperties).when(deps).getSystemProperties();
         doReturn(mock(ProxyTracker.class)).when(deps).makeProxyTracker(any(), any());
         doReturn(mMetricsService).when(deps).getMetricsLogger();
         doReturn(true).when(deps).queryUserAccess(anyInt(), anyInt());
         doReturn(mIpConnectivityMetrics).when(deps).getIpConnectivityMetrics();
         doReturn(mBatteryStatsService).when(deps).getBatteryStatsService();
-        doReturn(true).when(deps).hasService(Context.ETHERNET_SERVICE);
         doAnswer(inv -> {
             mPolicyTracker = new WrappedMultinetworkPolicyTracker(
                     inv.getArgument(0), inv.getArgument(1), inv.getArgument(2));
@@ -6146,7 +6151,7 @@
 
         // Switching default network updates TCP buffer sizes.
         verifyTcpBufferSizeChange(ConnectivityService.DEFAULT_TCP_BUFFER_SIZES);
-
+        verify(mSystemProperties, times(1)).setTcpInitRwnd(eq(TEST_TCP_INIT_RWND));
         // Add an IPv4 address. Expect prefix discovery to be stopped. Netd doesn't tell us that
         // the NAT64 prefix was removed because one was never discovered.
         cellLp.addLinkAddress(myIpv4);
@@ -6583,14 +6588,14 @@
         mCellNetworkAgent.connect(false);
         networkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
         verifyTcpBufferSizeChange(ConnectivityService.DEFAULT_TCP_BUFFER_SIZES);
-
+        verify(mSystemProperties, times(1)).setTcpInitRwnd(eq(TEST_TCP_INIT_RWND));
         // Change link Properties should have updated tcp buffer size.
         LinkProperties lp = new LinkProperties();
         lp.setTcpBufferSizes(testTcpBufferSizes);
         mCellNetworkAgent.sendLinkProperties(lp);
         networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
         verifyTcpBufferSizeChange(testTcpBufferSizes);
-
+        verify(mSystemProperties, times(2)).setTcpInitRwnd(eq(TEST_TCP_INIT_RWND));
         // Clean up.
         mCellNetworkAgent.disconnect();
         networkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
diff --git a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
index 753dbf8..f5b85ca 100644
--- a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
+++ b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
@@ -98,7 +98,6 @@
 
     @Mock Context mCtx;
     @Mock IDnsResolver mMockDnsResolver;
-    @Mock MockableSystemProperties mSystemProperties;
 
     private void assertResolverOptionsEquals(
             @NonNull ResolverOptionsParcel actual,
@@ -137,7 +136,7 @@
         mContentResolver.addProvider(Settings.AUTHORITY,
                 new FakeSettingsProvider());
         when(mCtx.getContentResolver()).thenReturn(mContentResolver);
-        mDnsManager = new DnsManager(mCtx, mMockDnsResolver, mSystemProperties);
+        mDnsManager = new DnsManager(mCtx, mMockDnsResolver);
 
         // Clear the private DNS settings
         Settings.Global.putString(mContentResolver, PRIVATE_DNS_DEFAULT_MODE, "");
@@ -159,7 +158,6 @@
         // Send a validation event that is tracked on the alternate netId
         mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES);
         mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp);
-        mDnsManager.setDefaultDnsSystemProperties(lp.getDnsServers());
         mDnsManager.flushVmDnsCache();
         mDnsManager.updateTransportsForNetwork(TEST_NETID_ALTERNATE, TEST_TRANSPORT_TYPES);
         mDnsManager.noteDnsServersForNetwork(TEST_NETID_ALTERNATE, lp);
@@ -196,7 +194,6 @@
                     }));
         mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES);
         mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp);
-        mDnsManager.setDefaultDnsSystemProperties(lp.getDnsServers());
         mDnsManager.flushVmDnsCache();
         fixedLp = new LinkProperties(lp);
         mDnsManager.updatePrivateDnsStatus(TEST_NETID, fixedLp);
@@ -232,7 +229,6 @@
         lp.addDnsServer(InetAddress.getByName("3.3.3.3"));
         mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES);
         mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp);
-        mDnsManager.setDefaultDnsSystemProperties(lp.getDnsServers());
         mDnsManager.flushVmDnsCache();
         mDnsManager.updatePrivateDnsValidation(
                 new DnsManager.PrivateDnsValidationUpdate(TEST_NETID,
@@ -246,7 +242,6 @@
                 mDnsManager.getPrivateDnsConfig());
         mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES);
         mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp);
-        mDnsManager.setDefaultDnsSystemProperties(lp.getDnsServers());
         mDnsManager.flushVmDnsCache();
         mDnsManager.updatePrivateDnsValidation(
                 new DnsManager.PrivateDnsValidationUpdate(TEST_NETID_UNTRACKED,
@@ -295,7 +290,6 @@
                 mDnsManager.getPrivateDnsConfig());
         mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES);
         mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp);
-        mDnsManager.setDefaultDnsSystemProperties(lp.getDnsServers());
         mDnsManager.flushVmDnsCache();
         mDnsManager.updatePrivateDnsValidation(
                 new DnsManager.PrivateDnsValidationUpdate(TEST_NETID,
@@ -341,7 +335,6 @@
         lp.addDnsServer(InetAddress.getByName("4.4.4.4"));
         mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES);
         mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp);
-        mDnsManager.setDefaultDnsSystemProperties(lp.getDnsServers());
         mDnsManager.flushVmDnsCache();
 
         final ArgumentCaptor<ResolverParamsParcel> resolverParamsParcelCaptor =
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index c3f1549..bc3db11 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -1155,7 +1155,7 @@
                     new String[] { EGRESS_IFACE, "l2tp", expectedAddr, "1701", profile.l2tpSecret,
                             "name", profile.username, "password", profile.password,
                             "linkname", "vpn", "refuse-eap", "nodefaultroute", "usepeerdns",
-                            "idle", "1800", "mtu", "1400", "mru", "1400" },
+                            "idle", "1800", "mtu", "1270", "mru", "1270" },
                     deps.mtpdArgs.get(10, TimeUnit.SECONDS));
             // Now wait for the runner to be ready before testing for the route.
             legacyRunnerReady.block(10_000);
@@ -1263,7 +1263,7 @@
         }
 
         @Override
-        public boolean checkInterfacePresent(final Vpn vpn, final String iface) {
+        public boolean isInterfacePresent(final Vpn vpn, final String iface) {
             return true;
         }
     }
diff --git a/tests/net/java/com/android/server/net/NetworkStatsAccessTest.java b/tests/net/java/com/android/server/net/NetworkStatsAccessTest.java
index 858358c..8b730af 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsAccessTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsAccessTest.java
@@ -22,7 +22,6 @@
 import android.Manifest;
 import android.Manifest.permission;
 import android.app.AppOpsManager;
-import android.app.admin.DeviceAdminInfo;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -167,13 +166,11 @@
     }
 
     private void setIsDeviceOwner(boolean isOwner) {
-        when(mDpmi.isActiveAdminWithPolicy(TEST_UID, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER))
-                .thenReturn(isOwner);
+        when(mDpmi.isActiveDeviceOwner(TEST_UID)).thenReturn(isOwner);
     }
 
     private void setIsProfileOwner(boolean isOwner) {
-        when(mDpmi.isActiveAdminWithPolicy(TEST_UID, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER))
-                .thenReturn(isOwner);
+        when(mDpmi.isActiveProfileOwner(TEST_UID)).thenReturn(isOwner);
     }
 
     private void setHasAppOpsPermission(int appOpsMode, boolean hasPermission) {
diff --git a/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java b/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
index 8f09377..6d2c7dc 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
@@ -21,6 +21,7 @@
 import static org.junit.Assert.fail;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -150,7 +151,7 @@
     }
 
     private void assertRatTypeChangedForSub(String subscriberId, int ratType) {
-        assertEquals(mMonitor.getRatTypeForSubscriberId(subscriberId), ratType);
+        assertEquals(ratType, mMonitor.getRatTypeForSubscriberId(subscriberId));
         final ArgumentCaptor<Integer> typeCaptor = ArgumentCaptor.forClass(Integer.class);
         // Verify callback with the subscriberId and the RAT type should be as expected.
         // It will fail if get a callback with an unexpected RAT type.
@@ -302,26 +303,84 @@
         reset(mDelegate);
 
         // Set IMSI to null again to simulate somehow IMSI is not available, such as
-        // modem crash. Verify service should not unregister listener.
+        // modem crash. Verify service should unregister listener.
         updateSubscriberIdForTestSub(TEST_SUBID1, null);
-        verify(mTelephonyManager, never()).listen(any(), anyInt());
-        assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS);
+        verify(mTelephonyManager, times(1)).listen(eq(ratTypeListenerCaptor.getValue()),
+                eq(PhoneStateListener.LISTEN_NONE));
+        assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
         reset(mDelegate);
+        clearInvocations(mTelephonyManager);
 
-        // Set RAT type of sim1 to LTE. Verify RAT type of sim1 is still changed even if the IMSI
-        // is not available. The monitor keeps the listener even if the IMSI disappears because
-        // the IMSI can never change for any given subId, therefore even if the IMSI is updated
-        // to null, the monitor should continue accepting updates of the RAT type. However,
-        // telephony is never actually supposed to do this, if the IMSI disappears there should
-        // not be updates, but it's still the right thing to do theoretically.
-        setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID1,
+        // Simulate somehow IMSI is back. Verify service will register with
+        // another listener and fire callback accordingly.
+        final ArgumentCaptor<RatTypeListener> ratTypeListenerCaptor2 =
+                ArgumentCaptor.forClass(RatTypeListener.class);
+        updateSubscriberIdForTestSub(TEST_SUBID1, TEST_IMSI1);
+        verify(mTelephonyManager, times(1)).listen(ratTypeListenerCaptor2.capture(),
+                eq(PhoneStateListener.LISTEN_SERVICE_STATE));
+        assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+        reset(mDelegate);
+        clearInvocations(mTelephonyManager);
+
+        // Set RAT type of sim1 to LTE. Verify RAT type of sim1 still works.
+        setRatTypeForSub(ratTypeListenerCaptor2.getAllValues(), TEST_SUBID1,
                 TelephonyManager.NETWORK_TYPE_LTE);
         assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_LTE);
         reset(mDelegate);
 
         mMonitor.stop();
+        verify(mTelephonyManager, times(1)).listen(eq(ratTypeListenerCaptor2.getValue()),
+                eq(PhoneStateListener.LISTEN_NONE));
+        assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+    }
+
+    /**
+     * Verify that when IMSI suddenly changed for a given subId, the service will register a new
+     * listener and unregister the old one, and report changes on updated IMSI. This is for modem
+     * feature that may be enabled for certain carrier, which changes to use a different IMSI while
+     * roaming on certain networks for multi-IMSI SIM cards, but the subId stays the same.
+     */
+    @Test
+    public void testSubscriberIdChanged() {
+        mMonitor.start();
+        // Insert sim1, verify RAT type is NETWORK_TYPE_UNKNOWN, and never get any callback
+        // before changing RAT type.
+        addTestSub(TEST_SUBID1, TEST_IMSI1);
+        final ArgumentCaptor<RatTypeListener> ratTypeListenerCaptor =
+                ArgumentCaptor.forClass(RatTypeListener.class);
+        verify(mTelephonyManager, times(1)).listen(ratTypeListenerCaptor.capture(),
+                eq(PhoneStateListener.LISTEN_SERVICE_STATE));
+        assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+
+        // Set RAT type of sim1 to UMTS.
+        // Verify RAT type of sim1 changes accordingly.
+        setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID1,
+                TelephonyManager.NETWORK_TYPE_UMTS);
+        assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS);
+        reset(mDelegate);
+        clearInvocations(mTelephonyManager);
+
+        // Simulate IMSI of sim1 changed to IMSI2. Verify the service will register with
+        // another listener and remove the old one. The RAT type of new IMSI stays at
+        // NETWORK_TYPE_UNKNOWN until received initial callback from telephony.
+        final ArgumentCaptor<RatTypeListener> ratTypeListenerCaptor2 =
+                ArgumentCaptor.forClass(RatTypeListener.class);
+        updateSubscriberIdForTestSub(TEST_SUBID1, TEST_IMSI2);
+        verify(mTelephonyManager, times(1)).listen(ratTypeListenerCaptor2.capture(),
+                eq(PhoneStateListener.LISTEN_SERVICE_STATE));
         verify(mTelephonyManager, times(1)).listen(eq(ratTypeListenerCaptor.getValue()),
                 eq(PhoneStateListener.LISTEN_NONE));
         assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+        assertRatTypeNotChangedForSub(TEST_IMSI2, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+        reset(mDelegate);
+
+        // Set RAT type of sim1 to UMTS for new listener to simulate the initial callback received
+        // from telephony after registration. Verify RAT type of sim1 changes with IMSI2
+        // accordingly.
+        setRatTypeForSub(ratTypeListenerCaptor2.getAllValues(), TEST_SUBID1,
+                TelephonyManager.NETWORK_TYPE_UMTS);
+        assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+        assertRatTypeChangedForSub(TEST_IMSI2, TelephonyManager.NETWORK_TYPE_UMTS);
+        reset(mDelegate);
     }
 }
diff --git a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
index ea803f2..a7d0860 100644
--- a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
+++ b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
@@ -49,7 +49,8 @@
             "android.view.WindowMetricsTest",
             "android.view.PendingInsetsControllerTest",
             "android.app.WindowContextTest",
-            "android.window.WindowMetricsHelperTest"
+            "android.window.WindowMetricsHelperTest",
+            "android.app.activity.ActivityThreadTest"
     };
 
     public FrameworksTestsFilter(Bundle testArgs) {
diff --git a/tests/vcn/OWNERS b/tests/vcn/OWNERS
new file mode 100644
index 0000000..33b9f0f
--- /dev/null
+++ b/tests/vcn/OWNERS
@@ -0,0 +1,7 @@
+set noparent
+
+benedictwong@google.com
+ckesting@google.com
+evitayan@google.com
+nharold@google.com
+jchalard@google.com
\ No newline at end of file
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
index 7497869..8862405 100644
--- a/tools/aapt2/dump/DumpManifest.cpp
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -1063,17 +1063,23 @@
  public:
   UsesPermission() = default;
   std::string name;
-  std::string requiredFeature;
-  std::string requiredNotFeature;
+  std::vector<std::string> requiredFeatures;
+  std::vector<std::string> requiredNotFeatures;
   int32_t required = true;
   int32_t maxSdkVersion = -1;
 
   void Extract(xml::Element* element) override {
     name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
-    requiredFeature = GetAttributeStringDefault(
-        FindAttribute(element, REQUIRED_FEATURE_ATTR), "");
-    requiredNotFeature = GetAttributeStringDefault(
-        FindAttribute(element, REQUIRED_NOT_FEATURE_ATTR), "");
+    std::string feature =
+        GetAttributeStringDefault(FindAttribute(element, REQUIRED_FEATURE_ATTR), "");
+    if (!feature.empty()) {
+      requiredFeatures.push_back(feature);
+    }
+    feature = GetAttributeStringDefault(FindAttribute(element, REQUIRED_NOT_FEATURE_ATTR), "");
+    if (!feature.empty()) {
+      requiredNotFeatures.push_back(feature);
+    }
+
     required = GetAttributeIntegerDefault(FindAttribute(element, REQUIRED_ATTR), 1);
     maxSdkVersion = GetAttributeIntegerDefault(
         FindAttribute(element, MAX_SDK_VERSION_ATTR), -1);
@@ -1090,13 +1096,13 @@
       if (maxSdkVersion >= 0) {
         printer->Print(StringPrintf(" maxSdkVersion='%d'", maxSdkVersion));
       }
-      if (!requiredFeature.empty()) {
-        printer->Print(StringPrintf(" requiredFeature='%s'", requiredFeature.data()));
-      }
-      if (!requiredNotFeature.empty()) {
-        printer->Print(StringPrintf(" requiredNotFeature='%s'", requiredNotFeature.data()));
-      }
       printer->Print("\n");
+      for (const std::string& requiredFeature : requiredFeatures) {
+        printer->Print(StringPrintf("  required-feature='%s'\n", requiredFeature.data()));
+      }
+      for (const std::string& requiredNotFeature : requiredNotFeatures) {
+        printer->Print(StringPrintf("  required-not-feature='%s'\n", requiredNotFeature.data()));
+      }
       if (required == 0) {
         printer->Print(StringPrintf("optional-permission: name='%s'", name.data()));
         if (maxSdkVersion >= 0) {
@@ -1116,6 +1122,38 @@
   }
 };
 
+/** Represents <required-feature> elements. **/
+class RequiredFeature : public ManifestExtractor::Element {
+ public:
+  RequiredFeature() = default;
+  std::string name;
+
+  void Extract(xml::Element* element) override {
+    name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+    auto parent_stack = extractor()->parent_stack();
+    if (!name.empty() && ElementCast<UsesPermission>(parent_stack[0])) {
+      UsesPermission* uses_permission = ElementCast<UsesPermission>(parent_stack[0]);
+      uses_permission->requiredFeatures.push_back(name);
+    }
+  }
+};
+
+/** Represents <required-not-feature> elements. **/
+class RequiredNotFeature : public ManifestExtractor::Element {
+ public:
+  RequiredNotFeature() = default;
+  std::string name;
+
+  void Extract(xml::Element* element) override {
+    name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+    auto parent_stack = extractor()->parent_stack();
+    if (!name.empty() && ElementCast<UsesPermission>(parent_stack[0])) {
+      UsesPermission* uses_permission = ElementCast<UsesPermission>(parent_stack[0]);
+      uses_permission->requiredNotFeatures.push_back(name);
+    }
+  }
+};
+
 /** Represents <uses-permission-sdk-23> elements. **/
 class UsesPermissionSdk23 : public ManifestExtractor::Element {
  public:
@@ -1845,7 +1883,8 @@
       for (xml::Element* child : element->GetChildElements()) {
         if (child->name == "uses-permission" || child->name == "uses-permission-sdk-23"
             || child->name == "permission") {
-          auto permission_element = ManifestExtractor::Element::Inflate(this, child);
+          // Inflate the element and its descendants
+          auto permission_element = Visit(child);
           manifest->AddChild(permission_element);
         }
       }
@@ -2237,38 +2276,40 @@
   }
 
   const std::unordered_map<std::string, bool> kTagCheck = {
-    {"action", std::is_base_of<Action, T>::value},
-    {"activity", std::is_base_of<Activity, T>::value},
-    {"application", std::is_base_of<Application, T>::value},
-    {"category", std::is_base_of<Category, T>::value},
-    {"compatible-screens", std::is_base_of<CompatibleScreens, T>::value},
-    {"feature-group", std::is_base_of<FeatureGroup, T>::value},
-    {"input-type", std::is_base_of<InputType, T>::value},
-    {"intent-filter", std::is_base_of<IntentFilter, T>::value},
-    {"meta-data", std::is_base_of<MetaData, T>::value},
-    {"manifest", std::is_base_of<Manifest, T>::value},
-    {"original-package", std::is_base_of<OriginalPackage, T>::value},
-    {"overlay", std::is_base_of<Overlay, T>::value},
-    {"package-verifier", std::is_base_of<PackageVerifier, T>::value},
-    {"permission", std::is_base_of<Permission, T>::value},
-    {"provider", std::is_base_of<Provider, T>::value},
-    {"receiver", std::is_base_of<Receiver, T>::value},
-    {"screen", std::is_base_of<Screen, T>::value},
-    {"service", std::is_base_of<Service, T>::value},
-    {"supports-gl-texture", std::is_base_of<SupportsGlTexture, T>::value},
-    {"supports-input", std::is_base_of<SupportsInput, T>::value},
-    {"supports-screens", std::is_base_of<SupportsScreen, T>::value},
-    {"uses-configuration", std::is_base_of<UsesConfiguarion, T>::value},
-    {"uses-feature", std::is_base_of<UsesFeature, T>::value},
-    {"uses-permission", std::is_base_of<UsesPermission, T>::value},
-    {"uses-permission-sdk-23", std::is_base_of<UsesPermissionSdk23, T>::value},
-    {"uses-library", std::is_base_of<UsesLibrary, T>::value},
-    {"uses-package", std::is_base_of<UsesPackage, T>::value},
-    {"static-library", std::is_base_of<StaticLibrary, T>::value},
-    {"uses-static-library", std::is_base_of<UsesStaticLibrary, T>::value},
-    {"additional-certificate", std::is_base_of<AdditionalCertificate, T>::value},
-    {"uses-sdk", std::is_base_of<UsesSdkBadging, T>::value},
-    {"uses-native-library", std::is_base_of<UsesNativeLibrary, T>::value},
+      {"action", std::is_base_of<Action, T>::value},
+      {"activity", std::is_base_of<Activity, T>::value},
+      {"additional-certificate", std::is_base_of<AdditionalCertificate, T>::value},
+      {"application", std::is_base_of<Application, T>::value},
+      {"category", std::is_base_of<Category, T>::value},
+      {"compatible-screens", std::is_base_of<CompatibleScreens, T>::value},
+      {"feature-group", std::is_base_of<FeatureGroup, T>::value},
+      {"input-type", std::is_base_of<InputType, T>::value},
+      {"intent-filter", std::is_base_of<IntentFilter, T>::value},
+      {"meta-data", std::is_base_of<MetaData, T>::value},
+      {"manifest", std::is_base_of<Manifest, T>::value},
+      {"original-package", std::is_base_of<OriginalPackage, T>::value},
+      {"overlay", std::is_base_of<Overlay, T>::value},
+      {"package-verifier", std::is_base_of<PackageVerifier, T>::value},
+      {"permission", std::is_base_of<Permission, T>::value},
+      {"provider", std::is_base_of<Provider, T>::value},
+      {"receiver", std::is_base_of<Receiver, T>::value},
+      {"required-feature", std::is_base_of<RequiredFeature, T>::value},
+      {"required-not-feature", std::is_base_of<RequiredNotFeature, T>::value},
+      {"screen", std::is_base_of<Screen, T>::value},
+      {"service", std::is_base_of<Service, T>::value},
+      {"static-library", std::is_base_of<StaticLibrary, T>::value},
+      {"supports-gl-texture", std::is_base_of<SupportsGlTexture, T>::value},
+      {"supports-input", std::is_base_of<SupportsInput, T>::value},
+      {"supports-screens", std::is_base_of<SupportsScreen, T>::value},
+      {"uses-configuration", std::is_base_of<UsesConfiguarion, T>::value},
+      {"uses-feature", std::is_base_of<UsesFeature, T>::value},
+      {"uses-library", std::is_base_of<UsesLibrary, T>::value},
+      {"uses-native-library", std::is_base_of<UsesNativeLibrary, T>::value},
+      {"uses-package", std::is_base_of<UsesPackage, T>::value},
+      {"uses-permission", std::is_base_of<UsesPermission, T>::value},
+      {"uses-permission-sdk-23", std::is_base_of<UsesPermissionSdk23, T>::value},
+      {"uses-sdk", std::is_base_of<UsesSdkBadging, T>::value},
+      {"uses-static-library", std::is_base_of<UsesStaticLibrary, T>::value},
   };
 
   auto check = kTagCheck.find(element->tag());
@@ -2288,39 +2329,41 @@
   const std::unordered_map<std::string,
                            std::function<std::unique_ptr<ManifestExtractor::Element>()>>
       kTagCheck = {
-    {"action", &CreateType<Action>},
-    {"activity", &CreateType<Activity>},
-    {"application", &CreateType<Application>},
-    {"category", &CreateType<Category>},
-    {"compatible-screens", &CreateType<CompatibleScreens>},
-    {"feature-group", &CreateType<FeatureGroup>},
-    {"input-type", &CreateType<InputType>},
-    {"intent-filter",&CreateType<IntentFilter>},
-    {"manifest", &CreateType<Manifest>},
-    {"meta-data", &CreateType<MetaData>},
-    {"original-package", &CreateType<OriginalPackage>},
-    {"overlay", &CreateType<Overlay>},
-    {"package-verifier", &CreateType<PackageVerifier>},
-    {"permission", &CreateType<Permission>},
-    {"provider", &CreateType<Provider>},
-    {"receiver", &CreateType<Receiver>},
-    {"screen", &CreateType<Screen>},
-    {"service", &CreateType<Service>},
-    {"supports-gl-texture", &CreateType<SupportsGlTexture>},
-    {"supports-input", &CreateType<SupportsInput>},
-    {"supports-screens", &CreateType<SupportsScreen>},
-    {"uses-configuration", &CreateType<UsesConfiguarion>},
-    {"uses-feature", &CreateType<UsesFeature>},
-    {"uses-permission", &CreateType<UsesPermission>},
-    {"uses-permission-sdk-23", &CreateType<UsesPermissionSdk23>},
-    {"uses-library", &CreateType<UsesLibrary>},
-    {"static-library", &CreateType<StaticLibrary>},
-    {"uses-static-library", &CreateType<UsesStaticLibrary>},
-    {"uses-package", &CreateType<UsesPackage>},
-    {"additional-certificate", &CreateType<AdditionalCertificate>},
-    {"uses-sdk", &CreateType<UsesSdkBadging>},
-    {"uses-native-library", &CreateType<UsesNativeLibrary>},
-  };
+          {"action", &CreateType<Action>},
+          {"activity", &CreateType<Activity>},
+          {"additional-certificate", &CreateType<AdditionalCertificate>},
+          {"application", &CreateType<Application>},
+          {"category", &CreateType<Category>},
+          {"compatible-screens", &CreateType<CompatibleScreens>},
+          {"feature-group", &CreateType<FeatureGroup>},
+          {"input-type", &CreateType<InputType>},
+          {"intent-filter", &CreateType<IntentFilter>},
+          {"manifest", &CreateType<Manifest>},
+          {"meta-data", &CreateType<MetaData>},
+          {"original-package", &CreateType<OriginalPackage>},
+          {"overlay", &CreateType<Overlay>},
+          {"package-verifier", &CreateType<PackageVerifier>},
+          {"permission", &CreateType<Permission>},
+          {"provider", &CreateType<Provider>},
+          {"receiver", &CreateType<Receiver>},
+          {"required-feature", &CreateType<RequiredFeature>},
+          {"required-not-feature", &CreateType<RequiredNotFeature>},
+          {"screen", &CreateType<Screen>},
+          {"service", &CreateType<Service>},
+          {"static-library", &CreateType<StaticLibrary>},
+          {"supports-gl-texture", &CreateType<SupportsGlTexture>},
+          {"supports-input", &CreateType<SupportsInput>},
+          {"supports-screens", &CreateType<SupportsScreen>},
+          {"uses-configuration", &CreateType<UsesConfiguarion>},
+          {"uses-feature", &CreateType<UsesFeature>},
+          {"uses-library", &CreateType<UsesLibrary>},
+          {"uses-native-library", &CreateType<UsesNativeLibrary>},
+          {"uses-package", &CreateType<UsesPackage>},
+          {"uses-permission", &CreateType<UsesPermission>},
+          {"uses-permission-sdk-23", &CreateType<UsesPermissionSdk23>},
+          {"uses-sdk", &CreateType<UsesSdkBadging>},
+          {"uses-static-library", &CreateType<UsesStaticLibrary>},
+      };
 
   // Attempt to map the xml tag to a element inflater
   std::unique_ptr<ManifestExtractor::Element> element;
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index dac21d7..3d8c25e 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -393,6 +393,8 @@
   manifest_action["protected-broadcast"];
   manifest_action["adopt-permissions"];
   manifest_action["uses-permission"];
+  manifest_action["uses-permission"]["required-feature"].Action(RequiredNameIsNotEmpty);
+  manifest_action["uses-permission"]["required-not-feature"].Action(RequiredNameIsNotEmpty);
   manifest_action["uses-permission-sdk-23"];
   manifest_action["permission"];
   manifest_action["permission"]["meta-data"] = meta_data_action;
diff --git a/tools/codegen/src/com/android/codegen/FileInfo.kt b/tools/codegen/src/com/android/codegen/FileInfo.kt
index 9094726..a1d0389 100644
--- a/tools/codegen/src/com/android/codegen/FileInfo.kt
+++ b/tools/codegen/src/com/android/codegen/FileInfo.kt
@@ -272,7 +272,7 @@
         /** Debug info */
         fun summary(): String = when(this) {
             is Code -> "${javaClass.simpleName}(${lines.size} lines): ${lines.getOrNull(0)?.take(70) ?: ""}..."
-            is DataClass -> "DataClass ${ast.nameAsString}:\n" +
+            is DataClass -> "DataClass ${ast.nameAsString} nested:${ast.nestedTypes.map { it.nameAsString }}:\n" +
                     chunks.joinToString("\n") { it.summary() } +
                     "\n//end ${ast.nameAsString}"
         }
diff --git a/tools/codegen/src/com/android/codegen/Generators.kt b/tools/codegen/src/com/android/codegen/Generators.kt
index 5a96cf1..6e1ab59 100644
--- a/tools/codegen/src/com/android/codegen/Generators.kt
+++ b/tools/codegen/src/com/android/codegen/Generators.kt
@@ -3,7 +3,10 @@
 import com.github.javaparser.ast.body.FieldDeclaration
 import com.github.javaparser.ast.body.MethodDeclaration
 import com.github.javaparser.ast.body.VariableDeclarator
-import com.github.javaparser.ast.expr.*
+import com.github.javaparser.ast.expr.AnnotationExpr
+import com.github.javaparser.ast.expr.ArrayInitializerExpr
+import com.github.javaparser.ast.expr.LiteralExpr
+import com.github.javaparser.ast.expr.UnaryExpr
 import java.io.File
 
 
@@ -703,7 +706,7 @@
 
             generateFieldJavadoc(forceHide = FeatureFlag.SETTERS.hidden)
             +GENERATED_MEMBER_HEADER
-            "public $ClassType set$NameUpperCamel($annotatedTypeForSetterParam value)" {
+            "public @$NonNull $ClassType set$NameUpperCamel($annotatedTypeForSetterParam value)" {
                 generateSetFrom("value")
                 +"return this;"
             }
diff --git a/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt b/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt
index d6953c0..69ff18d 100644
--- a/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt
+++ b/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt
@@ -41,7 +41,10 @@
         }
     } + ("class ${classAst.nameAsString}" +
             " extends ${classAst.extendedTypes.map { getFullClassName(it) }.ifEmpty { listOf("java.lang.Object") }.joinToString(", ")}" +
-            " implements [${classAst.implementedTypes.joinToString(", ") { getFullClassName(it) }}]")
+            " implements [${classAst.implementedTypes.joinToString(", ") { getFullClassName(it) }}]") +
+    classAst.nestedNonDataClasses.flatMap { nestedClass ->
+        generateInputSignaturesForClass(nestedClass)
+    }
 }
 
 private fun ClassPrinter.annotationsToString(annotatedAst: NodeWithAnnotations<*>): String {
@@ -141,6 +144,8 @@
 
     if (className[0].isLowerCase()) return className //primitive
 
+    if (className[0] == '?') return className //wildcard
+
     return thisPackagePrefix + className
 }
 
diff --git a/tools/codegen/src/com/android/codegen/SharedConstants.kt b/tools/codegen/src/com/android/codegen/SharedConstants.kt
index 6f740cd6..785aa910 100644
--- a/tools/codegen/src/com/android/codegen/SharedConstants.kt
+++ b/tools/codegen/src/com/android/codegen/SharedConstants.kt
@@ -1,7 +1,7 @@
 package com.android.codegen
 
 const val CODEGEN_NAME = "codegen"
-const val CODEGEN_VERSION = "1.0.15"
+const val CODEGEN_VERSION = "1.0.17"
 
 const val CANONICAL_BUILDER_CLASS = "Builder"
 const val BASE_BUILDER_CLASS = "BaseBuilder"
diff --git a/tools/codegen/src/com/android/codegen/Utils.kt b/tools/codegen/src/com/android/codegen/Utils.kt
index c19ae3b..7cfa784 100644
--- a/tools/codegen/src/com/android/codegen/Utils.kt
+++ b/tools/codegen/src/com/android/codegen/Utils.kt
@@ -103,6 +103,10 @@
 val TypeDeclaration<*>.nestedDataClasses get()
         = nestedTypes.filterIsInstance<ClassOrInterfaceDeclaration>()
             .filter { it.annotations.any { it.nameAsString.endsWith("DataClass") } }
+val TypeDeclaration<*>.nestedNonDataClasses get()
+        = nestedTypes.filterIsInstance<ClassOrInterfaceDeclaration>()
+            .filter { it.annotations.none { it.nameAsString.endsWith("DataClass") } }
+            .filterNot { it.isInterface }
 val TypeDeclaration<*>.startLine get() = range.get()!!.begin.line
 
 inline fun <T> List<T>.forEachSequentialPair(action: (T, T?) -> Unit) {
diff --git a/tools/powerstats/PowerStatsServiceProtoParser.java b/tools/powerstats/PowerStatsServiceProtoParser.java
index 8ab302a..76edd63 100644
--- a/tools/powerstats/PowerStatsServiceProtoParser.java
+++ b/tools/powerstats/PowerStatsServiceProtoParser.java
@@ -25,50 +25,95 @@
  * is output to STDOUT in csv format.
  */
 public class PowerStatsServiceProtoParser {
-    private static void printRailInfo(PowerStatsServiceProto proto) {
+    private static void printEnergyMeterInfo(PowerStatsServiceMeterProto proto) {
         String csvHeader = new String();
-        for (int i = 0; i < proto.getRailInfoCount(); i++) {
-            RailInfoProto railInfo = proto.getRailInfo(i);
-            csvHeader += "Index" + ","
-                + "Timestamp" + ","
-                + railInfo.getRailName() + "/" + railInfo.getSubsysName() + ",";
+        for (int i = 0; i < proto.getChannelInfoCount(); i++) {
+            ChannelInfoProto energyMeterInfo = proto.getChannelInfo(i);
+            csvHeader += "Index,Timestamp," + energyMeterInfo.getChannelId()
+                + "/" + energyMeterInfo.getChannelName() + ",";
         }
         System.out.println(csvHeader);
     }
 
-    private static void printEnergyData(PowerStatsServiceProto proto) {
-        int railInfoCount = proto.getRailInfoCount();
+    private static void printEnergyMeasurements(PowerStatsServiceMeterProto proto) {
+        int energyMeterInfoCount = proto.getChannelInfoCount();
 
-        if (railInfoCount > 0) {
-            int energyDataCount = proto.getEnergyDataCount();
-            int energyDataSetCount = energyDataCount / railInfoCount;
+        if (energyMeterInfoCount > 0) {
+            int energyMeasurementCount = proto.getEnergyMeasurementCount();
+            int energyMeasurementSetCount = energyMeasurementCount / energyMeterInfoCount;
 
-            for (int i = 0; i < energyDataSetCount; i++) {
+            for (int i = 0; i < energyMeasurementSetCount; i++) {
                 String csvRow = new String();
-                for (int j = 0; j < railInfoCount; j++) {
-                    EnergyDataProto energyData = proto.getEnergyData(i * railInfoCount + j);
-                    csvRow += energyData.getIndex() + ","
-                        + energyData.getTimestampMs() + ","
-                        + energyData.getEnergyUws() + ",";
+                for (int j = 0; j < energyMeterInfoCount; j++) {
+                    EnergyMeasurementProto energyMeasurement =
+                            proto.getEnergyMeasurement(i * energyMeterInfoCount + j);
+                    csvRow += energyMeasurement.getChannelId() + ","
+                        + energyMeasurement.getTimestampMs() + ","
+                        + energyMeasurement.getEnergyUws() + ",";
                 }
                 System.out.println(csvRow);
             }
         } else {
-            System.out.println("Error:  railInfoCount is zero");
+            System.out.println("Error:  energyMeterInfoCount is zero");
+        }
+    }
+
+    private static void printEnergyConsumerId(PowerStatsServiceModelProto proto) {
+        String csvHeader = new String();
+        for (int i = 0; i < proto.getEnergyConsumerIdCount(); i++) {
+            EnergyConsumerIdProto energyConsumerId = proto.getEnergyConsumerId(i);
+            csvHeader += "Index,Timestamp," + energyConsumerId.getEnergyConsumerId() + ",";
+        }
+        System.out.println(csvHeader);
+    }
+
+    private static void printEnergyConsumerResults(PowerStatsServiceModelProto proto) {
+        int energyConsumerIdCount = proto.getEnergyConsumerIdCount();
+
+        if (energyConsumerIdCount > 0) {
+            int energyConsumerResultCount = proto.getEnergyConsumerResultCount();
+            int energyConsumerResultSetCount = energyConsumerResultCount / energyConsumerIdCount;
+
+            for (int i = 0; i < energyConsumerResultSetCount; i++) {
+                String csvRow = new String();
+                for (int j = 0; j < energyConsumerIdCount; j++) {
+                    EnergyConsumerResultProto energyConsumerResult =
+                            proto.getEnergyConsumerResult(i * energyConsumerIdCount + j);
+                    csvRow += energyConsumerResult.getEnergyConsumerId() + ","
+                        + energyConsumerResult.getTimestampMs() + ","
+                        + energyConsumerResult.getEnergyUws() + ",";
+                }
+                System.out.println(csvRow);
+            }
+        } else {
+            System.out.println("Error:  energyConsumerIdCount is zero");
         }
     }
 
     private static void generateCsvFile(String pathToIncidentReport) {
         try {
-            IncidentReportProto irProto =
-                    IncidentReportProto.parseFrom(new FileInputStream(pathToIncidentReport));
+            // Print power meter data.
+            IncidentReportMeterProto irMeterProto =
+                    IncidentReportMeterProto.parseFrom(new FileInputStream(pathToIncidentReport));
 
-            if (irProto.hasIncidentReport()) {
-                PowerStatsServiceProto pssProto = irProto.getIncidentReport();
-                printRailInfo(pssProto);
-                printEnergyData(pssProto);
+            if (irMeterProto.hasIncidentReport()) {
+                PowerStatsServiceMeterProto pssMeterProto = irMeterProto.getIncidentReport();
+                printEnergyMeterInfo(pssMeterProto);
+                printEnergyMeasurements(pssMeterProto);
             } else {
-                System.out.println("Incident report not found.  Exiting.");
+                System.out.println("Meter incident report not found.  Exiting.");
+            }
+
+            // Print power model data.
+            IncidentReportModelProto irModelProto =
+                    IncidentReportModelProto.parseFrom(new FileInputStream(pathToIncidentReport));
+
+            if (irModelProto.hasIncidentReport()) {
+                PowerStatsServiceModelProto pssModelProto = irModelProto.getIncidentReport();
+                printEnergyConsumerId(pssModelProto);
+                printEnergyConsumerResults(pssModelProto);
+            } else {
+                System.out.println("Model incident report not found.  Exiting.");
             }
         } catch (IOException e) {
             System.out.println("Unable to open incident report file: " + pathToIncidentReport);
diff --git a/tools/stringslint/stringslint.py b/tools/stringslint/stringslint.py
index afe91cd..15088fc 100644
--- a/tools/stringslint/stringslint.py
+++ b/tools/stringslint/stringslint.py
@@ -1,4 +1,5 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
+#-*- coding: utf-8 -*-
 
 # Copyright (C) 2018 The Android Open Source Project
 #
@@ -33,9 +34,6 @@
 import re, sys, codecs
 import lxml.etree as ET
 
-reload(sys)
-sys.setdefaultencoding('utf8')
-
 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
 
 def format(fg=None, bg=None, bright=False, bold=False, dim=False, reset=False):
@@ -118,7 +116,7 @@
         raw = f.read()
         if len(raw.strip()) == 0:
             return warnings
-        tree = ET.fromstring(raw)
+        tree = ET.fromstring(bytes(raw, encoding='utf-8'))
         root = tree #tree.getroot()
 
     last_comment = None
@@ -231,6 +229,6 @@
 
 if len(after) > 0:
     for a in sorted(after.keys()):
-        print after[a]
-        print
+        print(after[a])
+        print()
     sys.exit(1)
diff --git a/tools/stringslint/stringslint_sha.sh b/tools/stringslint/stringslint_sha.sh
index bd80bb4..bd05698 100755
--- a/tools/stringslint/stringslint_sha.sh
+++ b/tools/stringslint/stringslint_sha.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
 LOCAL_DIR="$( dirname ${BASH_SOURCE} )"
 git show --name-only --pretty=format: $1 | grep values/strings.xml | while read file; do
-    python $LOCAL_DIR/stringslint.py <(git show $1:$file) <(git show $1^:$file)
+    python3 $LOCAL_DIR/stringslint.py <(git show $1:$file) <(git show $1^:$file)
 done
diff --git a/tools/xmlpersistence/src/main/kotlin/Generator.kt b/tools/xmlpersistence/src/main/kotlin/Generator.kt
index 28467b7..b2c5f4a 100644
--- a/tools/xmlpersistence/src/main/kotlin/Generator.kt
+++ b/tools/xmlpersistence/src/main/kotlin/Generator.kt
@@ -50,7 +50,7 @@
      */
 
     // Generated by xmlpersistence. DO NOT MODIFY!
-    // CHECKSTYLE:OFF
+    // CHECKSTYLE:OFF Generated code
     // @formatter:off
 """.trimIndent() + "\n\n"
 
@@ -128,9 +128,7 @@
         .addAnnotation(nullableType)
         .addModifiers(Modifier.PUBLIC)
         .returns(rootField.type)
-        .addControlFlow(
-            "try (final \$1T inputStream = mFile.openRead())", FileInputStream::class.java
-        ) {
+        .addControlFlow("try (\$1T inputStream = mFile.openRead())", FileInputStream::class.java) {
             addStatement("final \$1T parser = \$2T.newPullParser()", xmlPullParserType, xmlType)
             addStatement("parser.setInput(inputStream, null)")
             addStatement("return parse(parser)")
diff --git a/wifi/api/current.txt b/wifi/api/current.txt
index 6928618..f0cf75c 100644
--- a/wifi/api/current.txt
+++ b/wifi/api/current.txt
@@ -492,6 +492,7 @@
     method @IntRange(from=0) public int getPriority();
     method public int getPriorityGroup();
     method @Nullable public String getSsid();
+    method public int getSubscriptionId();
     method public boolean isAppInteractionRequired();
     method public boolean isCredentialSharedWithUser();
     method public boolean isEnhancedOpen();
@@ -520,6 +521,7 @@
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setPriority(@IntRange(from=0) int);
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setPriorityGroup(int);
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setSsid(@NonNull String);
+    method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setSubscriptionId(int);
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setUntrusted(boolean);
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setWapiEnterpriseConfig(@NonNull android.net.wifi.WifiEnterpriseConfig);
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setWapiPassphrase(@NonNull String);
@@ -561,6 +563,7 @@
     method public int getMaxServiceNameLength();
     method public int getMaxServiceSpecificInfoLength();
     method public int getSupportedCipherSuites();
+    method public boolean isInstantCommunicationModeSupported();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.aware.Characteristics> CREATOR;
     field public static final int WIFI_AWARE_CIPHER_SUITE_NCS_SK_128 = 1; // 0x1
@@ -582,7 +585,7 @@
     method public void onPublishStarted(@NonNull android.net.wifi.aware.PublishDiscoverySession);
     method public void onServiceDiscovered(android.net.wifi.aware.PeerHandle, byte[], java.util.List<byte[]>);
     method public void onServiceDiscoveredWithinRange(android.net.wifi.aware.PeerHandle, byte[], java.util.List<byte[]>, int);
-    method public void onServiceLost(@NonNull android.net.wifi.aware.PeerHandle);
+    method public void onServiceLost(@NonNull android.net.wifi.aware.PeerHandle, int);
     method public void onSessionConfigFailed();
     method public void onSessionConfigUpdated();
     method public void onSessionTerminated();
@@ -659,9 +662,12 @@
     method public android.net.wifi.aware.Characteristics getCharacteristics();
     method public boolean isAvailable();
     method public boolean isDeviceAttached();
+    method public boolean isInstantCommunicationModeEnabled();
     field public static final String ACTION_WIFI_AWARE_STATE_CHANGED = "android.net.wifi.aware.action.WIFI_AWARE_STATE_CHANGED";
     field public static final int WIFI_AWARE_DATA_PATH_ROLE_INITIATOR = 0; // 0x0
     field public static final int WIFI_AWARE_DATA_PATH_ROLE_RESPONDER = 1; // 0x1
+    field public static final int WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE = 1; // 0x1
+    field public static final int WIFI_AWARE_DISCOVERY_LOST_REASON_UNKNOWN = 0; // 0x0
   }
 
   public final class WifiAwareNetworkInfo implements android.os.Parcelable android.net.TransportInfo {
@@ -801,13 +807,13 @@
     method public String getFriendlyName();
     method @Nullable public long[] getMatchAllOis();
     method @Nullable public long[] getMatchAnyOis();
-    method @Nullable public String[] getOtherHomePartners();
+    method @NonNull public java.util.Collection<java.lang.String> getOtherHomePartnersList();
     method public long[] getRoamingConsortiumOis();
     method public void setFqdn(String);
     method public void setFriendlyName(String);
     method public void setMatchAllOis(@Nullable long[]);
     method public void setMatchAnyOis(@Nullable long[]);
-    method public void setOtherHomePartners(@Nullable String[]);
+    method public void setOtherHomePartnersList(@NonNull java.util.Collection<java.lang.String>);
     method public void setRoamingConsortiumOis(long[]);
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.HomeSp> CREATOR;
diff --git a/wifi/api/system-current.txt b/wifi/api/system-current.txt
index bf7003c..da5888b 100644
--- a/wifi/api/system-current.txt
+++ b/wifi/api/system-current.txt
@@ -263,7 +263,7 @@
     field public static final int BAND_2GHZ = 1; // 0x1
     field public static final int BAND_5GHZ = 2; // 0x2
     field public static final int BAND_6GHZ = 4; // 0x4
-    field public static final int BAND_ANY = 7; // 0x7
+    field @Deprecated public static final int BAND_ANY = 7; // 0x7
     field public static final int RANDOMIZATION_NONE = 0; // 0x0
     field public static final int RANDOMIZATION_PERSISTENT = 1; // 0x1
   }
@@ -350,6 +350,7 @@
     field @Deprecated public int numScorerOverrideAndSwitchedNetwork;
     field @Deprecated public boolean requirePmf;
     field @Deprecated public boolean shared;
+    field @Deprecated public int subscriptionId;
     field @Deprecated public boolean useExternalScores;
   }
 
@@ -799,6 +800,10 @@
     method @Deprecated public android.net.NetworkSpecifier createNetworkSpecifierPmk(@NonNull android.net.wifi.aware.PeerHandle, @NonNull byte[]);
   }
 
+  public class WifiAwareManager {
+    method public void enableInstantCommunicationMode(boolean);
+  }
+
   public class WifiAwareSession implements java.lang.AutoCloseable {
     method public android.net.NetworkSpecifier createNetworkSpecifierPmk(int, @NonNull byte[], @NonNull byte[]);
   }
diff --git a/wifi/api/system-lint-baseline.txt b/wifi/api/system-lint-baseline.txt
index 6547ee8..a5f3f7c 100644
--- a/wifi/api/system-lint-baseline.txt
+++ b/wifi/api/system-lint-baseline.txt
@@ -1,6 +1,11 @@
 // Baseline format: 1.0
 MissingGetterMatchingBuilder: android.net.wifi.rtt.RangingRequest.Builder#addResponder(android.net.wifi.rtt.ResponderConfig):
-    android.net.wifi.rtt.RangingRequest does not declare a `getResponders()` method matching method android.net.wifi.rtt.RangingRequest.Builder.addResponder(android.net.wifi.rtt.ResponderConfig)
+
+
 
 MissingNullability: android.net.wifi.rtt.RangingRequest.Builder#addResponder(android.net.wifi.rtt.ResponderConfig):
 
+
+
+MutableBareField: android.net.wifi.WifiConfiguration#subscriptionId:
+    Bare field subscriptionId must be marked final, or moved behind accessors if mutable
diff --git a/wifi/jarjar-rules.txt b/wifi/jarjar-rules.txt
index e253ae2..eef08b5 100644
--- a/wifi/jarjar-rules.txt
+++ b/wifi/jarjar-rules.txt
@@ -70,7 +70,6 @@
 rule android.net.util.InterfaceParams* com.android.wifi.x.@0
 rule android.net.util.SharedLog* com.android.wifi.x.@0
 rule android.net.util.NetUtils* com.android.wifi.x.@0
-rule android.net.util.IpUtils* com.android.wifi.x.@0
 
 rule androidx.annotation.** com.android.wifi.x.@0
 
diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java
index 237922a..2649b66c 100644
--- a/wifi/java/android/net/wifi/SoftApConfiguration.java
+++ b/wifi/java/android/net/wifi/SoftApConfiguration.java
@@ -90,6 +90,9 @@
      * Device is allowed to choose the optimal band (2Ghz, 5Ghz, 6Ghz) based on device capability,
      * operating country code and current radio conditions.
      * @hide
+     *
+     * @deprecated The bands are a bit mask - use any combination of {@code BAND_},
+     * for instance {@code BAND_2GHZ | BAND_5GHZ | BAND_6GHZ}.
      */
     @SystemApi
     public static final int BAND_ANY = BAND_2GHZ | BAND_5GHZ | BAND_6GHZ;
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index fc6c59a..fd4e1dd 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -35,6 +35,8 @@
 import android.os.Parcelable;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
@@ -881,6 +883,14 @@
     public int carrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
 
     /**
+     * The subscription ID identifies the SIM card for which this network configuration is valid.
+     * See {@link SubscriptionInfo#getSubscriptionId()}
+     * @hide
+     */
+    @SystemApi
+    public int subscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+    /**
      * @hide
      * Auto-join is allowed by user for this network.
      * Default true.
@@ -1616,6 +1626,14 @@
         private boolean mHasEverConnected;
 
         /**
+         * Boolean indicating if captive portal has never been detected on this network.
+         *
+         * This should be true by default, for newly created WifiConfigurations until a captive
+         * portal is detected.
+         */
+        private boolean mHasNeverDetectedCaptivePortal = true;
+
+        /**
          * set whether this network is visible in latest Qualified Network Selection
          * @param seen value set to candidate
          * @hide
@@ -1704,6 +1722,19 @@
             return mHasEverConnected;
         }
 
+        /**
+         * Set whether a captive portal has never been detected on this network.
+         * @hide
+         */
+        public void setHasNeverDetectedCaptivePortal(boolean value) {
+            mHasNeverDetectedCaptivePortal = value;
+        }
+
+        /** @hide */
+        public boolean hasNeverDetectedCaptivePortal() {
+            return mHasNeverDetectedCaptivePortal;
+        }
+
         /** @hide */
         public NetworkSelectionStatus() {
             // previously stored configs will not have this parameter, so we default to false.
@@ -1979,6 +2010,7 @@
             setCandidateScore(source.getCandidateScore());
             setConnectChoice(source.getConnectChoice());
             setHasEverConnected(source.hasEverConnected());
+            setHasNeverDetectedCaptivePortal(source.hasNeverDetectedCaptivePortal());
         }
 
         /** @hide */
@@ -1998,6 +2030,7 @@
                 dest.writeInt(CONNECT_CHOICE_NOT_EXISTS);
             }
             dest.writeInt(hasEverConnected() ? 1 : 0);
+            dest.writeInt(hasNeverDetectedCaptivePortal() ? 1 : 0);
         }
 
         /** @hide */
@@ -2016,6 +2049,7 @@
                 setConnectChoice(null);
             }
             setHasEverConnected(in.readInt() != 0);
+            setHasNeverDetectedCaptivePortal(in.readInt() != 0);
         }
     }
 
@@ -2277,6 +2311,8 @@
         }
         sbuf.append(" hasEverConnected: ")
                 .append(mNetworkSelectionStatus.hasEverConnected()).append("\n");
+        sbuf.append(" hasNeverDetectedCaptivePortal: ")
+                .append(mNetworkSelectionStatus.hasNeverDetectedCaptivePortal()).append("\n");
 
         if (this.numAssociation > 0) {
             sbuf.append(" numAssociation ").append(this.numAssociation).append("\n");
@@ -2617,6 +2653,25 @@
         return key;
     }
 
+    /**
+     * Get a key for this WifiConfig to generate Persist random Mac Address.
+     * @hide
+     */
+    public String getMacRandomKey() {
+        // Passpoint ephemeral networks have their unique identifier set. Return it as is to be
+        // able to match internally.
+        if (mPasspointUniqueId != null) {
+            return mPasspointUniqueId;
+        }
+
+        String key = getSsidAndSecurityTypeString();
+        if (!shared) {
+            key += "-" + UserHandle.getUserHandleForUid(creatorUid).getIdentifier();
+        }
+
+        return key;
+    }
+
     /** @hide
      *  return the SSID + security type in String format.
      */
@@ -2871,6 +2926,7 @@
             requirePmf = source.requirePmf;
             updateIdentifier = source.updateIdentifier;
             carrierId = source.carrierId;
+            subscriptionId = source.subscriptionId;
             mPasspointUniqueId = source.mPasspointUniqueId;
         }
     }
@@ -2946,6 +3002,7 @@
         dest.writeLong(randomizedMacLastModifiedTimeMs);
         dest.writeInt(carrierId);
         dest.writeString(mPasspointUniqueId);
+        dest.writeInt(subscriptionId);
     }
 
     /** Implement the Parcelable interface {@hide} */
@@ -3022,6 +3079,7 @@
                 config.randomizedMacLastModifiedTimeMs = in.readLong();
                 config.carrierId = in.readInt();
                 config.mPasspointUniqueId = in.readString();
+                config.subscriptionId = in.readInt();
                 return config;
             }
 
diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
index 9162c5f..c1f9005 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
@@ -29,6 +29,8 @@
 import android.net.wifi.util.SdkLevelUtil;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 
@@ -121,6 +123,12 @@
         private int mCarrierId;
 
         /**
+         * The Subscription ID identifies the SIM card for which this network configuration is
+         * valid.
+         */
+        private int mSubscriptionId;
+
+        /**
          * Whether this network is shared credential with user to allow user manually connect.
          */
         private boolean mIsSharedWithUser;
@@ -185,6 +193,7 @@
             mIsNetworkOemPaid = false;
             mPriorityGroup = 0;
             mIsEnhancedMacRandomizationEnabled = false;
+            mSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
         }
 
         /**
@@ -353,6 +362,27 @@
         }
 
         /**
+         * Set the subscription ID of the SIM card for which this suggestion is targeted.
+         * The suggestion will only apply to that SIM card.
+         * <p>
+         * The subscription ID must belong to a carrier ID which meets either of the following
+         * conditions:
+         * <li>The carrier ID specified by the cross carrier provider, or</li>
+         * <li>The carrier ID which is used to validate the suggesting carrier-privileged app, see
+         * {@link TelephonyManager#hasCarrierPrivileges()}</li>
+         *
+         * @param subId subscription ID see {@link SubscriptionInfo#getSubscriptionId()}
+         * @return Instance of {@link Builder} to enable chaining of the builder method.
+         */
+        public @NonNull Builder setSubscriptionId(int subId) {
+            if (!SdkLevelUtil.isAtLeastS()) {
+                throw new UnsupportedOperationException();
+            }
+            mSubscriptionId = subId;
+            return this;
+        }
+
+        /**
          * Set the priority group ID, {@link #setPriority(int)} will only impact the network
          * suggestions from the same priority group within the same app.
          *
@@ -360,6 +390,9 @@
          * @return Instance of {@link Builder} to enable chaining of the builder method.
          */
         public @NonNull Builder setPriorityGroup(int priorityGroup) {
+            if (!SdkLevelUtil.isAtLeastS()) {
+                throw new UnsupportedOperationException();
+            }
             mPriorityGroup = priorityGroup;
             return this;
         }
@@ -675,6 +708,7 @@
             wifiConfiguration.macRandomizationSetting = mIsEnhancedMacRandomizationEnabled
                     ? WifiConfiguration.RANDOMIZATION_ENHANCED
                     : WifiConfiguration.RANDOMIZATION_PERSISTENT;
+            wifiConfiguration.subscriptionId = mSubscriptionId;
             return wifiConfiguration;
         }
 
@@ -704,7 +738,9 @@
             wifiConfiguration.meteredOverride = mMeteredOverride;
             wifiConfiguration.trusted = !mIsNetworkUntrusted;
             wifiConfiguration.oemPaid = mIsNetworkOemPaid;
+            wifiConfiguration.subscriptionId = mSubscriptionId;
             mPasspointConfiguration.setCarrierId(mCarrierId);
+            mPasspointConfiguration.setSubscriptionId(mSubscriptionId);
             mPasspointConfiguration.setMeteredOverride(wifiConfiguration.meteredOverride);
             wifiConfiguration.macRandomizationSetting = mIsEnhancedMacRandomizationEnabled
                     ? WifiConfiguration.RANDOMIZATION_ENHANCED
@@ -946,7 +982,8 @@
     @Override
     public int hashCode() {
         return Objects.hash(wifiConfiguration.SSID, wifiConfiguration.BSSID,
-                wifiConfiguration.allowedKeyManagement, wifiConfiguration.getKey());
+                wifiConfiguration.allowedKeyManagement, wifiConfiguration.getKey(),
+                wifiConfiguration.subscriptionId, wifiConfiguration.carrierId);
     }
 
     /**
@@ -970,7 +1007,9 @@
                 && Objects.equals(this.wifiConfiguration.allowedKeyManagement,
                 lhs.wifiConfiguration.allowedKeyManagement)
                 && TextUtils.equals(this.wifiConfiguration.getKey(),
-                lhs.wifiConfiguration.getKey());
+                lhs.wifiConfiguration.getKey())
+                && this.wifiConfiguration.carrierId == lhs.wifiConfiguration.carrierId
+                && this.wifiConfiguration.subscriptionId == lhs.wifiConfiguration.subscriptionId;
     }
 
     @Override
@@ -1123,6 +1162,19 @@
      * @see Builder#setPriorityGroup(int)
      */
     public int getPriorityGroup() {
+        if (!SdkLevelUtil.isAtLeastS()) {
+            throw new UnsupportedOperationException();
+        }
         return priorityGroup;
     }
+
+    /**
+     * @see Builder#setSubscriptionId(int)
+     */
+    public int getSubscriptionId() {
+        if (!SdkLevelUtil.isAtLeastS()) {
+            throw new UnsupportedOperationException();
+        }
+        return wifiConfiguration.subscriptionId;
+    }
 }
diff --git a/wifi/java/android/net/wifi/aware/Characteristics.java b/wifi/java/android/net/wifi/aware/Characteristics.java
index d5fd48e..1167865 100644
--- a/wifi/java/android/net/wifi/aware/Characteristics.java
+++ b/wifi/java/android/net/wifi/aware/Characteristics.java
@@ -17,6 +17,7 @@
 package android.net.wifi.aware;
 
 import android.annotation.IntDef;
+import android.net.wifi.util.SdkLevelUtil;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -37,6 +38,9 @@
     public static final String KEY_MAX_MATCH_FILTER_LENGTH = "key_max_match_filter_length";
     /** @hide */
     public static final String KEY_SUPPORTED_CIPHER_SUITES = "key_supported_cipher_suites";
+    /** @hide */
+    public static final String KEY_IS_INSTANT_COMMUNICATION_MODE_SUPPORTED =
+            "key_is_instant_communication_mode_supported";
 
     private Bundle mCharacteristics = new Bundle();
 
@@ -83,6 +87,17 @@
         return mCharacteristics.getInt(KEY_MAX_MATCH_FILTER_LENGTH);
     }
 
+    /**
+     * Check if instant communication mode is supported by device.
+     * @return True if supported, false otherwise.
+     */
+    public boolean isInstantCommunicationModeSupported() {
+        if (!SdkLevelUtil.isAtLeastS()) {
+            throw new UnsupportedOperationException();
+        }
+        return mCharacteristics.getBoolean(KEY_IS_INSTANT_COMMUNICATION_MODE_SUPPORTED);
+    }
+
     /** @hide */
     @IntDef(flag = true, prefix = { "WIFI_AWARE_CIPHER_SUITE_" }, value = {
             WIFI_AWARE_CIPHER_SUITE_NCS_SK_128,
diff --git a/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java b/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
index e3800ad..da8e17e 100644
--- a/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
+++ b/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
@@ -191,14 +191,18 @@
     }
 
     /**
-     * Called when the discovered peer is no longer visible. All further operations on this
-     * discovery session will fail. If the peer is visible again,
+     * Called when the discovered service is not available. All further operations on this
+     * discovery session will fail. If the service is available again,
      * {@link #onServiceDiscovered(PeerHandle, byte[], List)} or
      * {@link #onServiceDiscoveredWithinRange(PeerHandle, byte[], List, int)} will be called.
      *
      * @param peerHandle An opaque handle to the peer matching our discovery operation.
+     * @param reason Discovered service lost reason code. One of
+     *               {@link WifiAwareManager#WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE},
+     *               {@link WifiAwareManager#WIFI_AWARE_DISCOVERY_LOST_REASON_UNKNOWN
      */
-    public void onServiceLost(@NonNull PeerHandle peerHandle) {
+    public void onServiceLost(@NonNull PeerHandle peerHandle,
+            @WifiAwareManager.DiscoveryLostReasonCode int reason) {
         /* empty */
     }
 }
diff --git a/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl b/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl
index f5b1edc..cd2ca69 100644
--- a/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl
+++ b/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl
@@ -37,6 +37,8 @@
     boolean isUsageEnabled();
     Characteristics getCharacteristics();
     boolean isDeviceAttached();
+    void enableInstantCommunicationMode(in String callingPackage, boolean enable);
+    boolean isInstantCommunicationModeEnabled();
 
     // client API
     void connect(in IBinder binder, in String callingPackage, in String callingFeatureId,
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
index d6e46fd..bb146e3 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
@@ -22,12 +22,14 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.NetworkRequest;
 import android.net.NetworkSpecifier;
 import android.net.wifi.util.HexEncoding;
+import android.net.wifi.util.SdkLevelUtil;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
@@ -151,6 +153,27 @@
      */
     public static final int WIFI_AWARE_DATA_PATH_ROLE_RESPONDER = 1;
 
+    /** @hide */
+    @IntDef({
+            WIFI_AWARE_DISCOVERY_LOST_REASON_UNKNOWN,
+            WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DiscoveryLostReasonCode {
+    }
+
+    /**
+     * Reason code provided in {@link DiscoverySessionCallback#onServiceLost(PeerHandle, int)}
+     * indicating that the service was lost for unknown reason.
+     */
+    public static final int WIFI_AWARE_DISCOVERY_LOST_REASON_UNKNOWN = 0;
+
+    /**
+     * Reason code provided in {@link DiscoverySessionCallback#onServiceLost(PeerHandle, int)}
+     * indicating that the service advertised by the peer is no longer visible. This may be because
+     * the peer is out of range or because the peer stopped advertising this service.
+     */
+    public static final int WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE = 1;
+
     private final Context mContext;
     private final IWifiAwareManager mService;
 
@@ -187,6 +210,9 @@
      *         or not (false).
      */
     public boolean isDeviceAttached() {
+        if (!SdkLevelUtil.isAtLeastS()) {
+            throw new UnsupportedOperationException();
+        }
         try {
             return mService.isDeviceAttached();
         } catch (RemoteException e) {
@@ -195,6 +221,42 @@
     }
 
     /**
+     * Enable the Wifi Aware Instant communication mode. If the device doesn't support this feature
+     * calling this API will result no action.
+     * @see Characteristics#isInstantCommunicationModeSupported()
+     * @param enable true for enable, false otherwise.
+     * @hide
+     */
+    @SystemApi
+    public void enableInstantCommunicationMode(boolean enable) {
+        if (!SdkLevelUtil.isAtLeastS()) {
+            throw new UnsupportedOperationException();
+        }
+        try {
+            mService.enableInstantCommunicationMode(mContext.getOpPackageName(), enable);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return the current status of the Wifi Aware instant communication mode.
+     * If the device doesn't support this feature, return will always be false.
+     * @see Characteristics#isInstantCommunicationModeSupported()
+     * @return true if it is enabled, false otherwise.
+     */
+    public boolean isInstantCommunicationModeEnabled() {
+        if (!SdkLevelUtil.isAtLeastS()) {
+            throw new UnsupportedOperationException();
+        }
+        try {
+            return mService.isInstantCommunicationModeEnabled();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Returns the characteristics of the Wi-Fi Aware interface: a set of parameters which specify
      * limitations on configurations, e.g. the maximum service name length.
      *
@@ -695,7 +757,8 @@
                             break;
                         case CALLBACK_MATCH_EXPIRED:
                             mOriginalCallback
-                                    .onServiceLost(new PeerHandle(msg.arg1));
+                                    .onServiceLost(new PeerHandle(msg.arg1),
+                                            WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE);
                     }
                 }
             };
diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
index 61a6e16..357c5bc 100644
--- a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
+++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
@@ -30,6 +30,8 @@
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
@@ -412,6 +414,12 @@
     private int mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
 
     /**
+     * The subscription ID identifies the SIM card who provides this network configuration.
+     * See {@link SubscriptionInfo#getSubscriptionId()}
+     */
+    private int mSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+    /**
      * Set the carrier ID associated with current configuration.
      * @param carrierId {@code mCarrierId}
      * @hide
@@ -430,6 +438,24 @@
     }
 
     /**
+     * Set the subscription ID associated with current configuration.
+     * @param subscriptionId {@code mSubscriptionId}
+     * @hide
+     */
+    public void setSubscriptionId(int subscriptionId) {
+        this.mSubscriptionId = subscriptionId;
+    }
+
+    /**
+     * Get the carrier ID associated with current configuration.
+     * @return {@code mSubscriptionId}
+     * @hide
+     */
+    public int getSubscriptionId() {
+        return mSubscriptionId;
+    }
+
+    /**
      * The auto-join configuration specifies whether or not the Passpoint Configuration is
      * considered for auto-connection. If true then yes, if false then it isn't considered as part
      * of auto-connection - but can still be manually connected to.
@@ -604,6 +630,7 @@
         mServiceFriendlyNames = source.mServiceFriendlyNames;
         mAaaServerTrustedNames = source.mAaaServerTrustedNames;
         mCarrierId = source.mCarrierId;
+        mSubscriptionId = source.mSubscriptionId;
         mIsAutojoinEnabled = source.mIsAutojoinEnabled;
         mIsMacRandomizationEnabled = source.mIsMacRandomizationEnabled;
         mIsEnhancedMacRandomizationEnabled = source.mIsEnhancedMacRandomizationEnabled;
@@ -641,6 +668,7 @@
         dest.writeBoolean(mIsMacRandomizationEnabled);
         dest.writeBoolean(mIsEnhancedMacRandomizationEnabled);
         dest.writeInt(mMeteredOverride);
+        dest.writeInt(mSubscriptionId);
     }
 
     @Override
@@ -671,6 +699,7 @@
                 && mUsageLimitDataLimit == that.mUsageLimitDataLimit
                 && mUsageLimitTimeLimitInMinutes == that.mUsageLimitTimeLimitInMinutes
                 && mCarrierId == that.mCarrierId
+                && mSubscriptionId == that.mSubscriptionId
                 && mIsAutojoinEnabled == that.mIsAutojoinEnabled
                 && mIsMacRandomizationEnabled == that.mIsMacRandomizationEnabled
                 && mIsEnhancedMacRandomizationEnabled == that.mIsEnhancedMacRandomizationEnabled
@@ -686,7 +715,7 @@
                 mSubscriptionExpirationTimeMillis, mUsageLimitUsageTimePeriodInMinutes,
                 mUsageLimitStartTimeInMillis, mUsageLimitDataLimit, mUsageLimitTimeLimitInMinutes,
                 mServiceFriendlyNames, mCarrierId, mIsAutojoinEnabled, mIsMacRandomizationEnabled,
-                mIsEnhancedMacRandomizationEnabled, mMeteredOverride);
+                mIsEnhancedMacRandomizationEnabled, mMeteredOverride, mSubscriptionId);
     }
 
     @Override
@@ -740,6 +769,7 @@
             builder.append("ServiceFriendlyNames: ").append(mServiceFriendlyNames);
         }
         builder.append("CarrierId:" + mCarrierId);
+        builder.append("SubscriptionId:" + mSubscriptionId);
         builder.append("IsAutojoinEnabled:" + mIsAutojoinEnabled);
         builder.append("mIsMacRandomizationEnabled:" + mIsMacRandomizationEnabled);
         builder.append("mIsEnhancedMacRandomizationEnabled:" + mIsEnhancedMacRandomizationEnabled);
@@ -853,6 +883,7 @@
                 config.mIsMacRandomizationEnabled = in.readBoolean();
                 config.mIsEnhancedMacRandomizationEnabled = in.readBoolean();
                 config.mMeteredOverride = in.readInt();
+                config.mSubscriptionId = in.readInt();
                 return config;
             }
 
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java b/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java
index 35a8ff6..64aad61 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java
+++ b/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java
@@ -16,6 +16,7 @@
 
 package android.net.wifi.hotspot2.pps;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -24,6 +25,7 @@
 
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
@@ -214,23 +216,52 @@
      *
      * @param otherHomePartners Array of Strings containing the FQDNs of other Home partner
      *                         providers
+     * @hide
      */
     public void setOtherHomePartners(@Nullable String[] otherHomePartners) {
         mOtherHomePartners = otherHomePartners;
     }
 
     /**
+     * Set the list of FQDN (Fully Qualified Domain Name) of other Home partner providers.
+     *
+     * @param otherHomePartners Collection of Strings containing the FQDNs of other Home partner
+     *                         providers
+     */
+    public void setOtherHomePartnersList(@NonNull Collection<String> otherHomePartners) {
+        if (otherHomePartners == null) {
+            return;
+        }
+        mOtherHomePartners = otherHomePartners.toArray(new String[otherHomePartners.size()]);
+    }
+
+    /**
      * Get the list of FQDN (Fully Qualified Domain Name) of other Home partner providers set in
      * the profile.
      *
      * @return Array of Strings containing the FQDNs of other Home partner providers set in the
      * profile
+     * @hide
      */
     public @Nullable String[] getOtherHomePartners() {
         return mOtherHomePartners;
     }
 
     /**
+     * Get the list of FQDN (Fully Qualified Domain Name) of other Home partner providers set in
+     * the profile.
+     *
+     * @return Collection of Strings containing the FQDNs of other Home partner providers set in the
+     * profile
+     */
+    public @NonNull Collection<String> getOtherHomePartnersList() {
+        if (mOtherHomePartners == null) {
+            return Collections.emptyList();
+        }
+        return Arrays.asList(mOtherHomePartners);
+    }
+
+    /**
      * List of Organization Identifiers (OIs) identifying a roaming consortium of
      * which this provider is a member.
      */
diff --git a/wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java b/wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java
index fb58d3f..91d25f9 100644
--- a/wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java
+++ b/wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java
@@ -153,15 +153,23 @@
         @Override
         public void OnScanResultReady() {
             Log.d(TAG, "Scan result ready event");
-            Binder.clearCallingIdentity();
-            mExecutor.execute(() -> mCallback.onScanResultReady());
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onScanResultReady());
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
         }
 
         @Override
         public void OnScanFailed() {
             Log.d(TAG, "Scan failed event");
-            Binder.clearCallingIdentity();
-            mExecutor.execute(() -> mCallback.onScanFailed());
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onScanFailed());
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
         }
     }
 
@@ -345,15 +353,23 @@
         @Override
         public void OnPnoNetworkFound() {
             Log.d(TAG, "Pno scan result event");
-            Binder.clearCallingIdentity();
-            mExecutor.execute(() -> mCallback.onScanResultReady());
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onScanResultReady());
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
         }
 
         @Override
         public void OnPnoScanFailed() {
             Log.d(TAG, "Pno Scan failed event");
-            Binder.clearCallingIdentity();
-            mExecutor.execute(() -> mCallback.onScanFailed());
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onScanFailed());
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
         }
     }
 
@@ -376,15 +392,24 @@
                         + client.getMacAddress() + " isConnected: " + isConnected);
             }
 
-            Binder.clearCallingIdentity();
-            mExecutor.execute(() -> mSoftApListener.onConnectedClientsChanged(client, isConnected));
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(
+                        () -> mSoftApListener.onConnectedClientsChanged(client, isConnected));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
         }
 
         @Override
         public void onSoftApChannelSwitched(int frequency, int bandwidth) {
-            Binder.clearCallingIdentity();
-            mExecutor.execute(() -> mSoftApListener.onSoftApChannelSwitched(frequency,
-                    toFrameworkBandwidth(bandwidth)));
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mSoftApListener.onSoftApChannelSwitched(frequency,
+                        toFrameworkBandwidth(bandwidth)));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
         }
 
         private @WifiAnnotations.Bandwidth int toFrameworkBandwidth(int bandwidth) {
@@ -437,8 +462,12 @@
                 if (mVerboseLoggingEnabled) {
                     Log.e(TAG, "Timed out waiting for ACK");
                 }
-                Binder.clearCallingIdentity();
-                mExecutor.execute(() -> mCallback.onFailure(SEND_MGMT_FRAME_ERROR_TIMEOUT));
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    mExecutor.execute(() -> mCallback.onFailure(SEND_MGMT_FRAME_ERROR_TIMEOUT));
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             });
             mWasCalled = false;
 
@@ -453,8 +482,12 @@
             // post to main thread
             mEventHandler.post(() -> runIfFirstCall(() -> {
                 mAlarmManager.cancel(mTimeoutCallback);
-                Binder.clearCallingIdentity();
-                mExecutor.execute(() -> mCallback.onAck(elapsedTimeMs));
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    mExecutor.execute(() -> mCallback.onAck(elapsedTimeMs));
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }));
         }
 
@@ -464,8 +497,12 @@
             // post to main thread
             mEventHandler.post(() -> runIfFirstCall(() -> {
                 mAlarmManager.cancel(mTimeoutCallback);
-                Binder.clearCallingIdentity();
-                mExecutor.execute(() -> mCallback.onFailure(reason));
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    mExecutor.execute(() -> mCallback.onFailure(reason));
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }));
         }
     }
diff --git a/wifi/tests/src/android/net/wifi/SoftApCapabilityTest.java b/wifi/tests/src/android/net/wifi/SoftApCapabilityTest.java
index b9c640c..3c93505 100644
--- a/wifi/tests/src/android/net/wifi/SoftApCapabilityTest.java
+++ b/wifi/tests/src/android/net/wifi/SoftApCapabilityTest.java
@@ -16,10 +16,11 @@
 
 package android.net.wifi;
 
-import android.os.Build;
+import android.net.wifi.util.SdkLevelUtil;
 import android.os.Parcel;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
 
 import androidx.test.filters.SmallTest;
 
@@ -92,14 +93,12 @@
 
     @Test(expected = IllegalArgumentException.class)
     public void testGetSupportedChannelListWithInvalidBand() {
+        assumeTrue(SdkLevelUtil.isAtLeastS());
+
         long testSoftApFeature = SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT
                 | SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD;
         SoftApCapability capability = new SoftApCapability(testSoftApFeature);
-        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) {
-            capability.getSupportedChannelList(
-                    SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ);
-        } else {
-            throw new IllegalArgumentException("API doesn't support in current SDK version");
-        }
+        capability.getSupportedChannelList(
+                SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ);
     }
 }
diff --git a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
index 254434b..7877ea1 100644
--- a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
@@ -21,7 +21,7 @@
 import static org.junit.Assert.assertNull;
 
 import android.net.MacAddress;
-import android.os.Build;
+import android.net.wifi.util.SdkLevelUtil;
 import android.os.Parcel;
 
 import androidx.test.filters.SmallTest;
@@ -81,18 +81,18 @@
         assertThat(original.getChannel()).isEqualTo(0);
         assertThat(original.isHiddenSsid()).isEqualTo(false);
         assertThat(original.getMaxNumberOfClients()).isEqualTo(0);
-        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) {
+        if (SdkLevelUtil.isAtLeastS()) {
             assertThat(original.getMacRandomizationSetting())
                     .isEqualTo(SoftApConfiguration.RANDOMIZATION_PERSISTENT);
         }
 
         SoftApConfiguration unparceled = parcelUnparcel(original);
-        assertThat(unparceled).isNotSameAs(original);
+        assertThat(unparceled).isNotSameInstanceAs(original);
         assertThat(unparceled).isEqualTo(original);
         assertThat(unparceled.hashCode()).isEqualTo(original.hashCode());
 
         SoftApConfiguration copy = new SoftApConfiguration.Builder(original).build();
-        assertThat(copy).isNotSameAs(original);
+        assertThat(copy).isNotSameInstanceAs(original);
         assertThat(copy).isEqualTo(original);
         assertThat(copy.hashCode()).isEqualTo(original.hashCode());
     }
@@ -111,12 +111,12 @@
         assertThat(original.getMaxNumberOfClients()).isEqualTo(0);
 
         SoftApConfiguration unparceled = parcelUnparcel(original);
-        assertThat(unparceled).isNotSameAs(original);
+        assertThat(unparceled).isNotSameInstanceAs(original);
         assertThat(unparceled).isEqualTo(original);
         assertThat(unparceled.hashCode()).isEqualTo(original.hashCode());
 
         SoftApConfiguration copy = new SoftApConfiguration.Builder(original).build();
-        assertThat(copy).isNotSameAs(original);
+        assertThat(copy).isNotSameInstanceAs(original);
         assertThat(copy).isEqualTo(original);
         assertThat(copy.hashCode()).isEqualTo(original.hashCode());
     }
@@ -137,7 +137,7 @@
                 .setClientControlByUserEnabled(true)
                 .setBlockedClientList(testBlockedClientList)
                 .setAllowedClientList(testAllowedClientList);
-        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) {
+        if (SdkLevelUtil.isAtLeastS()) {
             originalBuilder.setMacRandomizationSetting(SoftApConfiguration.RANDOMIZATION_NONE);
         }
         SoftApConfiguration original = originalBuilder.build();
@@ -153,18 +153,18 @@
         assertThat(original.isClientControlByUserEnabled()).isEqualTo(true);
         assertThat(original.getBlockedClientList()).isEqualTo(testBlockedClientList);
         assertThat(original.getAllowedClientList()).isEqualTo(testAllowedClientList);
-        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) {
+        if (SdkLevelUtil.isAtLeastS()) {
             assertThat(original.getMacRandomizationSetting())
                     .isEqualTo(SoftApConfiguration.RANDOMIZATION_NONE);
         }
 
         SoftApConfiguration unparceled = parcelUnparcel(original);
-        assertThat(unparceled).isNotSameAs(original);
+        assertThat(unparceled).isNotSameInstanceAs(original);
         assertThat(unparceled).isEqualTo(original);
         assertThat(unparceled.hashCode()).isEqualTo(original.hashCode());
 
         SoftApConfiguration copy = new SoftApConfiguration.Builder(original).build();
-        assertThat(copy).isNotSameAs(original);
+        assertThat(copy).isNotSameInstanceAs(original);
         assertThat(copy).isEqualTo(original);
         assertThat(copy.hashCode()).isEqualTo(original.hashCode());
     }
@@ -185,12 +185,12 @@
 
 
         SoftApConfiguration unparceled = parcelUnparcel(original);
-        assertThat(unparceled).isNotSameAs(original);
+        assertThat(unparceled).isNotSameInstanceAs(original);
         assertThat(unparceled).isEqualTo(original);
         assertThat(unparceled.hashCode()).isEqualTo(original.hashCode());
 
         SoftApConfiguration copy = new SoftApConfiguration.Builder(original).build();
-        assertThat(copy).isNotSameAs(original);
+        assertThat(copy).isNotSameInstanceAs(original);
         assertThat(copy).isEqualTo(original);
         assertThat(copy.hashCode()).isEqualTo(original.hashCode());
     }
@@ -212,12 +212,12 @@
 
 
         SoftApConfiguration unparceled = parcelUnparcel(original);
-        assertThat(unparceled).isNotSameAs(original);
+        assertThat(unparceled).isNotSameInstanceAs(original);
         assertThat(unparceled).isEqualTo(original);
         assertThat(unparceled.hashCode()).isEqualTo(original.hashCode());
 
         SoftApConfiguration copy = new SoftApConfiguration.Builder(original).build();
-        assertThat(copy).isNotSameAs(original);
+        assertThat(copy).isNotSameInstanceAs(original);
         assertThat(copy).isEqualTo(original);
         assertThat(copy.hashCode()).isEqualTo(original.hashCode());
     }
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index 8af7500..b82c67b 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -48,6 +48,7 @@
  */
 @SmallTest
 public class WifiConfigurationTest {
+    private static final String TEST_PASSPOINT_UNIQUE_ID = "uniqueId";
 
     @Before
     public void setUp() {
@@ -73,6 +74,8 @@
         config.fromWifiNetworkSuggestion = true;
         config.setRandomizedMacAddress(MacAddressUtils.createRandomUnicastAddress());
         MacAddress macBeforeParcel = config.getRandomizedMacAddress();
+        config.subscriptionId = 1;
+        config.carrierId = 1189;
         Parcel parcelW = Parcel.obtain();
         config.writeToParcel(parcelW, 0);
         byte[] bytes = parcelW.marshall();
@@ -389,6 +392,79 @@
     }
 
     /**
+     * Verifies that getMacRandomKey returns the correct String for networks of
+     * various different security types, the result should be stable.
+     */
+    @Test
+    public void testGetMacRandomKeyString() {
+        WifiConfiguration config = new WifiConfiguration();
+        final String mSsid = "TestAP";
+        config.SSID = mSsid;
+
+        // Test various combinations
+        config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
+        assertEquals(mSsid + KeyMgmt.strings[KeyMgmt.WPA_PSK],
+                config.getMacRandomKey());
+
+        config.allowedKeyManagement.clear();
+        config.allowedKeyManagement.set(KeyMgmt.WPA_EAP);
+        assertEquals(mSsid + KeyMgmt.strings[KeyMgmt.WPA_EAP],
+                config.getMacRandomKey());
+
+        config.wepKeys[0] = "TestWep";
+        config.allowedKeyManagement.clear();
+        assertEquals(mSsid + "WEP", config.getMacRandomKey());
+
+        // set WEP key and give a valid index.
+        config.wepKeys[0] = null;
+        config.wepKeys[2] = "TestWep";
+        config.wepTxKeyIndex = 2;
+        config.allowedKeyManagement.clear();
+        assertEquals(mSsid + "WEP", config.getMacRandomKey());
+
+        // set WEP key but does not give a valid index.
+        config.wepKeys[0] = null;
+        config.wepKeys[2] = "TestWep";
+        config.wepTxKeyIndex = 0;
+        config.allowedKeyManagement.clear();
+        config.allowedKeyManagement.set(KeyMgmt.OWE);
+        assertEquals(mSsid + KeyMgmt.strings[KeyMgmt.OWE], config.getMacRandomKey());
+
+        config.wepKeys[0] = null;
+        config.wepTxKeyIndex = 0;
+        config.allowedKeyManagement.clear();
+        config.allowedKeyManagement.set(KeyMgmt.OWE);
+        assertEquals(mSsid + KeyMgmt.strings[KeyMgmt.OWE], config.getMacRandomKey());
+
+        config.allowedKeyManagement.clear();
+        config.allowedKeyManagement.set(KeyMgmt.SAE);
+        assertEquals(mSsid + KeyMgmt.strings[KeyMgmt.SAE], config.getMacRandomKey());
+
+        config.allowedKeyManagement.clear();
+        config.allowedKeyManagement.set(KeyMgmt.SUITE_B_192);
+        assertEquals(mSsid + KeyMgmt.strings[KeyMgmt.SUITE_B_192],
+                config.getMacRandomKey());
+
+        config.allowedKeyManagement.clear();
+        config.allowedKeyManagement.set(KeyMgmt.NONE);
+        assertEquals(mSsid + KeyMgmt.strings[KeyMgmt.NONE], config.getMacRandomKey());
+
+        config.allowedKeyManagement.clear();
+        config.allowedKeyManagement.set(KeyMgmt.WAPI_PSK);
+        assertEquals(mSsid + KeyMgmt.strings[KeyMgmt.WAPI_PSK],
+                config.getMacRandomKey());
+
+        config.allowedKeyManagement.clear();
+        config.allowedKeyManagement.set(KeyMgmt.WAPI_CERT);
+        assertEquals(mSsid + KeyMgmt.strings[KeyMgmt.WAPI_CERT],
+                config.getMacRandomKey());
+
+        config.allowedKeyManagement.clear();
+        config.setPasspointUniqueId(TEST_PASSPOINT_UNIQUE_ID);
+        assertEquals(TEST_PASSPOINT_UNIQUE_ID, config.getMacRandomKey());
+    }
+
+    /**
      * Ensure that the {@link NetworkSelectionStatus.DisableReasonInfo}s are populated in
      * {@link NetworkSelectionStatus#DISABLE_REASON_INFOS} for reason codes from 0 to
      * {@link NetworkSelectionStatus#NETWORK_SELECTION_DISABLED_MAX} - 1.
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
index 6e08ca4..3f9ce5b 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
@@ -764,6 +764,32 @@
     }
 
     /**
+     * Verify that the builder creates the appropriate SIM credential suggestion with SubId, also
+     * verify {@link WifiNetworkSuggestion#equals(Object)} consider suggestion with different SubId
+     * as different suggestions.
+     */
+    @Test
+    public void testSimCredentialNetworkWithSubId() {
+        assumeTrue(SdkLevelUtil.isAtLeastS());
+        WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
+        enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.SIM);
+        enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.NONE);
+        WifiNetworkSuggestion suggestion1 = new WifiNetworkSuggestion.Builder()
+                .setSsid(TEST_SSID)
+                .setWpa2EnterpriseConfig(enterpriseConfig)
+                .setSubscriptionId(1)
+                .build();
+        assertEquals(1, suggestion1.getSubscriptionId());
+        WifiNetworkSuggestion suggestion2 = new WifiNetworkSuggestion.Builder()
+                .setSsid(TEST_SSID)
+                .setWpa2EnterpriseConfig(enterpriseConfig)
+                .setSubscriptionId(2)
+                .build();
+        assertEquals(2, suggestion2.getSubscriptionId());
+        assertNotEquals(suggestion1, suggestion2);
+    }
+
+    /**
      * Check that parcel marshalling/unmarshalling works
      */
     @Test
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
index 5fe0cb4..2cf7f2c 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
@@ -16,11 +16,13 @@
 
 package android.net.wifi.aware;
 
+import static android.net.wifi.aware.WifiAwareManager.WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE;
 import static android.net.wifi.aware.WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB;
 
 import static org.hamcrest.core.IsEqual.equalTo;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
@@ -37,6 +39,7 @@
 import android.net.MacAddress;
 import android.net.wifi.RttManager;
 import android.net.wifi.util.HexEncoding;
+import android.net.wifi.util.SdkLevelUtil;
 import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
@@ -155,6 +158,19 @@
         verify(mockAwareService).isDeviceAttached();
     }
 
+    /**
+     * Validate pass-through of isInstantCommunicationModeEnabled() and
+     * enableInstantCommunicationMode() API
+     */
+    @Test
+    public void testEnableInstantCommunicationMode() throws Exception {
+        assumeTrue(SdkLevelUtil.isAtLeastS());
+        mDut.isInstantCommunicationModeEnabled();
+        verify(mockAwareService).isInstantCommunicationModeEnabled();
+        mDut.enableInstantCommunicationMode(true);
+        verify(mockAwareService).enableInstantCommunicationMode(anyString(), eq(true));
+    }
+
     /*
      * WifiAwareEventCallbackProxy Tests
      */
@@ -372,7 +388,8 @@
         // (5) discovery session is no longer visible
         sessionProxyCallback.getValue().onMatchExpired(peerHandle.peerId);
         mMockLooper.dispatchAll();
-        inOrder.verify(mockSessionCallback).onServiceLost(peerIdCaptor.capture());
+        inOrder.verify(mockSessionCallback).onServiceLost(peerIdCaptor.capture(),
+                eq(WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE));
         assertEquals(peerHandle.peerId, peerIdCaptor.getValue().peerId);
 
         // (6) terminate
@@ -520,7 +537,8 @@
         // (5) discovery session is no longer visible
         sessionProxyCallback.getValue().onMatchExpired(peerHandle.peerId);
         mMockLooper.dispatchAll();
-        inOrder.verify(mockSessionCallback).onServiceLost(peerIdCaptor.capture());
+        inOrder.verify(mockSessionCallback).onServiceLost(peerIdCaptor.capture(),
+                eq(WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE));
         assertEquals(peerHandle.peerId, peerIdCaptor.getValue().peerId);
 
         // (6) terminate
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/PasspointTestUtils.java b/wifi/tests/src/android/net/wifi/hotspot2/PasspointTestUtils.java
index 8d55acb..5830a1e 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/PasspointTestUtils.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/PasspointTestUtils.java
@@ -30,6 +30,7 @@
 
 public class PasspointTestUtils {
     private static final int CERTIFICATE_FINGERPRINT_BYTES = 32;
+    private static final int TEST_SUB_ID = 1;
 
     /**
      * Utility function for creating a {@link android.net.wifi.hotspot2.pps.HomeSP}.
@@ -156,6 +157,7 @@
         friendlyNames.put("en", "ServiceName1");
         friendlyNames.put("kr", "ServiceName2");
         config.setServiceFriendlyNames(friendlyNames);
+        config.setSubscriptionId(TEST_SUB_ID);
         return config;
     }
 
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSpTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSpTest.java
index 93d471a..fe889fc 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSpTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSpTest.java
@@ -16,6 +16,7 @@
 
 package android.net.wifi.hotspot2.pps;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
@@ -26,7 +27,9 @@
 import org.junit.Test;
 
 import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -35,6 +38,7 @@
  */
 @SmallTest
 public class HomeSpTest {
+    private static final String[] OTHER_HOME_PARTNER_LIST = new String[]{"partner1", "partner2"};
 
     /**
      * Helper function for creating a map of home network IDs for testing.
@@ -62,7 +66,7 @@
         homeSp.setHomeNetworkIds(homeNetworkIds);
         homeSp.setMatchAllOis(new long[] {0x11L, 0x22L});
         homeSp.setMatchAnyOis(new long[] {0x33L, 0x44L});
-        homeSp.setOtherHomePartners(new String[] {"partner1", "partner2"});
+        homeSp.setOtherHomePartners(OTHER_HOME_PARTNER_LIST);
         homeSp.setRoamingConsortiumOis(new long[] {0x55, 0x66});
         return homeSp;
     }
@@ -218,4 +222,34 @@
         HomeSp copySp = new HomeSp(sourceSp);
         assertTrue(copySp.equals(sourceSp));
     }
+
+    /**
+     * Verify that the getOtherHomePartnersList gets the list of partners as expected.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void validateGetOtherHomePartnersList() throws Exception {
+        HomeSp homeSp = createHomeSpWithoutHomeNetworkIds();
+
+        Collection<String> otherHomePartnersList = homeSp.getOtherHomePartnersList();
+        assertEquals(2, otherHomePartnersList.size());
+        assertTrue(Arrays.equals(OTHER_HOME_PARTNER_LIST, otherHomePartnersList.toArray()));
+    }
+
+    /**
+     * Verify that the setOtherHomePartnersList sets the list of partners as expected.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void validateSetOtherHomePartnersList() throws Exception {
+        HomeSp homeSp = createHomeSpWithoutHomeNetworkIds();
+
+        final Collection<String> homePartners =
+                new ArrayList<>(Arrays.asList(OTHER_HOME_PARTNER_LIST));
+
+        homeSp.setOtherHomePartnersList(homePartners);
+        assertTrue(Arrays.equals(homeSp.getOtherHomePartners(), OTHER_HOME_PARTNER_LIST));
+    }
 }